Skip to content

Commit d427c47

Browse files
torsellopranaygp
andauthored
fix(core): allow Next.js route groups and dynamic segments in workflow names (#4070)
A workflow name is derived from the module path it is defined in, so Next.js App Router conventions end up in the name verbatim. `SAFE_WORKFLOW_NAME_PATTERN` permitted alphanumerics, `_`, `-`, `.`, `/` and `@`, but not parentheses or square brackets. Any workflow inside a route group (`app/(dashboard)/...`) or a dynamic segment (`app/[teamId]/...`, `app/[...slug]/...`) threw Invalid workflow name "workflow//./app/(group)/workflows/": must only contain alphanumeric characters, ... before it could be enqueued, and the generated name cannot be overridden. The pattern exists to keep unsafe characters out of the queue name it is interpolated into. These four are inert there: `ValidQueueName` accepts any suffix after its prefix, and the only other consumers of the name are OpenTelemetry span names. It is never placed in a URL or a SQL identifier. Fixes #3991 Signed-off-by: Matias Torsello <23641125+torsello@users.noreply.github.com> Co-authored-by: Matias Torsello <23641125+torsello@users.noreply.github.com> Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
1 parent fe8b27d commit d427c47

3 files changed

Lines changed: 47 additions & 3 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@workflow/core": patch
3+
---
4+
5+
Allow parentheses and square brackets in workflow names
6+
7+
A workflow name is derived from the module path it is defined in, so Next.js App
8+
Router conventions end up in the name verbatim. `SAFE_WORKFLOW_NAME_PATTERN` did
9+
not permit `(`, `)`, `[` or `]`, so any workflow inside a route group
10+
(`app/(dashboard)/…`) or a dynamic segment (`app/[teamId]/…`, `app/[...slug]/…`)
11+
threw `Invalid workflow name` before it could be enqueued, with no way to
12+
override the generated name.
13+
14+
These characters are inert in the queue name the pattern guards: `ValidQueueName`
15+
already accepts any suffix after its prefix, and the name is never interpolated
16+
into a URL or a SQL identifier.

‎packages/core/src/runtime/helpers.test.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,32 @@ describe('getWorkflowQueueName', () => {
151151
);
152152
});
153153

154+
it('should allow parentheses for Next.js route groups', () => {
155+
expect(
156+
getWorkflowQueueName(
157+
'workflow//./app/(marketing)/workflows/checkout.ts//processOrder'
158+
)
159+
).toBe(
160+
'__wkf_workflow_workflow//./app/(marketing)/workflows/checkout.ts//processOrder'
161+
);
162+
});
163+
164+
it('should allow square brackets for Next.js dynamic segments', () => {
165+
expect(
166+
getWorkflowQueueName(
167+
'workflow//./app/[teamId]/workflows/sync.ts//syncTeam'
168+
)
169+
).toBe(
170+
'__wkf_workflow_workflow//./app/[teamId]/workflows/sync.ts//syncTeam'
171+
);
172+
expect(
173+
getWorkflowQueueName('workflow//./app/[...slug]/workflows/x.ts//run')
174+
).toBe('__wkf_workflow_workflow//./app/[...slug]/workflows/x.ts//run');
175+
expect(
176+
getWorkflowQueueName('workflow//./app/[[...slug]]/workflows/x.ts//run')
177+
).toBe('__wkf_workflow_workflow//./app/[[...slug]]/workflows/x.ts//run');
178+
});
179+
154180
it('should throw for names containing spaces', () => {
155181
expect(() => getWorkflowQueueName('my workflow')).toThrow(
156182
'Invalid workflow name'

‎packages/core/src/runtime/helpers.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ const DEFAULT_HEALTH_CHECK_TIMEOUT = 30_000;
4343
/**
4444
* Pattern for safe workflow names. Only allows alphanumeric characters,
4545
* underscores, hyphens, dots, forward slashes (for namespaced workflows),
46-
* and at signs (for scoped packages).
46+
* at signs (for scoped packages), and parentheses and square brackets (for
47+
* Next.js route groups and dynamic segments, which appear verbatim in the
48+
* module path a workflow name is derived from).
4749
*/
48-
const SAFE_WORKFLOW_NAME_PATTERN = /^[a-zA-Z0-9_\-./@]+$/;
50+
const SAFE_WORKFLOW_NAME_PATTERN = /^[a-zA-Z0-9_\-./@()[\]]+$/;
4951

5052
/**
5153
* Validates a workflow name and returns the corresponding queue name.
@@ -58,7 +60,7 @@ export function getWorkflowQueueName(
5860
): ValidQueueName {
5961
if (!SAFE_WORKFLOW_NAME_PATTERN.test(workflowName)) {
6062
throw new Error(
61-
`Invalid workflow name "${workflowName}": must only contain alphanumeric characters, underscores, hyphens, dots, forward slashes, or at signs`
63+
`Invalid workflow name "${workflowName}": must only contain alphanumeric characters, underscores, hyphens, dots, forward slashes, at signs, parentheses, or square brackets`
6264
);
6365
}
6466
const prefix = getQueueTopicPrefix(

0 commit comments

Comments
 (0)