@teleporthq/teleport-plugin-next-workflows 0.43.29 → 0.43.30
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__/_helpers/load-handler.ts +11 -1
- package/__tests__/file-storage-upload-picked-files.test.ts +135 -0
- package/__tests__/runtime-operator-aliases.test.ts +180 -0
- package/__tests__/runtime-template-token-resolution.test.ts +225 -0
- package/__tests__/state-update-array-type-guard.test.ts +172 -0
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +134 -5
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/nodes/data/data-count.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-count.js +16 -0
- package/dist/cjs/nodes/data/data-count.js.map +1 -1
- package/dist/cjs/nodes/data/data-create-item.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-create-item.js +20 -0
- package/dist/cjs/nodes/data/data-create-item.js.map +1 -1
- package/dist/cjs/nodes/data/data-delete-item.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-delete-item.js +19 -0
- package/dist/cjs/nodes/data/data-delete-item.js.map +1 -1
- package/dist/cjs/nodes/data/data-select.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-select.js +18 -0
- package/dist/cjs/nodes/data/data-select.js.map +1 -1
- package/dist/cjs/nodes/data/data-update-item.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-update-item.js +36 -0
- package/dist/cjs/nodes/data/data-update-item.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/workflow-component-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-component-plugin.js +56 -9
- package/dist/cjs/workflow-component-plugin.js.map +1 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +134 -5
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/nodes/data/data-count.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-count.js +16 -0
- package/dist/esm/nodes/data/data-count.js.map +1 -1
- package/dist/esm/nodes/data/data-create-item.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-create-item.js +20 -0
- package/dist/esm/nodes/data/data-create-item.js.map +1 -1
- package/dist/esm/nodes/data/data-delete-item.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-delete-item.js +19 -0
- package/dist/esm/nodes/data/data-delete-item.js.map +1 -1
- package/dist/esm/nodes/data/data-select.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-select.js +18 -0
- package/dist/esm/nodes/data/data-select.js.map +1 -1
- package/dist/esm/nodes/data/data-update-item.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-update-item.js +36 -0
- package/dist/esm/nodes/data/data-update-item.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/workflow-component-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-component-plugin.js +56 -9
- package/dist/esm/workflow-component-plugin.js.map +1 -1
- package/package.json +2 -2
- package/src/executor-generator.ts +134 -5
- package/src/nodes/data/data-count.ts +20 -0
- package/src/nodes/data/data-create-item.ts +19 -0
- package/src/nodes/data/data-delete-item.ts +23 -0
- package/src/nodes/data/data-select.ts +22 -0
- package/src/nodes/data/data-update-item.ts +38 -0
- package/src/workflow-component-plugin.ts +61 -3
|
@@ -6,6 +6,26 @@ async function data_count(config: any, context: any) {
|
|
|
6
6
|
const filters = config.filters || []
|
|
7
7
|
const baseUrl = (context && context.__baseUrl) || ''
|
|
8
8
|
|
|
9
|
+
// Unresolved route-param sentinel in a filter → validation error (see
|
|
10
|
+
// resolveTemplateTokenString in runtime-utils) instead of counting nothing.
|
|
11
|
+
for (let __fi = 0; __fi < filters.length; __fi++) {
|
|
12
|
+
const __f: any = filters[__fi]
|
|
13
|
+
if (
|
|
14
|
+
__f &&
|
|
15
|
+
(__f.value === '__TQ_UNRESOLVED_ROUTE_PARAM__' ||
|
|
16
|
+
__f.destination === '__TQ_UNRESOLVED_ROUTE_PARAM__')
|
|
17
|
+
) {
|
|
18
|
+
const __col = __f.column || __f.source || __f.field || 'unknown'
|
|
19
|
+
return {
|
|
20
|
+
count: 0,
|
|
21
|
+
error:
|
|
22
|
+
'Filter on "' +
|
|
23
|
+
__col +
|
|
24
|
+
'" requires the page route parameter, which is not available in this context',
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
9
29
|
try {
|
|
10
30
|
const response = await fetch(baseUrl + '/api/data/' + dataSourceId + '/count', {
|
|
11
31
|
method: 'POST',
|
|
@@ -6,6 +6,25 @@ async function data_create_item(config: any, context: any) {
|
|
|
6
6
|
const columnMappings = config.columnMappings || {}
|
|
7
7
|
const baseUrl = (context && context.__baseUrl) || ''
|
|
8
8
|
|
|
9
|
+
// An unresolved route-param sentinel (see resolveTemplateTokenString in
|
|
10
|
+
// runtime-utils) in a columnMapping degrades to null so the INSERT itself
|
|
11
|
+
// survives — only attribution-style columns lose their value.
|
|
12
|
+
if (Array.isArray(columnMappings)) {
|
|
13
|
+
for (let __mi = 0; __mi < columnMappings.length; __mi++) {
|
|
14
|
+
const __m: any = columnMappings[__mi]
|
|
15
|
+
if (__m && __m.value === '__TQ_UNRESOLVED_ROUTE_PARAM__') {
|
|
16
|
+
__m.value = null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
} else if (columnMappings && typeof columnMappings === 'object') {
|
|
20
|
+
const __mKeys = Object.keys(columnMappings)
|
|
21
|
+
for (let __mi = 0; __mi < __mKeys.length; __mi++) {
|
|
22
|
+
if ((columnMappings as any)[__mKeys[__mi]] === '__TQ_UNRESOLVED_ROUTE_PARAM__') {
|
|
23
|
+
;(columnMappings as any)[__mKeys[__mi]] = null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
// Surface the workflow's anonymous-user UUID to the data-api so a
|
|
10
29
|
// guest-checkout INSERT can recover its `user_id` from the
|
|
11
30
|
// resolve-user output instead of being NULL'd by the UUID
|
|
@@ -7,6 +7,29 @@ async function data_delete_item(config: any, context: any) {
|
|
|
7
7
|
const baseUrl = (context && context.__baseUrl) || ''
|
|
8
8
|
const __env = (globalThis as any).process && (globalThis as any).process.env
|
|
9
9
|
|
|
10
|
+
// Unresolved route-param sentinel in a filter → validation error (see
|
|
11
|
+
// resolveTemplateTokenString in runtime-utils). A DELETE must never run
|
|
12
|
+
// with a filter that silently matches nothing (or worse, gets dropped).
|
|
13
|
+
for (let __fi = 0; __fi < filters.length; __fi++) {
|
|
14
|
+
const __f: any = filters[__fi]
|
|
15
|
+
if (
|
|
16
|
+
__f &&
|
|
17
|
+
(__f.value === '__TQ_UNRESOLVED_ROUTE_PARAM__' ||
|
|
18
|
+
__f.destination === '__TQ_UNRESOLVED_ROUTE_PARAM__')
|
|
19
|
+
) {
|
|
20
|
+
const __col = __f.column || __f.source || __f.field || 'unknown'
|
|
21
|
+
return {
|
|
22
|
+
deletedId: null,
|
|
23
|
+
success: false,
|
|
24
|
+
deletedCount: 0,
|
|
25
|
+
error:
|
|
26
|
+
'Filter on "' +
|
|
27
|
+
__col +
|
|
28
|
+
'" requires the page route parameter, which is not available in this context',
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
10
33
|
try {
|
|
11
34
|
const response = await fetch(baseUrl + '/api/data/' + dataSourceId + '/delete', {
|
|
12
35
|
method: 'POST',
|
|
@@ -11,6 +11,28 @@ async function data_select(config: any, context: any) {
|
|
|
11
11
|
const rawQueryUserPart = config.rawQueryUserPart
|
|
12
12
|
const baseUrl = (context && context.__baseUrl) || ''
|
|
13
13
|
|
|
14
|
+
// A filter whose value is the unresolved route-param sentinel (see
|
|
15
|
+
// resolveTemplateTokenString in runtime-utils) must fail loudly instead of
|
|
16
|
+
// querying 'WHERE col = <sentinel>' and silently returning 0 rows.
|
|
17
|
+
for (let __fi = 0; __fi < filters.length; __fi++) {
|
|
18
|
+
const __f: any = filters[__fi]
|
|
19
|
+
if (
|
|
20
|
+
__f &&
|
|
21
|
+
(__f.value === '__TQ_UNRESOLVED_ROUTE_PARAM__' ||
|
|
22
|
+
__f.destination === '__TQ_UNRESOLVED_ROUTE_PARAM__')
|
|
23
|
+
) {
|
|
24
|
+
const __col = __f.column || __f.source || __f.field || 'unknown'
|
|
25
|
+
return {
|
|
26
|
+
rows: [],
|
|
27
|
+
count: 0,
|
|
28
|
+
error:
|
|
29
|
+
'Filter on "' +
|
|
30
|
+
__col +
|
|
31
|
+
'" requires the page route parameter, which is not available in this context',
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
14
36
|
try {
|
|
15
37
|
const payload: any = { tableName, filters, sorts, selectedColumns }
|
|
16
38
|
if (limit !== undefined && limit !== null) {
|
|
@@ -8,6 +8,44 @@ async function data_update_item(config: any, context: any) {
|
|
|
8
8
|
const baseUrl = (context && context.__baseUrl) || ''
|
|
9
9
|
const __env = (globalThis as any).process && (globalThis as any).process.env
|
|
10
10
|
|
|
11
|
+
// Unresolved route-param sentinel (see resolveTemplateTokenString in
|
|
12
|
+
// runtime-utils): in a filter it is a validation error — an UPDATE keyed on
|
|
13
|
+
// it would match 0 rows and report failure with no hint why. In a
|
|
14
|
+
// columnMapping it degrades to null so the write itself survives.
|
|
15
|
+
for (let __fi = 0; __fi < filters.length; __fi++) {
|
|
16
|
+
const __f: any = filters[__fi]
|
|
17
|
+
if (
|
|
18
|
+
__f &&
|
|
19
|
+
(__f.value === '__TQ_UNRESOLVED_ROUTE_PARAM__' ||
|
|
20
|
+
__f.destination === '__TQ_UNRESOLVED_ROUTE_PARAM__')
|
|
21
|
+
) {
|
|
22
|
+
const __col = __f.column || __f.source || __f.field || 'unknown'
|
|
23
|
+
return {
|
|
24
|
+
id: null,
|
|
25
|
+
updatedCount: 0,
|
|
26
|
+
error:
|
|
27
|
+
'Filter on "' +
|
|
28
|
+
__col +
|
|
29
|
+
'" requires the page route parameter, which is not available in this context',
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(columnMappings)) {
|
|
34
|
+
for (let __mi = 0; __mi < columnMappings.length; __mi++) {
|
|
35
|
+
const __m: any = columnMappings[__mi]
|
|
36
|
+
if (__m && __m.value === '__TQ_UNRESOLVED_ROUTE_PARAM__') {
|
|
37
|
+
__m.value = null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else if (columnMappings && typeof columnMappings === 'object') {
|
|
41
|
+
const __mKeys = Object.keys(columnMappings)
|
|
42
|
+
for (let __mi = 0; __mi < __mKeys.length; __mi++) {
|
|
43
|
+
if ((columnMappings as any)[__mKeys[__mi]] === '__TQ_UNRESOLVED_ROUTE_PARAM__') {
|
|
44
|
+
;(columnMappings as any)[__mKeys[__mi]] = null
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
11
49
|
// Same anonymous-user hint as data-create-item — a workflow that
|
|
12
50
|
// updates a row written by a guest must be able to re-stamp its
|
|
13
51
|
// ownership column with the same anon UUID rather than NULL.
|
|
@@ -327,7 +327,14 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
|
|
|
327
327
|
const hasCurrentUserGlobalRefs = activeWorkflows.some((wf) =>
|
|
328
328
|
hasGlobalContextStateRefTransitively(wf.nodes, workflows.customNodes, new Set<string>())
|
|
329
329
|
)
|
|
330
|
-
|
|
330
|
+
// Node configs may also reference the logged-in user via a literal
|
|
331
|
+
// {{Current User.id}} template token (resolved at runtime from the
|
|
332
|
+
// __stateValues.currentUser bridge) — those pages need the same
|
|
333
|
+
// GlobalContext plumbing even without a state-get/update node.
|
|
334
|
+
const hasCurrentUserToken = activeWorkflows.some((wf) =>
|
|
335
|
+
wf.nodes.some((n) => JSON.stringify(n.config || {}).includes('{{Current User.'))
|
|
336
|
+
)
|
|
337
|
+
const needsGlobalContextBridge = hasAuthNodes || hasCurrentUserGlobalRefs || hasCurrentUserToken
|
|
331
338
|
|
|
332
339
|
const hasAudioNodes = activeWorkflows.some((wf) =>
|
|
333
340
|
wf.nodes.some((n) => n.type === 'audio-play' || n.type === 'audio-stop')
|
|
@@ -399,6 +406,15 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
|
|
|
399
406
|
(!pageProtection.allowedRoles || pageProtection.allowedRoles.length === 0)
|
|
400
407
|
)
|
|
401
408
|
|
|
409
|
+
const dynamicRouteAttributeRaw = (uidl as any).outputOptions?.dynamicRouteAttribute as
|
|
410
|
+
| string
|
|
411
|
+
| undefined
|
|
412
|
+
const dynamicRouteAttribute =
|
|
413
|
+
typeof dynamicRouteAttributeRaw === 'string' &&
|
|
414
|
+
/^[A-Za-z_][A-Za-z0-9_-]*$/.test(dynamicRouteAttributeRaw)
|
|
415
|
+
? dynamicRouteAttributeRaw
|
|
416
|
+
: undefined
|
|
417
|
+
|
|
402
418
|
const moduleCode = generateModuleLevelCode(
|
|
403
419
|
activeWorkflows,
|
|
404
420
|
matchedElementTriggers,
|
|
@@ -408,7 +424,8 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
|
|
|
408
424
|
globalStateChangeWorkflows,
|
|
409
425
|
hasAudioNodes,
|
|
410
426
|
needsAdminFormHydration,
|
|
411
|
-
isRowOwnedSelfGuardedPage
|
|
427
|
+
isRowOwnedSelfGuardedPage,
|
|
428
|
+
dynamicRouteAttribute
|
|
412
429
|
)
|
|
413
430
|
|
|
414
431
|
if (moduleCode) {
|
|
@@ -558,6 +575,14 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
|
|
|
558
575
|
path: `${pathPrefix}utils/workflows/runtime`,
|
|
559
576
|
}
|
|
560
577
|
|
|
578
|
+
// Router singleton for {{urlDifferentiator}}-style token resolution in
|
|
579
|
+
// __execWf (route params are only reachable client-side at call time).
|
|
580
|
+
dependencies.Router = {
|
|
581
|
+
type: 'library',
|
|
582
|
+
path: 'next/router',
|
|
583
|
+
version: '12.1.0',
|
|
584
|
+
}
|
|
585
|
+
|
|
561
586
|
dependencies.workflowClientHandlers = {
|
|
562
587
|
type: 'local',
|
|
563
588
|
path: `${pathPrefix}utils/workflows/node-handlers-client`,
|
|
@@ -1160,7 +1185,8 @@ const generateModuleLevelCode = (
|
|
|
1160
1185
|
globalStateChangeWorkflows: UIDLWorkflow[],
|
|
1161
1186
|
hasAudioNodes?: boolean,
|
|
1162
1187
|
includeAdminFormHydrationHelper?: boolean,
|
|
1163
|
-
isRowOwnedSelfGuardedPage?: boolean
|
|
1188
|
+
isRowOwnedSelfGuardedPage?: boolean,
|
|
1189
|
+
dynamicRouteAttribute?: string
|
|
1164
1190
|
): string => {
|
|
1165
1191
|
if (allWorkflows.length === 0) {
|
|
1166
1192
|
return ''
|
|
@@ -1451,6 +1477,26 @@ function __createWorkflowHandlers(stateSetters, stateTypes, stateValuesRef) {
|
|
|
1451
1477
|
) {
|
|
1452
1478
|
value = value.value;
|
|
1453
1479
|
}
|
|
1480
|
+
// Array-typed states must never receive a non-array payload — one
|
|
1481
|
+
// poisoned write (e.g. a ref resolving to a custom-js node's whole
|
|
1482
|
+
// { result: [...] } return object instead of its array property)
|
|
1483
|
+
// bricks every downstream consumer: the mapper renders nothing and
|
|
1484
|
+
// the next add-row spread throws "is not iterable". Mirrors the
|
|
1485
|
+
// GUI-side guard: unwrap an object with a single array-valued
|
|
1486
|
+
// property, otherwise fall back to an empty array.
|
|
1487
|
+
if (type === 'array' && !Array.isArray(value)) {
|
|
1488
|
+
console.warn(
|
|
1489
|
+
'[workflow] non-array value written to array state "' + property + '"; coercing',
|
|
1490
|
+
value
|
|
1491
|
+
);
|
|
1492
|
+
if (value && typeof value === 'object') {
|
|
1493
|
+
var arrayKeys = Object.keys(value);
|
|
1494
|
+
if (arrayKeys.length === 1 && Array.isArray(value[arrayKeys[0]])) {
|
|
1495
|
+
return value[arrayKeys[0]];
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
return [];
|
|
1499
|
+
}
|
|
1454
1500
|
if (type === 'boolean') return value === 'true' || value === true;
|
|
1455
1501
|
if (type === 'number' && typeof value === 'string') {
|
|
1456
1502
|
const parsed = Number(value);
|
|
@@ -1552,6 +1598,18 @@ function __createWorkflowHandlers(stateSetters, stateTypes, stateValuesRef) {
|
|
|
1552
1598
|
|
|
1553
1599
|
function __execWf(config, triggerContext, serverUrls) {
|
|
1554
1600
|
triggerContext.__stateValues = Object.assign({}, stateValuesRef.current);
|
|
1601
|
+
// Route context for {{urlDifferentiator}} / {{Current Page Entity.id}}
|
|
1602
|
+
// template tokens in node configs — resolved by the shared runtime's
|
|
1603
|
+
// resolveTemplateTokenString. Read at call time (not render time) so the
|
|
1604
|
+
// handlers survive client-side navigation between dynamic routes.
|
|
1605
|
+
if (typeof window !== 'undefined' && Router && Router.router && Router.router.query) {
|
|
1606
|
+
triggerContext.__routeParams = Object.assign({}, Router.router.query);
|
|
1607
|
+
}
|
|
1608
|
+
${
|
|
1609
|
+
dynamicRouteAttribute
|
|
1610
|
+
? ` triggerContext.__dynamicRouteParam = '${dynamicRouteAttribute}';\n`
|
|
1611
|
+
: ''
|
|
1612
|
+
}
|
|
1555
1613
|
var fullConfig = typeof workflowCustomNodes !== 'undefined'
|
|
1556
1614
|
? Object.assign({}, config, { customNodes: workflowCustomNodes })
|
|
1557
1615
|
: config;
|