@openapi-flow/n8n 0.0.0-stage → 0.2.0

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.
Files changed (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -2
  3. package/THIRD_PARTY_NOTICES.md +9 -0
  4. package/dist/legacy/workflow/common/plan-validation.d.ts +1 -0
  5. package/dist/legacy/workflow/common/plan-validation.js +11 -0
  6. package/dist/legacy/workflow/common/workflow-id.d.ts +1 -0
  7. package/dist/legacy/workflow/common/workflow-id.js +8 -0
  8. package/dist/legacy/workflow/inbound/build-inbound-workflow.d.ts +2 -0
  9. package/dist/legacy/workflow/inbound/build-inbound-workflow.js +60 -0
  10. package/dist/legacy/workflow/inbound/compile-inbound-workflow.d.ts +2 -0
  11. package/dist/legacy/workflow/inbound/compile-inbound-workflow.js +25 -0
  12. package/dist/legacy/workflow/inbound/inbound-response.d.ts +2 -0
  13. package/dist/legacy/workflow/inbound/inbound-response.js +51 -0
  14. package/dist/legacy/workflow/request/base-url.d.ts +1 -0
  15. package/dist/legacy/workflow/request/base-url.js +14 -0
  16. package/dist/legacy/workflow/request/build-request-workflow.d.ts +3 -0
  17. package/dist/legacy/workflow/request/build-request-workflow.js +47 -0
  18. package/dist/legacy/workflow/request/build-sequence-workflow.d.ts +3 -0
  19. package/dist/legacy/workflow/request/build-sequence-workflow.js +53 -0
  20. package/dist/legacy/workflow/request/compile-catalog-sequence.d.ts +2 -0
  21. package/dist/legacy/workflow/request/compile-catalog-sequence.js +122 -0
  22. package/dist/legacy/workflow/request/compile-sequence.d.ts +2 -0
  23. package/dist/legacy/workflow/request/compile-sequence.js +41 -0
  24. package/dist/legacy/workflow/request/compile-workflow.d.ts +4 -0
  25. package/dist/legacy/workflow/request/compile-workflow.js +93 -0
  26. package/dist/legacy/workflow/request/credential-binding.d.ts +5 -0
  27. package/dist/legacy/workflow/request/credential-binding.js +31 -0
  28. package/dist/legacy/workflow/request/effect-policy.d.ts +4 -0
  29. package/dist/legacy/workflow/request/effect-policy.js +16 -0
  30. package/dist/legacy/workflow/request/http-request-node.d.ts +2 -0
  31. package/dist/legacy/workflow/request/http-request-node.js +69 -0
  32. package/dist/legacy/workflow/request/prepare-sequence.d.ts +4 -0
  33. package/dist/legacy/workflow/request/prepare-sequence.js +103 -0
  34. package/dist/legacy/workflow/request/request-body.d.ts +4 -0
  35. package/dist/legacy/workflow/request/request-body.js +64 -0
  36. package/dist/legacy/workflow/request/request-headers.d.ts +4 -0
  37. package/dist/legacy/workflow/request/request-headers.js +30 -0
  38. package/dist/legacy/workflow/request/request-url.d.ts +4 -0
  39. package/dist/legacy/workflow/request/request-url.js +34 -0
  40. package/dist/legacy/workflow/request/response-assertion-code.d.ts +3 -0
  41. package/dist/legacy/workflow/request/response-assertion-code.js +28 -0
  42. package/dist/legacy/workflow/request/sequence-url.d.ts +4 -0
  43. package/dist/legacy/workflow/request/sequence-url.js +104 -0
  44. package/dist/legacy-api.d.ts +25 -0
  45. package/dist/legacy-api.js +20 -0
  46. package/dist/nodes/common/validation/compile-standalone-validator.d.ts +3 -0
  47. package/dist/nodes/common/validation/compile-standalone-validator.js +44 -0
  48. package/dist/nodes/gap/create-gap-node.d.ts +3 -0
  49. package/dist/nodes/gap/create-gap-node.js +35 -0
  50. package/dist/nodes/native/arrays/create-array-node-fragment.d.ts +4 -0
  51. package/dist/nodes/native/arrays/create-array-node-fragment.js +42 -0
  52. package/dist/nodes/native/arrays/create-array-reader-code.d.ts +3 -0
  53. package/dist/nodes/native/arrays/create-array-reader-code.js +12 -0
  54. package/dist/nodes/native/arrays/create-native-array-capability.d.ts +3 -0
  55. package/dist/nodes/native/arrays/create-native-array-capability.js +64 -0
  56. package/dist/nodes/native/common/create-native-fragment.d.ts +2 -0
  57. package/dist/nodes/native/common/create-native-fragment.js +10 -0
  58. package/dist/nodes/native/common/response-references.d.ts +3 -0
  59. package/dist/nodes/native/common/response-references.js +5 -0
  60. package/dist/nodes/native/create-exclusive-branch-merge-capability.d.ts +3 -0
  61. package/dist/nodes/native/create-exclusive-branch-merge-capability.js +33 -0
  62. package/dist/nodes/native/create-json-output-capability.d.ts +3 -0
  63. package/dist/nodes/native/create-json-output-capability.js +38 -0
  64. package/dist/nodes/native/create-native-capabilities.d.ts +2 -0
  65. package/dist/nodes/native/create-native-capabilities.js +14 -0
  66. package/dist/nodes/native/create-pass-through-capability.d.ts +3 -0
  67. package/dist/nodes/native/create-pass-through-capability.js +30 -0
  68. package/dist/nodes/native/create-response-array-capability.d.ts +3 -0
  69. package/dist/nodes/native/create-response-array-capability.js +73 -0
  70. package/dist/nodes/native/create-response-collection-capability.d.ts +3 -0
  71. package/dist/nodes/native/create-response-collection-capability.js +132 -0
  72. package/dist/nodes/native/if-capability.d.ts +2 -0
  73. package/dist/nodes/native/if-capability.js +76 -0
  74. package/dist/nodes/native/item-join/create-item-join-capability.d.ts +3 -0
  75. package/dist/nodes/native/item-join/create-item-join-capability.js +153 -0
  76. package/dist/nodes/native/item-join/create-item-join-reader-code.d.ts +2 -0
  77. package/dist/nodes/native/item-join/create-item-join-reader-code.js +18 -0
  78. package/dist/nodes/native/item-join/runtime/item-join-runtime.bundle.js +24 -0
  79. package/dist/nodes/native/item-join/runtime/join-api-items.d.ts +3 -0
  80. package/dist/nodes/native/item-join/runtime/join-api-items.js +37 -0
  81. package/dist/nodes/native/merge-append-capability.d.ts +2 -0
  82. package/dist/nodes/native/merge-append-capability.js +31 -0
  83. package/dist/nodes/native/response-assertion-capability.d.ts +2 -0
  84. package/dist/nodes/native/response-assertion-capability.js +53 -0
  85. package/dist/nodes/native/response-check-code.d.ts +3 -0
  86. package/dist/nodes/native/response-check-code.js +59 -0
  87. package/dist/nodes/native/response-checks/create-response-check-guard.d.ts +4 -0
  88. package/dist/nodes/native/response-checks/create-response-check-guard.js +22 -0
  89. package/dist/nodes/native/response-checks/create-response-check-reader.d.ts +3 -0
  90. package/dist/nodes/native/response-checks/create-response-check-reader.js +23 -0
  91. package/dist/nodes/native/stop-and-error-capability.d.ts +2 -0
  92. package/dist/nodes/native/stop-and-error-capability.js +29 -0
  93. package/dist/nodes/request/authentication/credential-field-name.d.ts +1 -0
  94. package/dist/nodes/request/authentication/credential-field-name.js +3 -0
  95. package/dist/nodes/request/authentication/resolve-http-request-authentication.d.ts +2 -0
  96. package/dist/nodes/request/authentication/resolve-http-request-authentication.js +42 -0
  97. package/dist/nodes/request/bindings/compile-native-output-validators.d.ts +3 -0
  98. package/dist/nodes/request/bindings/compile-native-output-validators.js +12 -0
  99. package/dist/nodes/request/bindings/compile-request-validator.d.ts +3 -0
  100. package/dist/nodes/request/bindings/compile-request-validator.js +13 -0
  101. package/dist/nodes/request/bindings/compile-source-response-validators.d.ts +2 -0
  102. package/dist/nodes/request/bindings/compile-source-response-validators.js +12 -0
  103. package/dist/nodes/request/bindings/create-bound-request-fragment.d.ts +3 -0
  104. package/dist/nodes/request/bindings/create-bound-request-fragment.js +77 -0
  105. package/dist/nodes/request/bindings/create-output-reader-code.d.ts +3 -0
  106. package/dist/nodes/request/bindings/create-output-reader-code.js +26 -0
  107. package/dist/nodes/request/bindings/resolve-output-binding-sources.d.ts +3 -0
  108. package/dist/nodes/request/bindings/resolve-output-binding-sources.js +36 -0
  109. package/dist/nodes/request/create-http-request-node.d.ts +2 -0
  110. package/dist/nodes/request/create-http-request-node.js +94 -0
  111. package/dist/nodes/request/deployment/create-request-deployment-notes.d.ts +2 -0
  112. package/dist/nodes/request/deployment/create-request-deployment-notes.js +3 -0
  113. package/dist/nodes/request/deployment/resolve-http-request-deployment.d.ts +2 -0
  114. package/dist/nodes/request/deployment/resolve-http-request-deployment.js +33 -0
  115. package/dist/nodes/request/response/create-http-response-decoder.d.ts +4 -0
  116. package/dist/nodes/request/response/create-http-response-decoder.js +56 -0
  117. package/dist/nodes/request/response/decode-http-response.d.ts +3 -0
  118. package/dist/nodes/request/response/decode-http-response.js +42 -0
  119. package/dist/nodes/request/response/http-response-media.d.ts +2 -0
  120. package/dist/nodes/request/response/http-response-media.js +32 -0
  121. package/dist/nodes/request/response/response-runtime.bundle.js +24 -0
  122. package/dist/nodes/request/runtime/materialize-http-request.d.ts +3 -0
  123. package/dist/nodes/request/runtime/materialize-http-request.js +17 -0
  124. package/dist/nodes/request/runtime/request-runtime.bundle.js +24 -0
  125. package/dist/nodes/request/serialization/create-serialized-http-request-node.d.ts +3 -0
  126. package/dist/nodes/request/serialization/create-serialized-http-request-node.js +17 -0
  127. package/dist/nodes/request/serialization/serialize-http-request.d.ts +5 -0
  128. package/dist/nodes/request/serialization/serialize-http-request.js +70 -0
  129. package/dist/public-api.d.ts +30 -0
  130. package/dist/public-api.js +17 -0
  131. package/dist/schemas/batch-execution-schema.d.ts +15 -0
  132. package/dist/schemas/batch-execution-schema.js +14 -0
  133. package/dist/schemas/create-native-comparison-planning-schema.d.ts +81 -0
  134. package/dist/schemas/create-native-comparison-planning-schema.js +36 -0
  135. package/dist/schemas/exclusive-branch-merge-schema.d.ts +4 -0
  136. package/dist/schemas/exclusive-branch-merge-schema.js +7 -0
  137. package/dist/schemas/item-join-schema.d.ts +13 -0
  138. package/dist/schemas/item-join-schema.js +26 -0
  139. package/dist/schemas/native-array-schema.d.ts +11 -0
  140. package/dist/schemas/native-array-schema.js +19 -0
  141. package/dist/schemas/native-capability-schemas.d.ts +100 -0
  142. package/dist/schemas/native-capability-schemas.js +62 -0
  143. package/dist/schemas/request-deployment-schema.d.ts +9 -0
  144. package/dist/schemas/request-deployment-schema.js +27 -0
  145. package/dist/schemas/response-array-schema.d.ts +11 -0
  146. package/dist/schemas/response-array-schema.js +19 -0
  147. package/dist/schemas/response-collection-schema.d.ts +11 -0
  148. package/dist/schemas/response-collection-schema.js +19 -0
  149. package/dist/types/array-iteration.d.ts +8 -0
  150. package/dist/types/array-iteration.js +1 -0
  151. package/dist/types/array-split.d.ts +13 -0
  152. package/dist/types/array-split.js +1 -0
  153. package/dist/types/authentication.d.ts +14 -0
  154. package/dist/types/authentication.js +1 -0
  155. package/dist/types/batch-execution.d.ts +23 -0
  156. package/dist/types/batch-execution.js +1 -0
  157. package/dist/types/http-response-decoding.d.ts +16 -0
  158. package/dist/types/http-response-decoding.js +1 -0
  159. package/dist/types/item-join.d.ts +40 -0
  160. package/dist/types/item-join.js +1 -0
  161. package/dist/types/legacy/catalog-workflow.d.ts +37 -0
  162. package/dist/types/legacy/catalog-workflow.js +1 -0
  163. package/dist/types/legacy/inbound-workflow.d.ts +27 -0
  164. package/dist/types/legacy/inbound-workflow.js +1 -0
  165. package/dist/types/legacy/request-node.d.ts +23 -0
  166. package/dist/types/legacy/request-node.js +1 -0
  167. package/dist/types/legacy/request-workflow.d.ts +43 -0
  168. package/dist/types/legacy/request-workflow.js +1 -0
  169. package/dist/types/legacy/sequence-workflow.d.ts +57 -0
  170. package/dist/types/legacy/sequence-workflow.js +1 -0
  171. package/dist/types/native-array.d.ts +8 -0
  172. package/dist/types/native-array.js +1 -0
  173. package/dist/types/native-capability.d.ts +37 -0
  174. package/dist/types/native-capability.js +1 -0
  175. package/dist/types/node-fragment.d.ts +51 -0
  176. package/dist/types/node-fragment.js +1 -0
  177. package/dist/types/output-binding-source.d.ts +22 -0
  178. package/dist/types/output-binding-source.js +1 -0
  179. package/dist/types/request-deployment.d.ts +17 -0
  180. package/dist/types/request-deployment.js +1 -0
  181. package/dist/types/request-materialization.d.ts +22 -0
  182. package/dist/types/request-materialization.js +1 -0
  183. package/dist/types/response-check-compilation.d.ts +11 -0
  184. package/dist/types/response-check-compilation.js +1 -0
  185. package/dist/types/response-check-guard.d.ts +6 -0
  186. package/dist/types/response-check-guard.js +1 -0
  187. package/dist/types/response-collection.d.ts +7 -0
  188. package/dist/types/response-collection.js +1 -0
  189. package/dist/types/reviewable-workflow.d.ts +27 -0
  190. package/dist/types/reviewable-workflow.js +1 -0
  191. package/dist/types/schema-compilation.d.ts +5 -0
  192. package/dist/types/schema-compilation.js +1 -0
  193. package/dist/types/serialized-http-request.d.ts +4 -0
  194. package/dist/types/serialized-http-request.js +1 -0
  195. package/dist/types/workflow-compilation.d.ts +36 -0
  196. package/dist/types/workflow-compilation.js +1 -0
  197. package/dist/utils/bundled-license-banner.d.ts +3 -0
  198. package/dist/utils/bundled-license-banner.js +32 -0
  199. package/dist/utils/javascript-json-literal.d.ts +2 -0
  200. package/dist/utils/javascript-json-literal.js +10 -0
  201. package/dist/utils/json-values-equal.cjs +24 -0
  202. package/dist/utils/json-values-equal.d.cts +1 -0
  203. package/dist/workflow/assemble-n8n-workflow.d.ts +2 -0
  204. package/dist/workflow/assemble-n8n-workflow.js +11 -0
  205. package/dist/workflow/batch/compile-batched-n8n-workflow.d.ts +3 -0
  206. package/dist/workflow/batch/compile-batched-n8n-workflow.js +18 -0
  207. package/dist/workflow/batch/create-batch-execution-scopes.d.ts +3 -0
  208. package/dist/workflow/batch/create-batch-execution-scopes.js +59 -0
  209. package/dist/workflow/batch/validate-batch-exclusive-rejoin.d.ts +3 -0
  210. package/dist/workflow/batch/validate-batch-exclusive-rejoin.js +49 -0
  211. package/dist/workflow/batch/validate-batch-item-flow.d.ts +3 -0
  212. package/dist/workflow/batch/validate-batch-item-flow.js +98 -0
  213. package/dist/workflow/build-n8n-workflow.d.ts +2 -0
  214. package/dist/workflow/build-n8n-workflow.js +143 -0
  215. package/dist/workflow/compile-native-workflow-nodes.d.ts +3 -0
  216. package/dist/workflow/compile-native-workflow-nodes.js +26 -0
  217. package/dist/workflow/compile-planned-n8n-workflow.d.ts +3 -0
  218. package/dist/workflow/compile-planned-n8n-workflow.js +17 -0
  219. package/dist/workflow/compile-reviewable-n8n-workflow.d.ts +3 -0
  220. package/dist/workflow/compile-reviewable-n8n-workflow.js +70 -0
  221. package/dist/workflow/compile-validated-workflow-fragments.d.ts +3 -0
  222. package/dist/workflow/compile-validated-workflow-fragments.js +20 -0
  223. package/dist/workflow/create-n8n-native-output-sources.d.ts +3 -0
  224. package/dist/workflow/create-n8n-native-output-sources.js +17 -0
  225. package/dist/workflow/review/create-gap-review-content.d.ts +2 -0
  226. package/dist/workflow/review/create-gap-review-content.js +28 -0
  227. package/dist/workflow/review/create-gap-review-region.d.ts +2 -0
  228. package/dist/workflow/review/create-gap-review-region.js +23 -0
  229. package/dist/workflow/review/escape-review-markdown.d.ts +1 -0
  230. package/dist/workflow/review/escape-review-markdown.js +7 -0
  231. package/dist/workflow/review/gap-review-region-layout.d.ts +4 -0
  232. package/dist/workflow/review/gap-review-region-layout.js +21 -0
  233. package/dist/workflow/validate-compiled-binding-sources.d.ts +4 -0
  234. package/dist/workflow/validate-compiled-binding-sources.js +40 -0
  235. package/dist/workflow/validate-node-fragments.d.ts +3 -0
  236. package/dist/workflow/validate-node-fragments.js +58 -0
  237. package/package.json +54 -4
@@ -0,0 +1,143 @@
1
+ import { trigger, workflow, validateWorkflow } from '@n8n/workflow-sdk';
2
+ import { validateNodeFragments } from './validate-node-fragments.js';
3
+ import { createBatchExecutionScopes } from './batch/create-batch-execution-scopes.js';
4
+ export function buildN8nWorkflow({ id, name, nodes, edges, starts, triggerConnections, annotations, batchScopes, }) {
5
+ if (!['connected', 'detached'].includes(triggerConnections))
6
+ throw new Error('triggerConnections must be connected or detached');
7
+ if (!Array.isArray(annotations) ||
8
+ annotations.some((item) => item.type !== 'n8n-nodes-base.stickyNote'))
9
+ throw new Error('workflow annotations must be Sticky Notes');
10
+ if (typeof id !== 'string' ||
11
+ !id.trim() ||
12
+ typeof name !== 'string' ||
13
+ !name.trim())
14
+ throw new Error('workflow.id and workflow.name must be non-empty');
15
+ if (!Array.isArray(nodes) ||
16
+ nodes.length === 0 ||
17
+ !Array.isArray(edges) ||
18
+ !Array.isArray(starts) ||
19
+ starts.length === 0)
20
+ throw new Error('workflow requires nodes, edges and explicit starts');
21
+ if (nodes.some((fragment) => fragment.nodes.some((item) => 'isTrigger' in item && item.isTrigger === true)))
22
+ throw new Error('workflow fragments must not introduce an additional trigger');
23
+ const byId = new Map(nodes.map((fragment) => [fragment.nodeId, fragment]));
24
+ if (byId.size !== nodes.length ||
25
+ nodes.some((fragment) => !fragment.nodeId.trim()))
26
+ throw new Error('workflow nodes require unique non-empty nodeId');
27
+ const batches = batchScopes === undefined
28
+ ? []
29
+ : createBatchExecutionScopes({
30
+ id,
31
+ name,
32
+ nodes,
33
+ edges,
34
+ starts,
35
+ batchScopes,
36
+ });
37
+ const sdkNodes = [
38
+ ...nodes.flatMap((fragment) => fragment.nodes),
39
+ ...annotations,
40
+ ...batches.map((batch) => batch.controller),
41
+ ];
42
+ const sdkIds = sdkNodes.map((node) => node.id);
43
+ const sdkNames = sdkNodes.map((node) => node.name);
44
+ if (new Set(sdkIds).size !== sdkIds.length ||
45
+ new Set(sdkNames).size !== sdkNames.length ||
46
+ sdkNames.includes('Start') ||
47
+ sdkIds.includes('openapi-flow-start'))
48
+ throw new Error('workflow SDK node IDs and names must be unique and not use the Start trigger identity');
49
+ validateNodeFragments(nodes);
50
+ if (starts.some((id) => {
51
+ const fragment = byId.get(id);
52
+ return (fragment?.inputEndpoints &&
53
+ new Set(Object.values(fragment.inputEndpoints).map((endpoint) => endpoint.nodeId)).size > 1);
54
+ }))
55
+ throw new Error('workflow starts cannot bypass independent fragment input endpoints');
56
+ const successors = new Map(nodes.map((fragment) => [fragment.nodeId, []]));
57
+ const inbound = new Map(nodes.map((fragment) => [fragment.nodeId, 0]));
58
+ const edgeIds = new Set();
59
+ for (const edge of edges) {
60
+ const source = byId.get(edge.from);
61
+ const target = byId.get(edge.to);
62
+ if (!source || !target)
63
+ throw new Error(`edge ${edge.from} -> ${edge.to} references a missing node`);
64
+ if (!Object.hasOwn(source.outputPorts, edge.output) ||
65
+ !Object.hasOwn(target.inputPorts, edge.input))
66
+ throw new Error(`edge ${edge.from} -> ${edge.to} references an undeclared port`);
67
+ const key = JSON.stringify(edge);
68
+ if (edgeIds.has(key))
69
+ throw new Error(`duplicate edge ${edge.from} -> ${edge.to}`);
70
+ edgeIds.add(key);
71
+ successors.get(edge.from).push(edge.to);
72
+ inbound.set(edge.to, inbound.get(edge.to) + 1);
73
+ }
74
+ const roots = [...inbound]
75
+ .filter(([, count]) => count === 0)
76
+ .map(([nodeId]) => nodeId);
77
+ if (new Set(starts).size !== starts.length ||
78
+ starts.some((nodeId) => !roots.includes(nodeId)) ||
79
+ roots.some((nodeId) => !starts.includes(nodeId)))
80
+ throw new Error('workflow.starts must identify every root exactly once');
81
+ const pending = [...roots];
82
+ let visited = 0;
83
+ while (pending.length) {
84
+ const current = pending.shift();
85
+ visited++;
86
+ for (const next of successors.get(current)) {
87
+ const count = inbound.get(next) - 1;
88
+ inbound.set(next, count);
89
+ if (count === 0)
90
+ pending.push(next);
91
+ }
92
+ }
93
+ if (visited !== nodes.length)
94
+ throw new Error('workflow DAG has a cycle');
95
+ const start = trigger({
96
+ type: 'n8n-nodes-base.manualTrigger',
97
+ version: 1,
98
+ config: { id: 'openapi-flow-start', name: 'Start', position: [0, 0] },
99
+ });
100
+ let built = workflow(id, name).add(start);
101
+ for (const fragment of nodes)
102
+ for (const node of fragment.nodes)
103
+ built = built.add(node);
104
+ for (const batch of batches) {
105
+ built = built.add(batch.controller);
106
+ built = built.connect(batch.controller, 1, byId.get(batch.scope.entryNodeId).entry, byId.get(batch.scope.entryNodeId).inputPorts.main);
107
+ built = built.connect(byId.get(batch.scope.exitNodeId).exit, byId.get(batch.scope.exitNodeId).outputPorts.main, batch.controller, 0);
108
+ }
109
+ for (const fragment of nodes)
110
+ for (const edge of fragment.internalEdges ?? [])
111
+ built = built.connect(fragment.nodes.find((item) => item.id === edge.from), edge.output, fragment.nodes.find((item) => item.id === edge.to), edge.input);
112
+ if (triggerConnections === 'connected')
113
+ for (const nodeId of starts)
114
+ built = built.connect(start, 0, batches.find((batch) => batch.scope.entryNodeId === nodeId)
115
+ ?.controller ?? byId.get(nodeId).entry, 0);
116
+ for (const note of annotations)
117
+ built = built.add(note);
118
+ for (const edge of edges) {
119
+ const source = byId.get(edge.from);
120
+ const target = byId.get(edge.to);
121
+ const endpoint = target.inputEndpoints?.[edge.input];
122
+ const sourceBatch = batches.find((batch) => batch.scope.exitNodeId === edge.from &&
123
+ !batch.scope.nodeIds.includes(edge.to));
124
+ const targetBatch = batches.find((batch) => batch.scope.entryNodeId === edge.to &&
125
+ !batch.scope.nodeIds.includes(edge.from));
126
+ built = built.connect(sourceBatch ? sourceBatch.controller : source.exit, sourceBatch ? 0 : source.outputPorts[edge.output], targetBatch
127
+ ? targetBatch.controller
128
+ : endpoint
129
+ ? target.nodes.find((node) => node.id === endpoint.nodeId)
130
+ : target.entry, targetBatch
131
+ ? 0
132
+ : endpoint
133
+ ? endpoint.input
134
+ : target.inputPorts[edge.input]);
135
+ }
136
+ const checked = validateWorkflow(built);
137
+ if (!checked.valid)
138
+ throw new Error(`n8n SDK validation failed: ${checked.errors.map((error) => error.message).join('; ')}`);
139
+ const result = built.toJSON();
140
+ if (triggerConnections === 'detached')
141
+ result.active = false;
142
+ return result;
143
+ }
@@ -0,0 +1,3 @@
1
+ import type { CompileNativeWorkflowNodesInput } from '../types/native-capability.js';
2
+ import type { N8nNodeFragment } from '../types/node-fragment.js';
3
+ export declare function compileNativeWorkflowNodes(input: CompileNativeWorkflowNodesInput): N8nNodeFragment[];
@@ -0,0 +1,26 @@
1
+ export function compileNativeWorkflowNodes(input) {
2
+ const names = Object.fromEntries(input.apiNodes.map((item) => [item.nodeId, item.exit.name]));
3
+ const apiResponseContracts = Object.fromEntries(input.materials.map((item) => [item.arguments.callId, item.operation]));
4
+ const nativeNodes = input.plan.nativeNodes.map((planned, index) => {
5
+ const capability = input.capabilities.find((item) => item.name === planned.capability);
6
+ if (!capability)
7
+ throw new Error(`Missing native compiler ${planned.capability}`);
8
+ const compiled = capability.compile({
9
+ planned,
10
+ apiNodeNames: names,
11
+ apiResponseContracts,
12
+ position: [600 + index * 250, 300],
13
+ });
14
+ if (compiled.nodeId !== planned.id ||
15
+ JSON.stringify(Object.keys(compiled.inputPorts)) !==
16
+ JSON.stringify(capability.inputPorts(planned.parameters)) ||
17
+ JSON.stringify(Object.keys(compiled.outputPorts)) !==
18
+ JSON.stringify(capability.outputPorts(planned.parameters)))
19
+ throw new Error(`Native compiler ${planned.capability} violated declared node identity/ports`);
20
+ if (compiled.rejoinsExclusiveOutputs !==
21
+ capability.rejoinsExclusiveOutputs?.(planned.parameters))
22
+ throw new Error(`Native compiler ${planned.capability} violated declared rejoinsExclusiveOutputs`);
23
+ return compiled;
24
+ });
25
+ return nativeNodes;
26
+ }
@@ -0,0 +1,3 @@
1
+ import type { CompilePlannedN8nWorkflowInput } from '../types/native-capability.js';
2
+ import type { N8nCompileResult } from '../types/workflow-compilation.js';
3
+ export declare function compilePlannedN8nWorkflow(input: CompilePlannedN8nWorkflowInput): N8nCompileResult;
@@ -0,0 +1,17 @@
1
+ import { assembleN8nWorkflow } from './assemble-n8n-workflow.js';
2
+ import { compileValidatedWorkflowFragments } from './compile-validated-workflow-fragments.js';
3
+ export function compilePlannedN8nWorkflow(input) {
4
+ const nodes = compileValidatedWorkflowFragments(input);
5
+ const result = assembleN8nWorkflow({
6
+ id: input.id,
7
+ name: input.name,
8
+ nodes,
9
+ edges: input.plan.edges,
10
+ starts: input.plan.starts,
11
+ });
12
+ result.workflow.settings = {
13
+ ...result.workflow.settings,
14
+ executionOrder: 'v1',
15
+ };
16
+ return result;
17
+ }
@@ -0,0 +1,3 @@
1
+ import type { CompileReviewableN8nWorkflowInput } from '../types/reviewable-workflow.js';
2
+ import type { N8nWorkflowResult } from '../types/workflow-compilation.js';
3
+ export declare function compileReviewableN8nWorkflow(input: CompileReviewableN8nWorkflowInput): N8nWorkflowResult;
@@ -0,0 +1,70 @@
1
+ import { validateReviewableWorkflowGraphPlan, createNativeOutputContracts, } from '@openapi-flow/core';
2
+ import { createGapReviewRegion } from './review/create-gap-review-region.js';
3
+ import { GAP_REVIEW_REGION_WIDTH, GAP_REVIEW_REGION_SPACING, } from './review/gap-review-region-layout.js';
4
+ import { compilePlannedN8nWorkflow } from './compile-planned-n8n-workflow.js';
5
+ import { compileNativeWorkflowNodes } from './compile-native-workflow-nodes.js';
6
+ import { buildN8nWorkflow } from './build-n8n-workflow.js';
7
+ import { validateCompiledBindingSources } from './validate-compiled-binding-sources.js';
8
+ export function compileReviewableN8nWorkflow(input) {
9
+ validateReviewableWorkflowGraphPlan(input);
10
+ const blocked = new Set(input.plan.blockedCalls.map((item) => item.callId));
11
+ const ready = input.materials.filter((item) => item.status === 'ready' && !blocked.has(item.arguments.callId));
12
+ const expected = ready.map((item) => item.arguments.callId).sort();
13
+ const actual = input.apiNodes.map((item) => item.nodeId).sort();
14
+ if (JSON.stringify(expected) !== JSON.stringify(actual))
15
+ throw new Error('review apiNodes must exactly match unblocked ready calls');
16
+ if (!input.plan.gaps.length)
17
+ return compilePlannedN8nWorkflow({ ...input, materials: ready });
18
+ const nativeNodes = compileNativeWorkflowNodes({
19
+ ...input,
20
+ materials: ready,
21
+ });
22
+ validateCompiledBindingSources(input.apiNodes, nativeNodes, createNativeOutputContracts({
23
+ nativeNodes: input.plan.nativeNodes,
24
+ capabilities: input.capabilities,
25
+ }), ready);
26
+ const occupiedPositions = [...input.apiNodes, ...nativeNodes].flatMap((fragment) => fragment.nodes.flatMap((node) => node.config.position === undefined ? [] : [node.config.position]));
27
+ const regionTop = Math.max(0, ...occupiedPositions.map((point) => point[1])) + 240;
28
+ const regionStep = GAP_REVIEW_REGION_WIDTH + GAP_REVIEW_REGION_SPACING;
29
+ const placeholders = input.plan.gaps.map((gap, index) => ({
30
+ nodeId: gap.id,
31
+ gaps: [gap],
32
+ position: [600 + index * regionStep, regionTop],
33
+ }));
34
+ for (const [index, item] of input.plan.blockedCalls.entries()) {
35
+ const material = input.materials.find((material) => (material.status === 'ready'
36
+ ? material.arguments.callId
37
+ : material.callId) === item.callId);
38
+ if (!material)
39
+ throw new Error('review placeholder material is missing');
40
+ placeholders.push({
41
+ nodeId: item.callId,
42
+ gaps: input.plan.gaps.filter((gap) => item.gapIds.includes(gap.id)),
43
+ operation: material.operation,
44
+ position: [
45
+ 600 + (input.plan.gaps.length + index) * regionStep,
46
+ regionTop,
47
+ ],
48
+ });
49
+ }
50
+ const regions = placeholders.map(createGapReviewRegion);
51
+ const result = buildN8nWorkflow({
52
+ id: input.id,
53
+ name: input.name,
54
+ nodes: [
55
+ ...input.apiNodes,
56
+ ...nativeNodes,
57
+ ...regions.map((region) => region.fragment),
58
+ ],
59
+ edges: input.plan.edges,
60
+ starts: input.plan.starts,
61
+ triggerConnections: 'detached',
62
+ annotations: regions.map((region) => region.annotation),
63
+ });
64
+ result.settings = { ...result.settings, executionOrder: 'v1' };
65
+ return {
66
+ status: 'needs-review',
67
+ workflow: result,
68
+ diagnostics: input.plan.gaps,
69
+ };
70
+ }
@@ -0,0 +1,3 @@
1
+ import type { CompilePlannedN8nWorkflowInput } from '../types/native-capability.js';
2
+ import type { N8nNodeFragment } from '../types/node-fragment.js';
3
+ export declare function compileValidatedWorkflowFragments(input: CompilePlannedN8nWorkflowInput): N8nNodeFragment[];
@@ -0,0 +1,20 @@
1
+ import { compileNativeWorkflowNodes } from './compile-native-workflow-nodes.js';
2
+ import { validateWorkflowGraphPlan, createNativeOutputContracts, } from '@openapi-flow/core';
3
+ import { validateCompiledBindingSources } from './validate-compiled-binding-sources.js';
4
+ export function compileValidatedWorkflowFragments(input) {
5
+ if (input.plan.gaps.length)
6
+ throw new Error(`Workflow needs review: ${JSON.stringify(input.plan.gaps)}`);
7
+ validateWorkflowGraphPlan(input);
8
+ const expectedIds = input.materials
9
+ .map((item) => item.arguments.callId)
10
+ .sort();
11
+ const actualIds = input.apiNodes.map((item) => item.nodeId).sort();
12
+ if (JSON.stringify(expectedIds) !== JSON.stringify(actualIds))
13
+ throw new Error('apiNodes must match material callIds exactly');
14
+ const nativeNodes = compileNativeWorkflowNodes(input);
15
+ validateCompiledBindingSources(input.apiNodes, nativeNodes, createNativeOutputContracts({
16
+ nativeNodes: input.plan.nativeNodes,
17
+ capabilities: input.capabilities,
18
+ }), input.materials);
19
+ return [...input.apiNodes, ...nativeNodes];
20
+ }
@@ -0,0 +1,3 @@
1
+ import type { CreateN8nNativeOutputSourcesInput, N8nNativeOutputSource } from '../types/output-binding-source.js';
2
+ /** Resolve runtime names from the actual SDK producer, not an ID/name guess. */
3
+ export declare function createN8nNativeOutputSources(input: CreateN8nNativeOutputSourcesInput): N8nNativeOutputSource[];
@@ -0,0 +1,17 @@
1
+ import { createNativeOutputContracts } from '@openapi-flow/core';
2
+ /** Resolve runtime names from the actual SDK producer, not an ID/name guess. */
3
+ export function createN8nNativeOutputSources(input) {
4
+ const outputs = createNativeOutputContracts(input);
5
+ return outputs.map((output) => {
6
+ const planned = input.nativeNodes.find((item) => item.id === output.nodeId);
7
+ const capability = input.capabilities.find((item) => item.name === planned.capability);
8
+ const fragment = capability.compile({
9
+ planned,
10
+ apiNodeNames: input.apiNodeNames,
11
+ position: [600, 300],
12
+ });
13
+ if (fragment.nodeId !== output.nodeId || !fragment.exit.name.trim())
14
+ throw new Error(`native output ${output.nodeId} requires its compiled identity/name`);
15
+ return { ...output, nodeName: fragment.exit.name };
16
+ });
17
+ }
@@ -0,0 +1,2 @@
1
+ import type { CreateGapNodeInput } from '../../types/reviewable-workflow.js';
2
+ export declare function createGapReviewContent(input: CreateGapNodeInput): string[];
@@ -0,0 +1,28 @@
1
+ import { escapeReviewMarkdown } from './escape-review-markdown.js';
2
+ const stageLabels = {
3
+ 'api-selection': 'API 선택',
4
+ 'api-bindings': '요청값 연결',
5
+ 'workflow-graph': '워크플로 구성',
6
+ };
7
+ export function createGapReviewContent(input) {
8
+ const content = [
9
+ '# 확인 필요',
10
+ '미해결 단계: ' + escapeReviewMarkdown(input.nodeId),
11
+ ...input.gaps.map((gap) => escapeReviewMarkdown(`${stageLabels[gap.stage]} — ${gap.description}`)),
12
+ '아래 노드는 미해결 작업의 위치를 표시하는 대역입니다. 실제 API를 호출하거나 응답을 만들지 않습니다.',
13
+ '실수로 실행하지 않도록 Start의 연결을 끊어 두었습니다. 이 대역을 직접 실행하거나 다시 연결하면 오류로 중단됩니다.',
14
+ '필요한 API·입력값·연결을 확인한 뒤 대역을 실제 구현으로 교체하고 검증하세요. 교체 전에는 Start를 연결하지 마세요.',
15
+ ];
16
+ for (const gap of input.gaps)
17
+ if (gap.callId !== undefined && gap.targetPointer !== undefined)
18
+ content.push('확인할 요청값: ' +
19
+ escapeReviewMarkdown(gap.callId + gap.targetPointer));
20
+ if (input.operation)
21
+ content.push('대상 OAS 작업: ' +
22
+ escapeReviewMarkdown(input.operation.key.documentId +
23
+ ' ' +
24
+ input.operation.method +
25
+ ' ' +
26
+ input.operation.path));
27
+ return content;
28
+ }
@@ -0,0 +1,2 @@
1
+ import type { CreateGapNodeInput, GapReviewRegion } from '../../types/reviewable-workflow.js';
2
+ export declare function createGapReviewRegion(input: CreateGapNodeInput): GapReviewRegion;
@@ -0,0 +1,23 @@
1
+ import { sticky } from '@n8n/workflow-sdk';
2
+ import { createGapNode } from '../../nodes/gap/create-gap-node.js';
3
+ import { createGapReviewContent } from './create-gap-review-content.js';
4
+ import { gapReviewRegionLayout } from './gap-review-region-layout.js';
5
+ export function createGapReviewRegion(input) {
6
+ const content = createGapReviewContent(input);
7
+ const layout = gapReviewRegionLayout(input.position, content);
8
+ const fragment = createGapNode({
9
+ ...input,
10
+ position: layout.nodePosition,
11
+ });
12
+ return {
13
+ fragment,
14
+ annotation: sticky(content.join('\n\n'), fragment.nodes, {
15
+ id: 'openapi-flow-gap-note-' + input.nodeId,
16
+ name: '확인 안내 · ' + input.nodeId,
17
+ position: layout.notePosition,
18
+ color: 3,
19
+ width: layout.width,
20
+ height: layout.height,
21
+ }),
22
+ };
23
+ }
@@ -0,0 +1 @@
1
+ export declare function escapeReviewMarkdown(value: string): string;
@@ -0,0 +1,7 @@
1
+ export function escapeReviewMarkdown(value) {
2
+ return value
3
+ .replace(/&/g, '&')
4
+ .replace(/</g, '&lt;')
5
+ .replace(/>/g, '&gt;')
6
+ .replace(/([\\`*_{}[\]()#+.!|~-])/g, '\\$1');
7
+ }
@@ -0,0 +1,4 @@
1
+ import type { GapReviewRegionLayout } from '../../types/reviewable-workflow.js';
2
+ export declare const GAP_REVIEW_REGION_WIDTH = 560;
3
+ export declare const GAP_REVIEW_REGION_SPACING = 64;
4
+ export declare function gapReviewRegionLayout(origin: [number, number], content: string[]): GapReviewRegionLayout;
@@ -0,0 +1,21 @@
1
+ export const GAP_REVIEW_REGION_WIDTH = 560;
2
+ export const GAP_REVIEW_REGION_SPACING = 64;
3
+ // Reserve the upper area for wrapped Markdown, then place the node below it.
4
+ // These are canvas spacing metrics, not limits on diagnostic text or OAS data.
5
+ export function gapReviewRegionLayout(origin, content) {
6
+ const textHeight = 96 +
7
+ content
8
+ .slice(1)
9
+ .reduce((height, paragraph) => height +
10
+ paragraph
11
+ .split('\n')
12
+ .reduce((lines, line) => lines + Math.max(1, Math.ceil(Array.from(line).length / 34)), 0) *
13
+ 28 +
14
+ 16, 0);
15
+ return {
16
+ notePosition: origin,
17
+ nodePosition: [origin[0] + 232, origin[1] + textHeight + 32],
18
+ width: GAP_REVIEW_REGION_WIDTH,
19
+ height: textHeight + 272,
20
+ };
21
+ }
@@ -0,0 +1,4 @@
1
+ import type { NativeOutputContract, WorkflowApiMaterial } from '@openapi-flow/core';
2
+ import type { N8nNodeFragment } from '../types/node-fragment.js';
3
+ /** The runtime reader must use the same producer/root/schema the plan validated. */
4
+ export declare function validateCompiledBindingSources(apiNodes: N8nNodeFragment[], nativeNodes: N8nNodeFragment[], contracts: NativeOutputContract[], materials: WorkflowApiMaterial[]): void;
@@ -0,0 +1,40 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ /** The runtime reader must use the same producer/root/schema the plan validated. */
3
+ export function validateCompiledBindingSources(apiNodes, nativeNodes, contracts, materials) {
4
+ const apis = new Map(apiNodes.map((fragment) => [fragment.nodeId, fragment]));
5
+ const natives = new Map(nativeNodes.map((fragment) => [fragment.nodeId, fragment]));
6
+ const outputs = new Map(contracts.map((contract) => [contract.nodeId, contract]));
7
+ for (const fragment of apiNodes) {
8
+ const material = materials.find((item) => item.arguments.callId === fragment.nodeId);
9
+ if (!material)
10
+ throw new Error(`binding fragment ${fragment.nodeId} has no API material`);
11
+ const expected = [
12
+ ...new Set(material.arguments.bindings
13
+ .map((binding) => binding.sourceNodeId)
14
+ .filter((id) => outputs.has(id))),
15
+ ].sort();
16
+ const actual = (fragment.bindingSources ?? [])
17
+ .filter((source) => source.kind === 'native-json')
18
+ .map((source) => source.nodeId)
19
+ .sort();
20
+ if (!isDeepStrictEqual(expected, actual))
21
+ throw new Error(`binding fragment ${fragment.nodeId} must declare every native producer exactly once`);
22
+ }
23
+ for (const fragment of [...apiNodes, ...nativeNodes]) {
24
+ for (const source of fragment.bindingSources ?? []) {
25
+ const producer = source.kind === 'api-response'
26
+ ? apis.get(source.nodeId)
27
+ : natives.get(source.nodeId);
28
+ if (!producer || producer.exit.name !== source.nodeName)
29
+ throw new Error(`binding source ${source.nodeId} does not match its compiled producer name/kind`);
30
+ if (source.kind === 'native-json' &&
31
+ !isDeepStrictEqual(source.schema, outputs.get(source.nodeId)?.schema))
32
+ throw new Error(`binding source ${source.nodeId} does not match its declared native output schema`);
33
+ if (source.kind === 'api-response' &&
34
+ source.operation &&
35
+ !isDeepStrictEqual(source.operation, materials.find((item) => item.arguments.callId === source.nodeId)
36
+ ?.operation))
37
+ throw new Error(`binding source ${source.nodeId} does not match its original OAS response contract`);
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,3 @@
1
+ import type { N8nNodeFragment } from '../types/node-fragment.js';
2
+ /** Internal implementation nodes must form one declared-entries-to-exit DAG. */
3
+ export declare function validateNodeFragments(fragments: N8nNodeFragment[]): void;
@@ -0,0 +1,58 @@
1
+ /** Internal implementation nodes must form one declared-entries-to-exit DAG. */
2
+ export function validateNodeFragments(fragments) {
3
+ for (const fragment of fragments) {
4
+ const byId = new Map(fragment.nodes.map((item) => [item.id, item]));
5
+ if (!fragment.nodes.includes(fragment.entry) ||
6
+ !fragment.nodes.includes(fragment.exit) ||
7
+ byId.size !== fragment.nodes.length)
8
+ throw new Error(`node fragment ${fragment.nodeId} has invalid entry/exit or duplicate IDs`);
9
+ const edges = fragment.internalEdges ?? [];
10
+ const endpoints = fragment.inputEndpoints;
11
+ if (endpoints &&
12
+ (Object.keys(endpoints).length !==
13
+ Object.keys(fragment.inputPorts).length ||
14
+ Object.keys(fragment.inputPorts).some((port) => !Object.hasOwn(endpoints, port) ||
15
+ !byId.has(endpoints[port].nodeId) ||
16
+ !Number.isInteger(endpoints[port].input) ||
17
+ endpoints[port].input < 0 ||
18
+ endpoints[port].input !== fragment.inputPorts[port]) ||
19
+ !Object.values(endpoints).some((endpoint) => endpoint.nodeId === fragment.entry.id)))
20
+ throw new Error(`node fragment ${fragment.nodeId} has invalid input endpoints`);
21
+ const entries = endpoints
22
+ ? new Set(Object.values(endpoints).map((endpoint) => endpoint.nodeId))
23
+ : new Set([fragment.entry.id]);
24
+ const keys = new Set();
25
+ for (const edge of edges) {
26
+ const key = JSON.stringify(edge);
27
+ if (!byId.has(edge.from) ||
28
+ !byId.has(edge.to) ||
29
+ keys.has(key) ||
30
+ !Number.isInteger(edge.output) ||
31
+ edge.output < 0 ||
32
+ !Number.isInteger(edge.input) ||
33
+ edge.input < 0)
34
+ throw new Error(`node fragment ${fragment.nodeId} has an invalid internal edge`);
35
+ keys.add(key);
36
+ }
37
+ const reached = new Set();
38
+ const active = new Set();
39
+ function visit(id) {
40
+ if (active.has(id))
41
+ throw new Error(`node fragment ${fragment.nodeId} has an internal cycle`);
42
+ if (reached.has(id))
43
+ return;
44
+ active.add(id);
45
+ for (const edge of edges.filter((item) => item.from === id))
46
+ visit(edge.to);
47
+ active.delete(id);
48
+ reached.add(id);
49
+ }
50
+ for (const entry of entries)
51
+ visit(entry);
52
+ if (reached.size !== byId.size ||
53
+ edges.some((edge) => entries.has(edge.to) || edge.from === fragment.exit.id) ||
54
+ fragment.nodes.some((item) => item !== fragment.exit &&
55
+ !edges.some((edge) => edge.from === item.id)))
56
+ throw new Error(`node fragment ${fragment.nodeId} must connect every internal node from entry to exit`);
57
+ }
58
+ }
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "@openapi-flow/n8n",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "description": "Deterministic OpenAPI request nodes and n8n workflow assembly",
7
+ "engines": {
8
+ "node": ">=24"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/public-api.d.ts",
13
+ "default": "./dist/public-api.js"
14
+ },
15
+ "./legacy": {
16
+ "types": "./dist/legacy-api.d.ts",
17
+ "default": "./dist/legacy-api.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE",
24
+ "THIRD_PARTY_NOTICES.md"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.json && node ../../scripts/build-request-runtime.mjs && node ../../scripts/build-item-join-runtime.mjs && node ../../scripts/build-response-runtime.mjs",
28
+ "typecheck": "tsc -p tsconfig.json --noEmit"
29
+ },
30
+ "dependencies": {
31
+ "@n8n/config": "2.41.2",
32
+ "@n8n/workflow-sdk": "0.36.4",
33
+ "@openapi-flow/core": "0.2.0",
34
+ "@sentry/node": "10.70.0",
35
+ "@types/estree": "1.0.8",
36
+ "@types/express": "5.0.1",
37
+ "@types/json-schema": "7.0.15",
38
+ "@types/luxon": "3.2.0",
39
+ "@types/semver": "7.7.0",
40
+ "@types/ssh2": "1.15.0",
41
+ "ajv": "8.20.0",
42
+ "ajv-formats": "3.0.1",
43
+ "esbuild": "0.28.2",
44
+ "nock": "14.0.14",
45
+ "zod": "4.6.5"
46
+ },
47
+ "publishConfig": {
48
+ "registry": "https://registry.npmjs.org/",
49
+ "access": "public"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/carefreelife98/openapi-flow.git",
54
+ "directory": "packages/n8n"
55
+ }
56
+ }