@uipath/maestro-builder-sdk 6.18.10 → 6.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/api-index.md +185 -184
  2. package/dist/api-members.md +504 -473
  3. package/dist/check.js +177 -650
  4. package/dist/core/actions.d.ts +119 -760
  5. package/dist/core/actions.js +0 -306
  6. package/dist/core/error-port.d.ts +6 -0
  7. package/dist/core/error-port.js +9 -0
  8. package/dist/core/node-classes.d.ts +4 -63
  9. package/dist/core/node-classes.js +0 -112
  10. package/dist/core/nodes/family.d.ts +36 -0
  11. package/dist/core/nodes/family.js +1 -0
  12. package/dist/core/nodes/node-type.d.ts +136 -0
  13. package/dist/core/nodes/node-type.js +1 -0
  14. package/dist/core/nodes/registry.d.ts +57 -0
  15. package/dist/core/nodes/registry.js +122 -0
  16. package/dist/core/nodes/versioned.d.ts +50 -0
  17. package/dist/core/nodes/versioned.js +46 -0
  18. package/dist/core/typed-raw-node.d.ts +13 -62
  19. package/dist/core/typed-raw-node.js +40 -154
  20. package/dist/decompile.js +92 -271
  21. package/dist/flow-definitions.d.ts +10 -0
  22. package/dist/flow-definitions.js +35 -0
  23. package/dist/flow-node-registry.d.ts +13 -0
  24. package/dist/flow-node-registry.js +19 -0
  25. package/dist/flow-node-type.d.ts +192 -0
  26. package/dist/flow-node-type.js +1 -0
  27. package/dist/flow-sdk.d.ts +7 -0
  28. package/dist/flow-sdk.js +8 -0
  29. package/dist/nodes/data-fabric/flow/check.d.ts +13 -0
  30. package/dist/nodes/data-fabric/flow/check.js +164 -0
  31. package/dist/nodes/data-fabric/flow/factories.d.ts +113 -0
  32. package/dist/nodes/data-fabric/flow/factories.js +134 -0
  33. package/dist/nodes/data-fabric/flow/family.d.ts +7 -0
  34. package/dist/nodes/data-fabric/flow/family.js +9 -0
  35. package/dist/nodes/data-fabric/flow/index.d.ts +8 -0
  36. package/dist/nodes/data-fabric/flow/index.js +8 -0
  37. package/dist/nodes/data-fabric/flow/raw.d.ts +32 -0
  38. package/dist/nodes/data-fabric/flow/raw.js +100 -0
  39. package/dist/nodes/data-fabric/flow/shared.d.ts +77 -0
  40. package/dist/nodes/data-fabric/flow/shared.js +174 -0
  41. package/dist/nodes/data-fabric/flow/spec.d.ts +238 -0
  42. package/dist/nodes/data-fabric/flow/spec.js +1 -0
  43. package/dist/nodes/data-fabric/flow/verbs.d.ts +29 -0
  44. package/dist/nodes/data-fabric/flow/verbs.js +239 -0
  45. package/dist/nodes/delay/flow/check.d.ts +16 -0
  46. package/dist/nodes/delay/flow/check.js +86 -0
  47. package/dist/nodes/delay/flow/factories.d.ts +28 -0
  48. package/dist/nodes/delay/flow/factories.js +38 -0
  49. package/dist/nodes/delay/flow/family.d.ts +3 -0
  50. package/dist/nodes/delay/flow/family.js +8 -0
  51. package/dist/nodes/delay/flow/index.d.ts +3 -0
  52. package/dist/nodes/delay/flow/index.js +3 -0
  53. package/dist/nodes/delay/flow/node-type.d.ts +13 -0
  54. package/dist/nodes/delay/flow/node-type.js +71 -0
  55. package/dist/nodes/delay/flow/spec.d.ts +43 -0
  56. package/dist/nodes/delay/flow/spec.js +16 -0
  57. package/dist/nodes/document/flow/factories.d.ts +27 -0
  58. package/dist/nodes/document/flow/factories.js +30 -0
  59. package/dist/nodes/document/flow/family.d.ts +3 -0
  60. package/dist/nodes/document/flow/family.js +7 -0
  61. package/dist/nodes/document/flow/index.d.ts +3 -0
  62. package/dist/nodes/document/flow/index.js +3 -0
  63. package/dist/nodes/document/flow/node-type.d.ts +14 -0
  64. package/dist/nodes/document/flow/node-type.js +150 -0
  65. package/dist/nodes/document/flow/spec.d.ts +65 -0
  66. package/dist/nodes/document/flow/spec.js +1 -0
  67. package/dist/nodes/mock/flow/factories.d.ts +51 -0
  68. package/dist/nodes/mock/flow/factories.js +47 -0
  69. package/dist/nodes/mock/flow/family.d.ts +3 -0
  70. package/dist/nodes/mock/flow/family.js +7 -0
  71. package/dist/nodes/mock/flow/index.d.ts +2 -0
  72. package/dist/nodes/mock/flow/index.js +2 -0
  73. package/dist/nodes/mock/flow/node-type.d.ts +11 -0
  74. package/dist/nodes/mock/flow/node-type.js +51 -0
  75. package/dist/nodes/script/flow/check.d.ts +19 -0
  76. package/dist/nodes/script/flow/check.js +49 -0
  77. package/dist/nodes/script/flow/factories.d.ts +14 -0
  78. package/dist/nodes/script/flow/factories.js +15 -0
  79. package/dist/nodes/script/flow/family.d.ts +3 -0
  80. package/dist/nodes/script/flow/family.js +8 -0
  81. package/dist/nodes/script/flow/index.d.ts +3 -0
  82. package/dist/nodes/script/flow/index.js +3 -0
  83. package/dist/nodes/script/flow/node-type.d.ts +7 -0
  84. package/dist/nodes/script/flow/node-type.js +125 -0
  85. package/dist/nodes/script/flow/spec.d.ts +52 -0
  86. package/dist/nodes/script/flow/spec.js +1 -0
  87. package/dist/nodes/transform/flow/check.d.ts +6 -0
  88. package/dist/nodes/transform/flow/check.js +229 -0
  89. package/dist/nodes/transform/flow/factories.d.ts +52 -0
  90. package/dist/nodes/transform/flow/factories.js +53 -0
  91. package/dist/nodes/transform/flow/family.d.ts +3 -0
  92. package/dist/nodes/transform/flow/family.js +8 -0
  93. package/dist/nodes/transform/flow/index.d.ts +3 -0
  94. package/dist/nodes/transform/flow/index.js +3 -0
  95. package/dist/nodes/transform/flow/node-type.d.ts +7 -0
  96. package/dist/nodes/transform/flow/node-type.js +244 -0
  97. package/dist/nodes/transform/flow/spec.d.ts +116 -0
  98. package/dist/nodes/transform/flow/spec.js +10 -0
  99. package/dist/raw-node-advice.d.ts +16 -0
  100. package/dist/raw-node-advice.js +36 -0
  101. package/dist/raw-node.d.ts +71 -0
  102. package/dist/raw-node.js +116 -0
  103. package/dist/serialize.js +138 -492
  104. package/package.json +2 -2
