Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node: [18, 22] # Min and max supported Node versions
node: [18, 22, 24] # Min and max officially supported Node versions + next max
platform: [linux-x64, linux-arm, macos-x64, macos-arm, windows-x64]
include:
- platform: linux-x64
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/conventions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22

- name: Get NPM cache directory
id: npm-cache-dir
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22

- name: Get NPM cache directory
id: npm-cache-dir
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22

- name: Get NPM cache directory
id: npm-cache-dir
Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node: [18, 22] # Min and max supported Node versions
node: [18, 22, 24] # Min and max officially supported Node versions + next max
platform: [linux-x64, linux-arm, macos-x64, macos-arm, windows-x64]
sample: [hello-world, fetch-esm, hello-world-mtls]
server: [cli, cloud]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22

- name: Get NPM cache directory
id: npm-cache-dir
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ packages/*/package-lock.json
# One test creates persisted SQLite DBs; they should normally be deleted automatically,
# but may be left behind in some error scenarios.
packages/test/temporal-db-*.sqlite
.DS_Store
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ See [sdk-structure.md](./docs/sdk-structure.md)

### Environment setup

The TS SDK can be executed on 18, 20 or 22. However, we recommend using Node 22 for SDK development.
TS SDK is officially supported on Node 18, 20, 22, or 24. However, we recommend using the
[Active LTS](https://nodejs.org/en/about/previous-releases#nodejs-releases) for SDK development.
For easier testing during development you may want to use a version manager, such as
[fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md).

Expand Down
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

155 changes: 84 additions & 71 deletions packages/test/src/test-workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ function compareCompletion(
);
}

// Compare stack traces while handling $CLASS keyword to match any identifier that isn't consistent across Node versions.
// As of Node 24.6.0 type names are now present on source mapped stack traces
// See [f33e0fcc83954f728fcfd2ef6ae59435bc4af059](https://github.com/nodejs/node/commit/f33e0fcc83954f728fcfd2ef6ae59435bc4af059)
function compareFailureStackTrace(t: ExecutionContext<Context>, actual: string, expected: string) {
const escapedTrace = expected
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\x2d')
.replaceAll('\\$CLASS', '(?:[A-Za-z]+)');
t.regex(actual, RegExp(`^${escapedTrace}$`));
}

function makeSuccess(
commands: coresdk.workflow_commands.IWorkflowCommand[] = [makeCompleteWorkflowExecution()],
usedInternalFlags: SdkFlag[] = []
Expand Down Expand Up @@ -307,13 +318,12 @@ function makeCompleteWorkflowExecution(result?: Payload): coresdk.workflow_comma

function makeFailWorkflowExecution(
message: string,
stackTrace: string,
type = 'Error',
nonRetryable = true
): coresdk.workflow_commands.IWorkflowCommand {
return {
failWorkflowExecution: {
failure: { message, stackTrace, applicationFailureInfo: { type, nonRetryable }, source: 'TypeScriptSDK' },
failure: { message, applicationFailureInfo: { type, nonRetryable }, source: 'TypeScriptSDK' },
},
};
}
Expand Down Expand Up @@ -453,22 +463,28 @@ function cleanWorkflowQueryFailureStackTrace(
return req;
}

function removeWorkflowFailureStackTrace(
req: coresdk.workflow_completion.IWorkflowActivationCompletion,
commandIndex = 0
) {
const stackTrace = req.successful!.commands![commandIndex].failWorkflowExecution!.failure!.stackTrace!;
delete req.successful!.commands![commandIndex].failWorkflowExecution!.failure!.stackTrace;
return stackTrace;
}

test('throwAsync', async (t) => {
const { workflowType } = t.context;
const req = cleanWorkflowFailureStackTrace(await activate(t, makeStartWorkflow(workflowType)));
compareCompletion(
const actualStackTrace = removeWorkflowFailureStackTrace(req);
compareCompletion(t, req, makeSuccess([makeFailWorkflowExecution('failure')]));
compareFailureStackTrace(
t,
req,
makeSuccess([
makeFailWorkflowExecution(
'failure',
dedent`
actualStackTrace,
dedent`
ApplicationFailure: failure
at Function.nonRetryable (common/src/failure.ts)
at $CLASS.nonRetryable (common/src/failure.ts)
at throwAsync (test/src/workflows/throw-async.ts)
`
),
])
);
});

Expand Down Expand Up @@ -768,27 +784,26 @@ test('interruptableWorkflow', async (t) => {
const req = cleanWorkflowFailureStackTrace(
await activate(t, await makeSignalWorkflow('interrupt', ['just because']))
);
const stackTrace = removeWorkflowFailureStackTrace(req);
compareCompletion(
t,
req,
makeSuccess(
[
makeFailWorkflowExecution(
'just because',
// The stack trace is weird here and might confuse users, it might be a JS limitation
// since the Error stack trace is generated in the constructor.
dedent`
ApplicationFailure: just because
at Function.retryable (common/src/failure.ts)
at test/src/workflows/interrupt-signal.ts
`,
'Error',
false
),
],
[makeFailWorkflowExecution('just because', 'Error', false)],
[SdkFlags.ProcessWorkflowActivationJobsAsSingleBatch]
)
);
compareFailureStackTrace(
t,
stackTrace,
// The stack trace is weird here and might confuse users, it might be a JS limitation
// since the Error stack trace is generated in the constructor.
dedent`
ApplicationFailure: just because
at $CLASS.retryable (common/src/failure.ts)
at test/src/workflows/interrupt-signal.ts
`
);
}
});

Expand All @@ -800,24 +815,24 @@ test('failSignalWorkflow', async (t) => {
}
{
const req = cleanWorkflowFailureStackTrace(await activate(t, await makeSignalWorkflow('fail', [])));
const stackTrace = removeWorkflowFailureStackTrace(req);
compareCompletion(
t,
req,
makeSuccess(
[
makeFailWorkflowExecution(
'Signal failed',
dedent`
ApplicationFailure: Signal failed
at Function.nonRetryable (common/src/failure.ts)
at test/src/workflows/fail-signal.ts
`,
'Error'
),
],
[makeFailWorkflowExecution('Signal failed', 'Error')],
[SdkFlags.ProcessWorkflowActivationJobsAsSingleBatch]
)
);
compareFailureStackTrace(
t,
stackTrace,
dedent`
ApplicationFailure: Signal failed
at $CLASS.nonRetryable (common/src/failure.ts)
at test/src/workflows/fail-signal.ts
`
);
}
});

Expand All @@ -840,23 +855,23 @@ test('asyncFailSignalWorkflow', async (t) => {
}
{
const req = cleanWorkflowFailureStackTrace(await activate(t, makeFireTimer(2)));
const stackTrace = removeWorkflowFailureStackTrace(req);
compareCompletion(
t,
req,
makeSuccess(
[
makeFailWorkflowExecution(
'Signal failed',
dedent`
ApplicationFailure: Signal failed
at Function.nonRetryable (common/src/failure.ts)
at test/src/workflows/async-fail-signal.ts`,
'Error'
),
],
[makeFailWorkflowExecution('Signal failed', 'Error')],
[SdkFlags.ProcessWorkflowActivationJobsAsSingleBatch]
)
);
compareFailureStackTrace(
t,
stackTrace,
dedent`
ApplicationFailure: Signal failed
at $CLASS.nonRetryable (common/src/failure.ts)
at test/src/workflows/async-fail-signal.ts`
);
}
});

Expand Down Expand Up @@ -1445,6 +1460,7 @@ test('nonCancellableInNonCancellable', async (t) => {
test('cancellationErrorIsPropagated', async (t) => {
const { workflowType, logs } = t.context;
const req = cleanWorkflowFailureStackTrace(await activate(t, makeStartWorkflow(workflowType)), 2);
const stackTrace = removeWorkflowFailureStackTrace(req, 2);
compareCompletion(
t,
req,
Expand All @@ -1455,22 +1471,26 @@ test('cancellationErrorIsPropagated', async (t) => {
failWorkflowExecution: {
failure: {
message: 'Cancellation scope cancelled',
stackTrace: dedent`
CancelledFailure: Cancellation scope cancelled
at CancellationScope.cancel (workflow/src/cancellation-scope.ts)
at test/src/workflows/cancellation-error-is-propagated.ts
at CancellationScope.runInContext (workflow/src/cancellation-scope.ts)
at CancellationScope.run (workflow/src/cancellation-scope.ts)
at Function.cancellable (workflow/src/cancellation-scope.ts)
at cancellationErrorIsPropagated (test/src/workflows/cancellation-error-is-propagated.ts)
`,
canceledFailureInfo: {},
source: 'TypeScriptSDK',
},
},
},
])
);
compareFailureStackTrace(
t,
stackTrace,
dedent`
CancelledFailure: Cancellation scope cancelled
at CancellationScope.cancel (workflow/src/cancellation-scope.ts)
at test/src/workflows/cancellation-error-is-propagated.ts
at CancellationScope.runInContext (workflow/src/cancellation-scope.ts)
at CancellationScope.run (workflow/src/cancellation-scope.ts)
at $CLASS.cancellable (workflow/src/cancellation-scope.ts)
at cancellationErrorIsPropagated (test/src/workflows/cancellation-error-is-propagated.ts)
`
);
t.deepEqual(logs, []);
});

Expand Down Expand Up @@ -1655,13 +1675,9 @@ test('resolve activity with failure - http', async (t) => {
},
})
);
compareCompletion(
t,
completion,
makeSuccess([
makeFailWorkflowExecution('Connection timeout', 'ApplicationFailure: Connection timeout', 'MockError'),
])
);
const stackTrace = removeWorkflowFailureStackTrace(completion);
compareCompletion(t, completion, makeSuccess([makeFailWorkflowExecution('Connection timeout', 'MockError')]));
compareFailureStackTrace(t, stackTrace, 'ApplicationFailure: Connection timeout');
}
});

Expand Down Expand Up @@ -1872,19 +1888,16 @@ test('tryToContinueAfterCompletion', async (t) => {
const { workflowType } = t.context;
{
const completion = cleanWorkflowFailureStackTrace(await activate(t, makeStartWorkflow(workflowType)));
compareCompletion(
const stackTrace = removeWorkflowFailureStackTrace(completion);
compareCompletion(t, completion, makeSuccess([makeFailWorkflowExecution('fail before continue')]));
compareFailureStackTrace(
t,
completion,
makeSuccess([
makeFailWorkflowExecution(
'fail before continue',
dedent`
stackTrace,
dedent`
ApplicationFailure: fail before continue
at Function.nonRetryable (common/src/failure.ts)
at $CLASS.nonRetryable (common/src/failure.ts)
at tryToContinueAfterCompletion (test/src/workflows/try-to-continue-after-completion.ts)
`
),
])
);
}
});
Expand Down
Loading