@teleporthq/teleport-plugin-next-workflows 0.43.47 → 0.43.49
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__/data-node-not-awaited.test.ts +12 -1
- package/__tests__/ecommerce-get-settings-client.test.ts +383 -0
- package/__tests__/runtime-stringified-boolean-truthiness.test.ts +76 -0
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +24 -3
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/nodes/account/account-get-current.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-get-current.js +27 -7
- package/dist/cjs/nodes/account/account-get-current.js.map +1 -1
- package/dist/cjs/nodes/ecommerce/ecommerce-get-settings.d.ts.map +1 -1
- package/dist/cjs/nodes/ecommerce/ecommerce-get-settings.js +59 -1
- package/dist/cjs/nodes/ecommerce/ecommerce-get-settings.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-project-plugin.js +11 -1
- package/dist/cjs/workflow-project-plugin.js.map +1 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +24 -3
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/nodes/account/account-get-current.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-get-current.js +27 -7
- package/dist/esm/nodes/account/account-get-current.js.map +1 -1
- package/dist/esm/nodes/ecommerce/ecommerce-get-settings.d.ts.map +1 -1
- package/dist/esm/nodes/ecommerce/ecommerce-get-settings.js +59 -1
- package/dist/esm/nodes/ecommerce/ecommerce-get-settings.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-project-plugin.js +11 -1
- package/dist/esm/workflow-project-plugin.js.map +1 -1
- package/package.json +3 -3
- package/src/executor-generator.ts +24 -3
- package/src/nodes/account/account-get-current.ts +27 -8
- package/src/nodes/ecommerce/ecommerce-get-settings.ts +62 -3
- package/src/workflow-project-plugin.ts +11 -1
|
@@ -476,12 +476,23 @@ describe('generated client runtime', () => {
|
|
|
476
476
|
const client = generateClientRuntimeCode()
|
|
477
477
|
|
|
478
478
|
it('dispatches an all-fire-and-forget segment without awaiting the round trip', () => {
|
|
479
|
-
expect(client).toContain('if (
|
|
479
|
+
expect(client).toContain('if (segIsFireAndForget) {')
|
|
480
480
|
expect(client).toContain('callServerSegment(ffUrl, context).catch(')
|
|
481
481
|
// The nodes still get their null entries so downstream reads are defined.
|
|
482
482
|
expect(client).toContain('context[seg.nodes[ffi].id] = null;')
|
|
483
483
|
})
|
|
484
484
|
|
|
485
|
+
it('upgrades a mixed segment to fire-and-forget when the awaited nodes were branch-skipped', () => {
|
|
486
|
+
// Static flag first, then the dynamic check over the LIVE (non-skipped)
|
|
487
|
+
// nodes — a mixed segment whose awaited nodes sit on a branch that was
|
|
488
|
+
// not taken has only fire-and-forget work left to do.
|
|
489
|
+
expect(client).toContain(
|
|
490
|
+
'var segLiveNodes = seg.nodes.filter(function(n) { return !(context.__skippedNodes && context.__skippedNodes[n.id]); });'
|
|
491
|
+
)
|
|
492
|
+
expect(client).toContain('var segIsFireAndForget = seg.fireAndForget ||')
|
|
493
|
+
expect(client).toContain('segLiveNodes.every(utils.isFireAndForgetNode)')
|
|
494
|
+
})
|
|
495
|
+
|
|
485
496
|
it('keeps awaiting a normal server segment', () => {
|
|
486
497
|
expect(client).toContain('const serverResults = await callServerSegment(url, context);')
|
|
487
498
|
})
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { splitIntoSegments, generateSharedRuntimeUtilsCode } from '../src'
|
|
2
|
+
import { nodeRegistry } from '../src/nodes'
|
|
3
|
+
import { NextWorkflowProjectPlugin } from '../src/workflow-project-plugin'
|
|
4
|
+
import { UIDLWorkflow } from '@teleporthq/teleport-types'
|
|
5
|
+
|
|
6
|
+
// The /api/ecommerce/settings route returns a literal baked at generation time
|
|
7
|
+
// — within a deployment it can never say anything else. Classifying the
|
|
8
|
+
// `ecommerce-get-settings` node as a SERVER node therefore bought nothing and
|
|
9
|
+
// cost a full blocking round trip per click, PLUS it fragmented the
|
|
10
|
+
// surrounding workflow into extra server segments (one round trip each).
|
|
11
|
+
//
|
|
12
|
+
// The contract this file pins down:
|
|
13
|
+
// 1. the node is client-classified, so the segment splitter never mints a
|
|
14
|
+
// server segment for it;
|
|
15
|
+
// 2. the client handler resolves from the baked window global first, then a
|
|
16
|
+
// memoized fetch, then the route;
|
|
17
|
+
// 3. cron/webhook routes (which execute every node server-side, with no
|
|
18
|
+
// window) still get a real server implementation via
|
|
19
|
+
// generateServerHandler — never the client-only stub;
|
|
20
|
+
// 4. the add-to-cart / cart-increment workflow shapes collapse to exactly
|
|
21
|
+
// ONE server segment: the product data-select.
|
|
22
|
+
|
|
23
|
+
describe('ecommerce-get-settings node classification', () => {
|
|
24
|
+
const generator = nodeRegistry['ecommerce-get-settings']
|
|
25
|
+
|
|
26
|
+
it('is client-classified', () => {
|
|
27
|
+
expect(generator.executionEnv).toBe('client')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('client handler reads the baked global, memoizes the fetch fallback', () => {
|
|
31
|
+
const source = generator.generateHandler()
|
|
32
|
+
expect(source).toContain('__teleportEcommerceSettings')
|
|
33
|
+
expect(source).toContain('__teleportEcommerceSettingsFetched')
|
|
34
|
+
expect(source).toContain('/api/ecommerce/settings')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('keeps a real server implementation for cron/webhook contexts', () => {
|
|
38
|
+
expect(generator.generateServerHandler).toBeDefined()
|
|
39
|
+
const source = generator.generateServerHandler!()
|
|
40
|
+
expect(source).toContain('/api/ecommerce/settings')
|
|
41
|
+
expect(source).not.toContain('window')
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('ecommerce-get-settings client handler behaviour', () => {
|
|
46
|
+
const buildHandler = () => {
|
|
47
|
+
const source = nodeRegistry['ecommerce-get-settings'].generateHandler()
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
49
|
+
return new Function(`return (${source})`)() as (
|
|
50
|
+
config: unknown,
|
|
51
|
+
context: Record<string, unknown>
|
|
52
|
+
) => Promise<Record<string, unknown>>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const globalAny = global as unknown as Record<string, unknown>
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
delete globalAny.window
|
|
59
|
+
delete globalAny.fetch
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('returns the baked global without fetching', async () => {
|
|
63
|
+
const baked = { guestCheckout: true, stockManagement: true, maxQuantityPerProduct: null }
|
|
64
|
+
globalAny.window = { __teleportEcommerceSettings: baked }
|
|
65
|
+
globalAny.fetch = jest.fn()
|
|
66
|
+
|
|
67
|
+
const result = await buildHandler()({}, {})
|
|
68
|
+
expect(result).toBe(baked)
|
|
69
|
+
expect(globalAny.fetch).not.toHaveBeenCalled()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('falls back to the route and memoizes a successful response', async () => {
|
|
73
|
+
const payload = { guestCheckout: false, stockManagement: true }
|
|
74
|
+
globalAny.window = {}
|
|
75
|
+
const fetchMock = jest.fn().mockResolvedValue({
|
|
76
|
+
ok: true,
|
|
77
|
+
json: async () => payload,
|
|
78
|
+
})
|
|
79
|
+
globalAny.fetch = fetchMock
|
|
80
|
+
|
|
81
|
+
const handler = buildHandler()
|
|
82
|
+
const first = await handler({}, {})
|
|
83
|
+
expect(first).toEqual(payload)
|
|
84
|
+
expect(fetchMock).toHaveBeenCalledTimes(1)
|
|
85
|
+
|
|
86
|
+
// Second call resolves from the memo — the route is immutable within a
|
|
87
|
+
// deployment, so this can never serve stale data.
|
|
88
|
+
const second = await handler({}, {})
|
|
89
|
+
expect(second).toEqual(payload)
|
|
90
|
+
expect(fetchMock).toHaveBeenCalledTimes(1)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('does not memoize a failed response', async () => {
|
|
94
|
+
globalAny.window = {}
|
|
95
|
+
const fetchMock = jest
|
|
96
|
+
.fn()
|
|
97
|
+
.mockResolvedValueOnce({ ok: false })
|
|
98
|
+
.mockResolvedValueOnce({ ok: true, json: async () => ({ guestCheckout: true }) })
|
|
99
|
+
globalAny.fetch = fetchMock
|
|
100
|
+
|
|
101
|
+
const handler = buildHandler()
|
|
102
|
+
const first = await handler({}, {})
|
|
103
|
+
expect(first).toEqual({ error: 'Failed to load e-commerce settings' })
|
|
104
|
+
|
|
105
|
+
const second = await handler({}, {})
|
|
106
|
+
expect(second).toEqual({ guestCheckout: true })
|
|
107
|
+
expect(fetchMock).toHaveBeenCalledTimes(2)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('segment shapes around a client settings node', () => {
|
|
112
|
+
const node = (id: string, type: string, config: Record<string, unknown> = {}) => ({
|
|
113
|
+
id,
|
|
114
|
+
type,
|
|
115
|
+
label: id,
|
|
116
|
+
config,
|
|
117
|
+
stepNumber: 0,
|
|
118
|
+
})
|
|
119
|
+
const edge = (source: string, target: string, sourceHandle?: string) => ({
|
|
120
|
+
id: `${source}->${target}`,
|
|
121
|
+
source,
|
|
122
|
+
target,
|
|
123
|
+
...(sourceHandle ? { sourceHandle } : {}),
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('cart-increment shape: ONE server segment holding only the data-select', () => {
|
|
127
|
+
// trigger → data-select (server) → get-settings → validate (client js) → if
|
|
128
|
+
const workflow = {
|
|
129
|
+
nodes: [
|
|
130
|
+
node('trigger', 'event-element-clicked', { isFirstNode: true }),
|
|
131
|
+
node('fetch-product', 'data-select', { tableName: 'teleport_products' }),
|
|
132
|
+
node('get-settings', 'ecommerce-get-settings'),
|
|
133
|
+
node('validate', 'general-custom-js', {
|
|
134
|
+
code: 'function customHandler(p){}',
|
|
135
|
+
context: 'client',
|
|
136
|
+
}),
|
|
137
|
+
node('gate', 'general-if-statement', {}),
|
|
138
|
+
],
|
|
139
|
+
edges: [
|
|
140
|
+
edge('trigger', 'fetch-product'),
|
|
141
|
+
edge('fetch-product', 'get-settings'),
|
|
142
|
+
edge('get-settings', 'validate'),
|
|
143
|
+
edge('validate', 'gate'),
|
|
144
|
+
],
|
|
145
|
+
} as unknown as UIDLWorkflow
|
|
146
|
+
|
|
147
|
+
const segments = splitIntoSegments(workflow)
|
|
148
|
+
const serverSegments = segments.filter((s) => s.env === 'server')
|
|
149
|
+
expect(serverSegments).toHaveLength(1)
|
|
150
|
+
expect(serverSegments[0].nodes.map((n) => n.id)).toEqual(['fetch-product'])
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('add-to-cart shape: settings between client nodes mints NO server segment', () => {
|
|
154
|
+
// extract (client js) → get-settings → guest-check (client js) → if →
|
|
155
|
+
// [redirect | data-select] — only the data-select is a server segment.
|
|
156
|
+
const workflow = {
|
|
157
|
+
nodes: [
|
|
158
|
+
node('extract', 'general-custom-js', {
|
|
159
|
+
code: 'function customHandler(p){}',
|
|
160
|
+
context: 'client',
|
|
161
|
+
}),
|
|
162
|
+
node('get-settings', 'ecommerce-get-settings'),
|
|
163
|
+
node('guest-check', 'general-custom-js', {
|
|
164
|
+
code: 'function customHandler(p){}',
|
|
165
|
+
context: 'client',
|
|
166
|
+
}),
|
|
167
|
+
node('gate', 'general-if-statement', {}),
|
|
168
|
+
node('redirect', 'navigation-navigate-to-url', { url: '/sign-in' }),
|
|
169
|
+
node('fetch-product', 'data-select', { tableName: 'teleport_products' }),
|
|
170
|
+
],
|
|
171
|
+
edges: [
|
|
172
|
+
edge('extract', 'get-settings'),
|
|
173
|
+
edge('get-settings', 'guest-check'),
|
|
174
|
+
edge('guest-check', 'gate'),
|
|
175
|
+
edge('gate', 'redirect', 'true'),
|
|
176
|
+
edge('gate', 'fetch-product', 'false'),
|
|
177
|
+
],
|
|
178
|
+
} as unknown as UIDLWorkflow
|
|
179
|
+
|
|
180
|
+
const segments = splitIntoSegments(workflow)
|
|
181
|
+
const serverSegments = segments.filter((s) => s.env === 'server')
|
|
182
|
+
expect(serverSegments).toHaveLength(1)
|
|
183
|
+
expect(serverSegments[0].nodes.map((n) => n.id)).toEqual(['fetch-product'])
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// ─── Dynamic fire-and-forget upgrade, end to end ─────────────────────────────
|
|
188
|
+
//
|
|
189
|
+
// Mirrors the "Resolve Current User" custom node's anonymous half: an
|
|
190
|
+
// if-statement routes a RETURNING guest to a fire-and-forget users-row
|
|
191
|
+
// re-ensure and a NEW guest to an awaited create. Both data nodes land in one
|
|
192
|
+
// server segment, so the segment is statically mixed (fireAndForget: false).
|
|
193
|
+
// When the taken branch leaves only fire-and-forget work live, the runtime
|
|
194
|
+
// must dispatch the segment WITHOUT awaiting; when the awaited branch is
|
|
195
|
+
// taken, it must still block.
|
|
196
|
+
describe('generated custom node — mixed segment upgrades to fire-and-forget per branch', () => {
|
|
197
|
+
const customNodes = {
|
|
198
|
+
'cn-resolve': {
|
|
199
|
+
id: 'cn-resolve',
|
|
200
|
+
name: 'Resolve-ish',
|
|
201
|
+
parameters: [],
|
|
202
|
+
nodes: [
|
|
203
|
+
{
|
|
204
|
+
id: 'extract',
|
|
205
|
+
type: 'general-custom-js',
|
|
206
|
+
label: 'Extract',
|
|
207
|
+
config: {
|
|
208
|
+
code: 'function customHandler(p, q) { return { returning: true } }',
|
|
209
|
+
context: 'client',
|
|
210
|
+
},
|
|
211
|
+
executionEnv: 'client',
|
|
212
|
+
stepNumber: 0,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'gate',
|
|
216
|
+
type: 'general-if-statement',
|
|
217
|
+
label: 'Returning Guest?',
|
|
218
|
+
config: {
|
|
219
|
+
conditionType: 'simple-comparison',
|
|
220
|
+
leftValue: {
|
|
221
|
+
type: 'workflowContext',
|
|
222
|
+
nodeId: 'extract',
|
|
223
|
+
path: ['extract', 'returning'],
|
|
224
|
+
},
|
|
225
|
+
operator: '===',
|
|
226
|
+
rightValue: true,
|
|
227
|
+
},
|
|
228
|
+
executionEnv: 'client',
|
|
229
|
+
stepNumber: 1,
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: 'ensure',
|
|
233
|
+
type: 'data-create-item',
|
|
234
|
+
label: 'Ensure Row',
|
|
235
|
+
config: { awaitResult: false },
|
|
236
|
+
executionEnv: 'server',
|
|
237
|
+
stepNumber: 2,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: 'create',
|
|
241
|
+
type: 'data-create-item',
|
|
242
|
+
label: 'Create Row',
|
|
243
|
+
config: {},
|
|
244
|
+
executionEnv: 'server',
|
|
245
|
+
stepNumber: 2,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: 'return-stored',
|
|
249
|
+
type: 'general-custom-js',
|
|
250
|
+
label: 'Return Stored',
|
|
251
|
+
config: {
|
|
252
|
+
code: 'function customHandler(p, q) { return { branch: "stored" } }',
|
|
253
|
+
context: 'client',
|
|
254
|
+
},
|
|
255
|
+
executionEnv: 'client',
|
|
256
|
+
stepNumber: 3,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'return-new',
|
|
260
|
+
type: 'general-custom-js',
|
|
261
|
+
label: 'Return New',
|
|
262
|
+
config: {
|
|
263
|
+
code: 'function customHandler(p, q) { return { branch: "new" } }',
|
|
264
|
+
context: 'client',
|
|
265
|
+
},
|
|
266
|
+
executionEnv: 'client',
|
|
267
|
+
stepNumber: 3,
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
edges: [
|
|
271
|
+
{ id: 'e1', source: 'extract', target: 'gate' },
|
|
272
|
+
{ id: 'e2', source: 'gate', target: 'ensure', sourceHandle: 'true' },
|
|
273
|
+
{ id: 'e3', source: 'gate', target: 'create', sourceHandle: 'false' },
|
|
274
|
+
{ id: 'e4', source: 'ensure', target: 'return-stored' },
|
|
275
|
+
{ id: 'e5', source: 'create', target: 'return-new' },
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
interface BootedCustomNode {
|
|
281
|
+
run: (
|
|
282
|
+
outerContext: Record<string, unknown>,
|
|
283
|
+
parameters: Record<string, unknown>,
|
|
284
|
+
handlers: Record<string, unknown>
|
|
285
|
+
) => Promise<unknown>
|
|
286
|
+
segmentCalls: string[]
|
|
287
|
+
resolveSegment: () => void
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const bootCustomNode = (): BootedCustomNode => {
|
|
291
|
+
const plugin = new NextWorkflowProjectPlugin() as unknown as {
|
|
292
|
+
generateCustomNodesFile: (
|
|
293
|
+
nodes: Record<string, unknown>,
|
|
294
|
+
urls: Record<string, Record<string, string>>
|
|
295
|
+
) => string
|
|
296
|
+
}
|
|
297
|
+
const source = plugin.generateCustomNodesFile(customNodes, {
|
|
298
|
+
'cn-resolve': { 'server-1': '/api/workflows/resolve-seg-1' },
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const utilsModule: { exports: Record<string, unknown> } = { exports: {} }
|
|
302
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
303
|
+
new Function('module', 'exports', generateSharedRuntimeUtilsCode())(
|
|
304
|
+
utilsModule,
|
|
305
|
+
utilsModule.exports
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
const segmentCalls: string[] = []
|
|
309
|
+
let releaseSegment: () => void = () => undefined
|
|
310
|
+
const segmentGate = new Promise<void>((resolve) => {
|
|
311
|
+
releaseSegment = resolve
|
|
312
|
+
})
|
|
313
|
+
const runtimeStub = {
|
|
314
|
+
findStreamingAINodes: () => ({}),
|
|
315
|
+
mergeServerResults: () => undefined,
|
|
316
|
+
callStreamingServerSegment: async () => ({}),
|
|
317
|
+
callServerSegment: async (url: string) => {
|
|
318
|
+
segmentCalls.push(url)
|
|
319
|
+
await segmentGate
|
|
320
|
+
return {}
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const requireStub = (id: string) =>
|
|
325
|
+
id === './runtime-utils' ? utilsModule.exports : id === './runtime' ? runtimeStub : {}
|
|
326
|
+
|
|
327
|
+
const moduleStub: { exports: Record<string, unknown> } = { exports: {} }
|
|
328
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
329
|
+
new Function('module', 'exports', 'require', source)(
|
|
330
|
+
moduleStub,
|
|
331
|
+
moduleStub.exports,
|
|
332
|
+
requireStub
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
const registry = moduleStub.exports as Record<string, BootedCustomNode['run']>
|
|
336
|
+
return { run: registry['cn-resolve'], segmentCalls, resolveSegment: releaseSegment }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const handlersFor = (returning: boolean) => ({
|
|
340
|
+
'general-custom-js': async (config: { code?: string }) => {
|
|
341
|
+
const code = config && config.code ? config.code : ''
|
|
342
|
+
if (code.includes('returning')) {
|
|
343
|
+
return { returning }
|
|
344
|
+
}
|
|
345
|
+
if (code.includes('stored')) {
|
|
346
|
+
return { branch: 'stored' }
|
|
347
|
+
}
|
|
348
|
+
return { branch: 'new' }
|
|
349
|
+
},
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('returning-guest branch (only fire-and-forget live) does not await the segment', async () => {
|
|
353
|
+
const booted = bootCustomNode()
|
|
354
|
+
|
|
355
|
+
// The gate promise is never released — if the runtime awaited the segment
|
|
356
|
+
// this would hang and jest would time the test out.
|
|
357
|
+
const result = (await booted.run({}, {}, handlersFor(true))) as Record<string, unknown>
|
|
358
|
+
|
|
359
|
+
expect(booted.segmentCalls).toEqual(['/api/workflows/resolve-seg-1'])
|
|
360
|
+
expect(result).toEqual({ branch: 'stored' })
|
|
361
|
+
|
|
362
|
+
booted.resolveSegment()
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
it('new-guest branch (awaited create live) still blocks on the segment', async () => {
|
|
366
|
+
const booted = bootCustomNode()
|
|
367
|
+
|
|
368
|
+
let settled = false
|
|
369
|
+
const running = booted.run({}, {}, handlersFor(false)).then((value) => {
|
|
370
|
+
settled = true
|
|
371
|
+
return value
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
// Give the runtime ample turns to (incorrectly) run to completion.
|
|
375
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
376
|
+
expect(booted.segmentCalls).toEqual(['/api/workflows/resolve-seg-1'])
|
|
377
|
+
expect(settled).toBe(false)
|
|
378
|
+
|
|
379
|
+
booted.resolveSegment()
|
|
380
|
+
const result = (await running) as Record<string, unknown>
|
|
381
|
+
expect(result).toEqual({ branch: 'new' })
|
|
382
|
+
})
|
|
383
|
+
})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { generateSharedRuntimeUtilsCode } from '../src'
|
|
2
|
+
|
|
3
|
+
// Producers that cannot type their output emit booleans as the strings
|
|
4
|
+
// "true"/"false" — the evaluate-auth custom-js most notably (see
|
|
5
|
+
// is-logged-in-gate.ts), plus any inspector text input. `is-true`/`is-false`
|
|
6
|
+
// and coerceForComparison already read those as booleans, but `is-truthy` was
|
|
7
|
+
// plain `!!left`, and the string "false" is truthy in JS: an is-truthy gate on
|
|
8
|
+
// isLoggedIn admitted EVERY visitor, and an is-falsy check never fired.
|
|
9
|
+
|
|
10
|
+
type SharedUtils = {
|
|
11
|
+
evaluateSingleComparison: (cfg: unknown, ctx: Record<string, unknown>) => boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function loadSharedRuntime(): SharedUtils {
|
|
15
|
+
const src = generateSharedRuntimeUtilsCode()
|
|
16
|
+
const wrapper: { exports: Record<string, unknown> } = { exports: {} }
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
18
|
+
new Function('module', 'exports', src)(wrapper, wrapper.exports)
|
|
19
|
+
return wrapper.exports as unknown as SharedUtils
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('workflow runtime truthiness of stringified booleans', () => {
|
|
23
|
+
const utils = loadSharedRuntime()
|
|
24
|
+
|
|
25
|
+
const isTruthy = (leftValue: unknown) =>
|
|
26
|
+
utils.evaluateSingleComparison({ leftValue, operator: 'is-truthy' }, {})
|
|
27
|
+
const isFalsy = (leftValue: unknown) =>
|
|
28
|
+
utils.evaluateSingleComparison({ leftValue, operator: 'is-falsy' }, {})
|
|
29
|
+
|
|
30
|
+
it('treats the string "false" as false, matching is-false and coerceForComparison', () => {
|
|
31
|
+
expect(isTruthy('false')).toBe(false)
|
|
32
|
+
expect(isFalsy('false')).toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('keeps the string "true" true', () => {
|
|
36
|
+
expect(isTruthy('true')).toBe(true)
|
|
37
|
+
expect(isFalsy('true')).toBe(false)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('agrees with is-true/is-false on the same value', () => {
|
|
41
|
+
for (const value of ['true', 'false', true, false]) {
|
|
42
|
+
expect(isTruthy(value)).toBe(
|
|
43
|
+
utils.evaluateSingleComparison({ leftValue: value, operator: 'is-true' }, {})
|
|
44
|
+
)
|
|
45
|
+
expect(isFalsy(value)).toBe(
|
|
46
|
+
utils.evaluateSingleComparison({ leftValue: value, operator: 'is-false' }, {})
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('leaves ordinary JS truthiness untouched', () => {
|
|
52
|
+
expect(isTruthy(true)).toBe(true)
|
|
53
|
+
expect(isTruthy(false)).toBe(false)
|
|
54
|
+
expect(isTruthy('anything')).toBe(true)
|
|
55
|
+
expect(isTruthy('')).toBe(false)
|
|
56
|
+
expect(isTruthy(0)).toBe(false)
|
|
57
|
+
expect(isTruthy(1)).toBe(true)
|
|
58
|
+
expect(isTruthy(null)).toBe(false)
|
|
59
|
+
expect(isTruthy(undefined)).toBe(false)
|
|
60
|
+
expect(isFalsy('')).toBe(true)
|
|
61
|
+
expect(isFalsy(0)).toBe(true)
|
|
62
|
+
expect(isFalsy('anything')).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('normalizes the snake_case and camelCase spellings the same way', () => {
|
|
66
|
+
expect(utils.evaluateSingleComparison({ leftValue: 'false', operator: 'is_truthy' }, {})).toBe(
|
|
67
|
+
false
|
|
68
|
+
)
|
|
69
|
+
expect(utils.evaluateSingleComparison({ leftValue: 'false', operator: 'isTruthy' }, {})).toBe(
|
|
70
|
+
false
|
|
71
|
+
)
|
|
72
|
+
expect(utils.evaluateSingleComparison({ leftValue: 'false', operator: 'is_falsy' }, {})).toBe(
|
|
73
|
+
true
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor-generator.d.ts","sourceRoot":"","sources":["../../src/executor-generator.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,QAAO,
|
|
1
|
+
{"version":3,"file":"executor-generator.d.ts","sourceRoot":"","sources":["../../src/executor-generator.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,QAAO,MAiuCjD,CAAA;AAED,eAAO,MAAM,yBAAyB,QAAO,MAqgB5C,CAAA;AAED,eAAO,MAAM,yBAAyB,QAAO,MAK5C,CAAA"}
|
|
@@ -543,6 +543,16 @@ function coerceForComparison(a, b) {
|
|
|
543
543
|
return [a, b];
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
+
// Several producers cannot type their output and emit booleans as the strings
|
|
547
|
+
// "true"/"false": the evaluate-auth custom-js, text inputs in the inspector,
|
|
548
|
+
// form fields. coerceForComparison and is-true/is-false already read those as
|
|
549
|
+
// booleans, so truthiness must agree — under a plain double-negation the
|
|
550
|
+
// string "false" is TRUTHY, which let an is-truthy gate on isLoggedIn admit
|
|
551
|
+
// every visitor (see is-logged-in-gate.ts).
|
|
552
|
+
function isStringifiedBoolean(value) {
|
|
553
|
+
return value === 'true' || value === 'false';
|
|
554
|
+
}
|
|
555
|
+
|
|
546
556
|
// The operator vocabulary is written by THREE producers that never agreed on
|
|
547
557
|
// a spelling: the worker canonicalizes to snake_case (is_not_empty,
|
|
548
558
|
// greater_than), the GUI editor emits camelCase named forms (startsWith,
|
|
@@ -587,8 +597,8 @@ function evaluateSingleComparison(config, context) {
|
|
|
587
597
|
case 'is-not-null': return left !== null && left !== undefined;
|
|
588
598
|
case 'is-true': return left === true || left === 'true';
|
|
589
599
|
case 'is-false': return left === false || left === 'false';
|
|
590
|
-
case 'is-truthy': return !!left;
|
|
591
|
-
case 'is-falsy': return !left;
|
|
600
|
+
case 'is-truthy': return isStringifiedBoolean(left) ? left === 'true' : !!left;
|
|
601
|
+
case 'is-falsy': return isStringifiedBoolean(left) ? left === 'false' : !left;
|
|
592
602
|
case 'matches-regex': try { return new RegExp(String(right)).test(String(left)); } catch(e) { return false; }
|
|
593
603
|
default:
|
|
594
604
|
console.warn('[workflow] Unknown comparison operator "' + config.operator + '" — falling back to loose equality');
|
|
@@ -1606,7 +1616,18 @@ async function executeWorkflowWithSegments(workflowConfig, triggerContext, clien
|
|
|
1606
1616
|
// still awaits each query before it responds; we simply ignore the
|
|
1607
1617
|
// response. Errors are logged, never routed to the error handler:
|
|
1608
1618
|
// the workflow already moved past this point.
|
|
1609
|
-
|
|
1619
|
+
//
|
|
1620
|
+
// seg.fireAndForget is computed STATICALLY (every node in the segment
|
|
1621
|
+
// is a fire-and-forget data node). A segment that MIXES fire-and-forget
|
|
1622
|
+
// and awaited nodes stays blocking statically — but when branch
|
|
1623
|
+
// skipping has already eliminated the awaited nodes at runtime, only
|
|
1624
|
+
// fire-and-forget work remains, so the same "nothing downstream can
|
|
1625
|
+
// read it" guarantee holds for the nodes that will actually run. The
|
|
1626
|
+
// dynamic check below upgrades exactly that case.
|
|
1627
|
+
var segLiveNodes = seg.nodes.filter(function(n) { return !(context.__skippedNodes && context.__skippedNodes[n.id]); });
|
|
1628
|
+
var segIsFireAndForget = seg.fireAndForget ||
|
|
1629
|
+
(segLiveNodes.length > 0 && segLiveNodes.every(utils.isFireAndForgetNode));
|
|
1630
|
+
if (segIsFireAndForget) {
|
|
1610
1631
|
const ffUrl = serverSegmentUrls[seg.id];
|
|
1611
1632
|
if (!ffUrl) throw new Error('No server URL for segment: ' + seg.id);
|
|
1612
1633
|
utils.registerPendingNodePromise(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor-generator.js","sourceRoot":"","sources":["../../src/executor-generator.ts"],"names":[],"mappings":";;;AAAO,MAAM,8BAA8B,GAAG,GAAW,EAAE;IACzD,OAAO
|
|
1
|
+
{"version":3,"file":"executor-generator.js","sourceRoot":"","sources":["../../src/executor-generator.ts"],"names":[],"mappings":";;;AAAO,MAAM,8BAA8B,GAAG,GAAW,EAAE;IACzD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+tCR,CAAA;AACD,CAAC,CAAA;AAjuCY,QAAA,8BAA8B,kCAiuC1C;AAEM,MAAM,yBAAyB,GAAG,GAAW,EAAE;IACpD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmgBR,CAAA;AACD,CAAC,CAAA;AArgBY,QAAA,yBAAyB,6BAqgBrC;AAEM,MAAM,yBAAyB,GAAG,GAAW,EAAE;IACpD,OAAO;;;CAGR,CAAA;AACD,CAAC,CAAA;AALY,QAAA,yBAAyB,6BAKrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-get-current.d.ts","sourceRoot":"","sources":["../../../../src/nodes/account/account-get-current.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmB,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"account-get-current.d.ts","sourceRoot":"","sources":["../../../../src/nodes/account/account-get-current.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmB,MAAM,UAAU,CAAA;AAuHhE,eAAO,MAAM,iBAAiB,EAAE,oBAS/B,CAAA"}
|
|
@@ -46,16 +46,36 @@ async function account_get_current(_config, context) {
|
|
|
46
46
|
// callback re-reads the `users` row) BEFORE the workflow's own request could
|
|
47
47
|
// start — for data the page had since it mounted.
|
|
48
48
|
//
|
|
49
|
-
// Only 'authenticated' short-circuits. '
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
49
|
+
// Only 'authenticated' short-circuits. 'unauthenticated' is also what
|
|
50
|
+
// next-auth reports when the provider's fetch FAILED, and treating a
|
|
51
|
+
// transient error as "signed out" would send a signed-in visitor down the
|
|
52
|
+
// guest branch. It falls through to the fetch — which for a guest is the
|
|
53
|
+
// cheap case anyway, since the jwt callback returns before touching the
|
|
54
|
+
// database when there is no token.
|
|
55
|
+
//
|
|
56
|
+
// 'loading' means the provider's FIRST /api/auth/session fetch is still in
|
|
57
|
+
// flight (a click that lands within ~a second of page load). Issuing our own
|
|
58
|
+
// fetch at that moment duplicates the exact request already on the wire —
|
|
59
|
+
// including its jwt-callback user-row read. Instead, briefly poll the bridge
|
|
60
|
+
// snapshot until the in-flight fetch settles; the bounded deadline means a
|
|
61
|
+
// hung provider fetch degrades to today's behaviour (our own fetch) instead
|
|
62
|
+
// of stalling the workflow.
|
|
55
63
|
if (typeof window !== 'undefined') {
|
|
56
64
|
try {
|
|
57
65
|
const bridge = window.__teleportNextAuth;
|
|
58
|
-
|
|
66
|
+
let snapshot = bridge && typeof bridge.getSession === 'function' ? bridge.getSession() : null;
|
|
67
|
+
if (snapshot && snapshot.status === 'loading') {
|
|
68
|
+
const deadline = Date.now() + 3000;
|
|
69
|
+
while (Date.now() < deadline) {
|
|
70
|
+
await new Promise(function (resolve) {
|
|
71
|
+
setTimeout(resolve, 50);
|
|
72
|
+
});
|
|
73
|
+
snapshot = bridge.getSession();
|
|
74
|
+
if (!snapshot || snapshot.status !== 'loading') {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
59
79
|
if (snapshot && snapshot.status === 'authenticated') {
|
|
60
80
|
const liveUser = snapshot.session && snapshot.session.user ? snapshot.session.user : null;
|
|
61
81
|
if (liveUser) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-get-current.js","sourceRoot":"","sources":["../../../../src/nodes/account/account-get-current.ts"],"names":[],"mappings":";;;AAAA,oCAAgE;AAEhE,KAAK,UAAU,mBAAmB,CAAC,OAAgB,EAAE,OAAgC;IACnF,MAAM,OAAO,GAAG,CAAC,OAAO,IAAK,OAAe,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IAC7D,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,4EAA4E;IAC5E,+BAA+B;IAC/B,6EAA6E;IAC7E,yEAAyE;IACzE,2DAA2D;IAC3D,MAAM,KAAK,GAAG,CAAC,IAAS,EAAE,EAAE;QAC1B,MAAM,CAAC,GAAQ,EAAE,CAAA;QACjB,IAAI,IAAI,EAAE;YACR,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAClC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;aACvB;SACF;QACD,CAAC,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,CAAA;QACrB,OAAO,CAAC,CAAA;IACV,CAAC,CAAA;IACD,mEAAmE;IACnE,gEAAgE;IAChE,MAAM,OAAO,GAAG,CAAC,IAAS,EAAE,EAAE;QAC5B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,OAAM;SACP;QACD,IAAI;YACF,IAAI,IAAI,EAAE;gBACR,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;aACxE;iBAAM;gBACL,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAA;aACrD;SACF;QAAC,OAAO,EAAE,EAAE,GAAE;IACjB,CAAC,CAAA;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,+EAA+E;IAC/E,uEAAuE;IACvE,6EAA6E;IAC7E,kDAAkD;IAClD,EAAE;IACF,
|
|
1
|
+
{"version":3,"file":"account-get-current.js","sourceRoot":"","sources":["../../../../src/nodes/account/account-get-current.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecommerce-get-settings.d.ts","sourceRoot":"","sources":["../../../../src/nodes/ecommerce/ecommerce-get-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmB,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"ecommerce-get-settings.d.ts","sourceRoot":"","sources":["../../../../src/nodes/ecommerce/ecommerce-get-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmB,MAAM,UAAU,CAAA;AAyEhE,eAAO,MAAM,oBAAoB,EAAE,oBAYlC,CAAA"}
|