@@ -0,0 +1,192 @@
1
+ /**
2
+ * The Flow specialization of the node-type contract (`core/nodes/`), and the
3
+ * contexts Flow lends a node type for each job. docs/NODE_TYPE_ARCHITECTURE_DESIGN.md
4
+ * §4.1–§4.3.
5
+ *
6
+ * A Flow node type serializes to a {@link FlowNodeContribution} — the same contract
7
+ * a custom `FlowAction.contribute()` returns, plus the two things built-in node
8
+ * types need and custom ones never had (`model`, `port`). The serializer still
9
+ * owns the node's id, label, `typeVersion`, layout, edges and definition dedupe;
10
+ * each context below is the whole of what a node type can see.
11
+ */
12
+ import type { ArtifactContexts, NodeType } from './core/nodes/node-type.js';
13
+ import type { VersionedNodeType } from './core/nodes/versioned.js';
14
+ import type { NodeFamily, Placement } from './core/nodes/family.js';
15
+ import type { DefinitionReference, NodeContribution } from './core/node-classes.js';
16
+ import type { CheckOpts } from './check.js';
17
+ /**
18
+ * What a Flow node type's `serialize` returns: the node's type-owned fields.
19
+ *
20
+ * The `inputs`, `outputs` and `bindings` are a {@link NodeContribution}'s; only
21
+ * `definition` differs (optional here), and `model` and `port` are Flow's own.
22
+ *
23
+ * A node type owns its bindings: together with what it hoists through
24
+ * `ctx.bind`, they must close every `<bindings.NAME>` its definition references.
25
+ * `serialize` enforces that (#942); `check` does not verify it, because the
26
+ * bindings exist only once the type serializes and no author input can open the
27
+ * gap, so the type's own tests are what pin it.
28
+ */
29
+ export interface FlowNodeContribution extends Omit<NodeContribution, 'definition'> {
30
+ /**
31
+ * The definition to compile against. A `bundled` type must omit it: the
32
+ * serializer resolves `<typeName>@<version>` from the bundle, and refuses one
33
+ * returned. A `per-tenant` type must supply the definition it built (a
34
+ * `snapshot` reference).
35
+ */
36
+ definition?: DefinitionReference;
37
+ /**
38
+ * Whether the node carries the definition's `model` (`'definition'`, the default)
39
+ * or none (`'omit'`) — Data Fabric nodes never carry one (see `makeNode`).
40
+ */
41
+ model?: 'definition' | 'omit';
42
+ /** The forward port the next step's edge leaves from. Default: the definition's default source handle. */
43
+ port?: string;
44
+ }
45
+ /** What `serialize` may ask of the serializer, for one placement. */
46
+ export interface FlowSerializeContext {
47
+ /** The step name the author gave this node. */
48
+ readonly step: string;
49
+ /**
50
+ * The version being emitted, always set: the author's `{ version }` when the step
51
+ * names one, else what `select()` or the default chose. It does not say whether
52
+ * the author pinned it.
53
+ */
54
+ readonly version: string;
55
+ /** Render one value (an `Expr`, or a literal) as the file stores it, with this scope's rename map. */
56
+ render(value: unknown): unknown;
57
+ /**
58
+ * Apply this scope's rename map to raw JavaScript that is not an `Expr` — a
59
+ * script body reads `$vars.<step>` like any expression, so a reused child's
60
+ * body must follow its copy's node ids too.
61
+ */
62
+ renameRefs(js: string): string;
63
+ /** The node id another step of this scope was emitted under — rename-aware, for subflow copies. */
64
+ stepNodeId(step: string): string;
65
+ /** A deterministic id for `seed`, unique to this flow and node. */
66
+ mint(seed: string): string;
67
+ /**
68
+ * Hoist a binding entry into the flow's `bindings`, deduplicated by `id`. With
69
+ * the contribution's own `bindings`, these must close every `<bindings.NAME>`
70
+ * the node's definition references: serialize refuses a name left open
71
+ * (`CONTRIBUTION_BINDING_UNRESOLVED`), and `check` does not see bindings.
72
+ */
73
+ bind(entry: {
74
+ readonly id: string;
75
+ } & Readonly<Record<string, unknown>>): void;
76
+ /** The bundled definition `<typeName>@<version>`, or undefined when the SDK does not ship it. */
77
+ definition(typeName: string, version: string): unknown;
78
+ }
79
+ /** What `deserialize` may ask of the decompiler, for one serialized node. */
80
+ export interface FlowDeserializeContext {
81
+ /** The serialized node's id. */
82
+ readonly nodeId: string;
83
+ /** A serialized value back as the spec holds it: `=js:` text becomes an `Expr`; a literal stays itself. */
84
+ expr(raw: unknown): unknown;
85
+ /**
86
+ * A serialized value's TEXT, unwrapped from an expression envelope: a string
87
+ * as is, a `{ type: 'jsExpression', expression }` envelope as `=js:<expression>`,
88
+ * any other envelope as its expression; undefined for anything else.
89
+ */
90
+ sourceText(raw: unknown): string | undefined;
91
+ /** A plain value, unwrapped from a `{ type: 'literal', expression: '<json>' }` envelope when it is one. */
92
+ plainValue(raw: unknown): unknown;
93
+ /** The step name a node id was emitted for. */
94
+ stepName(nodeId: string): string;
95
+ /** The file's own `definitions[]` entry for this node, if it carries one. */
96
+ definition(): unknown;
97
+ /** Report something the lift could not keep. Decompile prints it once. */
98
+ warn(message: string): void;
99
+ }
100
+ /** What `print` may ask of the decompiler. Every method returns source text. */
101
+ export interface FlowPrintContext {
102
+ /**
103
+ * Any spec value as source: an `Expr` as the helper an author writes (`input('x')`, `js\`…\``), an array or
104
+ * plain object recursively (`[a, b]`, `{ k: v }`), anything else as a literal.
105
+ */
106
+ value(value: unknown): string;
107
+ /** A string literal. */
108
+ string(text: string): string;
109
+ /** Any JSON value as a literal, verbatim (`{"a":1}`), where the source writes the value as data. */
110
+ json(value: unknown): string;
111
+ /** An object key: a bare identifier when legal, otherwise a quoted string. */
112
+ key(name: string): string;
113
+ /** An authoring helper (`lit`, `input`, …) by name: imported into the printed source, and its identifier. */
114
+ helper(name: string): string;
115
+ }
116
+ /** What a node-local `check` may ask of the checker. */
117
+ export interface FlowCheckContext {
118
+ /** The step being checked. */
119
+ readonly step: string;
120
+ /** The version the node will be emitted at. */
121
+ readonly version: string;
122
+ /** Report a diagnostic on this step. Its severity is the family catalog's for `code`. */
123
+ report(code: string, message: string, suggestion?: string): void;
124
+ /**
125
+ * The flow input or variable a bare reference (`$vars.x`, or an input's
126
+ * `$vars.<trigger>.output.x`) names, with its declared type when it has one;
127
+ * undefined for anything else — a step output's type is not known offline.
128
+ */
129
+ declaredRef(js: string): {
130
+ name: string;
131
+ type: string | undefined;
132
+ } | undefined;
133
+ /** The options `check` was called with (the connector-library views among them). */
134
+ readonly options: CheckOpts;
135
+ }
136
+ /** What a family-level `check` may ask of the checker. */
137
+ export interface FlowFamilyCheckContext {
138
+ /** Report a diagnostic on `step`. Its severity is the family catalog's for `code`. */
139
+ report(step: string, code: string, message: string, suggestion?: string): void;
140
+ /** Whether the flow has a step of that name, in any scope the family's placements can see. */
141
+ hasStep(step: string): boolean;
142
+ /** Where a step of that name is placed, when it is one of this registry's node types. */
143
+ placementOf(step: string): Placement | undefined;
144
+ /**
145
+ * Whether that step is one of this registry's node types whose `{ version }` did
146
+ * not resolve. It has no placement, and `NODE_VERSION_UNREGISTERED` already
147
+ * reports it, so a rule about it should stay silent rather than say it is missing.
148
+ */
149
+ versionUnresolved(step: string): boolean;
150
+ }
151
+ export interface FlowContexts extends ArtifactContexts {
152
+ serialize: FlowSerializeContext;
153
+ deserialize: FlowDeserializeContext;
154
+ print: FlowPrintContext;
155
+ check: FlowCheckContext;
156
+ familyCheck: FlowFamilyCheckContext;
157
+ }
158
+ /**
159
+ * What `rawNode` reports for a raw node of a registered type: the typed spelling
160
+ * to prefer (RAW_NODE_TYPED_FAMILY), and the inputs the product never reads
161
+ * (RAW_NODE_UNKNOWN_INPUT). Both apply to every version, registered or not —
162
+ * a raw node is often how an author reaches a version the SDK does not bundle.
163
+ */
164
+ export interface FlowRawNodeAdvice {
165
+ /** The typed spelling the warning names, e.g. `dataFabricRead({ entity, … })`. */
166
+ readonly factory: string;
167
+ /** Appended to the warning: what the factory writes, and what still needs a raw node. */
168
+ readonly hint?: string;
169
+ /**
170
+ * A raw node's inputs carry a key the product never reads: an `error` at a version
171
+ * the SDK bundles (`bundled`), where the key set is known, else a `warning`.
172
+ */
173
+ inputProblem?(inputs: unknown, version: string, bundled: boolean): {
174
+ severity: 'error' | 'warning';
175
+ message: string;
176
+ } | undefined;
177
+ }
178
+ /** One Flow node type at one version. */
179
+ export interface FlowNodeType<Spec> extends NodeType<Spec, FlowNodeContribution, FlowContexts> {
180
+ /**
181
+ * The node-level `model` every node of this type carries, whichever path emits
182
+ * it — this type's `serialize`, or a `rawNode` of its definition. `'omit'`: none
183
+ * (Data Fabric). Default `'definition'`; a contribution's own `model` wins.
184
+ */
185
+ readonly nodeModel?: 'definition' | 'omit';
186
+ /** What `rawNode` reports for a raw node of this type. */
187
+ readonly rawNode?: FlowRawNodeAdvice;
188
+ }
189
+ /** A Flow node type with several versions. */
190
+ export type FlowVersionedNodeType<Spec> = VersionedNodeType<Spec, FlowNodeContribution, FlowContexts, FlowNodeType<Spec>>;
191
+ /** A Flow node family. */
192
+ export type FlowNodeFamily = NodeFamily<FlowContexts>;
@@ -0,0 +1 @@
1
+ export {};
@@ -18,11 +18,18 @@
18
18
  */
