@teleporthq/teleport-plugin-next-workflows 0.45.13 → 0.45.15

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.
@@ -19,7 +19,7 @@ import {
19
19
  redactServerNodeConfig,
20
20
  } from './segment-splitter'
21
21
  import { isFireAndForgetSegment } from './await-result'
22
- import { collectSegmentStateKeys } from './segment-context-needs'
22
+ import { ALL_STATE_KEYS, collectSegmentStateKeys } from './segment-context-needs'
23
23
  import {
24
24
  ADMIN_CUSTOM_NODES_MODULE,
25
25
  callsCustomNodeTransitively,
@@ -162,6 +162,58 @@ const hasNodeTypeTransitively = (
162
162
  return false
163
163
  }
164
164
 
165
+ const reachableNodes = (
166
+ nodes: UIDLWorkflowNode[],
167
+ customNodes: Record<string, UIDLCustomWorkflowNode> | undefined,
168
+ visited: Set<string>
169
+ ): UIDLWorkflowNode[] =>
170
+ nodes.reduce((reached: UIDLWorkflowNode[], node) => {
171
+ reached.push(node)
172
+ const customNodeId = ((node.config || {}) as Record<string, unknown>).customNodeId
173
+ if (
174
+ node.type === 'general-custom-node' &&
175
+ customNodes &&
176
+ typeof customNodeId === 'string' &&
177
+ !visited.has(customNodeId)
178
+ ) {
179
+ visited.add(customNodeId)
180
+ const customNode = customNodes[customNodeId]
181
+ if (customNode && Array.isArray(customNode.nodes)) {
182
+ reached.push(...reachableNodes(customNode.nodes, customNodes, visited))
183
+ }
184
+ }
185
+ return reached
186
+ }, [])
187
+
188
+ /* Whether the nodes, or a custom node they call, read a global state. There
189
+ are four ways to: a `state-get-global-state` node; a `{{state.<name>}}`
190
+ token; a context reference that walks into the state values by name; and an
191
+ expression that names the state values as a whole, which counts when it
192
+ also names one of the global states. The last three read by name, so they
193
+ only count for a name the project declares as a global state. */
194
+ const readsGlobalStateTransitively = (
195
+ nodes: UIDLWorkflowNode[],
196
+ customNodes: Record<string, UIDLCustomWorkflowNode> | undefined,
197
+ globalStateNames: string[]
198
+ ): boolean => {
199
+ if (hasNodeTypeTransitively(nodes, 'state-get-global-state', customNodes, new Set<string>())) {
200
+ return true
201
+ }
202
+ if (globalStateNames.length === 0) {
203
+ return false
204
+ }
205
+ const stateKeys = collectSegmentStateKeys(nodes, customNodes)
206
+ if (stateKeys !== ALL_STATE_KEYS) {
207
+ return stateKeys.some((key) => globalStateNames.includes(key))
208
+ }
209
+ const configs = JSON.stringify(
210
+ reachableNodes(nodes, customNodes, new Set<string>()).map((node) => node.config || {})
211
+ )
212
+ return globalStateNames.some((name) =>
213
+ new RegExp(`(^|[^\\w$])${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}($|[^\\w$])`).test(configs)
214
+ )
215
+ }
216
+
165
217
  // State names that live in GlobalContext (useGlobalContext) rather than in the
166
218
  // per-page or GlobalStateProvider registry. When a workflow reads or writes
167
219
  // any of these via state-{get,update}-global-state, the page must bridge the
@@ -437,11 +489,21 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
437
489
  )
438
490
  )
439
491
  const hasGlobalStateChangeTriggers = globalStateChangeWorkflows.length > 0
440
- const needsGlobalState = hasGlobalStateUpdateNodes || hasGlobalStateChangeTriggers
441
492
  const globalStateDefs = (options.globalStateDefinitions || {}) as Record<
442
493
  string,
443
494
  UIDLGlobalStateDefinition
444
495
  >
496
+ // A workflow that only READS a global state needs the same wiring as one
497
+ // that writes it: every read resolves against the state values this file
498
+ // hands the workflow runtime, and a global state is among them only when
499
+ // the file takes it from `useGlobalState()`. Without it the read is
500
+ // undefined, so a condition on a global switch never holds.
501
+ const globalStateNames = Object.values(globalStateDefs).map((def) => def.name)
502
+ const hasGlobalStateReads = activeWorkflows.some((wf) =>
503
+ readsGlobalStateTransitively(wf.nodes, workflows.customNodes, globalStateNames)
504
+ )
505
+ const needsGlobalState =
506
+ hasGlobalStateUpdateNodes || hasGlobalStateChangeTriggers || hasGlobalStateReads
445
507
 
446
508
  const hasRealtimeUsage = activeWorkflows.some(
447
509
  (wf) =>