@uipath/maestro-builder-sdk 6.18.10 → 6.20.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.
- package/dist/api-index.md +185 -184
- package/dist/api-members.md +506 -473
- package/dist/check.js +358 -695
- package/dist/cli/commands/flow.js +3 -0
- package/dist/compile-cli.js +51 -0
- package/dist/core/actions.d.ts +132 -765
- package/dist/core/actions.js +1 -307
- package/dist/core/binding-identity.d.ts +22 -0
- package/dist/core/binding-identity.js +28 -0
- package/dist/core/context-index.d.ts +34 -0
- package/dist/core/context-index.js +38 -0
- package/dist/core/definitionless-node-types.d.ts +16 -0
- package/dist/core/definitionless-node-types.js +16 -0
- package/dist/core/error-port.d.ts +6 -0
- package/dist/core/error-port.js +9 -0
- package/dist/core/form-field-type.d.ts +33 -0
- package/dist/core/form-field-type.js +30 -0
- package/dist/core/hitl-routing.d.ts +8 -0
- package/dist/core/hitl-routing.js +17 -1
- package/dist/core/node-classes.d.ts +4 -63
- package/dist/core/node-classes.js +0 -112
- package/dist/core/nodes/family.d.ts +36 -0
- package/dist/core/nodes/family.js +1 -0
- package/dist/core/nodes/node-type.d.ts +136 -0
- package/dist/core/nodes/node-type.js +1 -0
- package/dist/core/nodes/registry.d.ts +57 -0
- package/dist/core/nodes/registry.js +122 -0
- package/dist/core/nodes/versioned.d.ts +50 -0
- package/dist/core/nodes/versioned.js +46 -0
- package/dist/core/process-resource-sort-order.d.ts +40 -0
- package/dist/core/process-resource-sort-order.js +44 -0
- package/dist/core/typed-raw-node.d.ts +13 -62
- package/dist/core/typed-raw-node.js +40 -154
- package/dist/core/wire-expr.d.ts +27 -0
- package/dist/core/wire-expr.js +34 -0
- package/dist/decompile-cli.js +11 -1
- package/dist/decompile.d.ts +8 -3
- package/dist/decompile.js +665 -354
- package/dist/flow-definitions.d.ts +10 -0
- package/dist/flow-definitions.js +35 -0
- package/dist/flow-expr-check.js +1 -1
- package/dist/flow-node-registry.d.ts +13 -0
- package/dist/flow-node-registry.js +19 -0
- package/dist/flow-node-type.d.ts +192 -0
- package/dist/flow-node-type.js +1 -0
- package/dist/flow-sdk.d.ts +40 -1
- package/dist/flow-sdk.js +29 -2
- package/dist/flow-step-tree.js +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/merge-cli.js +25 -8
- package/dist/merge-roundtrip.d.ts +81 -0
- package/dist/merge-roundtrip.js +404 -0
- package/dist/merge.d.ts +35 -6
- package/dist/merge.js +124 -24
- package/dist/node-placement.d.ts +93 -0
- package/dist/node-placement.js +163 -0
- package/dist/nodes/data-fabric/flow/check.d.ts +13 -0
- package/dist/nodes/data-fabric/flow/check.js +164 -0
- package/dist/nodes/data-fabric/flow/factories.d.ts +113 -0
- package/dist/nodes/data-fabric/flow/factories.js +134 -0
- package/dist/nodes/data-fabric/flow/family.d.ts +7 -0
- package/dist/nodes/data-fabric/flow/family.js +9 -0
- package/dist/nodes/data-fabric/flow/index.d.ts +8 -0
- package/dist/nodes/data-fabric/flow/index.js +8 -0
- package/dist/nodes/data-fabric/flow/raw.d.ts +32 -0
- package/dist/nodes/data-fabric/flow/raw.js +100 -0
- package/dist/nodes/data-fabric/flow/shared.d.ts +77 -0
- package/dist/nodes/data-fabric/flow/shared.js +174 -0
- package/dist/nodes/data-fabric/flow/spec.d.ts +238 -0
- package/dist/nodes/data-fabric/flow/spec.js +1 -0
- package/dist/nodes/data-fabric/flow/verbs.d.ts +29 -0
- package/dist/nodes/data-fabric/flow/verbs.js +239 -0
- package/dist/nodes/delay/flow/check.d.ts +16 -0
- package/dist/nodes/delay/flow/check.js +86 -0
- package/dist/nodes/delay/flow/factories.d.ts +28 -0
- package/dist/nodes/delay/flow/factories.js +38 -0
- package/dist/nodes/delay/flow/family.d.ts +3 -0
- package/dist/nodes/delay/flow/family.js +8 -0
- package/dist/nodes/delay/flow/index.d.ts +3 -0
- package/dist/nodes/delay/flow/index.js +3 -0
- package/dist/nodes/delay/flow/node-type.d.ts +13 -0
- package/dist/nodes/delay/flow/node-type.js +71 -0
- package/dist/nodes/delay/flow/spec.d.ts +43 -0
- package/dist/nodes/delay/flow/spec.js +16 -0
- package/dist/nodes/document/flow/factories.d.ts +27 -0
- package/dist/nodes/document/flow/factories.js +30 -0
- package/dist/nodes/document/flow/family.d.ts +3 -0
- package/dist/nodes/document/flow/family.js +7 -0
- package/dist/nodes/document/flow/index.d.ts +3 -0
- package/dist/nodes/document/flow/index.js +3 -0
- package/dist/nodes/document/flow/node-type.d.ts +14 -0
- package/dist/nodes/document/flow/node-type.js +150 -0
- package/dist/nodes/document/flow/spec.d.ts +65 -0
- package/dist/nodes/document/flow/spec.js +1 -0
- package/dist/nodes/mock/flow/factories.d.ts +51 -0
- package/dist/nodes/mock/flow/factories.js +47 -0
- package/dist/nodes/mock/flow/family.d.ts +3 -0
- package/dist/nodes/mock/flow/family.js +7 -0
- package/dist/nodes/mock/flow/index.d.ts +2 -0
- package/dist/nodes/mock/flow/index.js +2 -0
- package/dist/nodes/mock/flow/node-type.d.ts +11 -0
- package/dist/nodes/mock/flow/node-type.js +51 -0
- package/dist/nodes/script/flow/check.d.ts +19 -0
- package/dist/nodes/script/flow/check.js +49 -0
- package/dist/nodes/script/flow/factories.d.ts +14 -0
- package/dist/nodes/script/flow/factories.js +15 -0
- package/dist/nodes/script/flow/family.d.ts +3 -0
- package/dist/nodes/script/flow/family.js +8 -0
- package/dist/nodes/script/flow/index.d.ts +3 -0
- package/dist/nodes/script/flow/index.js +3 -0
- package/dist/nodes/script/flow/node-type.d.ts +7 -0
- package/dist/nodes/script/flow/node-type.js +125 -0
- package/dist/nodes/script/flow/spec.d.ts +52 -0
- package/dist/nodes/script/flow/spec.js +1 -0
- package/dist/nodes/transform/flow/check.d.ts +6 -0
- package/dist/nodes/transform/flow/check.js +229 -0
- package/dist/nodes/transform/flow/factories.d.ts +52 -0
- package/dist/nodes/transform/flow/factories.js +53 -0
- package/dist/nodes/transform/flow/family.d.ts +3 -0
- package/dist/nodes/transform/flow/family.js +8 -0
- package/dist/nodes/transform/flow/index.d.ts +3 -0
- package/dist/nodes/transform/flow/index.js +3 -0
- package/dist/nodes/transform/flow/node-type.d.ts +7 -0
- package/dist/nodes/transform/flow/node-type.js +245 -0
- package/dist/nodes/transform/flow/spec.d.ts +116 -0
- package/dist/nodes/transform/flow/spec.js +10 -0
- package/dist/raw-node-advice.d.ts +16 -0
- package/dist/raw-node-advice.js +36 -0
- package/dist/raw-node.d.ts +71 -0
- package/dist/raw-node.js +116 -0
- package/dist/registry/uip/pipeline-front-door.js +5 -3
- package/dist/serialize.js +246 -549
- package/package.json +2 -2
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Parity reference: `docs/FLOW_PARITY.md`.
|
|
21
21
|
*/
|
|
22
|
+
/**
|
|
23
|
+
* A diagnostic a node contributes during `check` — surfaced alongside the
|
|
24
|
+
* built-in rules with the same severities.
|
|
25
|
+
*/
|
|
22
26
|
export interface ContributionDiagnostic {
|
|
23
27
|
/** Stable machine-readable code, SCREAMING_SNAKE by convention. */
|
|
24
28
|
code: string;
|
|
@@ -177,66 +181,3 @@ export declare abstract class FlowResource extends FlowNode<never> {
|
|
|
177
181
|
/** Always `'resource'` for an attached node. */
|
|
178
182
|
get role(): 'resource';
|
|
179
183
|
}
|
|
180
|
-
/**
|
|
181
|
-
* Place a node this SDK has no factory for, carrying its definition verbatim
|
|
182
|
-
* (`uipath.exotic.thing@2.1` and the manifest the platform served for it).
|
|
183
|
-
*
|
|
184
|
-
* @remarks
|
|
185
|
-
* This is the escape hatch the coverage plan requires: an unknown registry
|
|
186
|
-
* definition is expressible without bypassing serializer invariants. The
|
|
187
|
-
* serializer still owns the node's id, label, `typeVersion` and edge wiring; you
|
|
188
|
-
* supply which definition and which inputs, exactly as a hand-written
|
|
189
|
-
* {@link FlowAction} subclass would.
|
|
190
|
-
*
|
|
191
|
-
* It is also what `decompile` emits for a node type it cannot name, so a
|
|
192
|
-
* round trip preserves the node's TYPE and VERSION instead of degrading it to a
|
|
193
|
-
* placeholder:
|
|
194
|
-
*
|
|
195
|
-
* ```ts
|
|
196
|
-
* .step('exotic', rawNode({
|
|
197
|
-
* nodeType: 'uipath.exotic.thing',
|
|
198
|
-
* version: '2.1',
|
|
199
|
-
* manifest: exoticManifest, // exactly what `registry get` returned
|
|
200
|
-
* inputs: { where: input('scope') },
|
|
201
|
-
* }))
|
|
202
|
-
* ```
|
|
203
|
-
*
|
|
204
|
-
* `manifest` must be a real definition — an exact copy of what the registry or
|
|
205
|
-
* Workbench serves for that `nodeType@version`. It is not permission to invent
|
|
206
|
-
* one: the platform resolves the node against its own catalog at run time, so a
|
|
207
|
-
* hand-written manifest validates locally and fails on the tenant.
|
|
208
|
-
*
|
|
209
|
-
* Prefer a typed factory when one exists — it carries the family's checks, its
|
|
210
|
-
* defaults, and its output contract, none of which a raw node can know.
|
|
211
|
-
*
|
|
212
|
-
* **Never for a connector.** A `uipath.connector.*` node type is refused by
|
|
213
|
-
* `check` and `compile`: a raw node carries the author's inputs verbatim, so the
|
|
214
|
-
* emitted node has no `inputs.detail` and no connection binding — `validate`
|
|
215
|
-
* only warns ("Connector is not configured") and the run can never reach
|
|
216
|
-
* Integration Service. Use {@link connector}, and `registry prepare` when
|
|
217
|
-
* compile reports an input the static library does not carry; use
|
|
218
|
-
* {@link onEvent} for a connector event trigger.
|
|
219
|
-
*
|
|
220
|
-
* **Prefer the typed factory** for a node type that has one: `check` warns
|
|
221
|
-
* (`RAW_NODE_TYPED_FAMILY`), because `validate` cannot see an input the
|
|
222
|
-
* registry definition does not declare. For a `core.datafabric.*` type, a key
|
|
223
|
-
* the product never reads (an invented `_sortOptions` in place of `_sort`) is
|
|
224
|
-
* an error at a version the SDK bundles (`RAW_NODE_UNKNOWN_INPUT`); the
|
|
225
|
-
* canvas's snapshots, such as the `_entityFields` a related-path filter
|
|
226
|
-
* needs, stay legal.
|
|
227
|
-
*
|
|
228
|
-
* @param spec - The definition to compile against, plus the node's inputs.
|
|
229
|
-
* @returns A node to pass to `.step(...)`.
|
|
230
|
-
*/
|
|
231
|
-
export declare function rawNode(spec: {
|
|
232
|
-
/** The definition's `nodeType`, verbatim. */
|
|
233
|
-
nodeType: string;
|
|
234
|
-
/** The exact definition version; also becomes the node's `typeVersion`. */
|
|
235
|
-
version: string;
|
|
236
|
-
/** The complete node manifest, exactly as the platform serves it. */
|
|
237
|
-
manifest: unknown;
|
|
238
|
-
/** The node's `inputs`. `Expr` values render through the usual pipeline. */
|
|
239
|
-
inputs?: Record<string, unknown>;
|
|
240
|
-
/** Overrides for the node's `outputs`; rarely needed. */
|
|
241
|
-
outputs?: Record<string, unknown>;
|
|
242
|
-
}): FlowAction;
|
|
@@ -19,12 +19,6 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Parity reference: `docs/FLOW_PARITY.md`.
|
|
21
21
|
*/
|
|
22
|
-
/**
|
|
23
|
-
* A diagnostic a node contributes during `check` — surfaced alongside the
|
|
24
|
-
* built-in rules with the same severities.
|
|
25
|
-
*/
|
|
26
|
-
import { connectorRawNodeRefusal } from './connector-raw-node.js';
|
|
27
|
-
import { typedFamilyRawNodeWarning, dataFabricUnknownInputs } from './typed-raw-node.js';
|
|
28
22
|
/**
|
|
29
23
|
* The abstract root of every definition-backed node the builder can place.
|
|
30
24
|
*
|
|
@@ -74,109 +68,3 @@ export class FlowResource extends FlowNode {
|
|
|
74
68
|
return 'resource';
|
|
75
69
|
}
|
|
76
70
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Place a node this SDK has no factory for, carrying its definition verbatim
|
|
79
|
-
* (`uipath.exotic.thing@2.1` and the manifest the platform served for it).
|
|
80
|
-
*
|
|
81
|
-
* @remarks
|
|
82
|
-
* This is the escape hatch the coverage plan requires: an unknown registry
|
|
83
|
-
* definition is expressible without bypassing serializer invariants. The
|
|
84
|
-
* serializer still owns the node's id, label, `typeVersion` and edge wiring; you
|
|
85
|
-
* supply which definition and which inputs, exactly as a hand-written
|
|
86
|
-
* {@link FlowAction} subclass would.
|
|
87
|
-
*
|
|
88
|
-
* It is also what `decompile` emits for a node type it cannot name, so a
|
|
89
|
-
* round trip preserves the node's TYPE and VERSION instead of degrading it to a
|
|
90
|
-
* placeholder:
|
|
91
|
-
*
|
|
92
|
-
* ```ts
|
|
93
|
-
* .step('exotic', rawNode({
|
|
94
|
-
* nodeType: 'uipath.exotic.thing',
|
|
95
|
-
* version: '2.1',
|
|
96
|
-
* manifest: exoticManifest, // exactly what `registry get` returned
|
|
97
|
-
* inputs: { where: input('scope') },
|
|
98
|
-
* }))
|
|
99
|
-
* ```
|
|
100
|
-
*
|
|
101
|
-
* `manifest` must be a real definition — an exact copy of what the registry or
|
|
102
|
-
* Workbench serves for that `nodeType@version`. It is not permission to invent
|
|
103
|
-
* one: the platform resolves the node against its own catalog at run time, so a
|
|
104
|
-
* hand-written manifest validates locally and fails on the tenant.
|
|
105
|
-
*
|
|
106
|
-
* Prefer a typed factory when one exists — it carries the family's checks, its
|
|
107
|
-
* defaults, and its output contract, none of which a raw node can know.
|
|
108
|
-
*
|
|
109
|
-
* **Never for a connector.** A `uipath.connector.*` node type is refused by
|
|
110
|
-
* `check` and `compile`: a raw node carries the author's inputs verbatim, so the
|
|
111
|
-
* emitted node has no `inputs.detail` and no connection binding — `validate`
|
|
112
|
-
* only warns ("Connector is not configured") and the run can never reach
|
|
113
|
-
* Integration Service. Use {@link connector}, and `registry prepare` when
|
|
114
|
-
* compile reports an input the static library does not carry; use
|
|
115
|
-
* {@link onEvent} for a connector event trigger.
|
|
116
|
-
*
|
|
117
|
-
* **Prefer the typed factory** for a node type that has one: `check` warns
|
|
118
|
-
* (`RAW_NODE_TYPED_FAMILY`), because `validate` cannot see an input the
|
|
119
|
-
* registry definition does not declare. For a `core.datafabric.*` type, a key
|
|
120
|
-
* the product never reads (an invented `_sortOptions` in place of `_sort`) is
|
|
121
|
-
* an error at a version the SDK bundles (`RAW_NODE_UNKNOWN_INPUT`); the
|
|
122
|
-
* canvas's snapshots, such as the `_entityFields` a related-path filter
|
|
123
|
-
* needs, stay legal.
|
|
124
|
-
*
|
|
125
|
-
* @param spec - The definition to compile against, plus the node's inputs.
|
|
126
|
-
* @returns A node to pass to `.step(...)`.
|
|
127
|
-
*/
|
|
128
|
-
export function rawNode(spec) {
|
|
129
|
-
return new RawNode(spec);
|
|
130
|
-
}
|
|
131
|
-
/** The {@link rawNode} implementation — a snapshot-backed contribution. */
|
|
132
|
-
class RawNode extends FlowAction {
|
|
133
|
-
spec;
|
|
134
|
-
constructor(spec) {
|
|
135
|
-
super();
|
|
136
|
-
this.spec = spec;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* `check` surfaces the connector refusal as an error, a typed family as a
|
|
140
|
-
* warning, and a Data Fabric key the product never reads as an error at a
|
|
141
|
-
* bundled version (a warning at any other); `compile` throws the errors
|
|
142
|
-
* (see {@link contribute}).
|
|
143
|
-
*/
|
|
144
|
-
diagnostics() {
|
|
145
|
-
const refusal = connectorRawNodeRefusal(this.spec.nodeType);
|
|
146
|
-
if (refusal)
|
|
147
|
-
return [{ code: 'RAW_NODE_CONNECTOR', severity: 'error', message: refusal }];
|
|
148
|
-
const out = [];
|
|
149
|
-
const unknown = dataFabricUnknownInputs(this.spec.nodeType, this.spec.version, this.spec.inputs);
|
|
150
|
-
if (unknown)
|
|
151
|
-
out.push({ code: 'RAW_NODE_UNKNOWN_INPUT', ...unknown });
|
|
152
|
-
const typed = typedFamilyRawNodeWarning(this.spec.nodeType);
|
|
153
|
-
if (typed)
|
|
154
|
-
out.push({ code: 'RAW_NODE_TYPED_FAMILY', severity: 'warning', message: typed });
|
|
155
|
-
return out;
|
|
156
|
-
}
|
|
157
|
-
contribute(context) {
|
|
158
|
-
// compile does not run diagnostics(); refuse here too so the escape hatch
|
|
159
|
-
// cannot emit an unconfigured connector node. (A Data Fabric key the
|
|
160
|
-
// product never reads is refused by the serializer, for every contribution.)
|
|
161
|
-
const refusal = connectorRawNodeRefusal(this.spec.nodeType);
|
|
162
|
-
if (refusal)
|
|
163
|
-
throw new Error(refusal);
|
|
164
|
-
const version = context.requestedVersion ?? this.spec.version;
|
|
165
|
-
if (context.requestedVersion !== undefined && context.requestedVersion !== this.spec.version) {
|
|
166
|
-
// Versions are exact everywhere in this SDK: a contributor may not
|
|
167
|
-
// substitute its own for an explicit request (design §2.6).
|
|
168
|
-
throw new Error(`rawNode("${this.spec.nodeType}") carries version ${this.spec.version}, but the step requests `
|
|
169
|
-
+ `${context.requestedVersion}. Pass the manifest for the requested version, or drop { version }.`);
|
|
170
|
-
}
|
|
171
|
-
return {
|
|
172
|
-
definition: {
|
|
173
|
-
source: 'snapshot',
|
|
174
|
-
nodeType: this.spec.nodeType,
|
|
175
|
-
version,
|
|
176
|
-
manifest: this.spec.manifest,
|
|
177
|
-
},
|
|
178
|
-
...(this.spec.inputs !== undefined ? { inputs: this.spec.inputs } : {}),
|
|
179
|
-
...(this.spec.outputs !== undefined ? { outputs: this.spec.outputs } : {}),
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node families — node types grouped under one owner (design §3, §4.4).
|
|
3
|
+
*
|
|
4
|
+
* A family is organizational: one directory, one CODEOWNERS line, one README for
|
|
5
|
+
* the developers who work on it, and one catalog of the diagnostic codes its types
|
|
6
|
+
* emit. Its only behaviour is `check`, for rules that need every placement of the
|
|
7
|
+
* family at once (a Data Fabric update whose `fromRead` names no read step). A
|
|
8
|
+
* family object is per artifact, like its node types: Flow's Data Fabric family and
|
|
9
|
+
* BPMN's are two families that may share a directory and nothing else.
|
|
10
|
+
*/
|
|
11
|
+
import type { ArtifactContexts } from './node-type.js';
|
|
12
|
+
import type { AnyNodeType } from './versioned.js';
|
|
13
|
+
/** Every code a family may emit, with its severity and a one-line summary. */
|
|
14
|
+
export type DiagnosticCatalog = Readonly<Record<string, {
|
|
15
|
+
readonly severity: 'error' | 'warning';
|
|
16
|
+
readonly summary: string;
|
|
17
|
+
}>>;
|
|
18
|
+
/** One placed node of a family, as the family-level check sees it. */
|
|
19
|
+
export interface Placement<Spec = unknown> {
|
|
20
|
+
/** The step name the author gave the node. */
|
|
21
|
+
readonly step: string;
|
|
22
|
+
/** The registered kind. */
|
|
23
|
+
readonly kind: string;
|
|
24
|
+
readonly spec: Spec;
|
|
25
|
+
/** The version the node will be emitted at. */
|
|
26
|
+
readonly version: string;
|
|
27
|
+
}
|
|
28
|
+
export interface NodeFamily<Ctx extends ArtifactContexts> {
|
|
29
|
+
/** Stable id, e.g. `'data-fabric'`. Unique within the artifact's registry. */
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly title: string;
|
|
32
|
+
readonly types: readonly AnyNodeType<Ctx>[];
|
|
33
|
+
readonly diagnostics: DiagnosticCatalog;
|
|
34
|
+
/** Rules over every placement of this family's types, after the per-node checks. */
|
|
35
|
+
check?(placements: readonly Placement[], ctx: Ctx['familyCheck']): void;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The node-type contract — one implementation per node type, per artifact, per
|
|
3
|
+
* version. docs/NODE_TYPE_ARCHITECTURE_DESIGN.md §4.
|
|
4
|
+
*
|
|
5
|
+
* A node type owns what one placed node means and how it looks in its artifact's
|
|
6
|
+
* file: serializing a spec, deserializing a serialized node back to its spec (the
|
|
7
|
+
* lift `decompile` runs), printing the spec as the factory call an author writes,
|
|
8
|
+
* checking it, and declaring its expressions, output and error port. The artifact
|
|
9
|
+
* keeps everything structural — ids, labels, layout, edges, definition dedupe — and
|
|
10
|
+
* lends the node type a narrow context for each job, never its own internals.
|
|
11
|
+
*
|
|
12
|
+
* The pattern is the one n8n (`INodeType`, `VersionedNodeType`), Jackson
|
|
13
|
+
* (`JsonSerializer` / `JsonDeserializer` with a context) and ESLint (a rule with a
|
|
14
|
+
* reporting `context`) use; §2 of the design maps the names.
|
|
15
|
+
*
|
|
16
|
+
* Generic over the artifact: `Serialized` is what `serialize` returns (Flow's
|
|
17
|
+
* `FlowNodeContribution`), and `Ctx` names the artifact's four contexts. Each
|
|
18
|
+
* artifact exports its own specialization (`FlowNodeType<Spec>` in
|
|
19
|
+
* `flow-node-type.ts`), so a contributor never writes these parameters.
|
|
20
|
+
*/
|
|
21
|
+
import type { TypeDesc } from '../expr.js';
|
|
22
|
+
/**
|
|
23
|
+
* Where a node type's definition comes from. `bundled`: a `<typeName>@<version>`
|
|
24
|
+
* entry the SDK ships, which every answer the definition can give (its error port,
|
|
25
|
+
* its declared inputs) is read from. `per-tenant`: built per tenant or per call —
|
|
26
|
+
* there is no bundled entry to ask, so the node type declares those answers itself.
|
|
27
|
+
*/
|
|
28
|
+
export type DefinitionSource = 'bundled' | 'per-tenant';
|
|
29
|
+
/**
|
|
30
|
+
* One place a spec carries a value the artifact evaluates against its scope.
|
|
31
|
+
* `value` is walked deeply for expressions; `code` is a script body whose
|
|
32
|
+
* references are checked although it is a plain string.
|
|
33
|
+
*/
|
|
34
|
+
export type ExprSite = {
|
|
35
|
+
readonly value: unknown;
|
|
36
|
+
} | {
|
|
37
|
+
readonly code: string;
|
|
38
|
+
};
|
|
39
|
+
/** A read of a node's output, as the output rules see it. */
|
|
40
|
+
export interface OutputRead {
|
|
41
|
+
/** The step name being read. */
|
|
42
|
+
node: string;
|
|
43
|
+
/** The reference as the author wrote it, e.g. `$vars.del.output`. */
|
|
44
|
+
ref: string;
|
|
45
|
+
/** The step whose expression contains the read. */
|
|
46
|
+
reader: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* What reading a node's output yields at run time.
|
|
50
|
+
*
|
|
51
|
+
* `none` keeps the diagnostic code of the rule it replaces
|
|
52
|
+
* (`DATAFABRIC_DELETE_HAS_NO_OUTPUT`, `MOCK_HAS_NO_OUTPUT`, …) and builds the
|
|
53
|
+
* message itself, so a port changes no diagnostic an author sees.
|
|
54
|
+
*/
|
|
55
|
+
export type OutputContract = {
|
|
56
|
+
readonly kind: 'none';
|
|
57
|
+
readonly code: string;
|
|
58
|
+
message(read: OutputRead): string;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: 'fields';
|
|
61
|
+
readonly fields: Readonly<Record<string, TypeDesc>>;
|
|
62
|
+
readonly open?: boolean;
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: 'opaque';
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* A factory call as decompile prints it: the factory's name, its positional
|
|
68
|
+
* arguments, then an options object — every value already rendered as source
|
|
69
|
+
* text. An option whose source is `undefined` is left out, and an options object
|
|
70
|
+
* with nothing in it is not printed. The artifact renders the call
|
|
71
|
+
* (`connector('slack', 'send', { … }, { connection: 'x' })`), owning import
|
|
72
|
+
* tracking and formatting, so output stays byte-identical to the hand-written
|
|
73
|
+
* printers it replaces (design §7).
|
|
74
|
+
*/
|
|
75
|
+
export interface FactoryCall {
|
|
76
|
+
readonly factory: string;
|
|
77
|
+
readonly positional?: readonly string[];
|
|
78
|
+
readonly options?: ReadonlyArray<readonly [name: string, source: string | undefined]>;
|
|
79
|
+
}
|
|
80
|
+
/** The contexts an artifact lends its node types, one per job. */
|
|
81
|
+
export interface ArtifactContexts {
|
|
82
|
+
serialize: unknown;
|
|
83
|
+
deserialize: unknown;
|
|
84
|
+
print: unknown;
|
|
85
|
+
check: unknown;
|
|
86
|
+
familyCheck: unknown;
|
|
87
|
+
}
|
|
88
|
+
/** One node type at one version, for one artifact. See the module header. */
|
|
89
|
+
export interface NodeType<Spec, Serialized, Ctx extends ArtifactContexts> {
|
|
90
|
+
/** The spec discriminator, e.g. `'dataFabricRead'`. Unique within the artifact's registry. */
|
|
91
|
+
readonly kind: string;
|
|
92
|
+
/** The type name in the file, e.g. `'core.datafabric.read'`. Unique within the artifact's registry. */
|
|
93
|
+
readonly typeName: string;
|
|
94
|
+
/** The exact version this implementation serializes and recognizes. */
|
|
95
|
+
readonly version: string;
|
|
96
|
+
/** The public factory that produces this spec, e.g. `'dataFabricRead'`. */
|
|
97
|
+
readonly factory: string;
|
|
98
|
+
/** Where the definition comes from; decides who answers `errorPort` (see below). */
|
|
99
|
+
readonly definition: DefinitionSource;
|
|
100
|
+
/**
|
|
101
|
+
* Whether the node has an error port. A `bundled` type must omit it — the answer
|
|
102
|
+
* is the definition's, at this version. A `per-tenant` type must declare it,
|
|
103
|
+
* because there is no definition to ask. The registry refuses either mistake.
|
|
104
|
+
*/
|
|
105
|
+
readonly errorPort?: boolean | ((spec: Spec) => boolean);
|
|
106
|
+
/** Spec → the node's type-owned fields. The artifact still owns id, label, layout, edges and dedupe. */
|
|
107
|
+
serialize(spec: Spec, ctx: Ctx['serialize']): Serialized;
|
|
108
|
+
/** The lift: one serialized node → the spec it came from, or `undefined` when it is not this type's. */
|
|
109
|
+
deserialize(node: unknown, ctx: Ctx['deserialize']): Spec | undefined;
|
|
110
|
+
/** Spec → the factory call decompile prints. Omit it for the artifact's default, `factory(spec.inputs)`. */
|
|
111
|
+
print?(spec: Spec, ctx: Ctx['print']): FactoryCall;
|
|
112
|
+
/** Node-local rules, reported through `ctx`. Never throws for an authoring mistake. */
|
|
113
|
+
check?(spec: Spec, ctx: Ctx['check']): void;
|
|
114
|
+
/**
|
|
115
|
+
* Rules for an error handler the author attached to this node, run only once the
|
|
116
|
+
* node is known to have an error port (that decision is the artifact's, from the
|
|
117
|
+
* definition or `errorPort`). For a node whose port exists but whose failures a
|
|
118
|
+
* handler cannot usefully catch — Flow's plain HTTP request completes on a
|
|
119
|
+
* 4xx/5xx and publishes no envelope. Reported through `ctx` like `check`.
|
|
120
|
+
*/
|
|
121
|
+
checkErrorHandler?(spec: Spec, ctx: Ctx['check']): void;
|
|
122
|
+
/**
|
|
123
|
+
* Every authored value evaluated against the artifact's scope. Required: a type
|
|
124
|
+
* with none returns `[]`, by decision, because an omitted hook would silently
|
|
125
|
+
* switch off every reference rule for its steps.
|
|
126
|
+
*/
|
|
127
|
+
expressions(spec: Spec): Iterable<ExprSite>;
|
|
128
|
+
/** What reading this node's output yields. */
|
|
129
|
+
output(spec: Spec): OutputContract;
|
|
130
|
+
/**
|
|
131
|
+
* Whether this node type is the one for `spec`, when several share a kind (see
|
|
132
|
+
* VersionedNodeType.matches). On a single-version type only; a versioned type
|
|
133
|
+
* declares it once, on the VersionedNodeType.
|
|
134
|
+
*/
|
|
135
|
+
matches?(spec: Spec): boolean;
|
|
136
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The node-type registry — one per artifact, built from an explicit list of
|
|
3
|
+
* families (design §4.5).
|
|
4
|
+
*
|
|
5
|
+
* There is no registration as a side effect of importing a module: the artifact's
|
|
6
|
+
* registry module names its families, so load order cannot change what is
|
|
7
|
+
* registered and the architecture lint's no-cycle rule (FB003) keeps holding.
|
|
8
|
+
*
|
|
9
|
+
* Construction validates every invariant the pipeline relies on, and refuses with
|
|
10
|
+
* every problem at once rather than the first:
|
|
11
|
+
* - type names and family ids are unique, and so are kinds — except that
|
|
12
|
+
* several node types may share one kind when each declares `matches(spec)`;
|
|
13
|
+
* - each version's implementation agrees with the entry it is listed under
|
|
14
|
+
* (kind, type name, factory, version key) and the default version is listed;
|
|
15
|
+
* - a `bundled` type has the definition the artifact bundles at each version and
|
|
16
|
+
* declares no `errorPort` (the definition answers); a `per-tenant` type
|
|
17
|
+
* declares one (nothing else can);
|
|
18
|
+
* - no diagnostic code is claimed by two families.
|
|
19
|
+
*/
|
|
20
|
+
import type { ArtifactContexts } from './node-type.js';
|
|
21
|
+
import type { NodeFamily } from './family.js';
|
|
22
|
+
import { type VersionedNodeType } from './versioned.js';
|
|
23
|
+
/** A registered node type with the family that owns it. */
|
|
24
|
+
export interface Registered<Ctx extends ArtifactContexts> {
|
|
25
|
+
readonly family: NodeFamily<Ctx>;
|
|
26
|
+
readonly type: VersionedNodeType<unknown, unknown, Ctx>;
|
|
27
|
+
}
|
|
28
|
+
export interface NodeTypeRegistryOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Whether the artifact bundles `<typeName>@<version>`. Required to validate a
|
|
31
|
+
* `bundled` type; an artifact with no bundled definitions passes `() => false`.
|
|
32
|
+
*/
|
|
33
|
+
hasBundledDefinition(typeName: string, version: string): boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare class NodeTypeRegistry<Ctx extends ArtifactContexts> {
|
|
36
|
+
#private;
|
|
37
|
+
readonly artifact: string;
|
|
38
|
+
readonly families: readonly NodeFamily<Ctx>[];
|
|
39
|
+
/**
|
|
40
|
+
* @param artifact - The artifact's name, for messages (`'flow'`).
|
|
41
|
+
* @param families - Every family this artifact supports, in no significant order.
|
|
42
|
+
* @param options - What the artifact knows that the contract does not.
|
|
43
|
+
*/
|
|
44
|
+
constructor(artifact: string, families: readonly NodeFamily<Ctx>[], options: NodeTypeRegistryOptions);
|
|
45
|
+
/**
|
|
46
|
+
* The node type for a spec, by its kind, or undefined when the kind is not
|
|
47
|
+
* registered. When several types share the kind, the one whose `matches(spec)`
|
|
48
|
+
* holds — undefined when none does; two matching is an SDK bug and throws.
|
|
49
|
+
*/
|
|
50
|
+
byKind(kind: string, spec?: unknown): Registered<Ctx> | undefined;
|
|
51
|
+
/** Every node type registered for a kind (several when they share it), in registration order. */
|
|
52
|
+
typesOfKind(kind: string): readonly Registered<Ctx>[];
|
|
53
|
+
/** The node type for a type name in the file, or undefined when it is not registered. */
|
|
54
|
+
byTypeName(typeName: string): Registered<Ctx> | undefined;
|
|
55
|
+
/** Every registered kind, sorted. */
|
|
56
|
+
kinds(): string[];
|
|
57
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { asVersioned } from './versioned.js';
|
|
2
|
+
import { ownEntry } from '../own-entry.js';
|
|
3
|
+
export class NodeTypeRegistry {
|
|
4
|
+
artifact;
|
|
5
|
+
families;
|
|
6
|
+
#byKind = new Map();
|
|
7
|
+
#byTypeName = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* @param artifact - The artifact's name, for messages (`'flow'`).
|
|
10
|
+
* @param families - Every family this artifact supports, in no significant order.
|
|
11
|
+
* @param options - What the artifact knows that the contract does not.
|
|
12
|
+
*/
|
|
13
|
+
constructor(artifact, families, options) {
|
|
14
|
+
this.artifact = artifact;
|
|
15
|
+
this.families = families;
|
|
16
|
+
const problems = [];
|
|
17
|
+
const familyIds = new Set();
|
|
18
|
+
const codeOwner = new Map();
|
|
19
|
+
for (const family of families) {
|
|
20
|
+
if (familyIds.has(family.id))
|
|
21
|
+
problems.push(`family "${family.id}" is listed twice`);
|
|
22
|
+
familyIds.add(family.id);
|
|
23
|
+
for (const code of Object.keys(family.diagnostics)) {
|
|
24
|
+
const owner = codeOwner.get(code);
|
|
25
|
+
if (owner !== undefined && owner !== family.id) {
|
|
26
|
+
problems.push(`diagnostic code ${code} is claimed by both "${owner}" and "${family.id}"`);
|
|
27
|
+
}
|
|
28
|
+
codeOwner.set(code, family.id);
|
|
29
|
+
}
|
|
30
|
+
for (const listed of family.types) {
|
|
31
|
+
const type = asVersioned(listed);
|
|
32
|
+
const where = `${family.id}/${type.kind}`;
|
|
33
|
+
const sharing = this.#byKind.get(type.kind);
|
|
34
|
+
if (sharing && (!type.matches || sharing.some((other) => !other.type.matches))) {
|
|
35
|
+
problems.push(`kind "${type.kind}" is registered twice (${[...sharing, { family, type }].map((e) => `${e.family.id}/${e.type.typeName}`).join(' and ')}); node types that share a kind must each declare matches(spec)`);
|
|
36
|
+
}
|
|
37
|
+
const typeOwner = this.#byTypeName.get(type.typeName);
|
|
38
|
+
if (typeOwner) {
|
|
39
|
+
problems.push(`type name "${type.typeName}" is registered twice (${placeOf(typeOwner)} and ${where}); one VersionedNodeType owns every version of a type`);
|
|
40
|
+
}
|
|
41
|
+
const versions = Object.keys(type.versions);
|
|
42
|
+
if (versions.length === 0)
|
|
43
|
+
problems.push(`${where} registers no versions`);
|
|
44
|
+
if (ownEntry(type.versions, type.defaultVersion) === undefined) {
|
|
45
|
+
problems.push(`${where}: default version ${type.defaultVersion} is not registered (${versions.join(', ')})`);
|
|
46
|
+
}
|
|
47
|
+
for (const [key, impl] of Object.entries(type.versions)) {
|
|
48
|
+
problems.push(...implementationProblems(where, key, impl, type, options));
|
|
49
|
+
}
|
|
50
|
+
const entry = { family, type };
|
|
51
|
+
this.#byKind.set(type.kind, [...(sharing ?? []), entry]);
|
|
52
|
+
this.#byTypeName.set(type.typeName, entry);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (problems.length > 0) {
|
|
56
|
+
throw new Error(`internal SDK error: the ${artifact} node-type registry is inconsistent:\n`
|
|
57
|
+
+ problems.map((p) => ` - ${p}`).join('\n'));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The node type for a spec, by its kind, or undefined when the kind is not
|
|
62
|
+
* registered. When several types share the kind, the one whose `matches(spec)`
|
|
63
|
+
* holds — undefined when none does; two matching is an SDK bug and throws.
|
|
64
|
+
*/
|
|
65
|
+
byKind(kind, spec) {
|
|
66
|
+
const all = this.#byKind.get(kind);
|
|
67
|
+
if (!all)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (all.length === 1)
|
|
70
|
+
return all[0];
|
|
71
|
+
const hits = all.filter((e) => e.type.matches(spec));
|
|
72
|
+
if (hits.length > 1) {
|
|
73
|
+
throw new Error(`internal SDK error: a ${kind} spec matches ${hits.map((h) => h.type.typeName).join(' and ')}; exactly one may.`);
|
|
74
|
+
}
|
|
75
|
+
return hits[0];
|
|
76
|
+
}
|
|
77
|
+
/** Every node type registered for a kind (several when they share it), in registration order. */
|
|
78
|
+
typesOfKind(kind) {
|
|
79
|
+
return this.#byKind.get(kind) ?? [];
|
|
80
|
+
}
|
|
81
|
+
/** The node type for a type name in the file, or undefined when it is not registered. */
|
|
82
|
+
byTypeName(typeName) {
|
|
83
|
+
return this.#byTypeName.get(typeName);
|
|
84
|
+
}
|
|
85
|
+
/** Every registered kind, sorted. */
|
|
86
|
+
kinds() {
|
|
87
|
+
return [...this.#byKind.keys()].sort();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Where a registered type lives, as the registry's messages name it: `<family>/<kind>`. */
|
|
91
|
+
function placeOf(entry) {
|
|
92
|
+
return `${entry.family.id}/${entry.type.kind}`;
|
|
93
|
+
}
|
|
94
|
+
/** What is wrong with one version's implementation, as registry problems. */
|
|
95
|
+
function implementationProblems(where, key, impl, type, options) {
|
|
96
|
+
const at = `${where}@${key}`;
|
|
97
|
+
const out = [];
|
|
98
|
+
if (impl.version !== key)
|
|
99
|
+
out.push(`${at}: listed under ${key} but declares version ${impl.version}`);
|
|
100
|
+
if (impl.kind !== type.kind)
|
|
101
|
+
out.push(`${at}: declares kind "${impl.kind}"`);
|
|
102
|
+
if (impl.typeName !== type.typeName)
|
|
103
|
+
out.push(`${at}: declares type name "${impl.typeName}"`);
|
|
104
|
+
if (impl.factory !== type.factory)
|
|
105
|
+
out.push(`${at}: declares factory "${impl.factory}", the type says "${type.factory}"`);
|
|
106
|
+
// Typed as required; checked here too, for an implementation the compiler did not see.
|
|
107
|
+
if (typeof impl.expressions !== 'function') {
|
|
108
|
+
out.push(`${at}: declares no expressions(spec); return [] when the type has no expression sites`);
|
|
109
|
+
}
|
|
110
|
+
if (impl.definition === 'bundled') {
|
|
111
|
+
if (!options.hasBundledDefinition(impl.typeName, impl.version)) {
|
|
112
|
+
out.push(`${at}: is bundled, but ${impl.typeName}@${impl.version} is not a bundled definition`);
|
|
113
|
+
}
|
|
114
|
+
if (impl.errorPort !== undefined) {
|
|
115
|
+
out.push(`${at}: is bundled, so its definition answers errorPort — remove the declaration (#1011)`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (impl.errorPort === undefined) {
|
|
119
|
+
out.push(`${at}: is per-tenant, so it must declare errorPort — there is no definition to ask`);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Versioned node types — one implementation per version (design §4.2, §7).
|
|
3
|
+
*
|
|
4
|
+
* A node type whose serialized shape changed between versions registers one
|
|
5
|
+
* {@link NodeType} per version under a {@link VersionedNodeType}, the way n8n's
|
|
6
|
+
* `VersionedNodeType` maps each version to its own implementation. A new version
|
|
7
|
+
* is a new entry (usually a subclass of the previous one), so adding it cannot
|
|
8
|
+
* change how a node at an older version serializes, deserializes or prints.
|
|
9
|
+
*/
|
|
10
|
+
import type { ArtifactContexts, NodeType } from './node-type.js';
|
|
11
|
+
export interface VersionedNodeType<Spec, Serialized, Ctx extends ArtifactContexts,
|
|
12
|
+
/** The implementation type: an artifact's own extension of NodeType, when it has one. */
|
|
13
|
+
Impl extends NodeType<Spec, Serialized, Ctx> = NodeType<Spec, Serialized, Ctx>> {
|
|
14
|
+
readonly kind: string;
|
|
15
|
+
readonly typeName: string;
|
|
16
|
+
readonly factory: string;
|
|
17
|
+
/** The version used when the author names none. Changing it is a breaking change. */
|
|
18
|
+
readonly defaultVersion: string;
|
|
19
|
+
/** Every registered version → its implementation. Keys equal each entry's `version`. */
|
|
20
|
+
readonly versions: Readonly<Record<string, Impl>>;
|
|
21
|
+
/**
|
|
22
|
+
* The version a spec needs when the author named none (`requested` undefined),
|
|
23
|
+
* e.g. a read that sets `resultMode` needs `core.datafabric.read@1.4`. Omit it
|
|
24
|
+
* and the default version is used. An explicit request is never second-guessed:
|
|
25
|
+
* the registry resolves it exactly or refuses it (see `resolveVersion`).
|
|
26
|
+
*/
|
|
27
|
+
select?(spec: Spec): string;
|
|
28
|
+
/**
|
|
29
|
+
* Whether this node type is the one for `spec`, when several share a kind —
|
|
30
|
+
* one authoring kind can cover several node types (`transform` and its
|
|
31
|
+
* `variant`s, `http` and `{ managed }`). Required for each type of a shared
|
|
32
|
+
* kind, and exactly one of them must match a given spec; omit it for a kind
|
|
33
|
+
* this type has alone.
|
|
34
|
+
*/
|
|
35
|
+
matches?(spec: Spec): boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Either form a family may list: one version, or several. */
|
|
38
|
+
export type AnyNodeType<Ctx extends ArtifactContexts> = NodeType<any, any, Ctx> | VersionedNodeType<any, any, Ctx>;
|
|
39
|
+
/** True for a {@link VersionedNodeType}; a plain {@link NodeType} carries `version` instead. */
|
|
40
|
+
export declare function isVersioned<Ctx extends ArtifactContexts>(t: AnyNodeType<Ctx>): t is AnyNodeType<Ctx> & VersionedNodeType<unknown, unknown, Ctx>;
|
|
41
|
+
/** A single-version node type as the one-entry {@link VersionedNodeType} the registry stores. */
|
|
42
|
+
export declare function asVersioned<Ctx extends ArtifactContexts>(t: AnyNodeType<Ctx>): VersionedNodeType<unknown, unknown, Ctx>;
|
|
43
|
+
/**
|
|
44
|
+
* The exact version to emit for `spec`: the author's request when there is one —
|
|
45
|
+
* refused unless it is registered — else `select(spec)`, else the default.
|
|
46
|
+
* An unregistered request throws for the author, naming the step (`where`) and
|
|
47
|
+
* the registered versions; a `select` that returns an unregistered version is the
|
|
48
|
+
* node type's bug, not the author's, and throws as an internal SDK error.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveVersion<Spec, Ctx extends ArtifactContexts>(t: VersionedNodeType<Spec, unknown, Ctx>, spec: Spec, requested: string | undefined, where: string): NodeType<Spec, unknown, Ctx>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ownEntry } from '../own-entry.js';
|
|
2
|
+
/** True for a {@link VersionedNodeType}; a plain {@link NodeType} carries `version` instead. */
|
|
3
|
+
export function isVersioned(t) {
|
|
4
|
+
return 'versions' in t;
|
|
5
|
+
}
|
|
6
|
+
/** A single-version node type as the one-entry {@link VersionedNodeType} the registry stores. */
|
|
7
|
+
export function asVersioned(t) {
|
|
8
|
+
if (isVersioned(t))
|
|
9
|
+
return t;
|
|
10
|
+
const one = t;
|
|
11
|
+
return {
|
|
12
|
+
kind: one.kind,
|
|
13
|
+
typeName: one.typeName,
|
|
14
|
+
factory: one.factory,
|
|
15
|
+
defaultVersion: one.version,
|
|
16
|
+
versions: { [one.version]: one },
|
|
17
|
+
...(one.matches ? { matches: (spec) => one.matches(spec) } : {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The exact version to emit for `spec`: the author's request when there is one —
|
|
22
|
+
* refused unless it is registered — else `select(spec)`, else the default.
|
|
23
|
+
* An unregistered request throws for the author, naming the step (`where`) and
|
|
24
|
+
* the registered versions; a `select` that returns an unregistered version is the
|
|
25
|
+
* node type's bug, not the author's, and throws as an internal SDK error.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveVersion(t, spec, requested, where) {
|
|
28
|
+
const selected = requested === undefined ? t.select?.(spec) : undefined;
|
|
29
|
+
const version = requested ?? selected ?? t.defaultVersion;
|
|
30
|
+
const impl = ownEntry(t.versions, version);
|
|
31
|
+
if (!impl) {
|
|
32
|
+
const known = Object.keys(t.versions).sort().join(', ');
|
|
33
|
+
if (requested === undefined && selected === undefined) {
|
|
34
|
+
// A registered type's default is always registered; this is a type used before registration.
|
|
35
|
+
throw new Error(`internal SDK error: ${t.typeName}'s defaultVersion ${version} is not one of its versions (${known}), `
|
|
36
|
+
+ `used for ${where}. This is a bug in the node type, not in the flow; please report it.`);
|
|
37
|
+
}
|
|
38
|
+
if (requested === undefined) {
|
|
39
|
+
throw new Error(`internal SDK error: ${t.typeName}'s select() chose ${version} for ${where}, but the type registers only `
|
|
40
|
+
+ `${known}. This is a bug in the node type, not in the flow; please report it.`);
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`${where} requests ${t.typeName}@${version}, but this SDK registers only ${known}. `
|
|
43
|
+
+ 'Versions are exact — omit { version } to use the default, or request one listed here.');
|
|
44
|
+
}
|
|
45
|
+
return impl;
|
|
46
|
+
}
|