19
19
  export * from './core/expr.js';
20
20
  export * from './core/actions.js';
21
+ export * from './nodes/data-fabric/flow/index.js';
22
+ export * from './nodes/delay/flow/index.js';
23
+ export * from './nodes/document/flow/index.js';
24
+ export * from './nodes/mock/flow/index.js';
25
+ export * from './nodes/script/flow/index.js';
26
+ export * from './nodes/transform/flow/index.js';
21
27
  export { resolveFlowRefs, resolveRefPath, hasFileRefs } from './ref-resolve.js';
22
28
  export type { RefReader, ResolveRefsOptions } from './ref-resolve.js';
23
29
  export * from './core/connectors.js';
24
30
  export * from './core/lookups.js';
25
31
  export * from './core/node-classes.js';
32
+ export * from './raw-node.js';
26
33
  import { Expr } from './core/expr.js';
27
34
  import type { ErrorEnvelopeField, TypeDesc } from './core/expr.js';
28
35
  import type { ActionSpec, EventSubscription, TriggerOptions } from './core/actions.js';
package/dist/flow-sdk.js CHANGED
@@ -21,12 +21,20 @@
21
21
  // single import an author needs.
22
22
  export * from './core/expr.js';
23
23
  export * from './core/actions.js';
24
+ // Node families' public surfaces (docs/NODE_TYPE_ARCHITECTURE_DESIGN.md §8).
25
+ export * from './nodes/data-fabric/flow/index.js';
26
+ export * from './nodes/delay/flow/index.js';
27
+ export * from './nodes/document/flow/index.js';
28
+ export * from './nodes/mock/flow/index.js';
29
+ export * from './nodes/script/flow/index.js';
30
+ export * from './nodes/transform/flow/index.js';
24
31
  // `$ref`-split ingestion (design §5): decompile resolves local-file refs into
