@uipath/maestro-builder-sdk 6.47.2 → 6.48.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 +44 -44
- package/dist/api-members.md +151 -151
- package/dist/bpmn/serialize.js +93 -0
- package/dist/check.js +6 -97
- package/dist/core/actions.d.ts +0 -151
- package/dist/core/actions.js +0 -96
- package/dist/core/nodes/registry.d.ts +2 -1
- package/dist/core/nodes/registry.js +13 -5
- package/dist/core/nodes/versioned.d.ts +8 -0
- package/dist/decompile.js +0 -31
- package/dist/nodes/document/flow/factories.d.ts +59 -4
- package/dist/nodes/document/flow/factories.js +60 -3
- package/dist/nodes/document/flow/family.d.ts +1 -1
- package/dist/nodes/document/flow/family.js +3 -2
- package/dist/nodes/document/flow/ixp.d.ts +34 -0
- package/dist/nodes/document/flow/ixp.js +389 -0
- package/dist/nodes/document/flow/node-type.js +1 -3
- package/dist/nodes/document/flow/spec.d.ts +102 -2
- package/dist/nodes/document/flow/spec.js +39 -1
- package/dist/serialize.d.ts +2 -21
- package/dist/serialize.js +6 -248
- package/package.json +2 -2
package/dist/bpmn/serialize.js
CHANGED
|
@@ -20,6 +20,8 @@ import { createConnectorEvent, createExternalTask, intsvcOutputVar } from './int
|
|
|
20
20
|
import { createExtensionPayload, createTypedNode, rowDeclaration, typedNodeOutputVar, verbatimOutputRows } from './typed-node.js';
|
|
21
21
|
import { connectorOutputVars, typedNodeDeclaredVars } from './declared-vars.js';
|
|
22
22
|
import { defaultEntryPointId } from './entry-point-id.js';
|
|
23
|
+
import { parseXmlRanges } from './xml-ranges.js';
|
|
24
|
+
import { ownEntry } from '../core/own-entry.js';
|
|
23
25
|
const require = createRequire(import.meta.url);
|
|
24
26
|
const uipathDescriptor = require('./uipath-moddle.v1.json');
|
|
25
27
|
const NS = {
|
|
@@ -181,6 +183,22 @@ export async function serialize(built, opts = {}) {
|
|
|
181
183
|
* of the trade. Revisit if the graders learn to treat absence as interrupting.
|
|
182
184
|
*/
|
|
183
185
|
export function finalizeBpmnXml(xml) {
|
|
186
|
+
return jsonInputBodiesAsCdata(outsideCdata(xml, normalizeMarkup));
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Apply `edit` to the markup only — never inside a CDATA section or a comment.
|
|
190
|
+
*
|
|
191
|
+
* The passes below are patterns over the text, and a CDATA payload is author data: a
|
|
192
|
+
* JSON value holding the literal `<` or `<bpmn:boundaryEvent>` must come out of a
|
|
193
|
+
* second `finalizeBpmnXml` (or `tidy`) byte for byte, not rewritten as markup.
|
|
194
|
+
*/
|
|
195
|
+
function outsideCdata(xml, edit) {
|
|
196
|
+
return xml
|
|
197
|
+
.split(/(<!\[CDATA\[[\s\S]*?\]\]>|<!--[\s\S]*?-->)/)
|
|
198
|
+
.map((part, i) => (i % 2 === 1 ? part : edit(part)))
|
|
199
|
+
.join('');
|
|
200
|
+
}
|
|
201
|
+
function normalizeMarkup(xml) {
|
|
184
202
|
return (xml
|
|
185
203
|
.replace(/<bpmn:boundaryEvent\b(?![^>]*\bcancelActivity=)([^>]*?)(\/?)>/g, '<bpmn:boundaryEvent cancelActivity="true"$1$2>')
|
|
186
204
|
// Same reason, other schema default: a PARALLEL multi-instance marker is
|
|
@@ -206,6 +224,81 @@ export function finalizeBpmnXml(xml) {
|
|
|
206
224
|
.replace(/&#(?:34|x22);/g, '"')
|
|
207
225
|
.replace(/&#(?:39|x27);/g, '''));
|
|
208
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Rewrite every json `uipath:input` text body as CDATA — the one body shape the cloud
|
|
229
|
+
* engine reads.
|
|
230
|
+
*
|
|
231
|
+
* `PO.BpmnEngine`'s `UiPathExtensionsProvider` (`ExtractContextInputs`,
|
|
232
|
+
* `ExtractActivityInputs`) takes an input's value from an `XCData` child or a `value`
|
|
233
|
+
* attribute and filters out any row with neither. moddle writes a body as entity-escaped
|
|
234
|
+
* TEXT, which is neither, so the engine silently dropped every json input the SDK
|
|
235
|
+
* authored: a script task ran with no `args` (`vars is not defined`), a connector task
|
|
236
|
+
* sent no body. `check`, `compile` and `uip maestro bpmn validate` all read text and
|
|
237
|
+
* CDATA alike, so nothing caught it before the run (#1390). Studio Web writes CDATA.
|
|
238
|
+
*
|
|
239
|
+
* moddle has no CDATA option for a body property, so this rewrites its output — by
|
|
240
|
+
* element range (`xml-ranges.ts`), not a pattern over the document. It runs LAST in
|
|
241
|
+
* `finalizeBpmnXml`, after the markup passes, which skip CDATA (`outsideCdata`), so no
|
|
242
|
+
* pass reaches into a payload this writes. An input whose content is not plain text (already CDATA,
|
|
243
|
+
* as `tidy` may hand it, or empty) is left alone, so this is idempotent.
|
|
244
|
+
*/
|
|
245
|
+
function jsonInputBodiesAsCdata(xml) {
|
|
246
|
+
const bodies = [];
|
|
247
|
+
const collect = (nodes) => {
|
|
248
|
+
for (const node of nodes) {
|
|
249
|
+
if (node.name === 'uipath:input' && node.attributes.type === 'json')
|
|
250
|
+
bodies.push(node);
|
|
251
|
+
else
|
|
252
|
+
collect(node.children);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
collect(parseXmlRanges(xml));
|
|
256
|
+
let out = xml;
|
|
257
|
+
// Splice from the end so the earlier offsets stay valid.
|
|
258
|
+
for (const input of bodies.sort((a, b) => b.innerStart - a.innerStart)) {
|
|
259
|
+
const text = xml.slice(input.innerStart, input.innerEnd);
|
|
260
|
+
if (text === '' || text.includes('<'))
|
|
261
|
+
continue;
|
|
262
|
+
out = out.slice(0, input.innerStart) + cdata(input.attributes.name ?? '', decodeXmlText(text)) + out.slice(input.innerEnd);
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
const NAMED_ENTITY = { lt: '<', gt: '>', amp: '&', quot: '"', apos: "'" };
|
|
267
|
+
/** Decode XML character data in one pass, so `&lt;` stays the text `<`. */
|
|
268
|
+
function decodeXmlText(text) {
|
|
269
|
+
return text.replace(/&(?:#(\d+)|#x([0-9a-fA-F]+)|(lt|gt|amp|quot|apos));/g, (_, dec, hex, named) => dec !== undefined
|
|
270
|
+
? String.fromCodePoint(Number(dec))
|
|
271
|
+
: hex !== undefined
|
|
272
|
+
? String.fromCodePoint(parseInt(hex, 16))
|
|
273
|
+
: ownEntry(NAMED_ENTITY, named));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* ONE CDATA section holding `text`'s value.
|
|
277
|
+
*
|
|
278
|
+
* A `]]>` cannot sit in a CDATA section, and splitting it across two is no answer: the
|
|
279
|
+
* engine reads the FIRST `XCData` child only (`OfType<XCData>().FirstOrDefault()`), so it
|
|
280
|
+
* would get the JSON cut off at the split. In valid JSON a `]]>` can only sit inside a
|
|
281
|
+
* string (`>` is not JSON outside one), so its `>` is written as the escape `\u003e` —
|
|
282
|
+
* different bytes, the same value. A body that is not JSON has no such escape and could
|
|
283
|
+
* only reach the engine truncated, so it is refused.
|
|
284
|
+
*/
|
|
285
|
+
function cdata(name, text) {
|
|
286
|
+
if (!text.includes(']]>'))
|
|
287
|
+
return `<![CDATA[${text}]]>`;
|
|
288
|
+
let value;
|
|
289
|
+
try {
|
|
290
|
+
value = JSON.parse(text);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
throw new Error(`json input "${name}" contains "]]>" and is not valid JSON, so it cannot be written as the single CDATA section the engine reads.`);
|
|
294
|
+
}
|
|
295
|
+
const escaped = text.replaceAll(']]>', ']]\\u003e');
|
|
296
|
+
// Holds by the argument above; checked because a wrong payload here would be silent.
|
|
297
|
+
if (JSON.stringify(JSON.parse(escaped)) !== JSON.stringify(value)) {
|
|
298
|
+
throw new Error(`json input "${name}": escaping "]]>" changed its value.`);
|
|
299
|
+
}
|
|
300
|
+
return `<![CDATA[${escaped}]]>`;
|
|
301
|
+
}
|
|
209
302
|
export function processId(id) {
|
|
210
303
|
return /^[A-Za-z_][\w.-]*$/.test(id) ? id : 'Process_1';
|
|
211
304
|
}
|
package/dist/check.js
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* duplicate step names, references to undeclared variables/steps, empty branch
|
|
6
6
|
* arms, and missing terminals.
|
|
7
7
|
*/
|
|
8
|
-
import { Expr, SCHEDULE_PRESETS,
|
|
8
|
+
import { Expr, SCHEDULE_PRESETS, parallelFork } from './flow-sdk.js';
|
|
9
9
|
import { FlowNode } from './core/node-classes.js';
|
|
10
10
|
import { planContributionBindings } from './core/contribution-bindings.js';
|
|
11
11
|
import { definitionHasErrorPort, errorPortRefusal } from './core/error-port.js';
|
|
12
|
-
import { bundledActionNodeType, bundledContainerDefinition, bundledDefinition,
|
|
12
|
+
import { bundledActionNodeType, bundledContainerDefinition, bundledDefinition, resolveContributionDefinition } from './serialize.js';
|
|
13
13
|
import { checkEventResolution, checkEventSubscription, eventParamsFor, } from './core/event-checks.js';
|
|
14
14
|
import { checkCookedEscapes } from './flow-expr-check.js';
|
|
15
15
|
import { FLOW_EXPR_DIALECT, checkBindingLabels, checkBindingPresence, checkConnectorContract as coreCheckConnectorContract, checkConnectorLookups as coreCheckConnectorLookups, checkConnectorSchema as coreCheckConnectorSchema, collectConnectionUses, connectionConnectorConflicts, } from './core/connector-checks.js';
|
|
@@ -528,8 +528,6 @@ function checkFlow(built, ancestors, opts = {}, ledger = newResourceIdLedger())
|
|
|
528
528
|
outputReaders.set(stepName, (field, rest, at, d) => rule(stepName, field, rest, inputs, at, d));
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
|
-
if (s.spec.kind === 'ixpExtract')
|
|
532
|
-
checkIxpExtract(s.name, s.spec.inputs, diags);
|
|
533
531
|
if (s.spec.kind === 'waitEvent') {
|
|
534
532
|
const stepName = s.name;
|
|
535
533
|
const push = (level) => (code, message, suggestion) => diags.push({
|
|
@@ -2194,76 +2192,6 @@ const OUTPUT_READ_RULES = {
|
|
|
2194
2192
|
+ 'its own false-positive sweep — flow-builder-sdk#855.',
|
|
2195
2193
|
},
|
|
2196
2194
|
};
|
|
2197
|
-
/**
|
|
2198
|
-
* The five forbidden inputs on an IxP node. They belonged to a prior schema and
|
|
2199
|
-
* v1's own authoring reference calls them "the most common training-data-recall
|
|
2200
|
-
* mistake" — the current node declares none of them, so each is an input the
|
|
2201
|
-
* definition does not have.
|
|
2202
|
-
*
|
|
2203
|
-
* They are not reachable through `ixpExtract()`'s typed surface, which is the
|
|
2204
|
-
* point of having one; this check exists for the JS caller and for a
|
|
2205
|
-
* hand-written spec object, where TypeScript is not in the way.
|
|
2206
|
-
*/
|
|
2207
|
-
const IXP_FORBIDDEN_INPUTS = ['digitizationMode', 'documentTaxonomy', 'attachmentId', 'fileName', 'mimeType'];
|
|
2208
|
-
/**
|
|
2209
|
-
* `ixpExtract(...)`-specific checks.
|
|
2210
|
-
*
|
|
2211
|
-
* All four identity fields are load-bearing on the tenant and none of them can
|
|
2212
|
-
* be derived, so a missing one is an error rather than a warning: the node type
|
|
2213
|
-
* is per-project, and the definition's own `inputDefinition` marks `modelName`
|
|
2214
|
-
* and `folderKey` required (`minLength: 1`) beside `fileRef` — which is why
|
|
2215
|
-
* `flow validate`'s `ixp-node` validator rejects a node with an empty one.
|
|
2216
|
-
*/
|
|
2217
|
-
function checkIxpExtract(step, inputs, diags) {
|
|
2218
|
-
const at = (code, message, suggestion) => {
|
|
2219
|
-
diags.push({ level: 'error', code, message, step, ...(suggestion ? { suggestion } : {}) });
|
|
2220
|
-
};
|
|
2221
|
-
const blank = (v) => v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
|
|
2222
|
-
const project = String(inputs?.project ?? '').trim();
|
|
2223
|
-
if (blank(project)) {
|
|
2224
|
-
at('IXP_NO_PROJECT', `IxP step "${step}" has no \`project\`. That is the extractor's whole node type, and it carries the `
|
|
2225
|
-
+ `project version id and folder key the platform resolves the deployment by. Copy it from the registry `
|
|
2226
|
-
+ `— never construct it: three different spellings of its tail exist in deployed flows.`, `project: 'uipath.ixp.<slug>.<projectVersionId>-<folderKey>' // uip maestro flow registry pull --force && uip maestro flow registry search -f "type:startsWith=uipath.ixp"`);
|
|
2227
|
-
}
|
|
2228
|
-
else if (!project.startsWith('uipath.ixp.')) {
|
|
2229
|
-
at('IXP_PROJECT_NOT_NODE_TYPE', `IxP step "${step}"'s \`project\` is ${JSON.stringify(inputs.project)}, which is not a \`uipath.ixp.*\` node `
|
|
2230
|
-
+ `type. This field takes the type verbatim from \`registry search\`, not the project's name or id — the `
|
|
2231
|
-
+ `name goes in \`name\` and the slug in \`modelName\`.`);
|
|
2232
|
-
}
|
|
2233
|
-
else if (parseIxpProjectType(project) === undefined) {
|
|
2234
|
-
at('IXP_PROJECT_TAIL_UNPARSEABLE', `IxP step "${step}"'s \`project\` ends in ${JSON.stringify(project.slice(project.lastIndexOf('.') + 1))}, which is `
|
|
2235
|
-
+ `not the \`<projectVersionId>-<folderKey>\` pair of uuids the current registry serves. Two older spellings `
|
|
2236
|
-
+ `exist in the corpus — a sanitized folder path, and the literal string "undefined" — and neither resolves `
|
|
2237
|
-
+ `today. Re-read the type from \`registry search\` after \`registry pull --force\`.`);
|
|
2238
|
-
}
|
|
2239
|
-
if (blank(inputs?.modelName)) {
|
|
2240
|
-
at('IXP_NO_MODEL_NAME', `IxP step "${step}" has no \`modelName\`. The definition marks it required (\`minLength: 1\`) and the `
|
|
2241
|
-
+ `platform's \`ixp-node\` validator refuses an empty one — it is the project's own name as the tenant `
|
|
2242
|
-
+ `spells it, which the node type's slug has lower-cased and de-punctuated beyond recovery.`, `modelName: 'invoiceixp-cef0d447-ixp' // registry get → inputDefaults.modelName`);
|
|
2243
|
-
}
|
|
2244
|
-
if (blank(inputs?.name)) {
|
|
2245
|
-
at('IXP_NO_NAME', `IxP step "${step}" has no \`name\`. It is the deployment's display name — the \`ixpDeployment\` binding's `
|
|
2246
|
-
+ `\`name\`, which is how the deployed flow finds the project.`, `name: 'InvoiceIXP' // registry get → inputDefaults.model.modelDisplayName`);
|
|
2247
|
-
}
|
|
2248
|
-
if (blank(inputs?.folderName)) {
|
|
2249
|
-
at('IXP_NO_FOLDER', `IxP step "${step}" has no \`folderName\`. IxP deployments are folder-scoped, and this is the binding's `
|
|
2250
|
-
+ `\`folderPath\` default — the folder the runtime looks in.`, `folderName: 'Shared' // registry get → inputDefaults.folderName`);
|
|
2251
|
-
}
|
|
2252
|
-
const fileRef = inputs?.fileRef;
|
|
2253
|
-
if (fileRef === undefined || fileRef === null
|
|
2254
|
-
|| (typeof fileRef === 'string' && fileRef.trim() === '')) {
|
|
2255
|
-
at('IXP_NO_FILE', `IxP step "${step}" has no \`fileRef\`. It is the node's only required INPUT (\`inputDefinition.required\`) `
|
|
2256
|
-
+ `and it takes a whole Flow Attachment object — normally a \`file\`-typed flow input.`, `fileRef: out('start', 'invoiceFile') // .input({ invoiceFile: types.file })`);
|
|
2257
|
-
}
|
|
2258
|
-
for (const f of IXP_FORBIDDEN_INPUTS) {
|
|
2259
|
-
if (inputs?.[f] !== undefined) {
|
|
2260
|
-
at('IXP_FORBIDDEN_INPUT', `IxP step "${step}" sets \`${f}\`, which the current node does not declare — it belonged to a prior `
|
|
2261
|
-
+ `schema. ${f === 'documentTaxonomy' ? 'The taxonomy travels in the resolved `model` blob.'
|
|
2262
|
-
: f === 'digitizationMode' ? 'The serializer defaults it to `fileUpload` internally.'
|
|
2263
|
-
: 'It is derived from `fileRef` upstream.'}`);
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
2195
|
/** Best guess at what the author meant, for a `SCHEDULE_NOT_ISO` suggestion. */
|
|
2268
2196
|
function nearestInterval(every) {
|
|
2269
2197
|
// By far the likeliest slip: a correct duration missing the "R/" repeat marker.
|
|
@@ -3093,30 +3021,11 @@ placed, carried) {
|
|
|
3093
3021
|
const bundledDef = bundledType
|
|
3094
3022
|
? bundledDefinition(bundledType, pinned) ?? (pinned !== undefined ? carried?.(bundledType, pinned) : undefined)
|
|
3095
3023
|
: undefined;
|
|
3096
|
-
//
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
perCallDef = perCallDefinition(spec);
|
|
3101
|
-
}
|
|
3102
|
-
catch {
|
|
3103
|
-
// The family's own checks report a spec its builder cannot lower.
|
|
3104
|
-
return;
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
const def = bundledType ? bundledDef : perCallDef;
|
|
3108
|
-
const noPort = def !== undefined
|
|
3109
|
-
? !definitionHasErrorPort(def)
|
|
3024
|
+
// Every per-tenant family is registered and answers above (its errorPort, or
|
|
3025
|
+
// a carried definition); what is left is bundled, or has no definition here.
|
|
3026
|
+
const noPort = bundledDef !== undefined
|
|
3027
|
+
? !definitionHasErrorPort(bundledDef)
|
|
3110
3028
|
: !bundledType && !NO_DEFINITION_ERROR_PORT_KINDS.has(spec.kind);
|
|
3111
|
-
if (noPort && perCallDef !== undefined) {
|
|
3112
|
-
diags.push({
|
|
3113
|
-
level: 'error',
|
|
3114
|
-
code: 'ONERROR_NO_ERROR_PORT',
|
|
3115
|
-
message: errorPortRefusal(step.name, perCallDef, { bundled: true }),
|
|
3116
|
-
step: step.name,
|
|
3117
|
-
});
|
|
3118
|
-
return;
|
|
3119
|
-
}
|
|
3120
3029
|
if (noPort) {
|
|
3121
3030
|
diags.push({
|
|
3122
3031
|
level: 'error',
|
package/dist/core/actions.d.ts
CHANGED
|
@@ -257,98 +257,6 @@ export interface InOutField extends FormFieldSettings {
|
|
|
257
257
|
required?: boolean;
|
|
258
258
|
}
|
|
259
259
|
export type FormField = ShownField | AskedField | InOutField;
|
|
260
|
-
/**
|
|
261
|
-
* Split an extractor's node type into the two GUIDs its tail packs.
|
|
262
|
-
*
|
|
263
|
-
* `uipath.ixp.<projectSlug>.<projectVersionId>-<folderKey>` — the version id is
|
|
264
|
-
* the `ixpDeployment` binding's `resourceKey` and the node's `model.id`; the
|
|
265
|
-
* folder key is `inputs.folderKey`. Returns `undefined` for a tail that is not
|
|
266
|
-
* exactly two uuids joined by `-`, which is how the two older spellings (a
|
|
267
|
-
* sanitized folder path, and the literal string `undefined`) are refused rather
|
|
268
|
-
* than mis-split.
|
|
269
|
-
*
|
|
270
|
-
* @internal Public for compatibility; not an authoring factory.
|
|
271
|
-
*/
|
|
272
|
-
export declare function parseIxpProjectType(nodeType: string): {
|
|
273
|
-
slug: string;
|
|
274
|
-
versionId: string;
|
|
275
|
-
folderKey: string;
|
|
276
|
-
} | undefined;
|
|
277
|
-
export interface IxpExtractInputs {
|
|
278
|
-
/**
|
|
279
|
-
* The extractor's node type, verbatim from
|
|
280
|
-
* `uip maestro flow registry search -f "type:startsWith=uipath.ixp"` — e.g.
|
|
281
|
-
* `'uipath.ixp.invoiceixp-cef0d447-ixp.ff973488-5d89-8035-86ac-f980b4be6deb-c4359cde-55f0-4f0e-9322-c6cdce74ab4c'`.
|
|
282
|
-
*
|
|
283
|
-
* **Run `uip maestro flow registry pull --force` first.** `registry search`
|
|
284
|
-
* and `registry get` answer from a local cache (`~/.uipath/nodes/`), so
|
|
285
|
-
* without a pull you are reading whenever you last synced.
|
|
286
|
-
*
|
|
287
|
-
* Two GUIDs are packed in that tail — the project **version** id and the
|
|
288
|
-
* **folder** key — and both are read back out of it here, which is why
|
|
289
|
-
* nothing else has to be passed twice.
|
|
290
|
-
*/
|
|
291
|
-
project: string;
|
|
292
|
-
/**
|
|
293
|
-
* The project's own name, as the tenant spells it — the slug, e.g.
|
|
294
|
-
* `'idp-benchmark---invoices-c735405a-ixp'`. It is
|
|
295
|
-
* `inputDefaults.modelName` in `registry get`, and the node carries it as
|
|
296
|
-
* both `modelName` and `projectName` (one value, two fields — the designer
|
|
297
|
-
* shows it in two disabled boxes).
|
|
298
|
-
*
|
|
299
|
-
* It cannot be recovered from {@link IxpExtractInputs.project}: the node type's slug is
|
|
300
|
-
* lower-cased and de-punctuated (`---` collapses to `-`), which does not
|
|
301
|
-
* invert.
|
|
302
|
-
*/
|
|
303
|
-
modelName: string;
|
|
304
|
-
/**
|
|
305
|
-
* The deployment's display name, e.g. `'InvoiceIXP'` —
|
|
306
|
-
* `inputDefaults.model.modelDisplayName`. It becomes the node's
|
|
307
|
-
* `model.modelDisplayName` and the `ixpDeployment` binding's `name`.
|
|
308
|
-
*/
|
|
309
|
-
name: string;
|
|
310
|
-
/**
|
|
311
|
-
* The folder the project is published in, e.g. `'Shared'` or
|
|
312
|
-
* `'Shared/uipath-maestro-flow/BillingDispute'` —
|
|
313
|
-
* `inputDefaults.folderName`. It becomes the binding's `folderPath` default
|
|
314
|
-
* and the definition's own `description`.
|
|
315
|
-
*/
|
|
316
|
-
folderName: string;
|
|
317
|
-
/**
|
|
318
|
-
* The document to extract from — a whole **Flow Attachment** object, the same
|
|
319
|
-
* shape and the same two ways of referencing it as
|
|
320
|
-
* {@link SummarizeInputs.attachment}:
|
|
321
|
-
*
|
|
322
|
-
* ```ts
|
|
323
|
-
* flow('InvoiceIntake')
|
|
324
|
-
* .input({ invoiceFile: types.file })
|
|
325
|
-
* .step('extract', ixpExtract({ …, fileRef: out('start', 'invoiceFile') }))
|
|
326
|
-
* ```
|
|
327
|
-
*
|
|
328
|
-
* It is the node's only authored input, and the definition marks it
|
|
329
|
-
* `required` (with `modelName` and `folderKey`, which this factory fills in).
|
|
330
|
-
*/
|
|
331
|
-
fileRef: Expr;
|
|
332
|
-
/** Pages to process, e.g. `'1-5'`. Empty (the default) means the whole document. */
|
|
333
|
-
pageRange?: string;
|
|
334
|
-
/** Pin a published version tag. Empty (the default) means the latest. */
|
|
335
|
-
versionTag?: string;
|
|
336
|
-
/**
|
|
337
|
-
* The folder's key CHAIN, when the project lives in a nested folder —
|
|
338
|
-
* `inputDefaults.model.folderPath`, a dot-joined path of ancestor folder keys
|
|
339
|
-
* (`<root>.<parent>.<folder>`). Defaults to the folder key alone, which is
|
|
340
|
-
* what the platform writes for a project published straight into `Shared`.
|
|
341
|
-
*/
|
|
342
|
-
folderPath?: string;
|
|
343
|
-
/**
|
|
344
|
-
* A free-text note on the node. Off by default, deliberately: the definition
|
|
345
|
-
* does not declare it (so setting it draws a "sets an input its type does not
|
|
346
|
-
* declare" warning unless the emitted definition is widened in the same
|
|
347
|
-
* write, which this does), and it is empty in 114 of the 117 deployed nodes
|
|
348
|
-
* that carry it at all.
|
|
349
|
-
*/
|
|
350
|
-
description?: string;
|
|
351
|
-
}
|
|
352
260
|
/**
|
|
353
261
|
* Where a process resource lives: deployed to an Orchestrator folder
|
|
354
262
|
* (`'published'`, the default) or a sibling project of the SAME solution
|
|
@@ -914,10 +822,6 @@ export interface ContextIndexRef extends AgentResourcePlacement {
|
|
|
914
822
|
* whole body rather than an inputs payload.
|
|
915
823
|
*/
|
|
916
824
|
export interface FlowActionSpecs {
|
|
917
|
-
/** The spec {@link ixpExtract} builds. */
|
|
918
|
-
ixpExtract: {
|
|
919
|
-
inputs: IxpExtractInputs;
|
|
920
|
-
};
|
|
921
825
|
/** The spec {@link connector} builds. */
|
|
922
826
|
connector: {
|
|
923
827
|
/** The connector key, e.g. `'uipath-salesforce-slack'`. */
|
|
@@ -977,61 +881,6 @@ export declare function builtinActionSpec<S extends object>(spec: S): S;
|
|
|
977
881
|
* @returns The same spec, as a `FlowTrigger` instance.
|
|
978
882
|
*/
|
|
979
883
|
export declare function builtinTriggerSpec<S extends object>(spec: S): S;
|
|
980
|
-
/**
|
|
981
|
-
* Extract fields from a document with a published IxP project — the platform's
|
|
982
|
-
* **Extract** node (`uipath.ixp.*`, service contract `IXP.Extraction`).
|
|
983
|
-
*
|
|
984
|
-
* @remarks
|
|
985
|
-
* **The result's shape is three levels deep and PascalCase.** A field lands at
|
|
986
|
-
* `out('<step>', 'ExtractionResult.ResultsDocument.Fields')` — an array of
|
|
987
|
-
* `{ FieldId, FieldName, FieldType, IsMissing, Values[], Confidence }`, looked
|
|
988
|
-
* up by `FieldName`. `Fields[]` is under `ResultsDocument`, not directly under
|
|
989
|
-
* `ExtractionResult`, and there is no `result` wrapper: the platform's
|
|
990
|
-
* serializer strips it before the value reaches the variable.
|
|
991
|
-
*
|
|
992
|
-
* **A local run cannot extract anything** — extraction needs a real document on
|
|
993
|
-
* the IxP service. An offline run returns the baked
|
|
994
|
-
* fixture shape, and only a platform run reaches the model. So a green local
|
|
995
|
-
* rung proves the wiring and the definition, never that a document was read.
|
|
996
|
-
*
|
|
997
|
-
* **`fileRef` is emitted as a PLAIN `=js:` string, not the expression envelope
|
|
998
|
-
* the rest of the file uses** — the one place this family diverges from the
|
|
999
|
-
* format's general spelling, and the contract this node owes the platform. The
|
|
1000
|
-
* product's own `ixp-node` validator tests `typeof fileRef !== 'string'` and
|
|
1001
|
-
* refuses the object form (*"inputs.fileRef must be a `=js:$vars.…`
|
|
1002
|
-
* expression"*), the node's registry manifest declares
|
|
1003
|
-
* `{type:'string', minLength:1}`, and so do all 150 v1-authored instances in the
|
|
1004
|
-
* deployed corpus that carry the field — at file versions 1.6, 1.8 and 1.9 alike,
|
|
1005
|
-
* with zero envelopes among them. The RUNTIME
|
|
1006
|
-
* accepts both, so only `uip maestro flow validate` sees a wrong one; the
|
|
1007
|
-
* emitter's exemption lives in `migrate.ts` (`UNWRAPPED_INPUTS`).
|
|
1008
|
-
* See the IxP node reference (`website/reference/ixp.md`) §3.
|
|
1009
|
-
*
|
|
1010
|
-
* @example
|
|
1011
|
-
* **Extract an invoice total and read it out of the nested result**
|
|
1012
|
-
* ```ts
|
|
1013
|
-
* flow('InvoiceIntake')
|
|
1014
|
-
* .input({ invoiceFile: types.file })
|
|
1015
|
-
* .output({ total: types.string })
|
|
1016
|
-
* .step('extract', ixpExtract({
|
|
1017
|
-
* project: 'uipath.ixp.invoiceixp-cef0d447-ixp.ff973488-5d89-8035-86ac-f980b4be6deb-c4359cde-55f0-4f0e-9322-c6cdce74ab4c',
|
|
1018
|
-
* modelName: 'invoiceixp-cef0d447-ixp',
|
|
1019
|
-
* name: 'InvoiceIXP',
|
|
1020
|
-
* folderName: 'Shared',
|
|
1021
|
-
* fileRef: out('start', 'invoiceFile'),
|
|
1022
|
-
* }))
|
|
1023
|
-
* .step('readTotal', script({ code: `
|
|
1024
|
-
* const fields = $vars.extract.output.ExtractionResult.ResultsDocument.Fields || [];
|
|
1025
|
-
* return { total: fields.find(f => f.FieldName === 'invoiceTotal')?.Values?.[0] };
|
|
1026
|
-
* ` }))
|
|
1027
|
-
* .return({ total: out('readTotal', 'total') })
|
|
1028
|
-
* ```
|
|
1029
|
-
*
|
|
1030
|
-
* @param inputs - The published IxP `project` and `modelName`, and the document
|
|
1031
|
-
* to read (`fileRef`).
|
|
1032
|
-
* @returns An action spec to pass to `.step(name, …)`.
|
|
1033
|
-
*/
|
|
1034
|
-
export declare function ixpExtract(inputs: IxpExtractInputs): ActionSpec;
|
|
1035
884
|
/**
|
|
1036
885
|
* Declare an Integration Service connector action — the typed form, where a
|
|
1037
886
|
* generated descriptor supplies the nodeType and statically-checked input types:
|
package/dist/core/actions.js
CHANGED
|
@@ -1,44 +1,5 @@
|
|
|
1
1
|
import { splitConnectorNodeType, isTriggerDescriptor, } from './connectors.js';
|
|
2
2
|
import { FlowAction, FlowTrigger } from './node-classes.js';
|
|
3
|
-
// ─── Document extraction (uipath.ixp.*) ──────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// The platform's *Extract* node (that is its `display.canvasLabel`): it hands a
|
|
6
|
-
// document to a published IxP — Intelligent eXtraction Platform — project and
|
|
7
|
-
// reads the trained model's fields back. Every extractor on a tenant is its own
|
|
8
|
-
// node type, `uipath.ixp.<projectSlug>.<projectVersionId>-<folderKey>`, so the
|
|
9
|
-
// four identity fields below come from the tenant and are named exactly as the
|
|
10
|
-
// node names them.
|
|
11
|
-
//
|
|
12
|
-
// The type is RESOLVED, never constructed: three different spellings of that
|
|
13
|
-
// tail exist in the deployed corpus (the sanitized folder path, the literal
|
|
14
|
-
// string `undefined`, and today's version-id + folder-key pair), so copy the
|
|
15
|
-
// one `registry search` prints.
|
|
16
|
-
/**
|
|
17
|
-
* Split an extractor's node type into the two GUIDs its tail packs.
|
|
18
|
-
*
|
|
19
|
-
* `uipath.ixp.<projectSlug>.<projectVersionId>-<folderKey>` — the version id is
|
|
20
|
-
* the `ixpDeployment` binding's `resourceKey` and the node's `model.id`; the
|
|
21
|
-
* folder key is `inputs.folderKey`. Returns `undefined` for a tail that is not
|
|
22
|
-
* exactly two uuids joined by `-`, which is how the two older spellings (a
|
|
23
|
-
* sanitized folder path, and the literal string `undefined`) are refused rather
|
|
24
|
-
* than mis-split.
|
|
25
|
-
*
|
|
26
|
-
* @internal Public for compatibility; not an authoring factory.
|
|
27
|
-
*/
|
|
28
|
-
export function parseIxpProjectType(nodeType) {
|
|
29
|
-
const rest = nodeType.startsWith('uipath.ixp.') ? nodeType.slice('uipath.ixp.'.length) : undefined;
|
|
30
|
-
if (!rest)
|
|
31
|
-
return undefined;
|
|
32
|
-
const dot = rest.lastIndexOf('.');
|
|
33
|
-
if (dot <= 0)
|
|
34
|
-
return undefined;
|
|
35
|
-
const slug = rest.slice(0, dot);
|
|
36
|
-
const tail = rest.slice(dot + 1);
|
|
37
|
-
const m = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i.exec(tail);
|
|
38
|
-
if (!m)
|
|
39
|
-
return undefined;
|
|
40
|
-
return { slug, versionId: m[1], folderKey: m[2] };
|
|
41
|
-
}
|
|
42
3
|
// ── Built-in spec adapters ──────────────────────────────────────────────────
|
|
43
4
|
// Every factory result IS a FlowAction/FlowTrigger instance (design §4.4 pass
|
|
44
5
|
// 1), so `instanceof` can tell an SDK-owned construct from a raw literal. The
|
|
@@ -88,63 +49,6 @@ export function builtinActionSpec(spec) {
|
|
|
88
49
|
export function builtinTriggerSpec(spec) {
|
|
89
50
|
return new BuiltinTrigger(spec);
|
|
90
51
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Extract fields from a document with a published IxP project — the platform's
|
|
93
|
-
* **Extract** node (`uipath.ixp.*`, service contract `IXP.Extraction`).
|
|
94
|
-
*
|
|
95
|
-
* @remarks
|
|
96
|
-
* **The result's shape is three levels deep and PascalCase.** A field lands at
|
|
97
|
-
* `out('<step>', 'ExtractionResult.ResultsDocument.Fields')` — an array of
|
|
98
|
-
* `{ FieldId, FieldName, FieldType, IsMissing, Values[], Confidence }`, looked
|
|
99
|
-
* up by `FieldName`. `Fields[]` is under `ResultsDocument`, not directly under
|
|
100
|
-
* `ExtractionResult`, and there is no `result` wrapper: the platform's
|
|
101
|
-
* serializer strips it before the value reaches the variable.
|
|
102
|
-
*
|
|
103
|
-
* **A local run cannot extract anything** — extraction needs a real document on
|
|
104
|
-
* the IxP service. An offline run returns the baked
|
|
105
|
-
* fixture shape, and only a platform run reaches the model. So a green local
|
|
106
|
-
* rung proves the wiring and the definition, never that a document was read.
|
|
107
|
-
*
|
|
108
|
-
* **`fileRef` is emitted as a PLAIN `=js:` string, not the expression envelope
|
|
109
|
-
* the rest of the file uses** — the one place this family diverges from the
|
|
110
|
-
* format's general spelling, and the contract this node owes the platform. The
|
|
111
|
-
* product's own `ixp-node` validator tests `typeof fileRef !== 'string'` and
|
|
112
|
-
* refuses the object form (*"inputs.fileRef must be a `=js:$vars.…`
|
|
113
|
-
* expression"*), the node's registry manifest declares
|
|
114
|
-
* `{type:'string', minLength:1}`, and so do all 150 v1-authored instances in the
|
|
115
|
-
* deployed corpus that carry the field — at file versions 1.6, 1.8 and 1.9 alike,
|
|
116
|
-
* with zero envelopes among them. The RUNTIME
|
|
117
|
-
* accepts both, so only `uip maestro flow validate` sees a wrong one; the
|
|
118
|
-
* emitter's exemption lives in `migrate.ts` (`UNWRAPPED_INPUTS`).
|
|
119
|
-
* See the IxP node reference (`website/reference/ixp.md`) §3.
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* **Extract an invoice total and read it out of the nested result**
|
|
123
|
-
* ```ts
|
|
124
|
-
* flow('InvoiceIntake')
|
|
125
|
-
* .input({ invoiceFile: types.file })
|
|
126
|
-
* .output({ total: types.string })
|
|
127
|
-
* .step('extract', ixpExtract({
|
|
128
|
-
* project: 'uipath.ixp.invoiceixp-cef0d447-ixp.ff973488-5d89-8035-86ac-f980b4be6deb-c4359cde-55f0-4f0e-9322-c6cdce74ab4c',
|
|
129
|
-
* modelName: 'invoiceixp-cef0d447-ixp',
|
|
130
|
-
* name: 'InvoiceIXP',
|
|
131
|
-
* folderName: 'Shared',
|
|
132
|
-
* fileRef: out('start', 'invoiceFile'),
|
|
133
|
-
* }))
|
|
134
|
-
* .step('readTotal', script({ code: `
|
|
135
|
-
* const fields = $vars.extract.output.ExtractionResult.ResultsDocument.Fields || [];
|
|
136
|
-
* return { total: fields.find(f => f.FieldName === 'invoiceTotal')?.Values?.[0] };
|
|
137
|
-
* ` }))
|
|
138
|
-
* .return({ total: out('readTotal', 'total') })
|
|
139
|
-
* ```
|
|
140
|
-
*
|
|
141
|
-
* @param inputs - The published IxP `project` and `modelName`, and the document
|
|
142
|
-
* to read (`fileRef`).
|
|
143
|
-
* @returns An action spec to pass to `.step(name, …)`.
|
|
144
|
-
*/
|
|
145
|
-
export function ixpExtract(inputs) {
|
|
146
|
-
return builtinActionSpec({ kind: 'ixpExtract', inputs });
|
|
147
|
-
}
|
|
148
52
|
export function connector(a, b, c, d) {
|
|
149
53
|
if (typeof a === 'string') {
|
|
150
54
|
const opts = (d ?? {});
|
|
@@ -52,7 +52,8 @@ export declare class NodeTypeRegistry<Ctx extends ArtifactContexts> {
|
|
|
52
52
|
typesOfKind(kind: string): readonly Registered<Ctx>[];
|
|
53
53
|
/**
|
|
54
54
|
* The node type for a type name in the file — the exact name, else the type
|
|
55
|
-
* whose `typeNamePrefix` it starts with
|
|
55
|
+
* whose `typeNamePrefix` it starts with (and whose `typeNameMatches`, if any,
|
|
56
|
+
* accepts it) — or undefined when no type owns it.
|
|
56
57
|
* The bare prefix counts: it is what an empty `key` builds, and the old lift
|
|
57
58
|
* read it as its factory, so it still does.
|
|
58
59
|
*/
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { asVersioned } from './versioned.js';
|
|
2
2
|
import { ownEntry } from '../own-entry.js';
|
|
3
|
+
/** Whether a prefix entry owns `typeName`: under its prefix, and accepted by its narrowing, if it has one. */
|
|
4
|
+
function owns(p, typeName) {
|
|
5
|
+
return typeName.startsWith(p.prefix) && (p.matches?.(typeName) ?? true);
|
|
6
|
+
}
|
|
3
7
|
export class NodeTypeRegistry {
|
|
4
8
|
artifact;
|
|
5
9
|
families;
|
|
@@ -44,15 +48,18 @@ export class NodeTypeRegistry {
|
|
|
44
48
|
const overlap = this.#byPrefix.find((p) => p.prefix.startsWith(prefix) || prefix.startsWith(p.prefix));
|
|
45
49
|
if (overlap)
|
|
46
50
|
problems.push(`${where}: prefix "${prefix}" overlaps "${overlap.prefix}" (${placeOf(overlap.entry)})`);
|
|
47
|
-
const shadowed = [...this.#byTypeName.keys()].find((n) => n.startsWith(prefix));
|
|
51
|
+
const shadowed = [...this.#byTypeName.keys()].find((n) => n.startsWith(prefix) && (type.typeNameMatches?.(n) ?? true));
|
|
48
52
|
if (shadowed)
|
|
49
53
|
problems.push(`${where}: prefix "${prefix}" would also own the registered type "${shadowed}"`);
|
|
50
54
|
}
|
|
51
55
|
else {
|
|
52
|
-
const owner = this.#byPrefix.find((p) => type.typeName
|
|
56
|
+
const owner = this.#byPrefix.find((p) => owns(p, type.typeName));
|
|
53
57
|
if (owner)
|
|
54
58
|
problems.push(`${where}: type name "${type.typeName}" falls under the prefix "${owner.prefix}" (${placeOf(owner.entry)})`);
|
|
55
59
|
}
|
|
60
|
+
if (type.typeNameMatches !== undefined && type.typeNamePrefix === undefined) {
|
|
61
|
+
problems.push(`${where}: typeNameMatches narrows a typeNamePrefix, and the type declares none`);
|
|
62
|
+
}
|
|
56
63
|
const typeOwner = this.#byTypeName.get(type.typeName);
|
|
57
64
|
if (typeOwner) {
|
|
58
65
|
problems.push(`type name "${type.typeName}" is registered twice (${placeOf(typeOwner)} and ${where}); one VersionedNodeType owns every version of a type`);
|
|
@@ -70,7 +77,7 @@ export class NodeTypeRegistry {
|
|
|
70
77
|
this.#byKind.set(type.kind, [...(sharing ?? []), entry]);
|
|
71
78
|
this.#byTypeName.set(type.typeName, entry);
|
|
72
79
|
if (type.typeNamePrefix !== undefined) {
|
|
73
|
-
this.#byPrefix.push({ prefix: type.typeNamePrefix, entry });
|
|
80
|
+
this.#byPrefix.push({ prefix: type.typeNamePrefix, matches: type.typeNameMatches?.bind(type), entry });
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
}
|
|
@@ -102,7 +109,8 @@ export class NodeTypeRegistry {
|
|
|
102
109
|
}
|
|
103
110
|
/**
|
|
104
111
|
* The node type for a type name in the file — the exact name, else the type
|
|
105
|
-
* whose `typeNamePrefix` it starts with
|
|
112
|
+
* whose `typeNamePrefix` it starts with (and whose `typeNameMatches`, if any,
|
|
113
|
+
* accepts it) — or undefined when no type owns it.
|
|
106
114
|
* The bare prefix counts: it is what an empty `key` builds, and the old lift
|
|
107
115
|
* read it as its factory, so it still does.
|
|
108
116
|
*/
|
|
@@ -110,7 +118,7 @@ export class NodeTypeRegistry {
|
|
|
110
118
|
const exact = this.#byTypeName.get(typeName);
|
|
111
119
|
if (exact)
|
|
112
120
|
return exact;
|
|
113
|
-
return this.#byPrefix.find((p) =>
|
|
121
|
+
return this.#byPrefix.find((p) => owns(p, typeName))?.entry;
|
|
114
122
|
}
|
|
115
123
|
/** Every registered kind, sorted. */
|
|
116
124
|
kinds() {
|
|
@@ -25,6 +25,14 @@ Impl extends NodeType<Spec, Serialized, Ctx> = NodeType<Spec, Serialized, Ctx>>
|
|
|
25
25
|
* per-tenant: no definition could be bundled for a name the SDK cannot know.
|
|
26
26
|
*/
|
|
27
27
|
readonly typeNamePrefix?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Narrows {@link typeNamePrefix}: the type owns a name under the prefix only
|
|
30
|
+
* when this accepts it. For a family whose names share a stem with exact names
|
|
31
|
+
* of other types — an IxP extractor is `uipath.ixp.<slug>.<versionId>-<folderKey>`,
|
|
32
|
+
* beside `uipath.ixp.extract-document-builder` — the registry then lets an
|
|
33
|
+
* exact name sit under the prefix as long as this rejects it.
|
|
34
|
+
*/
|
|
35
|
+
typeNameMatches?(typeName: string): boolean;
|
|
28
36
|
readonly factory: string;
|
|
29
37
|
/** The version used when the author names none. Changing it is a breaking change. */
|
|
30
38
|
readonly defaultVersion: string;
|
package/dist/decompile.js
CHANGED
|
@@ -38,7 +38,6 @@ import { commentText } from './core/comment-text.js';
|
|
|
38
38
|
import { tsKey as key, tsLiteral, tsNumber, tsObject as objLit, tsOptions as opts, tsText } from './core/ts-literal.js';
|
|
39
39
|
import { wireExprBody } from './core/wire-expr.js';
|
|
40
40
|
import { hasExits } from './core/step-ports.js';
|
|
41
|
-
import { parseIxpProjectType } from './core/actions.js';
|
|
42
41
|
import { readFlowGlobals } from './flow-variables.js';
|
|
43
42
|
import { outputBackingGlobal, outputBackingName } from './flow-output-backing.js';
|
|
44
43
|
import { mapJsCode } from './core/js-code-spans.js';
|
|
@@ -802,36 +801,6 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
|
|
|
802
801
|
if (sub !== undefined)
|
|
803
802
|
return `${imp.use('waitForEvent')}(${sub})`;
|
|
804
803
|
}
|
|
805
|
-
// A published IxP extractor: `uipath.ixp.<slug>.<versionId>-<folderKey>`.
|
|
806
|
-
// The document-processing pair (`uipath.ixp.extract-document-builder`,
|
|
807
|
-
// `uipath.document.classify`) are their own families, handled above and
|
|
808
|
-
// excluded here by the parse — their tails are not the GUID pair.
|
|
809
|
-
if (node.type.startsWith('uipath.ixp.') && parseIxpProjectType(node.type) !== undefined) {
|
|
810
|
-
const i = (node.inputs ?? {});
|
|
811
|
-
const plain = (k) => {
|
|
812
|
-
const v = unwrapSource(i[k]);
|
|
813
|
-
return v === undefined || v === '' ? undefined : v;
|
|
814
|
-
};
|
|
815
|
-
const fr = unwrapSource(i.fileRef) ?? '';
|
|
816
|
-
// The DEPLOYMENT's display name is nested in `inputs.model`, not at the
|
|
817
|
-
// top of `inputs` — the two are different values (the model block's
|
|
818
|
-
// `modelDisplayName` is 'InvoiceIXP'; `modelName` is the slug), so
|
|
819
|
-
// reading the wrong one recovers the slug as the name.
|
|
820
|
-
const model = (i.model ?? {});
|
|
821
|
-
const nested = (k) => typeof model[k] === 'string' && model[k] !== '' ? model[k] : undefined;
|
|
822
|
-
return `${imp.use('ixpExtract')}(${opts([
|
|
823
|
-
['project', tsLiteral(node.type)],
|
|
824
|
-
// `modelName`/`projectName` are one value in two fields; either recovers it.
|
|
825
|
-
['modelName', tsLiteral(plain('modelName') ?? plain('projectName') ?? nested('modelName') ?? '')],
|
|
826
|
-
['name', tsLiteral(nested('modelDisplayName') ?? plain('modelName') ?? '')],
|
|
827
|
-
['folderName', tsLiteral(plain('folderName') ?? nested('folderName') ?? '')],
|
|
828
|
-
['fileRef', fr.startsWith('=js:') ? emitBareExpr(fr.slice(4), imp, inputNames) : tsLiteral(fr)],
|
|
829
|
-
['pageRange', plain('pageRange') !== undefined ? tsLiteral(plain('pageRange')) : undefined],
|
|
830
|
-
['versionTag', plain('versionTag') !== undefined ? tsLiteral(plain('versionTag')) : undefined],
|
|
831
|
-
['description', plain('description') ?? nested('description') !== undefined
|
|
832
|
-
? tsLiteral((plain('description') ?? nested('description'))) : undefined],
|
|
833
|
-
])})`;
|
|
834
|
-
}
|
|
835
804
|
// Every family this SDK AUTHORS is recovered above — a guard test asserts
|
|
836
805
|
// it (`flow-decompile-families.test.ts`). Reaching here means a node type
|
|
837
806
|
// the SDK cannot author: a hand-written file, a newer platform family, or
|