@teleporthq/teleport-plugin-next-workflows 0.43.37 → 0.43.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/global-workflows-handler-map-integrity.test.ts +15 -2
- package/__tests__/resolve-handler-entry-name.test.ts +130 -0
- package/dist/cjs/api-route-generator.d.ts.map +1 -1
- package/dist/cjs/api-route-generator.js +73 -47
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/nodes/types.d.ts +1 -0
- package/dist/cjs/nodes/types.d.ts.map +1 -1
- package/dist/cjs/nodes/types.js +81 -1
- package/dist/cjs/nodes/types.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/workflow-project-plugin.d.ts +0 -1
- package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-project-plugin.js +26 -31
- package/dist/cjs/workflow-project-plugin.js.map +1 -1
- package/dist/esm/api-route-generator.d.ts.map +1 -1
- package/dist/esm/api-route-generator.js +73 -47
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/nodes/types.d.ts +1 -0
- package/dist/esm/nodes/types.d.ts.map +1 -1
- package/dist/esm/nodes/types.js +79 -0
- package/dist/esm/nodes/types.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/workflow-project-plugin.d.ts +0 -1
- package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-project-plugin.js +26 -31
- package/dist/esm/workflow-project-plugin.js.map +1 -1
- package/package.json +2 -2
- package/src/api-route-generator.ts +85 -64
- package/src/nodes/types.ts +83 -0
- package/src/workflow-project-plugin.ts +26 -36
package/src/nodes/types.ts
CHANGED
|
@@ -4,6 +4,89 @@ export function handlerToString(fn: HandlerFn): string {
|
|
|
4
4
|
return fn.toString()
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
// Resolves the name a handler's entry function is ACTUALLY declared with in
|
|
8
|
+
// `source`, rather than assuming it always equals the
|
|
9
|
+
// `nodeType.replace(/-/g, '_')` convention name.
|
|
10
|
+
//
|
|
11
|
+
// Handlers built from `handlerToString(fn)` on a real function embed
|
|
12
|
+
// `fn.toString()` — a snapshot of whatever `fn` was actually named at the
|
|
13
|
+
// moment it's read. When this package is bundled and minified by a consumer
|
|
14
|
+
// (e.g. teleport-gui's browser packer worker, built with Next.js/Terser), the
|
|
15
|
+
// minifier freely renames `fn`'s declaration: nothing in the bundle calls it
|
|
16
|
+
// by name, only this runtime `.toString()` read does, which is invisible to
|
|
17
|
+
// the minifier. Callers that then reference the embedded handler by the
|
|
18
|
+
// static convention name get a "Collecting page data"/prerender
|
|
19
|
+
// ReferenceError, since local dev never runs a minifier.
|
|
20
|
+
//
|
|
21
|
+
// Handlers composed from string-literal templates (not `fn.toString()`) are
|
|
22
|
+
// immune to that class of breakage — a minifier never rewrites the contents
|
|
23
|
+
// of a string literal — so the convention name is still correct for those;
|
|
24
|
+
// this only needs to look further when it's genuinely absent from `source`.
|
|
25
|
+
export function resolveHandlerEntryName(source: string, nodeType: string): string {
|
|
26
|
+
const conventionName = nodeType.replace(/-/g, '_')
|
|
27
|
+
const conventionNameUsed = new RegExp(`(?:^|[^\\w$])${conventionName}\\s*\\(`).test(source)
|
|
28
|
+
if (conventionNameUsed) {
|
|
29
|
+
return conventionName
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// `source` came from `handlerToString(fn)`/`fn.toString()` on a real,
|
|
33
|
+
// possibly-minified function, and may be concatenated with OTHER
|
|
34
|
+
// `.toString()`'d functions:
|
|
35
|
+
// - payment-charge-user.ts puts the entry FIRST, its own helpers after
|
|
36
|
+
// (`handlerToString(payment_charge_user) + '\n' + chargeWithStripe.toString() + ...`).
|
|
37
|
+
// - ai-custom-prompt.ts puts shared AI-provider utils BEFORE the entry
|
|
38
|
+
// (`generateAIProviderUtils() + '\n\n' + ai_custom_prompt.toString()`),
|
|
39
|
+
// where each util is `wrapWithGuard`-wrapped: `var X = typeof X !==
|
|
40
|
+
// 'undefined' ? X : function Y() {...};` — a function EXPRESSION
|
|
41
|
+
// embedded in a ternary, not a statement-level declaration.
|
|
42
|
+
// So the entry is not reliably first or last by position across handlers,
|
|
43
|
+
// and helper functions can outnumber (or precede) the entry. What IS
|
|
44
|
+
// reliable: every real entry point has exactly the HandlerFn signature —
|
|
45
|
+
// 2 params (config, context) or 3 (config, context, streamCallback) — by
|
|
46
|
+
// contract, while a handler's OWN internal helpers only coincidentally
|
|
47
|
+
// share that arity (e.g. general-rate-limiter's __checkRateLimit takes 3
|
|
48
|
+
// params too). So: collect every statement-level function declaration
|
|
49
|
+
// (skipping ternary-embedded expressions like wrapWithGuard's), and prefer
|
|
50
|
+
// an exactly-2-param candidate, then an exactly-3-param one, falling back
|
|
51
|
+
// to positional order only if nothing matches the entry-point arity.
|
|
52
|
+
const declPattern = /(?:async\s+)?function\s+([A-Za-z0-9_$]+)\s*\(([^)]*)\)/g
|
|
53
|
+
const candidates: string[] = []
|
|
54
|
+
let twoParamCandidate: string | undefined
|
|
55
|
+
let threeParamCandidate: string | undefined
|
|
56
|
+
let declMatch: RegExpExecArray | null = declPattern.exec(source)
|
|
57
|
+
while (declMatch !== null) {
|
|
58
|
+
const precedingNonSpace = source.slice(0, declMatch.index).trimEnd().slice(-1)
|
|
59
|
+
if (precedingNonSpace === ':' || precedingNonSpace === '?') {
|
|
60
|
+
declMatch = declPattern.exec(source)
|
|
61
|
+
continue // function EXPRESSION embedded in a ternary (e.g. wrapWithGuard's re-declaration guard)
|
|
62
|
+
}
|
|
63
|
+
const name = declMatch[1]
|
|
64
|
+
candidates.push(name)
|
|
65
|
+
const params = declMatch[2].trim()
|
|
66
|
+
const paramCount = params === '' ? 0 : params.split(',').length
|
|
67
|
+
if (paramCount === 2 && twoParamCandidate === undefined) {
|
|
68
|
+
twoParamCandidate = name
|
|
69
|
+
} else if (paramCount === 3 && threeParamCandidate === undefined) {
|
|
70
|
+
threeParamCandidate = name
|
|
71
|
+
}
|
|
72
|
+
declMatch = declPattern.exec(source)
|
|
73
|
+
}
|
|
74
|
+
if (twoParamCandidate !== undefined) {
|
|
75
|
+
return twoParamCandidate
|
|
76
|
+
}
|
|
77
|
+
if (threeParamCandidate !== undefined) {
|
|
78
|
+
return threeParamCandidate
|
|
79
|
+
}
|
|
80
|
+
if (candidates.length > 0) {
|
|
81
|
+
return candidates[0]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Could not resolve the entry function for workflow node type "${nodeType}" — ` +
|
|
86
|
+
`expected a "${conventionName}" declaration or a single toString()'d function, found neither.`
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
7
90
|
export interface NodeHandlerGenerator {
|
|
8
91
|
nodeType: string
|
|
9
92
|
executionEnv: 'client' | 'server' | 'universal'
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
collectUsedRealtimeActionTypes,
|
|
30
30
|
} from './graph-utils'
|
|
31
31
|
import { nodeRegistry } from './nodes'
|
|
32
|
+
import { resolveHandlerEntryName } from './nodes/types'
|
|
32
33
|
import {
|
|
33
34
|
generateClientRuntimeCode,
|
|
34
35
|
generateServerRuntimeCode,
|
|
@@ -933,7 +934,7 @@ export class NextWorkflowProjectPlugin implements ProjectPlugin {
|
|
|
933
934
|
const entries = exportNames
|
|
934
935
|
.map((t, index) => {
|
|
935
936
|
const source = handlers[index].trim()
|
|
936
|
-
const entryFn =
|
|
937
|
+
const entryFn = resolveHandlerEntryName(source, t)
|
|
937
938
|
return ` '${t}': (function () {\n${source}\nreturn ${entryFn};\n})()`
|
|
938
939
|
})
|
|
939
940
|
.join(',\n')
|
|
@@ -946,38 +947,6 @@ ${entries}
|
|
|
946
947
|
`
|
|
947
948
|
}
|
|
948
949
|
|
|
949
|
-
// Resolves the name a handler's entry function is ACTUALLY declared with in
|
|
950
|
-
// `source`, rather than assuming it always equals the
|
|
951
|
-
// `nodeType.replace(/-/g, '_')` convention name (see the comment above
|
|
952
|
-
// `generateNodeHandlerFile`'s `entries` for why that assumption can break
|
|
953
|
-
// under minification).
|
|
954
|
-
//
|
|
955
|
-
// Handlers composed from string-literal templates (not `fn.toString()`) are
|
|
956
|
-
// immune to that class of breakage — a minifier never rewrites the contents
|
|
957
|
-
// of a string literal — so the convention name is still correct for those;
|
|
958
|
-
// this only needs to look further when it's genuinely absent from `source`.
|
|
959
|
-
private resolveHandlerEntryName(source: string, nodeType: string): string {
|
|
960
|
-
const conventionName = nodeType.replace(/-/g, '_')
|
|
961
|
-
const conventionNameUsed = new RegExp(`(?:^|[^\\w$])${conventionName}\\s*\\(`).test(source)
|
|
962
|
-
if (conventionNameUsed) {
|
|
963
|
-
return conventionName
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
// `source` came from `handlerToString(fn)` on a real, possibly-minified
|
|
967
|
-
// function — its ENTIRE text is that one function's declaration, so
|
|
968
|
-
// whatever name follows the leading `function`/`async function` keyword
|
|
969
|
-
// is the name it was actually assigned at runtime.
|
|
970
|
-
const leadingDeclaration = source.match(/^(?:async\s+)?function\s*([A-Za-z0-9_$]+)\s*\(/)
|
|
971
|
-
if (leadingDeclaration) {
|
|
972
|
-
return leadingDeclaration[1]
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
throw new Error(
|
|
976
|
-
`generateNodeHandlerFile: could not resolve the entry function for workflow node type "${nodeType}" — ` +
|
|
977
|
-
`expected a "${conventionName}" declaration or a single toString()'d function, found neither.`
|
|
978
|
-
)
|
|
979
|
-
}
|
|
980
|
-
|
|
981
950
|
private getDefaultRouteUrl(uidl: ProjectUIDL, strategy: ProjectStrategy): string | null {
|
|
982
951
|
const routeDef = uidl.root?.stateDefinitions?.route
|
|
983
952
|
if (!routeDef?.values || !routeDef.defaultValue) {
|
|
@@ -1578,8 +1547,31 @@ module.exports = __customNodeRegistry;
|
|
|
1578
1547
|
}
|
|
1579
1548
|
})
|
|
1580
1549
|
|
|
1550
|
+
// See resolveHandlerEntryName: handlers built from `handlerToString(fn)`
|
|
1551
|
+
// on a real function (e.g. general-if-statement) can have `fn` renamed by
|
|
1552
|
+
// a consumer's own minifier (nothing in the bundle calls it by name, only
|
|
1553
|
+
// this runtime `.toString()` read does) — so the reference below must
|
|
1554
|
+
// match whatever `handlers[index]` actually declares, not the
|
|
1555
|
+
// `t.replace(/-/g, '_')` convention name.
|
|
1556
|
+
//
|
|
1557
|
+
// Each entry is ALSO isolated in its own IIFE, rather than declaring every
|
|
1558
|
+
// handler as a bare sibling statement inside `useGlobalWorkflows()`. Two
|
|
1559
|
+
// DIFFERENT node types are minified independently (each in its own
|
|
1560
|
+
// source file), so their real declared names can coincidentally collide —
|
|
1561
|
+
// e.g. state-update-local-state and payment-cancel-plan can both
|
|
1562
|
+
// legitimately mangle down to the same short name. Declared as siblings
|
|
1563
|
+
// in one shared function body, the second declaration would silently
|
|
1564
|
+
// shadow the first, so BOTH map entries end up pointing at the SAME
|
|
1565
|
+
// (wrong-for-one-of-them) function — a silent wrong-handler-executes bug,
|
|
1566
|
+
// not even a crash. An IIFE per entry gives every handler its own scope,
|
|
1567
|
+
// exactly like generateNodeHandlerFile already does, so a same-named
|
|
1568
|
+
// collision between two unrelated handlers can never shadow each other.
|
|
1581
1569
|
const handlerEntries = emittedHandlerTypes
|
|
1582
|
-
.map((t) =>
|
|
1570
|
+
.map((t, index) => {
|
|
1571
|
+
const source = handlers[index].trim()
|
|
1572
|
+
const entryFn = resolveHandlerEntryName(source, t)
|
|
1573
|
+
return ` '${t}': (function () {\n${source}\nreturn ${entryFn};\n})()`
|
|
1574
|
+
})
|
|
1583
1575
|
.join(',\n')
|
|
1584
1576
|
|
|
1585
1577
|
// NOTE: this module is pulled into the GLOBAL client bundle (every page,
|
|
@@ -1597,8 +1589,6 @@ import workflowRuntime from './runtime';
|
|
|
1597
1589
|
const executeWorkflowWithSegments = workflowRuntime.executeWorkflowWithSegments;
|
|
1598
1590
|
|
|
1599
1591
|
export function useGlobalWorkflows() {
|
|
1600
|
-
${handlers.join('\n')}
|
|
1601
|
-
|
|
1602
1592
|
const clientNodeHandlers = {
|
|
1603
1593
|
${handlerEntries}
|
|
1604
1594
|
};
|