25
32
  // one object model. Exported so a host doing its own file reading can too.
26
33
  export { resolveFlowRefs, resolveRefPath, hasFileRefs } from './ref-resolve.js';
27
34
  export * from './core/connectors.js';
28
35
  export * from './core/lookups.js';
29
36
  export * from './core/node-classes.js';
37
+ export * from './raw-node.js';
30
38
  import { err as errRef, toExpr, types } from './core/expr.js';
31
39
  import { builtinActionSpec, builtinTriggerSpec, subscriptionFrom } from './core/actions.js';
32
40
  import { checkFlowExpressions, FlowBuildError } from './flow-expr-check.js';
@@ -0,0 +1,13 @@
1
+ import type { DataFabricCreateInputs, DataFabricDeleteInputs, DataFabricReadInputs, DataFabricUpdateInputs } from './spec.js';
2
+ import type { DiagnosticCatalog, Placement } from '../../../core/nodes/family.js';
3
+ import type { FlowCheckContext, FlowFamilyCheckContext } from '../../../flow-node-type.js';
4
+ export declare const DATA_FABRIC_DIAGNOSTICS: DiagnosticCatalog;
5
+ export declare function checkRead(inputs: DataFabricReadInputs, ctx: FlowCheckContext): void;
6
+ export declare function checkCreate(inputs: DataFabricCreateInputs, ctx: FlowCheckContext): void;
7
+ export declare function checkUpdate(inputs: DataFabricUpdateInputs, ctx: FlowCheckContext): void;
8
+ export declare function checkWrite(inputs: DataFabricDeleteInputs, ctx: FlowCheckContext): void;
9
+ /**
10
+ * Update and delete target a record `{ fromRead: '<step>' }` with the same
11
+ * selector, so they share the check: the step must be a read, and not a list.
12
+ */
13
+ export declare function checkFromRead(placements: readonly Placement[], ctx: FlowFamilyCheckContext): void;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * The Flow Data Fabric family's rules: the per-step checks each verb runs, and
3
+ * the family-level `{ fromRead }` rules that need every placement at once. Moved
4
+ * verbatim from check.ts (Phase 2 of docs/NODE_TYPE_ARCHITECTURE_DESIGN.md);
5
+ * codes, messages and order are unchanged.
6
+ */
7
+ import { Expr } from '../../../core/expr.js';
8
+ export const DATA_FABRIC_DIAGNOSTICS = {
9
+ DATAFABRIC_NO_ENTITY: { severity: 'error', summary: 'a verb names no entity' },
10
+ DATAFABRIC_EMPTY_VALUES: { severity: 'error', summary: 'a create writes no non-blank column' },
11
+ DATAFABRIC_EMPTY_SET: { severity: 'error', summary: 'an update writes no column' },
12
+ DATAFABRIC_SYSTEM_COLUMN: { severity: 'error', summary: 'a write targets a system column' },
13
+ DATAFABRIC_FOLDER_NO_RESOURCE_KEY: { severity: 'error', summary: 'folderKey without resourceKey' },
14
+ DATAFABRIC_RESOURCE_KEY_NO_FOLDER: { severity: 'error', summary: 'resourceKey without folderKey' },
15
+ DATAFABRIC_QUERY_OPTS_WITHOUT_MULTIPLE: { severity: 'error', summary: 'list-query options on a single read' },
16
+ DATAFABRIC_LIMIT_RANGE: { severity: 'error', summary: 'limit outside 1–1000' },
17
+ DATAFABRIC_IN_REQUIRES_MULTIPLE: { severity: 'error', summary: "an 'in' filter on a single read" },
18
+ DATAFABRIC_FILTER_BLANK_VALUE: { severity: 'error', summary: 'a filter with a blank value' },
19
+ DATAFABRIC_FROMREAD_UNKNOWN: { severity: 'error', summary: '{ fromRead } names no read step' },
20
+ DATAFABRIC_FROMREAD_MULTIPLE: { severity: 'error', summary: "{ fromRead } names a resultMode: 'multiple' query" },
21
+ DATAFABRIC_DELETE_HAS_NO_OUTPUT: { severity: 'error', summary: "a read of a delete's output" },
22
+ };
23
+ function checkEntity(inputs, ctx) {
24
+ if (!String(inputs.entity ?? '').trim()) {
25
+ ctx.report('DATAFABRIC_NO_ENTITY', `"${ctx.step}" names no Data Fabric entity. Set { entity: '<entity name>' }.`);
26
+ }
27
+ }
28
+ export function checkRead(inputs, ctx) {
29
+ checkEntity(inputs, ctx);
30
+ checkQuery(inputs, ctx);
31
+ checkScope(inputs, ctx);
32
+ }
33
+ export function checkCreate(inputs, ctx) {
34
+ checkEntity(inputs, ctx);
35
+ // Not the same failure as an empty update: a BLANK value is omitted from the
36
+ // insert so the column default applies, so an all-blank `fieldValues`
37
+ // serializes to an empty body and runs green having inserted nothing. The
38
+ // node's schema rejects that — and the fix is a real value, never a
39
+ // placeholder to satisfy the rule.
40
+ const written = Object.values(inputs.values ?? {})
41
+ .filter((v) => (v instanceof Expr ? true : String(v ?? '').trim() !== ''));
42
+ if (written.length === 0) {
43
+ ctx.report('DATAFABRIC_EMPTY_VALUES', `"${ctx.step}" sets no column to a non-blank value, so it would insert nothing and run green. `
44
+ + 'The node\'s schema rejects that — give a real column a real value rather than a placeholder.');
45
+ }
46
+ checkColumns(Object.keys(inputs.values ?? {}), 'values', ctx);
47
+ checkScope(inputs, ctx);
48
+ }
49
+ export function checkUpdate(inputs, ctx) {
50
+ checkEntity(inputs, ctx);
51
+ if (Object.keys(inputs.set ?? {}).length === 0) {
52
+ ctx.report('DATAFABRIC_EMPTY_SET', `"${ctx.step}" writes no columns — { set } is empty, so the update would no-op green.`);
53
+ }
54
+ checkColumns(Object.keys(inputs.set ?? {}), 'set', ctx);
55
+ checkScope(inputs, ctx);
56
+ }
57
+ export function checkWrite(inputs, ctx) {
58
+ checkEntity(inputs, ctx);
59
+ checkScope(inputs, ctx);
60
+ }
61
+ /**
62
+ * Columns Data Fabric assigns and never accepts a write for.
63
+ *
64
+ * The canvas hides them from both write editors, but the serializer has no
65
+ * guard: a hand-authored `Id` row satisfies the node's own "at least one field"
66
+ * rule, ships in the body, and is rejected — and the rejection is only logged,
67
+ * so the row comes back unchanged and the run goes green.
68
+ */
69
+ const SYSTEM_COLUMNS = new Set(['Id', 'CreateTime', 'CreatedBy', 'UpdateTime', 'UpdatedBy']);
70
+ function checkColumns(columns, option, ctx) {
71
+ const system = columns.filter((c) => SYSTEM_COLUMNS.has(c));
72
+ if (system.length) {
73
+ ctx.report('DATAFABRIC_SYSTEM_COLUMN', `"${ctx.step}" writes ${system.map((c) => JSON.stringify(c)).join(', ')} in { ${option} }. `
74
+ + 'Data Fabric assigns its system columns (Id, CreateTime, CreatedBy, UpdateTime, UpdatedBy) and rejects a '
75
+ + 'write to them — and it only LOGS the rejection, so the row comes back unchanged and the run goes green.');
76
+ }
77
+ }
78
+ /**
79
+ * A folder-scoped entity is portable only with its `Entity` bindings.
80
+ *
81
+ * `_folderKey` on its own still serializes — it falls back to a source-org
82
+ * literal and logs a warning — so the flow looks fine until it is deployed to
83
+ * another org. Refusing the half-authored pair here is the whole point: the
84
+ * alternative is a defect that only appears at deploy time, in a different org,
85
+ * to somebody else.
86
+ */
87
+ function checkScope(inputs, ctx) {
88
+ if (inputs.folderKey !== undefined && inputs.resourceKey === undefined) {
89
+ ctx.report('DATAFABRIC_FOLDER_NO_RESOURCE_KEY', `"${ctx.step}" sets { folderKey } without { resourceKey }. A folder-scoped entity needs both — with only `
90
+ + 'the folder key the entity serializes as a source-org literal, which validates, runs in this org, and '
91
+ + 'breaks on deploy to another one.', 'resolve the key with `uip solution resources list --kind Entity`, or drop { folderKey } and keep '
92
+ + 'the entity tenant-scoped');
93
+ }
94
+ if (inputs.resourceKey !== undefined && inputs.folderKey === undefined) {
95
+ ctx.report('DATAFABRIC_RESOURCE_KEY_NO_FOLDER', `"${ctx.step}" sets { resourceKey } without { folderKey }. The pair exists to scope a FOLDER entity; a `
96
+ + 'tenant-scoped entity needs neither and serializes as a dotted literal.');
97
+ }
98
+ }
99
+ /**
100
+ * The query options, none of which the node's `inputDefinition` declares — they
101
+ * are `_`-prefixed extras, so `uip maestro flow validate` checks none of them
102
+ * and every mistake here is silent at run time.
103
+ */
104
+ function checkQuery(inputs, ctx) {
105
+ const step = ctx.step;
106
+ const listOnly = ['limit', 'skip', 'sort', 'fields'].filter((k) => inputs[k] !== undefined);
107
+ if (listOnly.length && inputs.resultMode !== 'multiple') {
108
+ ctx.report('DATAFABRIC_QUERY_OPTS_WITHOUT_MULTIPLE', `"${step}" sets { ${listOnly.join(', ')} }, which only a list query uses, but its resultMode is `
109
+ + `${inputs.resultMode ? JSON.stringify(inputs.resultMode) : 'unset (a single-record read)'}.`, "resultMode: 'multiple'");
110
+ }
111
+ // The emitted query ALWAYS carries a limit — omitted it resolves to 100 — and
112
+ // 1000 is a hard ceiling that TRUNCATES rather than erroring above it.
113
+ if (inputs.limit !== undefined && (!Number.isInteger(inputs.limit) || inputs.limit < 1 || inputs.limit > 1000)) {
114
+ ctx.report('DATAFABRIC_LIMIT_RANGE', `"${step}": limit must be a whole number between 1 and 1000 (got ${inputs.limit}). 1000 is a hard `
115
+ + 'ceiling — a larger value truncates silently, so page with { skip } instead.');
116
+ }
117
+ // `in` is honoured only on a list query; on a single read the row compiles
118
+ // away, and a read whose filters compiled away returns EVERY record.
119
+ if (inputs.resultMode !== 'multiple' && (inputs.filters ?? []).some((f) => f.operator === 'in')) {
120
+ ctx.report('DATAFABRIC_IN_REQUIRES_MULTIPLE', `"${step}" filters with the 'in' operator, which is honoured only when resultMode is 'multiple'. `
121
+ + 'On a single-record read that row compiles away — and a read whose filters compiled away matches every record.');
122
+ }
123
+ // A filter expression switched on and left blank is refused by the serializer,
124
+ // and it refuses the WHOLE query rather than dropping the row, which strands
125
+ // every downstream reference to this step.
126
+ for (const f of inputs.filters ?? []) {
127
+ const blank = f.value instanceof Expr ? f.value.js.trim() === '' : String(f.value ?? '').trim() === '';
128
+ if (blank) {
129
+ ctx.report('DATAFABRIC_FILTER_BLANK_VALUE', `"${step}" has a filter on ${JSON.stringify(f.field)} with a blank value. The serializer refuses the `
130
+ + 'ENTIRE query rather than dropping the row, so the step emits nothing and every downstream $vars read of '
131
+ + 'it breaks.');
132
+ }
133
+ }
134
+ }
135
+ /**
136
+ * Update and delete target a record `{ fromRead: '<step>' }` with the same
137
+ * selector, so they share the check: the step must be a read, and not a list.
138
+ */
139
+ export function checkFromRead(placements, ctx) {
140
+ for (const p of placements) {
141
+ if (p.kind !== 'dataFabricUpdate' && p.kind !== 'dataFabricDelete')
142
+ continue;
143
+ const fromRead = p.spec.inputs?.record?.fromRead;
144
+ if (typeof fromRead !== 'string')
145
+ continue;
146
+ const factory = p.kind;
147
+ // A step whose version did not resolve is already an error; saying it does
148
+ // not exist would send the author to the selector instead of the pin.
149
+ if (ctx.versionUnresolved(fromRead))
150
+ continue;
151
+ const read = ctx.placementOf(fromRead);
152
+ if (read?.kind !== 'dataFabricRead') {
153
+ ctx.report(p.step, 'DATAFABRIC_FROMREAD_UNKNOWN', `"${p.step}" targets record: { fromRead: ${JSON.stringify(fromRead)} }, but no dataFabricRead step has that name.`, `${factory}({ …, record: { byId: '<record id>' } })`);
154
+ continue;
155
+ }
156
+ // A LIST is not a write target: the serializer refuses it and emits a plain
157
+ // no-op task, so the flow validates, runs green, and writes nothing.
158
+ if (read.spec.inputs?.resultMode === 'multiple') {
159
+ ctx.report(p.step, 'DATAFABRIC_FROMREAD_MULTIPLE', `"${p.step}" targets record: { fromRead: ${JSON.stringify(fromRead)} }, but "${fromRead}" is a `
160
+ + "resultMode: 'multiple' query. A multi-record read is refused as a write target — the step compiles to "
161
+ + 'a no-op and the run goes green having written nothing.', `loop the results and target each record with ${factory}({ …, record: { byId: … } })`);
162
+ }
163
+ }
164
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The Flow Data Fabric factories: `dataFabricRead()`, `dataFabricCreate()`,
3
+ * `dataFabricUpdate()` and `dataFabricDelete()`. Moved from core/actions.ts in
4
+ * Phase 2c of docs/NODE_TYPE_ARCHITECTURE_DESIGN.md; the package entry re-exports
5
+ * them, so authors import them exactly as before.
6
+ */
7
+ import { type ActionSpec } from '../../../core/actions.js';
8
+ import type { DataFabricCreateInputs, DataFabricDeleteInputs, DataFabricReadInputs, DataFabricUpdateInputs } from './spec.js';
9
+ /**
10
+ * Query entity records from Data Fabric (`core.datafabric.read`). The result
11
+ * publishes to the step's `output`; downstream references re-fetch fresh data
12
+ * at activation time (the engine re-runs the query rather than caching).
13
+ *
14
+ * @remarks
15
+ * This node is **read-one AND query-many**, on its 1.4 definition: `resultMode:
16
+ * 'multiple'` publishes the matching records under `output.results`, and the
17
+ * platform renames the node "Query entity records" to say so. The SDK still
18
+ * pins 1.0 — read-one — as the no-`{ version }` default, so passing
19
+ * `resultMode` is what selects 1.4; see {@link DataFabricReadInputs.resultMode}.
20
+ *
21
+ * The native family covers all four CRUD verbs: this one,
22
+ * {@link dataFabricCreate}, {@link dataFabricUpdate} and
23
+ * {@link dataFabricDelete}. What it does NOT cover is get-by-id, a query row
24
+ * limit, file record fields, and entity events — those are operations on the
25
+ * SAME product reached through its Integration Service connector,
26
+ * `uipath-uipath-dataservice` (the tenant displays it as **UiPath Data
27
+ * Fabric**). So a flow that needs one of those still straddles two surfaces,
28
+ * and the connector remains the one surface that covers everything:
29
+ *
30
+ * ```ts
31
+ * .step('byId', connector('uipath-uipath-dataservice', 'get-entity-record-by-id',
32
+ * { entityName: 'Contracts', recordId: input('id') }, { connection: 'df' }))
33
+ * ```
34
+ *
35
+ * Get-by-id matters for long text. This node is a query even when it reads one
36
+ * record by an `Id` filter, and a Data Service query returns a PREVIEW of a
37
+ * long-text (`MULTILINE_MAX`) field: the first 10,000 characters plus
38
+ * `...[Truncated]`, with no flag in the output. A 15,551-character value comes
39
+ * back as 10,014 characters. When a field may exceed 10,000 characters, read
40
+ * it with `get-entity-record-by-id`, which returns the whole value
41
+ * (flow-builder-sdk#954).
42
+ *
43
+ * A connector operation's body fields come from the entity, so the library
44
+ * cannot carry them: run `uip maestro registry prepare uipath-uipath-dataservice
45
+ * get-entity-record-by-id -f entityName=<Entity>` first.
46
+ * The routing table is in the `data-fabric.md` reference and the parent-field
47
+ * loop in `connector-params.md` — cited by name rather than linked, because this
48
+ * comment renders both into the flat corpus, where those files sit alongside
49
+ * it, and onto the site, where each symbol is its own page and a relative
50
+ * `./data-fabric` resolves to nothing.
51
+ *
52
+ * @param inputs - The entity, optional filter rows, result mode, and folder scope.
53
+ * @returns An action spec to pass to `.step(name, …)`.
54
+ */
55
+ export declare function dataFabricRead(inputs: DataFabricReadInputs): ActionSpec;
56
+ /**
57
+ * Create an entity record in Data Fabric (`core.datafabric.create`). The new
58
+ * record publishes to the step's `output`, so a following
59
+ * {@link dataFabricUpdate} or {@link dataFabricDelete} can read its id.
60
+ *
61
+ * @remarks
62
+ * `values` must name at least one column — the node's schema rejects an empty
63
+ * field list rather than inserting a blank row, and `check` says so before the
64
+ * platform does. Field names are the ENTITY's, so copy them from the scenario
65
+ * or the tenant; nothing offline can tell a real column from a typo.
66
+ *
67
+ * The published `output` is the record as a query returns it, so a long-text
68
+ * (`MULTILINE_MAX`) field longer than 10,000 characters comes back cut, with
69
+ * `...[Truncated]` appended, even though the whole value was stored. Use the
70
+ * value you passed in, or re-read the record with the connector's
71
+ * `get-entity-record-by-id`, when the whole text is needed (flow-builder-sdk#954).
72
+ *
73
+ * See {@link dataFabricRead} for what the native family still does not cover
74
+ * and which connector operation covers it.
75
+ *
76
+ * @param inputs - The entity, the columns the record starts with, and folder scope.
77
+ * @returns An action spec to pass to `.step(name, …)`.
78
+ */
79
+ export declare function dataFabricCreate(inputs: DataFabricCreateInputs): ActionSpec;
80
+ /**
81
+ * Update an entity record in Data Fabric (`core.datafabric.update`). Target the
82
+ * record by id or through an earlier {@link dataFabricRead} step; downstream
83
+ * reads of this step observe the record AFTER the write.
84
+ *
85
+ * @remarks
86
+ * `set` must write at least one column: an empty `set` is a no-op the platform
87
+ * reports as success, so `check` rejects it.
88
+ *
89
+ * See {@link dataFabricRead} for what the native family still does not cover
90
+ * and which connector operation covers it.
91
+ *
92
+ * @param inputs - The entity, the record selector, and the columns to write.
93
+ * @returns An action spec to pass to `.step(name, …)`.
94
+ */
95
+ export declare function dataFabricUpdate(inputs: DataFabricUpdateInputs): ActionSpec;
96
+ /**
97
+ * Delete an entity record from Data Fabric (`core.datafabric.delete`). Target
98
+ * the record by id or through an earlier {@link dataFabricRead} step, exactly
99
+ * as {@link dataFabricUpdate} does.
100
+ *
101
+ * @remarks
102
+ * A delete **publishes nothing** — its definition declares no output — so
103
+ * `$vars.<step>.output` is undefined at run time and `check` rejects a read of
104
+ * it. Take what you need off the read step that found the record, before the
105
+ * delete.
106
+ *
107
+ * See {@link dataFabricRead} for what the native family still does not cover
108
+ * and which connector operation covers it.
109
+ *
110
+ * @param inputs - The entity, the record selector, and folder scope.
111
+ * @returns An action spec to pass to `.step(name, …)`.
112
+ */
113
+ export declare function dataFabricDelete(inputs: DataFabricDeleteInputs): ActionSpec;