@uipath/maestro-builder-sdk 6.46.2 → 6.46.4
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 +23 -22
- package/dist/api-members.md +133 -131
- package/dist/case/case-sdk.d.ts +29 -9
- package/dist/case/case-sdk.js +17 -8
- package/dist/case/check.js +11 -0
- package/dist/case/compile-cli.js +132 -70
- package/dist/case/decompile.js +41 -9
- package/dist/case/resource-identity.d.ts +9 -1
- package/dist/case/resource-identity.js +10 -2
- package/dist/case/serialize.js +199 -23
- package/package.json +2 -2
package/dist/case/case-sdk.d.ts
CHANGED
|
@@ -491,6 +491,16 @@ export interface ActionField {
|
|
|
491
491
|
label: string;
|
|
492
492
|
}>;
|
|
493
493
|
}
|
|
494
|
+
/** The Action App an action task opens. */
|
|
495
|
+
export interface ActionAppRef {
|
|
496
|
+
/** The app's deployment title, e.g. `SimpleApprovalApp`. */
|
|
497
|
+
name: string;
|
|
498
|
+
/**
|
|
499
|
+
* The app's deployment folder, e.g. `Shared/Approvals`, or `'solution_folder'`
|
|
500
|
+
* for an app built in this solution.
|
|
501
|
+
*/
|
|
502
|
+
folder: string;
|
|
503
|
+
}
|
|
494
504
|
export interface ActionSpecData {
|
|
495
505
|
title?: string;
|
|
496
506
|
priority?: 'Low' | 'Medium' | 'High' | 'Critical';
|
|
@@ -500,7 +510,11 @@ export interface ActionSpecData {
|
|
|
500
510
|
};
|
|
501
511
|
/** Action Center labels (persisted as a single `data.labels` string). */
|
|
502
512
|
labels?: string;
|
|
503
|
-
/**
|
|
513
|
+
/**
|
|
514
|
+
* An Action Center task catalog, resolved by name in the app's deployment
|
|
515
|
+
* folder. Not the app: a catalog only groups tasks. Omit it unless the catalog
|
|
516
|
+
* is known to exist there.
|
|
517
|
+
*/
|
|
504
518
|
actionCatalogName?: string;
|
|
505
519
|
/** Read-only context fields the assignee sees (`data.inputs[]`). */
|
|
506
520
|
inputs?: ActionField[];
|
|
@@ -544,6 +558,7 @@ export interface BuiltTask {
|
|
|
544
558
|
/** Task key + display name (also the handle used by `selected-tasks-completed`). */
|
|
545
559
|
name: string;
|
|
546
560
|
kind: TaskKind;
|
|
561
|
+
/** The published resource the task binds: a reference task's process, agent, … or an action task's Action App. */
|
|
547
562
|
ref?: TaskRef;
|
|
548
563
|
/** business-rule task: the rule's decision-keyed result schema, when known. */
|
|
549
564
|
businessRule?: BusinessRuleSpecData;
|
|
@@ -765,7 +780,7 @@ declare class TaskBuilder {
|
|
|
765
780
|
* Reference a published Maestro process.
|
|
766
781
|
*
|
|
767
782
|
* @param name - The published process's name.
|
|
768
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
783
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
769
784
|
* @returns This builder, so calls chain.
|
|
770
785
|
*/
|
|
771
786
|
process(name: string, opts?: {
|
|
@@ -788,7 +803,7 @@ declare class TaskBuilder {
|
|
|
788
803
|
* Reference a published agent.
|
|
789
804
|
*
|
|
790
805
|
* @param name - The published agent's name.
|
|
791
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
806
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
792
807
|
* @returns This builder, so calls chain.
|
|
793
808
|
*/
|
|
794
809
|
agent(name: string, opts?: {
|
|
@@ -798,7 +813,7 @@ declare class TaskBuilder {
|
|
|
798
813
|
* Reference a published RPA process.
|
|
799
814
|
*
|
|
800
815
|
* @param name - The published RPA process's name.
|
|
801
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
816
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
802
817
|
* @returns This builder, so calls chain.
|
|
803
818
|
*/
|
|
804
819
|
rpa(name: string, opts?: {
|
|
@@ -808,7 +823,7 @@ declare class TaskBuilder {
|
|
|
808
823
|
* Reference a published API workflow.
|
|
809
824
|
*
|
|
810
825
|
* @param name - The published API workflow's name.
|
|
811
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
826
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
812
827
|
* @returns This builder, so calls chain.
|
|
813
828
|
*/
|
|
814
829
|
apiWorkflow(name: string, opts?: {
|
|
@@ -820,7 +835,7 @@ declare class TaskBuilder {
|
|
|
820
835
|
* io-binding as reference-mode tasks.
|
|
821
836
|
*
|
|
822
837
|
* @param name - The published child case's name.
|
|
823
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
838
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
824
839
|
* @returns This builder, so calls chain.
|
|
825
840
|
*/
|
|
826
841
|
caseManagement(name: string, opts?: {
|
|
@@ -830,7 +845,7 @@ declare class TaskBuilder {
|
|
|
830
845
|
* Reference a published Maestro Flow.
|
|
831
846
|
*
|
|
832
847
|
* @param name - The published Flow's name.
|
|
833
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
848
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
834
849
|
* @returns This builder, so calls chain.
|
|
835
850
|
*/
|
|
836
851
|
flowProcess(name: string, opts?: {
|
|
@@ -886,9 +901,13 @@ declare class TaskBuilder {
|
|
|
886
901
|
* An Action Center human task. `recipient` may be an email (→ Type 2) or
|
|
887
902
|
* `{ type, value }`. `inputs`/`outputs` declare the task's form fields — inputs
|
|
888
903
|
* are read-only context the assignee sees, outputs are what they fill in.
|
|
889
|
-
* `
|
|
904
|
+
* `app` names the Action App the task opens (`{ name, folder }`); compile
|
|
905
|
+
* binds it through an `app` name/folderPath pair keyed `<folder>.<name>`.
|
|
906
|
+
* `labels` tag the task. `actionCatalogName` names a task catalog, which is
|
|
907
|
+
* resolved in the app's deployment folder; it is not the app, and a name that
|
|
908
|
+
* folder does not hold fails the task when it opens.
|
|
890
909
|
*
|
|
891
|
-
* @param spec - The human task: its `title`, `priority`, `recipient`, and the `inputs` / `outputs` its form shows and collects.
|
|
910
|
+
* @param spec - The human task: its `title`, `priority`, `recipient`, the Action `app`, and the `inputs` / `outputs` its form shows and collects.
|
|
892
911
|
* @returns This builder, so calls chain.
|
|
893
912
|
*/
|
|
894
913
|
action(spec?: {
|
|
@@ -899,6 +918,7 @@ declare class TaskBuilder {
|
|
|
899
918
|
value: string;
|
|
900
919
|
};
|
|
901
920
|
labels?: string;
|
|
921
|
+
app?: ActionAppRef;
|
|
902
922
|
actionCatalogName?: string;
|
|
903
923
|
inputs?: ActionField[];
|
|
904
924
|
outputs?: ActionField[];
|
package/dist/case/case-sdk.js
CHANGED
|
@@ -190,7 +190,7 @@ class TaskBuilder {
|
|
|
190
190
|
* Reference a published Maestro process.
|
|
191
191
|
*
|
|
192
192
|
* @param name - The published process's name.
|
|
193
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
193
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
194
194
|
* @returns This builder, so calls chain.
|
|
195
195
|
*/
|
|
196
196
|
process(name, opts) {
|
|
@@ -216,7 +216,7 @@ class TaskBuilder {
|
|
|
216
216
|
* Reference a published agent.
|
|
217
217
|
*
|
|
218
218
|
* @param name - The published agent's name.
|
|
219
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
219
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
220
220
|
* @returns This builder, so calls chain.
|
|
221
221
|
*/
|
|
222
222
|
agent(name, opts) {
|
|
@@ -226,7 +226,7 @@ class TaskBuilder {
|
|
|
226
226
|
* Reference a published RPA process.
|
|
227
227
|
*
|
|
228
228
|
* @param name - The published RPA process's name.
|
|
229
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
229
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
230
230
|
* @returns This builder, so calls chain.
|
|
231
231
|
*/
|
|
232
232
|
rpa(name, opts) {
|
|
@@ -236,7 +236,7 @@ class TaskBuilder {
|
|
|
236
236
|
* Reference a published API workflow.
|
|
237
237
|
*
|
|
238
238
|
* @param name - The published API workflow's name.
|
|
239
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
239
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
240
240
|
* @returns This builder, so calls chain.
|
|
241
241
|
*/
|
|
242
242
|
apiWorkflow(name, opts) {
|
|
@@ -248,7 +248,7 @@ class TaskBuilder {
|
|
|
248
248
|
* io-binding as reference-mode tasks.
|
|
249
249
|
*
|
|
250
250
|
* @param name - The published child case's name.
|
|
251
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
251
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
252
252
|
* @returns This builder, so calls chain.
|
|
253
253
|
*/
|
|
254
254
|
caseManagement(name, opts) {
|
|
@@ -258,7 +258,7 @@ class TaskBuilder {
|
|
|
258
258
|
* Reference a published Maestro Flow.
|
|
259
259
|
*
|
|
260
260
|
* @param name - The published Flow's name.
|
|
261
|
-
* @param opts - `folder` — the Orchestrator folder it lives in.
|
|
261
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
262
262
|
* @returns This builder, so calls chain.
|
|
263
263
|
*/
|
|
264
264
|
flowProcess(name, opts) {
|
|
@@ -332,13 +332,22 @@ class TaskBuilder {
|
|
|
332
332
|
* An Action Center human task. `recipient` may be an email (→ Type 2) or
|
|
333
333
|
* `{ type, value }`. `inputs`/`outputs` declare the task's form fields — inputs
|
|
334
334
|
* are read-only context the assignee sees, outputs are what they fill in.
|
|
335
|
-
* `
|
|
335
|
+
* `app` names the Action App the task opens (`{ name, folder }`); compile
|
|
336
|
+
* binds it through an `app` name/folderPath pair keyed `<folder>.<name>`.
|
|
337
|
+
* `labels` tag the task. `actionCatalogName` names a task catalog, which is
|
|
338
|
+
* resolved in the app's deployment folder; it is not the app, and a name that
|
|
339
|
+
* folder does not hold fails the task when it opens.
|
|
336
340
|
*
|
|
337
|
-
* @param spec - The human task: its `title`, `priority`, `recipient`, and the `inputs` / `outputs` its form shows and collects.
|
|
341
|
+
* @param spec - The human task: its `title`, `priority`, `recipient`, the Action `app`, and the `inputs` / `outputs` its form shows and collects.
|
|
338
342
|
* @returns This builder, so calls chain.
|
|
339
343
|
*/
|
|
340
344
|
action(spec = {}) {
|
|
341
345
|
this.setKind('action');
|
|
346
|
+
// The app is the task's resource, carried as its `ref` like every other
|
|
347
|
+
// bound resource, so the resource-ref rules in check and the binding pair
|
|
348
|
+
// in serialize both see it.
|
|
349
|
+
if (spec.app)
|
|
350
|
+
this._ref = { name: spec.app.name, folderPath: spec.app.folder };
|
|
342
351
|
const recipient = typeof spec.recipient === 'string' ? { type: 2, value: spec.recipient } : spec.recipient;
|
|
343
352
|
this._action = {
|
|
344
353
|
title: spec.title,
|
package/dist/case/check.js
CHANGED
|
@@ -483,6 +483,17 @@ function checkStage(stage, diags, refStage, refPlacement) {
|
|
|
483
483
|
message: `Task "${t.name}" binding "${'var' in invalidBinding ? invalidBinding.var : invalidBinding.name}" has unsupported type ${JSON.stringify(invalidBinding.type)}. Use a type name such as "string" or "jsonSchema"; jsonSchema(...) is only for declaring a structured case variable.`,
|
|
484
484
|
where: t.name,
|
|
485
485
|
});
|
|
486
|
+
// The builder binds a resource task itself: it mints the name/folderPath
|
|
487
|
+
// binding pair from the literal name and folder. A `=bindings.<id>` string
|
|
488
|
+
// passed as either becomes part of the binding key, which then names no
|
|
489
|
+
// resource (`=bindings.f.=bindings.n`) and dangles at deploy.
|
|
490
|
+
if (t.ref && [t.ref.name, t.ref.folderPath].some((v) => v.trim().startsWith('=bindings.')))
|
|
491
|
+
diags.push({
|
|
492
|
+
level: 'error',
|
|
493
|
+
code: 'RESOURCE_REF_IS_BINDING',
|
|
494
|
+
message: `Task "${t.name}" passes a "=bindings.<id>" reference as its resource name or folder. Pass the resource's own name and its folder, e.g. .agent('Contract Analysis', { folder: 'Shared/Legal' }); compile writes the name/folderPath binding pair keyed '<folder>.<name>' and points the task at it.`,
|
|
495
|
+
where: t.name,
|
|
496
|
+
});
|
|
486
497
|
if (t.kind === 'business-rule') {
|
|
487
498
|
if (!t.ref || !t.ref.name.trim() || !t.ref.folderPath.trim())
|
|
488
499
|
diags.push({
|
package/dist/case/compile-cli.js
CHANGED
|
@@ -292,7 +292,8 @@ function syncEntryPoints(caseplan, outPath) {
|
|
|
292
292
|
* artifact. `resources refresh` reads bindings_v2.json, not caseplan.json, and
|
|
293
293
|
* succeeds with zero warnings when the sidecar is stale or empty.
|
|
294
294
|
*
|
|
295
|
-
* Non-connector resources come from root binding
|
|
295
|
+
* Non-connector resources come from the root binding rows only, as in
|
|
296
|
+
* `uip maestro case bindings sync`.
|
|
296
297
|
* A connector's connection and folder relationship comes from the context[]
|
|
297
298
|
* that references both binding ids.
|
|
298
299
|
* No-op when compiling outside a scaffolded project.
|
|
@@ -308,65 +309,29 @@ function syncBindingsV2(caseplan, outPath) {
|
|
|
308
309
|
.filter((binding) => typeof binding.id === 'string')
|
|
309
310
|
.map((binding) => [binding.id, binding]));
|
|
310
311
|
const resources = [];
|
|
311
|
-
|
|
312
|
+
// One entry per (resource, resourceKey) group of root binding rows, in the
|
|
313
|
+
// shape `uip maestro case bindings sync` derives (`deriveCaseBindingsV2` in
|
|
314
|
+
// cli case-bindings-service.ts). `validate --strict` compares the two
|
|
315
|
+
// entry by entry as serialized JSON, so field order matters as well as
|
|
316
|
+
// field values.
|
|
317
|
+
const groups = new Map();
|
|
312
318
|
for (const binding of bindings) {
|
|
313
319
|
const resource = stringValue(binding.resource);
|
|
314
320
|
const key = stringValue(binding.resourceKey);
|
|
315
|
-
if (!resource || !key ||
|
|
321
|
+
if (!resource || !key || typeof binding.propertyAttribute !== 'string')
|
|
322
|
+
continue;
|
|
323
|
+
if (resource.toLowerCase() === 'connection')
|
|
316
324
|
continue;
|
|
317
325
|
const groupKey = `${resource}\0${key}`;
|
|
318
|
-
|
|
319
|
-
if (
|
|
320
|
-
group
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const property = stringValue(binding.propertyAttribute) ?? stringValue(binding.name);
|
|
324
|
-
// An in-solution resource binds an empty folderPath in the caseplan, while
|
|
325
|
-
// the sidecar names its folder by the sentinel the key carries — the split
|
|
326
|
-
// `uip maestro case bindings sync` writes (resource-identity.ts).
|
|
327
|
-
const defaultValue = property === 'folderPath' && isInSolutionKey(key)
|
|
328
|
-
? SOLUTION_FOLDER
|
|
329
|
-
: binding.default ?? key;
|
|
330
|
-
if (property)
|
|
331
|
-
group.value[property] = { defaultValue };
|
|
332
|
-
const subType = stringValue(binding.resourceSubType);
|
|
333
|
-
if (subType)
|
|
334
|
-
group.subType = subType;
|
|
326
|
+
const group = groups.get(groupKey);
|
|
327
|
+
if (group)
|
|
328
|
+
group.push(binding);
|
|
329
|
+
else
|
|
330
|
+
groups.set(groupKey, [binding]);
|
|
335
331
|
}
|
|
336
|
-
const referenceTaskSubTypes = new Map([
|
|
337
|
-
['process', 'ProcessOrchestration'],
|
|
338
|
-
['agent', 'Agent'],
|
|
339
|
-
['rpa', undefined],
|
|
340
|
-
['api-workflow', 'Api'],
|
|
341
|
-
['case-management', 'CaseManagement'],
|
|
342
|
-
['flow-process', 'ProcessOrchestration'],
|
|
343
|
-
]);
|
|
344
332
|
const seenConnections = new Set();
|
|
345
333
|
const connectionResources = [];
|
|
346
334
|
visitRecords(caseplan, (record) => {
|
|
347
|
-
const type = stringValue(record.type);
|
|
348
|
-
const data = isRecord(record.data) ? record.data : undefined;
|
|
349
|
-
if (type && data && referenceTaskSubTypes.has(type)) {
|
|
350
|
-
const name = referencedBindingValue(data.name, byId);
|
|
351
|
-
const folderPath = referencedBindingValue(data.folderPath, byId);
|
|
352
|
-
if (name && folderPath) {
|
|
353
|
-
const key = referencedBindingResourceKey(data.name, byId)
|
|
354
|
-
?? referencedBindingResourceKey(data.folderPath, byId)
|
|
355
|
-
?? `${folderPath}.${name}`;
|
|
356
|
-
const groupKey = `process\0${key}`;
|
|
357
|
-
if (!grouped.has(groupKey)) {
|
|
358
|
-
grouped.set(groupKey, {
|
|
359
|
-
resource: 'process',
|
|
360
|
-
key,
|
|
361
|
-
value: {
|
|
362
|
-
name: { defaultValue: name },
|
|
363
|
-
folderPath: { defaultValue: folderPath },
|
|
364
|
-
},
|
|
365
|
-
subType: referenceTaskSubTypes.get(type),
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
335
|
if (!Array.isArray(record.context))
|
|
371
336
|
return;
|
|
372
337
|
const context = new Map(record.context
|
|
@@ -389,23 +354,126 @@ function syncBindingsV2(caseplan, outPath) {
|
|
|
389
354
|
metadata: { connector },
|
|
390
355
|
});
|
|
391
356
|
});
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
key: group.key,
|
|
396
|
-
value: group.value,
|
|
397
|
-
// A BusinessRule entry carries the bindings version deploy needs to
|
|
398
|
-
// override its folder — the metadata `uip maestro case bindings sync`
|
|
399
|
-
// writes for it (no subType).
|
|
400
|
-
metadata: group.subType
|
|
401
|
-
? { subType: group.subType }
|
|
402
|
-
: group.resource === 'BusinessRule' ? { BindingsVersion: '2.2' } : {},
|
|
403
|
-
});
|
|
404
|
-
}
|
|
357
|
+
const labels = bindingLabels(caseplan.nodes);
|
|
358
|
+
for (const group of groups.values())
|
|
359
|
+
resources.push(sidecarResourceEntry(group, labels));
|
|
405
360
|
resources.push(...connectionResources);
|
|
406
361
|
writeJsonFile(sidecarPath, { version: '2.0', resources });
|
|
407
362
|
return resources.length;
|
|
408
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* The Case Designer's label for each value slot of an app or process entry.
|
|
366
|
+
* These resources get the Designer shape (cli#5003, MST-16424): an `id`, each
|
|
367
|
+
* slot flagged and labelled, and the `BindingsVersion` / `SolutionsSupport`
|
|
368
|
+
* metadata without which deploy cannot override the entry.
|
|
369
|
+
*/
|
|
370
|
+
const DESIGNER_SLOT_NAMES = new Map([
|
|
371
|
+
['app', { name: 'App name', folderPath: 'Folder path', solutionsSupport: true }],
|
|
372
|
+
['process', { name: 'Process name', folderPath: 'Folder path', solutionsSupport: true }],
|
|
373
|
+
['ixpDeployment', { name: 'name', folderPath: 'Folder path', solutionsSupport: false }],
|
|
374
|
+
]);
|
|
375
|
+
/** The bindings version deploy needs to override an entry. */
|
|
376
|
+
const RESOURCE_BINDINGS_VERSION = '2.2';
|
|
377
|
+
/**
|
|
378
|
+
* One sidecar entry for a group of root binding rows that share a resource and
|
|
379
|
+
* a resource key. The subtype comes from the rows only: a Flow's rows carry
|
|
380
|
+
* none, so its entry has none either, as the Designer saves it.
|
|
381
|
+
*
|
|
382
|
+
* `metadata.ActivityName` labels a Designer-shaped entry with the first task
|
|
383
|
+
* that references one of its rows ({@link bindingLabels}).
|
|
384
|
+
*/
|
|
385
|
+
function sidecarResourceEntry(group, labels) {
|
|
386
|
+
const resource = group[0].resource;
|
|
387
|
+
const key = group[0].resourceKey;
|
|
388
|
+
const attributeDefault = (attribute) => {
|
|
389
|
+
const found = group.find((binding) => binding.propertyAttribute === attribute)?.default;
|
|
390
|
+
return typeof found === 'string' ? found : undefined;
|
|
391
|
+
};
|
|
392
|
+
// An in-solution resource binds an empty folderPath in the caseplan, while
|
|
393
|
+
// the sidecar names its folder by the sentinel the key carries.
|
|
394
|
+
const folderPath = isInSolutionKey(key) ? SOLUTION_FOLDER : attributeDefault('folderPath');
|
|
395
|
+
const name = attributeDefault('name');
|
|
396
|
+
const subType = group.map((binding) => binding.resourceSubType).find((found) => found !== undefined);
|
|
397
|
+
const designer = DESIGNER_SLOT_NAMES.get(resource);
|
|
398
|
+
if (designer) {
|
|
399
|
+
const value = {};
|
|
400
|
+
if (folderPath !== undefined) {
|
|
401
|
+
value.folderPath = { defaultValue: folderPath, isExpression: false, displayName: designer.folderPath };
|
|
402
|
+
}
|
|
403
|
+
if (name !== undefined) {
|
|
404
|
+
value.name = { defaultValue: name, isExpression: false, displayName: designer.name };
|
|
405
|
+
}
|
|
406
|
+
const activityName = group
|
|
407
|
+
.map((binding) => (typeof binding.id === 'string' ? labels.get(binding.id) : undefined))
|
|
408
|
+
.find((found) => found !== undefined);
|
|
409
|
+
return {
|
|
410
|
+
resource,
|
|
411
|
+
key,
|
|
412
|
+
id: `${resource}${key}`,
|
|
413
|
+
value,
|
|
414
|
+
metadata: {
|
|
415
|
+
...(activityName !== undefined && { ActivityName: activityName }),
|
|
416
|
+
BindingsVersion: RESOURCE_BINDINGS_VERSION,
|
|
417
|
+
DisplayLabel: 'name',
|
|
418
|
+
...(designer.solutionsSupport && { SolutionsSupport: 'true' }),
|
|
419
|
+
...(subType !== undefined && { subType }),
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const value = {};
|
|
424
|
+
if (name !== undefined)
|
|
425
|
+
value.name = { defaultValue: name };
|
|
426
|
+
if (folderPath !== undefined)
|
|
427
|
+
value.folderPath = { defaultValue: folderPath };
|
|
428
|
+
// A solution-resource picker (such as a Data Fabric entity) carries its folder key as a slot of its own.
|
|
429
|
+
const folderKey = attributeDefault('folderKey');
|
|
430
|
+
if (folderKey !== undefined)
|
|
431
|
+
value.folderKey = { defaultValue: folderKey };
|
|
432
|
+
return {
|
|
433
|
+
resource,
|
|
434
|
+
key,
|
|
435
|
+
value,
|
|
436
|
+
metadata: {
|
|
437
|
+
BindingsVersion: RESOURCE_BINDINGS_VERSION,
|
|
438
|
+
...(subType !== undefined && { subType }),
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* The label of the first object that references each binding id
|
|
444
|
+
* (`=bindings.<id>`), in document order: the nearest enclosing `displayName`,
|
|
445
|
+
* `display.label` or `label`. The same walk as the CLI's
|
|
446
|
+
* `collectBindingLabels`, so both name an entry after the same task.
|
|
447
|
+
*/
|
|
448
|
+
function bindingLabels(nodes) {
|
|
449
|
+
const found = new Map();
|
|
450
|
+
const ownLabel = (node) => {
|
|
451
|
+
const display = isRecord(node.display) ? node.display.label : undefined;
|
|
452
|
+
return [node.displayName, display, node.label].map(stringValue).find((label) => label !== undefined);
|
|
453
|
+
};
|
|
454
|
+
const walk = (node, label) => {
|
|
455
|
+
if (typeof node === 'string') {
|
|
456
|
+
if (label !== undefined && node.startsWith('=bindings.')) {
|
|
457
|
+
const id = node.slice('=bindings.'.length);
|
|
458
|
+
if (!found.has(id))
|
|
459
|
+
found.set(id, label);
|
|
460
|
+
}
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (Array.isArray(node)) {
|
|
464
|
+
for (const item of node)
|
|
465
|
+
walk(item, label);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (!isRecord(node))
|
|
469
|
+
return;
|
|
470
|
+
const scoped = ownLabel(node) ?? label;
|
|
471
|
+
for (const value of Object.values(node))
|
|
472
|
+
walk(value, scoped);
|
|
473
|
+
};
|
|
474
|
+
walk(nodes ?? [], undefined);
|
|
475
|
+
return found;
|
|
476
|
+
}
|
|
409
477
|
function referencedBindingValue(value, bindings) {
|
|
410
478
|
const ref = stringValue(value);
|
|
411
479
|
if (!ref)
|
|
@@ -415,12 +483,6 @@ function referencedBindingValue(value, bindings) {
|
|
|
415
483
|
const binding = bindings.get(ref.slice('=bindings.'.length));
|
|
416
484
|
return binding && (stringValue(binding.default) ?? stringValue(binding.resourceKey));
|
|
417
485
|
}
|
|
418
|
-
function referencedBindingResourceKey(value, bindings) {
|
|
419
|
-
const ref = stringValue(value);
|
|
420
|
-
if (!ref?.startsWith('=bindings.'))
|
|
421
|
-
return undefined;
|
|
422
|
-
return stringValue(bindings.get(ref.slice('=bindings.'.length))?.resourceKey);
|
|
423
|
-
}
|
|
424
486
|
function visitRecords(value, visit) {
|
|
425
487
|
if (Array.isArray(value)) {
|
|
426
488
|
for (const item of value)
|
package/dist/case/decompile.js
CHANGED
|
@@ -344,11 +344,24 @@ function bindingName(value, refs) {
|
|
|
344
344
|
const id = value.slice('=bindings.'.length);
|
|
345
345
|
return refs.bindingById.get(id)?.id ?? id;
|
|
346
346
|
}
|
|
347
|
-
|
|
347
|
+
/**
|
|
348
|
+
* The literal a resource task's `data.name` / `data.folderPath` names: the
|
|
349
|
+
* value itself, or the `default` of the root binding row a `=bindings.<id>`
|
|
350
|
+
* reference points at. A reference the plan's root `bindings[]` cannot answer
|
|
351
|
+
* (no such row, or a row with no `default`) is refused: the plan names no
|
|
352
|
+
* resource there, and emitting the reference as the name would only fail
|
|
353
|
+
* again at compile.
|
|
354
|
+
*/
|
|
355
|
+
function bindingDefault(value, refs, owner, field) {
|
|
348
356
|
if (typeof value !== 'string' || !value.startsWith('=bindings.'))
|
|
349
357
|
return value;
|
|
350
358
|
const id = value.slice('=bindings.'.length);
|
|
351
|
-
|
|
359
|
+
const row = refs.bindingById.get(id);
|
|
360
|
+
if (typeof row?.default !== 'string')
|
|
361
|
+
throw new Error(`${owner} data.${field} is "${value}", but the plan's root bindings ${row ? `row "${id}" has no default` : `declare no row "${id}"`}, `
|
|
362
|
+
+ `so decompile cannot recover the resource ${field === 'name' ? 'name' : 'folder'}. `
|
|
363
|
+
+ `Give the task a name/folderPath binding pair whose rows carry the resource's name and folder as their defaults (what Studio Web writes), then decompile again.`);
|
|
364
|
+
return row.default;
|
|
352
365
|
}
|
|
353
366
|
/**
|
|
354
367
|
* The folder a resource task's `folderPath` reference names, as the author
|
|
@@ -357,13 +370,13 @@ function bindingDefault(value, refs) {
|
|
|
357
370
|
* (resource-identity.ts), so lift the sentinel back rather than the empty
|
|
358
371
|
* default, which would recompile to the key `.<name>`.
|
|
359
372
|
*/
|
|
360
|
-
function bindingFolder(value, refs) {
|
|
373
|
+
function bindingFolder(value, refs, owner) {
|
|
361
374
|
if (typeof value === 'string' && value.startsWith('=bindings.')) {
|
|
362
375
|
const row = refs.bindingById.get(value.slice('=bindings.'.length));
|
|
363
376
|
if (typeof row?.resourceKey === 'string' && isInSolutionKey(row.resourceKey))
|
|
364
377
|
return SOLUTION_FOLDER;
|
|
365
378
|
}
|
|
366
|
-
return bindingDefault(value, refs);
|
|
379
|
+
return bindingDefault(value, refs, owner, 'folderPath');
|
|
367
380
|
}
|
|
368
381
|
function selectNextStageByExit(plan, refs) {
|
|
369
382
|
const raw = plan.metadata?.waitForHumanSelectNextStageDFConnector;
|
|
@@ -606,9 +619,12 @@ function emitTaskKind(task, refs, library) {
|
|
|
606
619
|
throw new Error(`unresolved ${kind} task "${task.displayName ?? task.id ?? '?'}" cannot carry input or output bindings.`);
|
|
607
620
|
return `.unresolved(${tsLiteral(kind)})`;
|
|
608
621
|
}
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
const
|
|
622
|
+
const owner = `${kind} task "${task.displayName ?? task.id ?? '?'}"`;
|
|
623
|
+
const name = bindingDefault(data.name, refs, owner, 'name');
|
|
624
|
+
const folderPath = bindingFolder(data.folderPath, refs, owner);
|
|
625
|
+
// No folder already means the case's own solution folder
|
|
626
|
+
// (resource-identity.ts), so an in-solution resource decompiles to none.
|
|
627
|
+
const folder = folderPath && folderPath !== SOLUTION_FOLDER ? `, ${opts([['folder', tsLiteral(folderPath)]])}` : '';
|
|
612
628
|
return `.${method}(${tsLiteral(name ?? '')}${folder})`;
|
|
613
629
|
}
|
|
614
630
|
if (isAuthorableBusinessRule(task))
|
|
@@ -628,6 +644,7 @@ function emitTaskKind(task, refs, library) {
|
|
|
628
644
|
['priority', data.priority && data.priority !== 'Medium' ? tsLiteral(data.priority) : undefined],
|
|
629
645
|
['recipient', recipient],
|
|
630
646
|
['labels', data.labels !== undefined ? tsLiteral(data.labels) : undefined],
|
|
647
|
+
['app', actionApp(data, refs, task)],
|
|
631
648
|
['actionCatalogName', data.actionCatalogName !== undefined ? tsLiteral(data.actionCatalogName) : undefined],
|
|
632
649
|
['inputs', actionFields(data.inputs)],
|
|
633
650
|
['outputs', actionFields(data.outputs)],
|
|
@@ -663,6 +680,21 @@ function emitTaskKind(task, refs, library) {
|
|
|
663
680
|
});
|
|
664
681
|
return `.__UNRESOLVED_TASK_KIND__(${tsLiteral(kind)}, ${tsLiteral(data)})`;
|
|
665
682
|
}
|
|
683
|
+
/**
|
|
684
|
+
* The `app: { name, folder }` of an action task bound to its Action App, or
|
|
685
|
+
* undefined for one with no app. Dropping it lost the app binding on every
|
|
686
|
+
* round trip of a Studio Web plan (#1240).
|
|
687
|
+
*/
|
|
688
|
+
function actionApp(data, refs, task) {
|
|
689
|
+
if (data.name === undefined && data.folderPath === undefined)
|
|
690
|
+
return undefined;
|
|
691
|
+
const owner = `action task "${task.displayName ?? task.id ?? '?'}"`;
|
|
692
|
+
const name = bindingDefault(data.name, refs, owner, 'name');
|
|
693
|
+
const folder = bindingFolder(data.folderPath, refs, owner);
|
|
694
|
+
if (typeof name !== 'string' || !name || typeof folder !== 'string')
|
|
695
|
+
throw new Error(`${owner} names its app by neither a binding nor a literal name and folder.`);
|
|
696
|
+
return opts([['name', tsLiteral(name)], ['folder', tsLiteral(folder)]]);
|
|
697
|
+
}
|
|
666
698
|
/** A business-rule task `.businessRule()` can rebuild: v3 only. */
|
|
667
699
|
function isAuthorableBusinessRule(task) {
|
|
668
700
|
return task.type === 'business-rule' && task.data?.version === 'v3';
|
|
@@ -674,8 +706,8 @@ function isBusinessRuleOwnOutput(row) {
|
|
|
674
706
|
function businessRuleCall(task, refs) {
|
|
675
707
|
const data = task.data ?? {};
|
|
676
708
|
const owner = `business-rule task "${task.displayName ?? task.id ?? '?'}"`;
|
|
677
|
-
const name = bindingDefault(data.name, refs);
|
|
678
|
-
const folderPath = bindingFolder(data.folderPath, refs);
|
|
709
|
+
const name = bindingDefault(data.name, refs, owner, 'name');
|
|
710
|
+
const folderPath = bindingFolder(data.folderPath, refs, owner);
|
|
679
711
|
if (typeof name !== 'string' || !name || typeof folderPath !== 'string')
|
|
680
712
|
throw new Error(`${owner} must name its rule and folder (data.name / data.folderPath).`);
|
|
681
713
|
const outputRow = (Array.isArray(data.outputs) ? data.outputs : [])
|
|
@@ -30,7 +30,15 @@ export interface ResourceIdentity {
|
|
|
30
30
|
/** The `default` of the `folderPath` binding row. */
|
|
31
31
|
folderPath: string;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The binding identity of resource `name` authored in `folder`. No folder
|
|
35
|
+
* means the case's own solution folder, the same as `solution_folder`:
|
|
36
|
+
*
|
|
37
|
+
* .process('notify') -> resourceKey 'solution_folder.notify', folderPath ''
|
|
38
|
+
*
|
|
39
|
+
* (A literal `.notify` key would name no folder, and deploy and `resources
|
|
40
|
+
* refresh` could not match it.)
|
|
41
|
+
*/
|
|
34
42
|
export declare function resourceIdentity(name: string, folder: string): ResourceIdentity;
|
|
35
43
|
/** True for the key of a resource built inside the same solution. */
|
|
36
44
|
export declare function isInSolutionKey(resourceKey: string): boolean;
|
|
@@ -25,9 +25,17 @@
|
|
|
25
25
|
*/
|
|
26
26
|
export const SOLUTION_FOLDER = 'solution_folder';
|
|
27
27
|
const SOLUTION_FOLDER_KEY_PREFIX = `${SOLUTION_FOLDER}.`;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* The binding identity of resource `name` authored in `folder`. No folder
|
|
30
|
+
* means the case's own solution folder, the same as `solution_folder`:
|
|
31
|
+
*
|
|
32
|
+
* .process('notify') -> resourceKey 'solution_folder.notify', folderPath ''
|
|
33
|
+
*
|
|
34
|
+
* (A literal `.notify` key would name no folder, and deploy and `resources
|
|
35
|
+
* refresh` could not match it.)
|
|
36
|
+
*/
|
|
29
37
|
export function resourceIdentity(name, folder) {
|
|
30
|
-
if (folder === SOLUTION_FOLDER)
|
|
38
|
+
if (folder === SOLUTION_FOLDER || !folder.trim())
|
|
31
39
|
return { resourceKey: `${SOLUTION_FOLDER_KEY_PREFIX}${name}`, folderPath: '' };
|
|
32
40
|
return { resourceKey: `${folder}.${name}`, folderPath: folder };
|
|
33
41
|
}
|