diff --git a/.eslintrc.js b/.eslintrc.js index 151e7a7..6504cd2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { "@typescript-eslint/comma-dangle": ["error","always-multiline"], "@typescript-eslint/consistent-type-definitions": "off", "@typescript-eslint/no-unused-vars": "warn", - "@typescript-eslint/no-extraneous-class": "off" + "@typescript-eslint/no-extraneous-class": "off", + "@typescript-eslint/member-delimiter-style": "off" } } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e9bd932..cc98f09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,6 @@ jobs: - name: Build 📦 run: npm run build - # - name: Run tests 🧪 - # run: npm test + - name: Run tests 🧪 + run: npm test diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ccf6887 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "azurite.azurite", + "connor4312.nodejs-testing", + "yoavbls.pretty-ts-errors" + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ba5f839..3aa77f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { "name": "simple-workflows", - "version": "0.1.0-beta15", + "version": "0.1.0-beta16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "simple-workflows", - "version": "0.1.0-beta15", + "version": "0.1.0-beta16", "license": "MIT", "dependencies": { "@azure/data-tables": "^13.0.1", "@azure/storage-blob": "^12.10.0", "async-mutex": "^0.4.0", - "ms": "^2.1.3" + "ms": "^2.1.3", + "nanoid": "^5.0.7" }, "devDependencies": { "@types/ms": "^0.7.31", @@ -23,7 +24,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.5.0", "eslint-plugin-promise": "^6.1.1", - "superjson": "^1.12.1", + "superjson": "^2.2.1", "typescript": "^5.3.3" }, "engines": { @@ -1211,6 +1212,7 @@ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", "dev": true, + "license": "MIT", "dependencies": { "is-what": "^4.1.8" }, @@ -2566,6 +2568,7 @@ "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.13" }, @@ -2749,6 +2752,24 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3333,15 +3354,16 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "node_modules/superjson": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz", - "integrity": "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", + "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", "dev": true, + "license": "MIT", "dependencies": { "copy-anything": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=16" } }, "node_modules/supports-color": { @@ -3518,10 +3540,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5607,6 +5630,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5998,9 +6026,9 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "superjson": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz", - "integrity": "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", + "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", "dev": true, "requires": { "copy-anything": "^3.0.2" @@ -6133,9 +6161,9 @@ } }, "typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true }, "unbox-primitive": { diff --git a/package.json b/package.json index f36a853..309d35b 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "simple-workflows", - "version": "0.1.0-beta15", + "version": "0.1.0-beta16", "description": "Workflows as code in TypeScript", "main": "lib/index.js", + "type": "module", "engines": { "node": ">=18.17.0" }, @@ -13,7 +14,7 @@ "scripts": { "build": "npx tsc", "build-watch": "npx tsc -w", - "test": "node --trace-warnings --test lib/tests/", + "test": "node --test", "lint": "eslint ./src/ --ext .ts" }, "author": "Allan Hvam", @@ -27,13 +28,14 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.5.0", "eslint-plugin-promise": "^6.1.1", - "superjson": "^1.12.1", + "superjson": "^2.2.1", "typescript": "^5.3.3" }, "dependencies": { "@azure/data-tables": "^13.0.1", "@azure/storage-blob": "^12.10.0", "async-mutex": "^0.4.0", - "ms": "^2.1.3" + "ms": "^2.1.3", + "nanoid": "^5.0.7" } } diff --git a/src/DefaultRetryPolicy.ts b/src/DefaultRetryPolicy.ts index 6fbec3e..9040609 100644 --- a/src/DefaultRetryPolicy.ts +++ b/src/DefaultRetryPolicy.ts @@ -1,5 +1,5 @@ -import { type IRetryPolicy } from "./IRetryPolicy"; -import { sleep } from "./sleep"; +import { type IRetryPolicy } from "./IRetryPolicy.js"; +import { sleep } from "./sleep.js"; /** * @internal diff --git a/src/DefaultSerializer.ts b/src/DefaultSerializer.ts index 2bb2b06..64d00d8 100644 --- a/src/DefaultSerializer.ts +++ b/src/DefaultSerializer.ts @@ -1,4 +1,4 @@ -import { type ISerializer } from "./ISerializer"; +import { type ISerializer } from "./ISerializer.js"; /** * @internal diff --git a/src/IWorker.ts b/src/IWorker.ts index 7da05ca..7a107f6 100644 --- a/src/IWorker.ts +++ b/src/IWorker.ts @@ -1,5 +1,5 @@ -import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore"; -import { type BaseWorkflowHandle, type Workflow } from "./Workflow"; +import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore.js"; +import { type BaseWorkflowHandle, type Workflow } from "./Workflow.js"; export declare type WithWorkflowArgs = T & (Parameters extends [any, ...any[]] ? { /** diff --git a/src/IWorkflowContext.ts b/src/IWorkflowContext.ts index 7634391..424ee99 100644 --- a/src/IWorkflowContext.ts +++ b/src/IWorkflowContext.ts @@ -1,4 +1,4 @@ -import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore"; +import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore.js"; import { type MutexInterface } from "async-mutex"; export interface IWorkflowContext { diff --git a/src/Worker.ts b/src/Worker.ts index 3e39fa0..6243414 100644 --- a/src/Worker.ts +++ b/src/Worker.ts @@ -1,13 +1,13 @@ import { AsyncLocalStorage } from "async_hooks"; -import { type IWorkflowContext } from "./IWorkflowContext"; -import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore"; -import { MemoryWorkflowHistoryStore } from "./stores/MemoryWorkflowHistoryStore"; -import { type BaseWorkflowHandle, type Workflow, type WorkflowResultType, type WorkflowReturnType } from "./Workflow"; +import { type IWorkflowContext } from "./IWorkflowContext.js"; +import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore.js"; +import { MemoryWorkflowHistoryStore } from "./stores/MemoryWorkflowHistoryStore.js"; +import { type BaseWorkflowHandle, type Workflow, type WorkflowResultType, type WorkflowReturnType } from "./Workflow.js"; import msPkg from "ms"; -import { deserializeError, serializeError } from "./serialize-error"; import { Mutex } from "async-mutex"; -import { sleep } from "./sleep"; -import { type IWorker, type WorkflowStartOptions } from "./IWorker"; +import { sleep } from "./sleep.js"; +import { type IWorker, type WorkflowStartOptions } from "./IWorker.js"; +import { nanoid } from "nanoid"; export class Worker implements IWorker { public static asyncLocalStorage = new AsyncLocalStorage(); @@ -29,7 +29,7 @@ export class Worker implements IWorker { } public async start(workflow: T, options?: WorkflowStartOptions): Promise> { - let workflowId = "wf-id-" + Math.floor(Math.random() * 1000); + let workflowId = "workflow-" + nanoid(); if (options?.workflowId) { workflowId = options.workflowId; } @@ -79,7 +79,7 @@ export class Worker implements IWorker { workflowId, store, result: async () => { - const reason = deserializeError(error); + const reason = error; return await Promise.reject(reason); }, }; @@ -133,7 +133,7 @@ export class Worker implements IWorker { workflowInstance.result = result; } else { workflowContext.log(() => `${workflowId}: end (error, ${duration})`); - workflowInstance.error = serializeError(error); + workflowInstance.error = error; } if (store) { diff --git a/src/Workflow.ts b/src/Workflow.ts index 182f272..41ebf9d 100644 --- a/src/Workflow.ts +++ b/src/Workflow.ts @@ -1,4 +1,4 @@ -import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore"; +import { type IWorkflowHistoryStore } from "./stores/IWorkflowHistoryStore.js"; export declare type WorkflowReturnType = Promise; diff --git a/src/WorkflowContext.ts b/src/WorkflowContext.ts index 1a092df..eec0319 100644 --- a/src/WorkflowContext.ts +++ b/src/WorkflowContext.ts @@ -1,5 +1,5 @@ -import { type IWorkflowContext } from "./IWorkflowContext"; -import { Worker } from "./Worker"; +import { type IWorkflowContext } from "./IWorkflowContext.js"; +import { Worker } from "./Worker.js"; export class WorkflowContext { public static current(): IWorkflowContext | undefined { diff --git a/src/debug.ts b/src/debug.ts index 0f972d1..3b8264f 100644 --- a/src/debug.ts +++ b/src/debug.ts @@ -1,8 +1,8 @@ -import { Worker } from "./Worker"; -import { greetWorkflow } from "./tests/workflows/greet-workflow"; -import { FileSystemWorkflowHistoryStore } from "./stores/FileSystemWorkflowHistoryStore"; +import { Worker } from "./Worker.js"; +import { greetWorkflow } from "./tests/workflows/greet-workflow.js"; +import { FileSystemWorkflowHistoryStore } from "./stores/FileSystemWorkflowHistoryStore.js"; -let run = async () => { +const run = async (): Promise => { const worker = Worker.getInstance(); worker.store = new FileSystemWorkflowHistoryStore(); @@ -14,7 +14,7 @@ let run = async () => { // Assert console.log(`Started workflow ${handle.workflowId}`); - let result = await handle.result(); + const result = await handle.result(); console.dir(result); }; @@ -22,4 +22,4 @@ run().then(() => { process.exit(); }).catch((e) => { console.error(e); -}); \ No newline at end of file +}); diff --git a/src/index.ts b/src/index.ts index cb850d5..d29ff83 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -export * from "./proxyActivities"; -export { Worker as WorkflowWorker } from "./Worker"; -export * from "./stores/"; -export * from "./IWorker"; -export * from "./WorkflowContext"; -export * from "./IWorkflowContext"; +export * from "./proxyActivities.js"; +export { Worker as WorkflowWorker } from "./Worker.js"; +export * from "./stores/index.js"; +export * from "./IWorker.js"; +export * from "./WorkflowContext.js"; +export * from "./IWorkflowContext.js"; diff --git a/src/proxyActivities.ts b/src/proxyActivities.ts index 708637d..0b9558a 100644 --- a/src/proxyActivities.ts +++ b/src/proxyActivities.ts @@ -1,8 +1,7 @@ import { isDeepStrictEqual } from "node:util"; -import { DefaultRetryPolicy } from "./DefaultRetryPolicy"; -import { deserializeError, serializeError } from "./serialize-error"; -import { type WorkflowActivityInstance, type WorkflowInstance } from "./stores/IWorkflowHistoryStore"; -import { Worker } from "./Worker"; +import { DefaultRetryPolicy } from "./DefaultRetryPolicy.js"; +import type { WorkflowActivity, WorkflowInstance } from "./stores/IWorkflowHistoryStore.js"; +import { Worker } from "./Worker.js"; type PromiseFuncKeys = { [K in keyof T]: T[K] extends ((...args: any[]) => Promise) ? K : never; @@ -57,7 +56,7 @@ export function proxyActivities(activities: A, options?: { ret } let activityName = String(activityType); - if (obj.constructor.name && obj.constructor.name !== "Object") { + if (obj.constructor?.name && obj.constructor?.name !== "Object") { activityName = `${obj.constructor.name}.${activityType}`; } const logPrefix = `${workflowId}/${activityName}${logArgs}`; @@ -67,7 +66,7 @@ export function proxyActivities(activities: A, options?: { ret // NOTE: if object is passed, make sure we have a copy of it, if it is changed later const originalArgs = structuredClone(args); - const startActivity = await mutex.runExclusive(async (): Promise => { + const startActivity = await mutex.runExclusive(async (): Promise => { const instance = await store?.getInstance(workflowId); if (instance?.status === "timeout") { return instance?.status; @@ -103,7 +102,7 @@ export function proxyActivities(activities: A, options?: { ret return activity.result; } else if (activity && Object.prototype.hasOwnProperty.call(activity, "error")) { log(() => `${logPrefix}: skip (error)`); - const reason = deserializeError(activity.error); + const reason = activity.error; return await Promise.reject(reason); } @@ -154,7 +153,7 @@ export function proxyActivities(activities: A, options?: { ret activity.end = new Date(); const duration = `${activity.end.getTime() - activity.start.getTime()} ms`; if (error) { - activity.error = serializeError(error); + activity.error = error; log(() => `${logPrefix}: end (error, ${executions > 1 ? `${executions} executions, ` : ""}${duration})`); } else { activity.result = result; diff --git a/src/serialize-error/index.ts b/src/serialize-error/index.ts index c9381d2..6931680 100644 --- a/src/serialize-error/index.ts +++ b/src/serialize-error/index.ts @@ -27,7 +27,7 @@ export interface IOptions { } export class NonError extends Error { - name = "NonError"; + override name = "NonError"; constructor(message) { super(NonError.prepareSuperMessage(message)); diff --git a/src/stores/DurableFunctionsWorkflowHistoryStore.ts b/src/stores/DurableFunctionsWorkflowHistoryStore.ts index fa005a1..66890b6 100644 --- a/src/stores/DurableFunctionsWorkflowHistoryStore.ts +++ b/src/stores/DurableFunctionsWorkflowHistoryStore.ts @@ -1,11 +1,11 @@ -import type { WorkflowActivityInstance, WorkflowInstance, WorkflowInstanceHeader } from "./IWorkflowHistoryStore"; +import type { GetInstancesOptions, GetInstancesResult, WorkflowActivity, WorkflowInstance, WorkflowInstanceHeader } from "./IWorkflowHistoryStore.js"; import { type GetTableEntityResponse, TableClient, type TableEntity, type TableEntityResult, TableServiceClient, TableTransaction } from "@azure/data-tables"; -import { deserializeError, serializeError } from "../serialize-error"; +import { deserializeError, serializeError } from "../serialize-error/index.js"; import { BlobServiceClient, type ContainerClient } from "@azure/storage-blob"; import zlib from "zlib"; import { Mutex } from "async-mutex"; -import { type ISerializer } from "../ISerializer"; -import { SerializedWorkflowHistoryStore } from "./SerializedWorkflowHistoryStore"; +import { type ISerializer } from "../ISerializer.js"; +import { SerializedWorkflowHistoryStore } from "./SerializedWorkflowHistoryStore.js"; interface IDurableFunctionsWorkflowHistory { Name: string @@ -166,7 +166,7 @@ export class DurableFunctionsWorkflowHistoryStore extends SerializedWorkflowHist args: [], start: entity.CreatedTime, end: entity.CompletedTime, - activities: new Array(), + activities: new Array(), }; if (entity.Input) { @@ -450,65 +450,52 @@ export class DurableFunctionsWorkflowHistoryStore extends SerializedWorkflowHist }); }; - public getInstances = async (): Promise => { + public getInstances = async (options?: GetInstancesOptions): GetInstancesResult => { return await this.mutex.runExclusive(async () => { await this.init(); - const instancesIterator = this.instances.listEntities().byPage({ maxPageSize: 50 }); - - const workflows = new Array(); - for await (const page of instancesIterator) { - for await (const entity of page) { - const name = entity.Name; - if (!name) { - continue; - } - const instance = await this.getInstanceInternal(name); - if (instance) { - workflows.push(instance); - } - } + const queryFilters = new Array(); + if (options?.filter?.from) { + queryFilters.push(`CreatedTime ge datetime'${options.filter.from.toISOString()}'`); } - return workflows; - }); - }; + if (options?.filter?.to) { + queryFilters.push(`CreatedTime lt datetime'${options.filter.to.toISOString()}'`); + } - public getInstanceHeaders = async (): Promise> => { - return await this.mutex.runExclusive(async () => { - await this.init(); + const instancesIterator = this.instances.listEntities( + { + queryOptions: { + filter: queryFilters.join(" and "), + select: ["Name", "CustomStatus", "CreatedTime", "CompletedTime"], + }, + }, + ).byPage({ + maxPageSize: options?.pageSize ?? 50, // max 1000 + continuationToken: options?.continuationToken, + }); - const instancesIterator = this.instances.listEntities().byPage({ maxPageSize: 50 }); + const instances = new Array(); + let continuationToken: undefined | string; - const headers = new Array(); for await (const page of instancesIterator) { - for await (const entity of page) { - const id = entity.Name; - try { - const entity = await this.instances.getEntity(id, ""); - - const header: WorkflowInstanceHeader = { - instanceId: id, - status: entity.CustomStatus as any, - start: entity.CreatedTime, - end: entity.CompletedTime, - }; - - if (header.end && entity.Output && entity.RuntimeStatus === "Failed") { - header.error = true; - } + continuationToken = page.continuationToken; + for await (const instance of page) { + const header: WorkflowInstanceHeader = { + instanceId: instance.Name, + status: instance.CustomStatus === "timeout" ? "timeout" : undefined, + start: instance.CreatedTime, + end: instance.CompletedTime, + }; - headers.push(header); - } catch (e) { - if (typeof e === "object" && e && "statusCode" in e && e.statusCode === 404) { - continue; - } - throw e; - } + instances.push(header); } + break; } - - return headers; + return { + instances, + continuationToken, + }; }); }; diff --git a/src/stores/FileSystemWorkflowHistoryStore.ts b/src/stores/FileSystemWorkflowHistoryStore.ts index 17d8030..9dbfe69 100644 --- a/src/stores/FileSystemWorkflowHistoryStore.ts +++ b/src/stores/FileSystemWorkflowHistoryStore.ts @@ -1,10 +1,10 @@ -import type { WorkflowInstance, WorkflowInstanceHeader } from "./IWorkflowHistoryStore"; +import type { GetInstancesOptions, GetInstancesResult, WorkflowInstance } from "./IWorkflowHistoryStore.js"; import { resolve, parse as pathParse } from "path"; import { cwd } from "process"; import * as fs from "node:fs"; -import { deserializeError, serializeError } from "../serialize-error"; -import { type ISerializer } from "../ISerializer"; -import { SerializedWorkflowHistoryStore } from "./SerializedWorkflowHistoryStore"; +import { deserializeError, serializeError } from "../serialize-error/index.js"; +import { type ISerializer } from "../ISerializer.js"; +import { SerializedWorkflowHistoryStore } from "./SerializedWorkflowHistoryStore.js"; export class FileSystemWorkflowHistoryStore extends SerializedWorkflowHistoryStore { public workflowHistory: Array = []; @@ -92,7 +92,7 @@ export class FileSystemWorkflowHistoryStore extends SerializedWorkflowHistorySto }); } - public getInstances = async (): Promise> => { + public getInstances = async (options?: GetInstancesOptions): GetInstancesResult => { let files = fs.readdirSync(this.options.path); files = files.filter(file => fs.lstatSync(file).isFile()); @@ -106,20 +106,8 @@ export class FileSystemWorkflowHistoryStore extends SerializedWorkflowHistorySto instances.push(instance); } } - return instances; - }; - public getInstanceHeaders = async (): Promise> => { - const instances = await this.getInstances(); - return await Promise.resolve(instances.map(instance => { - return { - instanceId: instance.instanceId, - status: instance.status, - start: instance.start, - end: instance.end, - error: !!instance.error, - }; - })); + return await this.getWorkflowInstanceHeaders(instances, options); }; public removeInstance = async (id: string): Promise => { diff --git a/src/stores/IWorkflowHistoryStore.ts b/src/stores/IWorkflowHistoryStore.ts index ae893a4..2b4ab00 100644 --- a/src/stores/IWorkflowHistoryStore.ts +++ b/src/stores/IWorkflowHistoryStore.ts @@ -11,10 +11,10 @@ export type WorkflowInstance = Omit & { result?: unknown error?: unknown - activities: Array + activities: Array }; -export type WorkflowActivityInstance = { +export type WorkflowActivity = { name: string args: Array start: Date @@ -23,11 +23,26 @@ export type WorkflowActivityInstance = { error?: unknown }; +export type GetInstancesOptions = { + continuationToken?: string + pageSize?: number + filter?: { + from?: Date + to?: Date + } +}; + +export type GetInstancesResult = Promise<{ + instances: Array + continuationToken?: string +}>; + export interface IWorkflowHistoryStore { - getInstance: (id: string) => Promise - setInstance: (instance: WorkflowInstance) => Promise - getInstances: () => Promise> - getInstanceHeaders: () => Promise> - removeInstance: (id: string) => Promise - equal: (val1: any, val2: any) => boolean + getInstance: (id: string) => Promise; + setInstance: (instance: WorkflowInstance) => Promise; + removeInstance: (id: string) => Promise; + + getInstances: (options?: GetInstancesOptions) => GetInstancesResult; + + equal: (val1: any, val2: any) => boolean; } diff --git a/src/stores/MemoryWorkflowHistoryStore.ts b/src/stores/MemoryWorkflowHistoryStore.ts index 38d58d4..2deb6b2 100644 --- a/src/stores/MemoryWorkflowHistoryStore.ts +++ b/src/stores/MemoryWorkflowHistoryStore.ts @@ -1,16 +1,17 @@ import { isDeepStrictEqual } from "node:util"; -import { type IWorkflowHistoryStore, type WorkflowInstance, type WorkflowInstanceHeader } from "./IWorkflowHistoryStore"; +import type { GetInstancesOptions, GetInstancesResult, WorkflowInstance } from "./IWorkflowHistoryStore.js"; +import { WorkflowHistoryStore } from "./WorkflowHistoryStore.js"; -export class MemoryWorkflowHistoryStore implements IWorkflowHistoryStore { +export class MemoryWorkflowHistoryStore extends WorkflowHistoryStore { public workflowHistory: Array = []; - public async getInstance(id: string): Promise { + public getInstance = async (id: string): Promise => { const workflowInstance = this.workflowHistory.find(w => w.instanceId === id); return await Promise.resolve(workflowInstance); - } + }; - public async setInstance(instance: WorkflowInstance): Promise { + public setInstance = async (instance: WorkflowInstance): Promise => { const current = await this.getInstance(instance.instanceId); if (!current) { this.workflowHistory.push(instance); @@ -18,30 +19,19 @@ export class MemoryWorkflowHistoryStore implements IWorkflowHistoryStore { Object.assign(current, instance); } return await Promise.resolve(); - } + }; - public async getInstances(): Promise { - return await Promise.resolve(this.workflowHistory); - } + public getInstances = async (options?: GetInstancesOptions): GetInstancesResult => { + return await this.getWorkflowInstanceHeaders(this.workflowHistory, options); + }; - public async getInstanceHeaders(): Promise> { - return await Promise.resolve(this.workflowHistory.map(instance => { - return { - instanceId: instance.instanceId, - status: instance.status, - start: instance.start, - end: instance.end, - error: !!instance.error, - }; - })); - } - - public async removeInstance(id: string): Promise { + public removeInstance = async (id: string): Promise => { const index = this.workflowHistory.findIndex(i => i.instanceId === id); if (index > -1) { this.workflowHistory.splice(index, 1); } - } + return await Promise.resolve(); + }; public equal = isDeepStrictEqual; } diff --git a/src/stores/SerializedWorkflowHistoryStore.ts b/src/stores/SerializedWorkflowHistoryStore.ts index df7e88a..ac02c28 100644 --- a/src/stores/SerializedWorkflowHistoryStore.ts +++ b/src/stores/SerializedWorkflowHistoryStore.ts @@ -1,12 +1,14 @@ -import { type IWorkflowHistoryStore, type WorkflowInstance, type WorkflowInstanceHeader } from "./IWorkflowHistoryStore"; -import { type ISerializer } from "../ISerializer"; -import { DefaultSerializer } from "../DefaultSerializer"; +import type { WorkflowInstance, GetInstancesOptions, GetInstancesResult } from "./IWorkflowHistoryStore.js"; +import type { ISerializer } from "../ISerializer.js"; +import { DefaultSerializer } from "../DefaultSerializer.js"; import { isDeepStrictEqual } from "node:util"; +import { WorkflowHistoryStore } from "./WorkflowHistoryStore.js"; -export abstract class SerializedWorkflowHistoryStore implements IWorkflowHistoryStore { +export abstract class SerializedWorkflowHistoryStore extends WorkflowHistoryStore { protected readonly serializer: ISerializer; public constructor(serializer?: ISerializer) { + super(); this.serializer = serializer ?? new DefaultSerializer(); } @@ -14,9 +16,9 @@ export abstract class SerializedWorkflowHistoryStore implements IWorkflowHistory return (this.serializer.equal ?? isDeepStrictEqual)(val1, val2); }; - abstract getInstance: (id: string) => Promise; - abstract setInstance: (instance: WorkflowInstance) => Promise; - abstract getInstances: () => Promise; - abstract getInstanceHeaders: () => Promise; - abstract removeInstance: (id: string) => Promise; -} + abstract override getInstance: (id: string) => Promise; + abstract override setInstance: (instance: WorkflowInstance) => Promise; + abstract override removeInstance: (id: string) => Promise; + + abstract override getInstances: (options?: GetInstancesOptions) => GetInstancesResult; +}; diff --git a/src/stores/WorkflowHistoryStore.ts b/src/stores/WorkflowHistoryStore.ts new file mode 100644 index 0000000..c5fda17 --- /dev/null +++ b/src/stores/WorkflowHistoryStore.ts @@ -0,0 +1,57 @@ +import type { IWorkflowHistoryStore, WorkflowInstance, GetInstancesOptions, GetInstancesResult, WorkflowInstanceHeader } from "./IWorkflowHistoryStore.js"; + +export abstract class WorkflowHistoryStore implements IWorkflowHistoryStore { + protected getWorkflowInstanceHeaders = async (instances: Array, options?: GetInstancesOptions): GetInstancesResult => { + let headers = instances.map(this.getWorkflowInstanceHeader); + + const from = options?.filter?.from; + if (from) { + headers = headers.filter(header => header.start >= from); + } + + const to = options?.filter?.to; + if (to) { + headers = headers.filter(header => header.start < to); + } + + headers.sort((a, b) => a.start.getTime() - b.start.getTime()); + + if (options?.continuationToken) { + const index = headers.findIndex(header => header.instanceId === options.continuationToken); + if (index) { + headers = headers.slice(index); + } + } + + let continuationToken: string | undefined; + const pageSize = options?.pageSize; + if (pageSize) { + if (headers.length > pageSize) { + continuationToken = headers[pageSize].instanceId; + } + headers = headers.slice(0, pageSize); + } + + return { + instances: headers, + continuationToken, + }; + }; + + protected getWorkflowInstanceHeader = (instance: WorkflowInstance): WorkflowInstanceHeader => { + return { + instanceId: instance.instanceId, + status: instance.status, + start: instance.start, + end: instance.end, + error: !!instance.error, + }; + }; + + abstract equal: (val1: any, val2: any) => boolean; + abstract getInstance: (id: string) => Promise; + abstract setInstance: (instance: WorkflowInstance) => Promise; + abstract removeInstance: (id: string) => Promise; + + abstract getInstances: (options?: GetInstancesOptions) => GetInstancesResult; +}; diff --git a/src/stores/index.ts b/src/stores/index.ts index dabbe3d..3a0d337 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,5 +1,5 @@ -export * from "./SerializedWorkflowHistoryStore"; -export * from "./DurableFunctionsWorkflowHistoryStore"; -export * from "./FileSystemWorkflowHistoryStore"; -export * from "./MemoryWorkflowHistoryStore"; -export * from "./IWorkflowHistoryStore"; +export * from "./SerializedWorkflowHistoryStore.js"; +export * from "./DurableFunctionsWorkflowHistoryStore.js"; +export * from "./FileSystemWorkflowHistoryStore.js"; +export * from "./MemoryWorkflowHistoryStore.js"; +export * from "./IWorkflowHistoryStore.js"; diff --git a/src/tests/activities/call-twice.ts b/src/tests/activities/call-twice.ts index 0557a14..9dc34d5 100644 --- a/src/tests/activities/call-twice.ts +++ b/src/tests/activities/call-twice.ts @@ -1,4 +1,4 @@ -import { Counters } from "./Counters"; +import { Counters } from "./Counters.js"; export async function callTwice(): Promise { const called = Counters.get("call-twice"); diff --git a/src/tests/activities/get-counter.ts b/src/tests/activities/get-counter.ts index 75c6f28..c296aa5 100644 --- a/src/tests/activities/get-counter.ts +++ b/src/tests/activities/get-counter.ts @@ -1,4 +1,4 @@ -import { Counters } from "./Counters"; +import { Counters } from "./Counters.js"; export async function getCounter(name: string): Promise { return Counters.get(name); diff --git a/src/tests/activities/get-workflow-id.ts b/src/tests/activities/get-workflow-id.ts index bf37e54..665317d 100644 --- a/src/tests/activities/get-workflow-id.ts +++ b/src/tests/activities/get-workflow-id.ts @@ -1,4 +1,4 @@ -import { WorkflowContext } from "../../WorkflowContext"; +import { WorkflowContext } from "../../WorkflowContext.js"; export async function getWorkflowId(): Promise { return WorkflowContext.current()?.workflowId; diff --git a/src/tests/activities/increment-counter.ts b/src/tests/activities/increment-counter.ts index a08ce4b..7c901e0 100644 --- a/src/tests/activities/increment-counter.ts +++ b/src/tests/activities/increment-counter.ts @@ -1,4 +1,4 @@ -import { Counters } from "./Counters"; +import { Counters } from "./Counters.js"; export async function incrementCounter(name: string): Promise { Counters.increment(name); diff --git a/src/tests/activities/index.ts b/src/tests/activities/index.ts index 9034047..dcc6c19 100644 --- a/src/tests/activities/index.ts +++ b/src/tests/activities/index.ts @@ -1,12 +1,12 @@ -export * from "./add"; -export * from "./call-twice"; -export * from "./get-counter"; -export * from "./get-distance"; -export * from "./get-workflow-id"; -export * from "./greet"; -export * from "./increment-counter"; -export * from "./move"; -export * from "./now"; -export * from "./sleep"; -export * from "./throw-error-message"; -export * from "./throw-message"; +export * from "./add.js"; +export * from "./call-twice.js"; +export * from "./get-counter.js"; +export * from "./get-distance.js"; +export * from "./get-workflow-id.js"; +export * from "./greet.js"; +export * from "./increment-counter.js"; +export * from "./move.js"; +export * from "./now.js"; +export * from "./sleep.js"; +export * from "./throw-error-message.js"; +export * from "./throw-message.js"; diff --git a/src/tests/activities/sleep.ts b/src/tests/activities/sleep.ts index 7770898..b7791fb 100644 --- a/src/tests/activities/sleep.ts +++ b/src/tests/activities/sleep.ts @@ -1,4 +1,4 @@ -import { sleep as sleepImpl } from "../../sleep"; +import { sleep as sleepImpl } from "../../sleep.js"; export async function sleep(ms: string): Promise { await sleepImpl(ms); diff --git a/src/tests/services/GreetService.ts b/src/tests/services/GreetService.ts index 4488843..3f4f150 100644 --- a/src/tests/services/GreetService.ts +++ b/src/tests/services/GreetService.ts @@ -1,4 +1,4 @@ -import { greet } from "../activities/greet"; +import { greet } from "../activities/greet.js"; export class GreetService { public prop = "prop"; diff --git a/src/tests/store.test.ts b/src/tests/store.test.ts index 8bffa93..1995393 100644 --- a/src/tests/store.test.ts +++ b/src/tests/store.test.ts @@ -1,16 +1,31 @@ import { test } from "node:test"; import assert from "node:assert"; -import * as stores from "../stores"; -import { Worker } from "../Worker"; -import { testWorkflow } from "./workflows/test-workflow"; +import { Worker } from "../Worker.js"; +import { testWorkflow } from "./workflows/test-workflow.js"; +import { DurableFunctionsWorkflowHistoryStore, MemoryWorkflowHistoryStore, type WorkflowInstanceHeader } from "../stores/index.js"; +import { sleep } from "../sleep.js"; test.before(async () => { const worker = Worker.getInstance(); - const store = new stores.DurableFunctionsWorkflowHistoryStore({ connectionString: "UseDevelopmentStorage=true", taskHubName: "StoreTestWorkflow" }); - // let store = new FileSystemWorkflowHistoryStore(); - await store.clear(); - // let store = new MemoryWorkflowHistoryStore(); - worker.store = store; + + let isStorageEmulatorRunning = false; + try { + const response = await fetch("http://127.0.0.1:10000"); + if (response.status === 400) { + isStorageEmulatorRunning = true; + } + } catch { + console.log("Storage emulator not running, using memory."); + } + + if (isStorageEmulatorRunning) { + const store = new DurableFunctionsWorkflowHistoryStore({ + connectionString: "UseDevelopmentStorage=true", + taskHubName: "StoreTestWorkflow", + }); + await store.clear(); + worker.store = store; + } worker.log = (s: string) => console.log(`[${new Date().toISOString()}] ${s}`); }); @@ -24,14 +39,55 @@ void test("Workflow store, removeInstance", async (t) => { await handle.result(); // Act - let workflowInstances = await store.getInstanceHeaders(); + let workflowInstances = await store.getInstances(); // Assert - let workflow = workflowInstances.find(wi => wi.instanceId === workflowId); + let workflow = workflowInstances.instances.find(wi => wi.instanceId === workflowId); assert.ok(workflow); await store.removeInstance(workflow.instanceId); - workflowInstances = await store.getInstanceHeaders(); - workflow = workflowInstances.find(wi => wi.instanceId === workflowId); + workflowInstances = await store.getInstances(); + workflow = workflowInstances.instances.find(wi => wi.instanceId === workflowId); assert.ok(!workflow); }); + +void test("Workflow store, getInstances options", async (t) => { + // Arrange + const worker = Worker.getInstance(); + const store = new MemoryWorkflowHistoryStore(); + + let halfDate = new Date(); + let eightyDate = new Date(); + + for (let i = 0; i !== 100; i++) { + if (i === 50) { + halfDate = new Date(); + } + if (i === 80) { + eightyDate = new Date(); + } + const handle = await worker.start(testWorkflow, { + workflowId: i.toString(), + store, + }); + await handle.result(); + await sleep("1ms"); + } + + // Act + const half = await store.getInstances({ filter: { from: halfDate } }); + const thirty = await store.getInstances({ filter: { from: halfDate, to: eightyDate } }); + + const all = new Array(); + let continuationToken: string | undefined = ""; + while (continuationToken !== undefined) { + const result = await store.getInstances({ continuationToken, pageSize: 10 }); + continuationToken = result.continuationToken; + all.push(...result.instances); + } + + // Assert + assert.equal(half.instances.length, 50); + assert.equal(thirty.instances.length, 30); + assert.equal(all.length, 100); +}); diff --git a/src/tests/workflow.test.ts b/src/tests/workflow.test.ts index a831657..93d0f10 100644 --- a/src/tests/workflow.test.ts +++ b/src/tests/workflow.test.ts @@ -1,38 +1,52 @@ import { test } from "node:test"; import assert from "node:assert"; -import { Worker } from "../Worker"; -import { greetWorkflow } from "./workflows/greet-workflow"; -import { incrementCounterWorkflow } from "./workflows/increment-counter-workflow"; -import { testWorkflow } from "./workflows/test-workflow"; -import { addWorkflow } from "./workflows/add-workflow"; -import { voidWorkflow } from "./workflows/void-workflow"; -import { Counters } from "./activities/Counters"; -import { timeoutWorkflow } from "./workflows/timeout-workflow"; -import { distanceWorkflow } from "./workflows/distance-workflow"; -import { moveWorkflow } from "./workflows/move-workflow"; -import { throwErrorWorkflow } from "./workflows/throw-error-workflow"; -import { callTwiceWorkflow } from "./workflows/call-twice-workflow"; -import { noStore } from "./workflows/no-store"; -import { nestedWorkflow } from "./workflows/nested-workflow"; -import { longWorkflow } from "./workflows/long-workflow"; -import { largeWorkflow } from "./workflows/large-workflow"; -import { concurrentWorkflow } from "./workflows/concurrent-workflow"; -import { noTimeoutWorkflow } from "./workflows/no-timeout-workflow"; -import { nowWorkflow } from "./workflows/now-workflow"; -import { FileSystemWorkflowHistoryStore, MemoryWorkflowHistoryStore, DurableFunctionsWorkflowHistoryStore } from "../stores"; -import { sleep } from "../sleep"; -import { throwWorkflow } from "./workflows/throw-workflow"; +import { Worker } from "../Worker.js"; +import { greetWorkflow } from "./workflows/greet-workflow.js"; +import { incrementCounterWorkflow } from "./workflows/increment-counter-workflow.js"; +import { testWorkflow } from "./workflows/test-workflow.js"; +import { addWorkflow } from "./workflows/add-workflow.js"; +import { voidWorkflow } from "./workflows/void-workflow.js"; +import { Counters } from "./activities/Counters.js"; +import { timeoutWorkflow } from "./workflows/timeout-workflow.js"; +import { distanceWorkflow } from "./workflows/distance-workflow.js"; +import { moveWorkflow } from "./workflows/move-workflow.js"; +import { throwErrorWorkflow } from "./workflows/throw-error-workflow.js"; +import { callTwiceWorkflow } from "./workflows/call-twice-workflow.js"; +import { noStore } from "./workflows/no-store.js"; +import { nestedWorkflow } from "./workflows/nested-workflow.js"; +import { longWorkflow } from "./workflows/long-workflow.js"; +import { largeWorkflow } from "./workflows/large-workflow.js"; +import { concurrentWorkflow } from "./workflows/concurrent-workflow.js"; +import { noTimeoutWorkflow } from "./workflows/no-timeout-workflow.js"; +import { nowWorkflow } from "./workflows/now-workflow.js"; +import { DurableFunctionsWorkflowHistoryStore } from "../stores/index.js"; +import { sleep } from "../sleep.js"; +import { throwWorkflow } from "./workflows/throw-workflow.js"; import superjson from "superjson"; -import { greetServiceWorkflow } from "./workflows/greet-service-workflow"; -import { stateServiceWorkflow } from "./workflows/state-service-workflow"; +import { greetServiceWorkflow } from "./workflows/greet-service-workflow.js"; +import { stateServiceWorkflow } from "./workflows/state-service-workflow.js"; + +let isStorageEmulatorRunning = false; test.before(async () => { const worker = Worker.getInstance(); - const store = new DurableFunctionsWorkflowHistoryStore({ connectionString: "UseDevelopmentStorage=true" }); - // let store = new FileSystemWorkflowHistoryStore(); - await store.clear(); - // let store = new MemoryWorkflowHistoryStore(); - worker.store = store; + + try { + const response = await fetch("http://127.0.0.1:10000"); + if (response.status === 400) { + isStorageEmulatorRunning = true; + } + } catch { + console.log("Storage emulator not running, using memory."); + } + + if (isStorageEmulatorRunning) { + const store = new DurableFunctionsWorkflowHistoryStore({ + connectionString: "UseDevelopmentStorage=true", + }); + await store.clear(); + worker.store = store; + } worker.log = (s: string) => console.log(`[${new Date().toISOString()}] ${s}`); }); @@ -219,7 +233,7 @@ void test("timeout-workflow", async (t) => { // Assert assert.equal(Counters.get("timeout-start"), 1); - await sleep(5000); + await sleep("3s"); // Note: tests that activity execution is stopped assert.equal(Counters.get("timeout-end"), 0); @@ -239,7 +253,7 @@ void test("no-timeout-workflow", async (t) => { }); await handle.result(); - await sleep(10000); + await sleep("1s"); // Assert assert.equal(Counters.get("no-timeout-start"), 1); @@ -300,20 +314,14 @@ void test("throw-error-workflow", async (t) => { // Act & Assert const workflowId = "throw-error"; try { - console.log("D"); const handle = await worker.start(throwErrorWorkflow, { workflowId }); - console.log("B"); await handle.result(); - console.log("C"); assert.fail(); } catch { - console.log("A"); // Ignore, expected to throw } - console.log("1"); const instance = await worker.store.getInstance(workflowId); - console.log("2"); assert.ok(instance?.end, "Expected instance end to be set."); assert.deepEqual(instance.result, undefined, "Expected instance result to be undefined."); assert.ok(instance.error, "Expected error to be set"); @@ -476,6 +484,11 @@ void test("greet-workflow-no-await-result", async (t) => { }); void test("now", async (t) => { + if (!isStorageEmulatorRunning) { + // Skip + assert.ok(true); + return; + } // Arrange const worker = Worker.getInstance(); diff --git a/src/tests/workflows/add-workflow.ts b/src/tests/workflows/add-workflow.ts index a6ab696..fc290eb 100644 --- a/src/tests/workflows/add-workflow.ts +++ b/src/tests/workflows/add-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { add } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/call-twice-workflow.ts b/src/tests/workflows/call-twice-workflow.ts index a22d1d8..6174b0d 100644 --- a/src/tests/workflows/call-twice-workflow.ts +++ b/src/tests/workflows/call-twice-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { callTwice } = proxyActivities(activities, { retry: 5 }); diff --git a/src/tests/workflows/concurrent-workflow.ts b/src/tests/workflows/concurrent-workflow.ts index 4828f01..cbbc46d 100644 --- a/src/tests/workflows/concurrent-workflow.ts +++ b/src/tests/workflows/concurrent-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { sleep } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/distance-workflow.ts b/src/tests/workflows/distance-workflow.ts index c17e140..7873eaa 100644 --- a/src/tests/workflows/distance-workflow.ts +++ b/src/tests/workflows/distance-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { getDistance } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/greet-service-workflow.ts b/src/tests/workflows/greet-service-workflow.ts index 9a6b183..817481c 100644 --- a/src/tests/workflows/greet-service-workflow.ts +++ b/src/tests/workflows/greet-service-workflow.ts @@ -1,5 +1,5 @@ -import { proxyActivities } from "../../proxyActivities"; -import { GreetService } from "../services/GreetService"; +import { proxyActivities } from "../../proxyActivities.js"; +import { GreetService } from "../services/GreetService.js"; const greetService = proxyActivities(new GreetService()); diff --git a/src/tests/workflows/greet-workflow.ts b/src/tests/workflows/greet-workflow.ts index 40607fa..78886a3 100644 --- a/src/tests/workflows/greet-workflow.ts +++ b/src/tests/workflows/greet-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { greet } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/increment-counter-workflow.ts b/src/tests/workflows/increment-counter-workflow.ts index 0b78a19..3fa234d 100644 --- a/src/tests/workflows/increment-counter-workflow.ts +++ b/src/tests/workflows/increment-counter-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { incrementCounter, getCounter } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/large-workflow.ts b/src/tests/workflows/large-workflow.ts index a933270..72e059a 100644 --- a/src/tests/workflows/large-workflow.ts +++ b/src/tests/workflows/large-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { greet } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/long-workflow.ts b/src/tests/workflows/long-workflow.ts index 6fb6e5c..ac4813c 100644 --- a/src/tests/workflows/long-workflow.ts +++ b/src/tests/workflows/long-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { greet } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/move-workflow.ts b/src/tests/workflows/move-workflow.ts index d339b4e..dba7a57 100644 --- a/src/tests/workflows/move-workflow.ts +++ b/src/tests/workflows/move-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { move } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/nested-workflow.ts b/src/tests/workflows/nested-workflow.ts index 609b5db..5c8fc42 100644 --- a/src/tests/workflows/nested-workflow.ts +++ b/src/tests/workflows/nested-workflow.ts @@ -1,5 +1,6 @@ -import { Worker } from "../../Worker"; -import { WorkflowContext } from "../../WorkflowContext"; +import { Worker } from "../../Worker.js"; +import { WorkflowContext } from "../../WorkflowContext.js"; +import { sleep } from "../activities/index.js"; async function childWorkflow(): Promise { const id = WorkflowContext.current()?.workflowId; @@ -21,6 +22,12 @@ export async function nestedWorkflow(): Promise { return await Promise.reject(new Error()); } - const handle = await Worker.getInstance().start(childWorkflow, { workflowId: "child" }); + await sleep("5ms"); + + const handle = await Worker.getInstance().start(childWorkflow, { + workflowId: "child", + }); await handle.result(); + + await sleep("5ms"); } diff --git a/src/tests/workflows/no-store.ts b/src/tests/workflows/no-store.ts index e268b47..2fae536 100644 --- a/src/tests/workflows/no-store.ts +++ b/src/tests/workflows/no-store.ts @@ -1,4 +1,4 @@ -import { WorkflowContext } from "../../WorkflowContext"; +import { WorkflowContext } from "../../WorkflowContext.js"; export async function noStore(): Promise { const store = WorkflowContext.current()?.store; diff --git a/src/tests/workflows/no-timeout-workflow.ts b/src/tests/workflows/no-timeout-workflow.ts index 8bc6885..6ec0069 100644 --- a/src/tests/workflows/no-timeout-workflow.ts +++ b/src/tests/workflows/no-timeout-workflow.ts @@ -1,6 +1,6 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; -import { sleep } from "../../sleep"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; +import { sleep } from "../../sleep.js"; const { incrementCounter } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/now-workflow.ts b/src/tests/workflows/now-workflow.ts index 6c026bc..c1fe4a1 100644 --- a/src/tests/workflows/now-workflow.ts +++ b/src/tests/workflows/now-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { now } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/state-service-workflow.ts b/src/tests/workflows/state-service-workflow.ts index 834e5c3..3e8cd5e 100644 --- a/src/tests/workflows/state-service-workflow.ts +++ b/src/tests/workflows/state-service-workflow.ts @@ -1,5 +1,5 @@ -import { proxyActivities } from "../../proxyActivities"; -import { StateService } from "../services/StateService"; +import { proxyActivities } from "../../proxyActivities.js"; +import { StateService } from "../services/StateService.js"; const stateService = proxyActivities(new StateService()); diff --git a/src/tests/workflows/test-workflow.ts b/src/tests/workflows/test-workflow.ts index 35b240e..853fefd 100644 --- a/src/tests/workflows/test-workflow.ts +++ b/src/tests/workflows/test-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { getWorkflowId } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/throw-error-workflow.ts b/src/tests/workflows/throw-error-workflow.ts index 5b20b3e..e2ecab1 100644 --- a/src/tests/workflows/throw-error-workflow.ts +++ b/src/tests/workflows/throw-error-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { throwErrorMessage } = proxyActivities(activities); diff --git a/src/tests/workflows/throw-workflow.ts b/src/tests/workflows/throw-workflow.ts index fa3a36b..e3e69cc 100644 --- a/src/tests/workflows/throw-workflow.ts +++ b/src/tests/workflows/throw-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { throwMessage } = proxyActivities(activities); diff --git a/src/tests/workflows/timeout-workflow.ts b/src/tests/workflows/timeout-workflow.ts index e36f617..7df48f0 100644 --- a/src/tests/workflows/timeout-workflow.ts +++ b/src/tests/workflows/timeout-workflow.ts @@ -1,6 +1,6 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; -import { sleep } from "../../sleep"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; +import { sleep } from "../../sleep.js"; const { incrementCounter } = proxyActivities(activities, {}); diff --git a/src/tests/workflows/void-workflow.ts b/src/tests/workflows/void-workflow.ts index 0546dc7..64766cc 100644 --- a/src/tests/workflows/void-workflow.ts +++ b/src/tests/workflows/void-workflow.ts @@ -1,5 +1,5 @@ -import * as activities from "../activities"; -import { proxyActivities } from "../../proxyActivities"; +import * as activities from "../activities/index.js"; +import { proxyActivities } from "../../proxyActivities.js"; const { incrementCounter } = proxyActivities(activities, {}); diff --git a/tsconfig.json b/tsconfig.json index 495ce1c..c1383da 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target": "es2015", - "module": "commonjs", + "target": "es2021", + "module": "NodeNext", "declaration": true, "sourceMap": true, "removeComments": false, @@ -11,23 +11,18 @@ "noImplicitAny": false, "allowUnreachableCode": false, "forceConsistentCasingInFileNames": true, - "moduleResolution": "node", "noImplicitThis": true, "noUnusedLocals": false, "noUnusedParameters": false, "pretty": true, "esModuleInterop": true, "stripInternal": true, + "strict": true, - // "target": "es5", - // "module": "commonjs", - // "sourceMap": true, + // "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "outDir": "lib", - // "rootDir": "src/", - // "lib": [ - // "dom", - // "es2016" // was es5 - // ] }, "include": [ "src/**/*"