@uipath/maestro-builder-sdk 6.3.0 → 6.5.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 +6 -6
- package/dist/cli/commands/registry.js +68 -12
- package/dist/registry/archive/entry-policy.d.ts +36 -0
- package/dist/registry/archive/entry-policy.js +60 -0
- package/dist/registry/archive/extract.d.ts +31 -0
- package/dist/registry/archive/extract.js +92 -0
- package/dist/registry/archive/zip-reader.d.ts +38 -0
- package/dist/registry/archive/zip-reader.js +256 -0
- package/dist/registry/codecs/pyjson.d.ts +62 -0
- package/dist/registry/codecs/pyjson.js +208 -0
- package/dist/registry/compat/compare.d.ts +18 -0
- package/dist/registry/compat/compare.js +32 -0
- package/dist/registry/compat/paths.d.ts +4 -0
- package/dist/registry/compat/paths.js +17 -0
- package/dist/registry/compat/pystr.d.ts +50 -0
- package/dist/registry/compat/pystr.js +221 -0
- package/dist/registry/concurrency.d.ts +10 -0
- package/dist/registry/concurrency.js +34 -0
- package/dist/registry/curated-overlay.d.ts +36 -4
- package/dist/registry/curated-overlay.js +87 -14
- package/dist/registry/errors.d.ts +26 -0
- package/dist/registry/errors.js +38 -0
- package/dist/registry/extractor.d.ts +9 -10
- package/dist/registry/extractor.js +5 -15
- package/dist/registry/files.d.ts +26 -0
- package/dist/registry/files.js +160 -0
- package/dist/registry/generators/descriptors.d.ts +81 -0
- package/dist/registry/generators/descriptors.js +405 -0
- package/dist/registry/generators/fields.d.ts +46 -0
- package/dist/registry/generators/fields.js +111 -0
- package/dist/registry/generators/markdown/common.d.ts +21 -0
- package/dist/registry/generators/markdown/common.js +55 -0
- package/dist/registry/generators/markdown/index.d.ts +33 -0
- package/dist/registry/generators/markdown/index.js +37 -0
- package/dist/registry/generators/markdown/page.d.ts +8 -0
- package/dist/registry/generators/markdown/page.js +52 -0
- package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
- package/dist/registry/generators/markdown/pyvalue.js +106 -0
- package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
- package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
- package/dist/registry/generators/markdown/sections/example.js +295 -0
- package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/filtering.js +76 -0
- package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/header.js +37 -0
- package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
- package/dist/registry/generators/markdown/sections/inputs.js +151 -0
- package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
- package/dist/registry/generators/markdown/sections/lookups.js +150 -0
- package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
- package/dist/registry/generators/markdown/sections/outputs.js +9 -0
- package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
- package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
- package/dist/registry/generators/materialize.d.ts +30 -0
- package/dist/registry/generators/materialize.js +132 -0
- package/dist/registry/generators/md-symbols.d.ts +22 -0
- package/dist/registry/generators/md-symbols.js +58 -0
- package/dist/registry/generators/triggers.d.ts +15 -0
- package/dist/registry/generators/triggers.js +85 -0
- package/dist/registry/generators/type-registry.d.ts +50 -0
- package/dist/registry/generators/type-registry.js +154 -0
- package/dist/registry/generators/type-tree.d.ts +65 -0
- package/dist/registry/generators/type-tree.js +179 -0
- package/dist/registry/library-build/build.d.ts +57 -0
- package/dist/registry/library-build/build.js +286 -0
- package/dist/registry/library-build/canonical.d.ts +119 -0
- package/dist/registry/library-build/canonical.js +512 -0
- package/dist/registry/library-build/cli.d.ts +45 -0
- package/dist/registry/library-build/cli.js +182 -0
- package/dist/registry/library-build/context.d.ts +107 -0
- package/dist/registry/library-build/context.js +267 -0
- package/dist/registry/library-build/enrich.d.ts +36 -0
- package/dist/registry/library-build/enrich.js +158 -0
- package/dist/registry/library-build/events.d.ts +108 -0
- package/dist/registry/library-build/events.js +476 -0
- package/dist/registry/library-build/generic-expand.d.ts +77 -0
- package/dist/registry/library-build/generic-expand.js +363 -0
- package/dist/registry/library-build/plan.d.ts +71 -0
- package/dist/registry/library-build/plan.js +270 -0
- package/dist/registry/library-build/py.d.ts +45 -0
- package/dist/registry/library-build/py.js +167 -0
- package/dist/registry/library-build/schema-action.d.ts +38 -0
- package/dist/registry/library-build/schema-action.js +107 -0
- package/dist/registry/library-build/write.d.ts +6 -0
- package/dist/registry/library-build/write.js +26 -0
- package/dist/registry/library-reader.d.ts +20 -0
- package/dist/registry/library-reader.js +50 -0
- package/dist/registry/lookup-specs.d.ts +55 -0
- package/dist/registry/lookup-specs.js +140 -0
- package/dist/registry/materialize-cli.js +28 -14
- package/dist/registry/materializer.d.ts +25 -3
- package/dist/registry/materializer.js +45 -29
- package/dist/registry/model/library.d.ts +254 -0
- package/dist/registry/model/library.js +23 -0
- package/dist/registry/naming.d.ts +54 -0
- package/dist/registry/naming.js +142 -0
- package/dist/registry/orphans.d.ts +4 -0
- package/dist/registry/orphans.js +86 -0
- package/dist/registry/prepare/actions.d.ts +26 -0
- package/dist/registry/prepare/actions.js +94 -0
- package/dist/registry/prepare/connections.d.ts +181 -0
- package/dist/registry/prepare/connections.js +399 -0
- package/dist/registry/prepare/event.d.ts +96 -0
- package/dist/registry/prepare/event.js +268 -0
- package/dist/registry/prepare/handoff.d.ts +21 -0
- package/dist/registry/prepare/handoff.js +91 -0
- package/dist/registry/prepare/ledger.d.ts +104 -0
- package/dist/registry/prepare/ledger.js +216 -0
- package/dist/registry/prepare/messages.d.ts +190 -0
- package/dist/registry/prepare/messages.js +472 -0
- package/dist/registry/prepare/objects.d.ts +130 -0
- package/dist/registry/prepare/objects.js +318 -0
- package/dist/registry/prepare/overlay.d.ts +149 -0
- package/dist/registry/prepare/overlay.js +487 -0
- package/dist/registry/prepare/parents.d.ts +41 -0
- package/dist/registry/prepare/parents.js +122 -0
- package/dist/registry/prepare/pipeline.d.ts +91 -0
- package/dist/registry/prepare/pipeline.js +376 -0
- package/dist/registry/prepare/regenerate.d.ts +9 -0
- package/dist/registry/prepare/regenerate.js +50 -0
- package/dist/registry/prepare/resolve.d.ts +199 -0
- package/dist/registry/prepare/resolve.js +470 -0
- package/dist/registry/prepare/retry.d.ts +83 -0
- package/dist/registry/prepare/retry.js +109 -0
- package/dist/registry/prepare/source-import.d.ts +54 -0
- package/dist/registry/prepare/source-import.js +192 -0
- package/dist/registry/prepare/support.d.ts +41 -0
- package/dist/registry/prepare/support.js +178 -0
- package/dist/registry/prepare-cli.d.ts +16 -5
- package/dist/registry/prepare-cli.js +60 -53
- package/dist/registry/pull-cli.js +8 -7
- package/dist/registry/reporter.d.ts +26 -0
- package/dist/registry/reporter.js +41 -0
- package/dist/registry/uip/client.d.ts +99 -0
- package/dist/registry/uip/client.js +226 -0
- package/dist/registry/uip/commands.d.ts +40 -0
- package/dist/registry/uip/commands.js +80 -0
- package/dist/registry/uip/envelope.d.ts +37 -0
- package/dist/registry/uip/envelope.js +84 -0
- package/dist/registry/uip/response-cache.d.ts +28 -0
- package/dist/registry/uip/response-cache.js +57 -0
- package/lib/README.md +10 -8
- package/package.json +3 -3
- package/dist/generators/_connections.py +0 -331
- package/dist/generators/_events.py +0 -355
- package/dist/generators/_lookups.py +0 -114
- package/dist/generators/_naming.py +0 -185
- package/dist/generators/_objects.py +0 -299
- package/dist/generators/_resolve.py +0 -319
- package/dist/generators/convert_library_to_md.py +0 -1441
- package/dist/generators/extract_library.py +0 -125
- package/dist/generators/generate_connectors_ts.py +0 -936
- package/dist/generators/generate_library_json.py +0 -2527
- package/dist/generators/generate_triggers_ts.py +0 -110
- package/dist/generators/materialize_registry.py +0 -155
- package/dist/generators/prepare_connector.py +0 -1860
- package/dist/registry/python.d.ts +0 -26
- package/dist/registry/python.js +0 -46
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every line `prepare_connector.py` printed, as functions that RETURN the text.
|
|
3
|
+
*
|
|
4
|
+
* The messages are the product. An agent reads "prepare-connector: …" and does
|
|
5
|
+
* what it says, so each string here is kept verbatim from the Python — the
|
|
6
|
+
* same words, the same arrows, the same `…` — and the only thing that changed
|
|
7
|
+
* is that nothing in this module writes to a stream. The orchestration decides
|
|
8
|
+
* where a line goes (stdout for progress, stderr for `WARN:` and `note:`), and a
|
|
9
|
+
* test asserts on the text without capturing anything.
|
|
10
|
+
*
|
|
11
|
+
* Two conventions the Python had are made explicit:
|
|
12
|
+
*
|
|
13
|
+
* - `sys.exit("prepare-connector: …")` texts carry their prefix INSIDE the
|
|
14
|
+
* string, while a helper's exception was prefixed at the exit site
|
|
15
|
+
* (`sys.exit(f"prepare-connector: {error}")`). {@link PrepareRefusal} is a
|
|
16
|
+
* refusal whose message is already the full text; {@link exitText} spells
|
|
17
|
+
* both kinds the way the terminal saw them.
|
|
18
|
+
* - the pasteable `npx flow-sdk registry prepare` spelling is a parameter with
|
|
19
|
+
* the Python's literal as its default, so a caller driven from `uip` can
|
|
20
|
+
* spell the command for the binary in the reader's hand (see
|
|
21
|
+
* `core/cli-spelling.ts`) without this module reading the environment.
|
|
22
|
+
*/
|
|
23
|
+
import { Refusal, messageOf } from "../errors.js";
|
|
24
|
+
import { closestActions } from "./actions.js";
|
|
25
|
+
import { pyGet, pyOr, pyRepr, pyStrAny, pyTruthy } from "./support.js";
|
|
26
|
+
export const PREFIX = "prepare-connector: ";
|
|
27
|
+
/** The spelling the Python hard-coded into every pasteable re-run. */
|
|
28
|
+
export const PREPARE_COMMAND = "npx flow-sdk registry prepare";
|
|
29
|
+
/** `prepare-connector: <text>`. */
|
|
30
|
+
export function prefixed(text) {
|
|
31
|
+
return `${PREFIX}${text}`;
|
|
32
|
+
}
|
|
33
|
+
/** `prepare-connector: next: <text>` — the last lines of a run, each one a call the reader can paste. */
|
|
34
|
+
export function nextLine(text) {
|
|
35
|
+
return prefixed(`next: ${text}`);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A refusal whose message is the COMPLETE text the Python passed to
|
|
39
|
+
* `sys.exit`, prefix included. Every other {@link Refusal} reaching the exit
|
|
40
|
+
* boundary is a helper's and gets the prefix there, as the Python's
|
|
41
|
+
* `sys.exit(f"prepare-connector: {error}")` did.
|
|
42
|
+
*/
|
|
43
|
+
export class PrepareRefusal extends Refusal {
|
|
44
|
+
constructor(message, code) {
|
|
45
|
+
super(message, code);
|
|
46
|
+
this.name = "PrepareRefusal";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** What the terminal showed for a thrown value: the Python's `sys.exit` text. */
|
|
50
|
+
export function exitText(error) {
|
|
51
|
+
if (error instanceof PrepareRefusal)
|
|
52
|
+
return error.message;
|
|
53
|
+
return prefixed(messageOf(error));
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Stage 0: the connection and bindings.json
|
|
57
|
+
/** ` connection: <Name> (<id>)` — `found.get('Name')` renders `None` when the row has none, as Python did. */
|
|
58
|
+
export function connectionLine(row, connectionId) {
|
|
59
|
+
return ` connection: ${pyStrAny(pyGet(row, "Name", null))} (${connectionId})`;
|
|
60
|
+
}
|
|
61
|
+
/** ` bindings: <symbolic name> -> <bindings.json>`, once per row this run changed. */
|
|
62
|
+
export function bindingsLine(symbolicName, bindingsPath) {
|
|
63
|
+
return ` bindings: ${symbolicName} -> ${bindingsPath}`;
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Refusals of the action path
|
|
67
|
+
/** `_missing_generic_schema_message`: a Generic action that expanded to nothing. */
|
|
68
|
+
export function missingGenericSchemaMessage(operation, objectName) {
|
|
69
|
+
const target = objectName
|
|
70
|
+
? `object "${objectName}" was not found or does not support action "${operation}"`
|
|
71
|
+
: `no object in the connected catalog supports action "${operation}"`;
|
|
72
|
+
return (`${PREFIX}${target}. A connection-resolved schema requires a ` +
|
|
73
|
+
"working live connection id. For an offline/validate-only flow that does " +
|
|
74
|
+
"not need connection-specific fields, use the baked connector descriptor " +
|
|
75
|
+
"without custom fields instead.");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* `_unresolved_action_message`: what to do when the action id is not a
|
|
79
|
+
* registry key. `actions` is what `registry search <key>` served (see
|
|
80
|
+
* `actions.ts`); the message is pure over it.
|
|
81
|
+
*
|
|
82
|
+
* NOT auto-substituted, deliberately: the baked action id and the registry key
|
|
83
|
+
* are different namespaces, and guessing between them would emit a node both
|
|
84
|
+
* offline validators accept and the runtime dispatches to the wrong operation.
|
|
85
|
+
*/
|
|
86
|
+
export function unresolvedActionMessage(connectorKey, action, nodeType, actions) {
|
|
87
|
+
const lines = [
|
|
88
|
+
`${PREFIX}\`registry get ${nodeType}\` found no such node.`,
|
|
89
|
+
"",
|
|
90
|
+
"The baked library's action id is not always the registry's key — a Generic",
|
|
91
|
+
"op is `list-all-records` in the library and materializes per object, while",
|
|
92
|
+
"the registry serves the un-expanded form. Re-run with a registry action id.",
|
|
93
|
+
];
|
|
94
|
+
if (actions.length === 0) {
|
|
95
|
+
lines.push("", `\`registry search ${connectorKey}\` returned nothing. If the catalog looks`, "stale, refresh it first: `uip maestro flow registry pull --force`.");
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
const close = closestActions(action, actions);
|
|
99
|
+
if (close.length > 0)
|
|
100
|
+
lines.push("", `Closest to "${action}": ${close.slice(0, 3).join(", ")}`);
|
|
101
|
+
lines.push("", `All actions the registry serves for ${connectorKey}:`, ...actions.map((a) => ` ${a}`));
|
|
102
|
+
return lines.join("\n");
|
|
103
|
+
}
|
|
104
|
+
export function noCanonicalEntryMessage(nodeType) {
|
|
105
|
+
return `${PREFIX}no canonical entry for ${nodeType} (unexpected registry shape).`;
|
|
106
|
+
}
|
|
107
|
+
/** The `-f` names do not match any api-type ObjectAction; `specs` are the actions' parent lists. */
|
|
108
|
+
export function parentFieldNamesMismatchMessage(given, specs) {
|
|
109
|
+
const expected = specs.map(([, fields]) => fields.join(", ")).join(" or ") || "<none advertised>";
|
|
110
|
+
return (`${PREFIX}parent-field names must match one api-type ObjectAction exactly. Given: ` +
|
|
111
|
+
`${given.join(", ")}. Expected: ${expected}.`);
|
|
112
|
+
}
|
|
113
|
+
export function genericActionNeedsObjectMessage() {
|
|
114
|
+
return (`${PREFIX}this is a Generic action; pass --object <name> to prepare one object or --all-objects ` +
|
|
115
|
+
"to prepare the full connected catalog.");
|
|
116
|
+
}
|
|
117
|
+
export function objectFlagsOnlyForGenericMessage() {
|
|
118
|
+
return `${PREFIX}--object/--all-objects apply only to Generic connector actions.`;
|
|
119
|
+
}
|
|
120
|
+
/** `_parse_fields`'s refusal of a `-f` without `=`. */
|
|
121
|
+
export function fieldFlagMessage(pair) {
|
|
122
|
+
return `${PREFIX}--field expects NAME=VALUE, got ${pyRepr(pair)}.`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `_rejected_parent_fields_message`: the CLI's own rejection is the actionable
|
|
126
|
+
* half and the pipeline otherwise swallows it, so quote the service.
|
|
127
|
+
* `failures` are `(connector, object, operation, message)` tuples; the first
|
|
128
|
+
* non-empty message is the one quoted.
|
|
129
|
+
*/
|
|
130
|
+
export function rejectedParentFieldsMessage(pairs, failures = []) {
|
|
131
|
+
const given = pairs.join(", ");
|
|
132
|
+
let detail = "";
|
|
133
|
+
for (const failure of failures) {
|
|
134
|
+
const message = failure[failure.length - 1];
|
|
135
|
+
if (pyTruthy(message)) {
|
|
136
|
+
detail = ` The service said: ${pyStrAny(message)}`;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return (`${PREFIX}the parent-field set was rejected: ${given}.${detail} ` +
|
|
141
|
+
"Re-run with NO -f to list the parent fields this operation declares, " +
|
|
142
|
+
"resolve a value for EVERY one of them, then pass them all together. " +
|
|
143
|
+
"Values are connection- and parent-scoped (a Jira issue-type id is valid " +
|
|
144
|
+
"only inside its project), so they cannot be copied from another " +
|
|
145
|
+
"project, connection, or task.");
|
|
146
|
+
}
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// The object decision
|
|
149
|
+
/** ` object: <note>` — the ladder's normalization sentence. */
|
|
150
|
+
export function objectNoteLine(note) {
|
|
151
|
+
return ` object: ${note}`;
|
|
152
|
+
}
|
|
153
|
+
/** ` object: <requested> -> <matched> recorded in <ledger>`. */
|
|
154
|
+
export function objectRecordedLine(requested, matched, ledgerPath) {
|
|
155
|
+
return ` object: ${requested} -> ${matched} recorded in ${ledgerPath}`;
|
|
156
|
+
}
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// The summary and its notes
|
|
159
|
+
/** `_input_field_note`: `N input field(s)`, or the unavailable marker when the count could not be read. */
|
|
160
|
+
export function inputFieldNote(count) {
|
|
161
|
+
return count !== undefined && count !== null ? `${count} input field(s)` : "input fields: <unavailable>";
|
|
162
|
+
}
|
|
163
|
+
/** `JSON overlay only` or `overlay + <key>.ts`. */
|
|
164
|
+
export function tsNote(jsonOnly, connectorKey) {
|
|
165
|
+
return jsonOnly ? "JSON overlay only" : `overlay + ${connectorKey}.ts`;
|
|
166
|
+
}
|
|
167
|
+
/** The action path's success line. */
|
|
168
|
+
export function preparedSummaryLine(nodeType, out, ts, variants, fieldNote) {
|
|
169
|
+
return `${PREFIX}${nodeType} → ${out}/ (${ts}, ${variants} variant(s), ${fieldNote})`;
|
|
170
|
+
}
|
|
171
|
+
/** Why this run did not overwrite the overlay (`_keep_prepared_schema`). */
|
|
172
|
+
export function keptSchemaMessage(action, kept, discarded) {
|
|
173
|
+
return (`${PREFIX}kept the prepared schema for ${action} ` +
|
|
174
|
+
`(${kept} input field(s)) — this run passed no -f, so its schema ` +
|
|
175
|
+
`action did not run and the unresolved entry (${discarded} field(s)) ` +
|
|
176
|
+
"would have replaced it. The resolutions above are recorded either " +
|
|
177
|
+
"way; re-run with -f to refresh the schema itself.");
|
|
178
|
+
}
|
|
179
|
+
/** The set was SHAPED right but resolved to nothing new: a VALUE is wrong for this connection. */
|
|
180
|
+
export function parentOnlyMessage(parents, fieldPairs) {
|
|
181
|
+
return (`${PREFIX}the parent-field values did not resolve — the ` +
|
|
182
|
+
`prepared schema is still parent-only (${parents.join(", ")}` +
|
|
183
|
+
`). Given: ${fieldPairs.join(", ")}. The names were accepted, so a ` +
|
|
184
|
+
"VALUE is wrong for this connection: resolve each one against this " +
|
|
185
|
+
"connection (ids are parent-scoped — a Jira issue-type id is valid " +
|
|
186
|
+
"only inside its project) rather than reusing one from another " +
|
|
187
|
+
"project, connection, or task.");
|
|
188
|
+
}
|
|
189
|
+
function valueOf(values, name) {
|
|
190
|
+
if (values instanceof Map)
|
|
191
|
+
return values.get(name);
|
|
192
|
+
const record = values;
|
|
193
|
+
return Object.prototype.hasOwnProperty.call(record, name) ? record[name] : undefined;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The paste-ready re-run when every schema parent already has a recorded
|
|
197
|
+
* resolution: the ids `--resolve` just wrote ARE the `-f` values.
|
|
198
|
+
*/
|
|
199
|
+
export function resolvedParentsNote(connectorKey, action, actionParents, resolvedParents, command = PREPARE_COMMAND) {
|
|
200
|
+
const fieldsArg = actionParents.map((name) => `-f ${name}=${valueOf(resolvedParents, name)}`).join(" ");
|
|
201
|
+
return (`${PREFIX}note: this operation resolves its fields ` +
|
|
202
|
+
"from the VALUES of parent inputs, and the resolutions above " +
|
|
203
|
+
"ARE those values. Re-run to materialize the full field set:\n" +
|
|
204
|
+
` ${command} ${connectorKey} ${action} ${fieldsArg}`);
|
|
205
|
+
}
|
|
206
|
+
/** The note when some schema parent did not arrive as `-f`, with the example re-run. */
|
|
207
|
+
export function missingParentsNote(connectorKey, action, actionParents, missing, parentFields, command = PREPARE_COMMAND) {
|
|
208
|
+
const given = actionParents
|
|
209
|
+
.filter((name) => valueOf(parentFields, name) !== undefined)
|
|
210
|
+
.map((name) => `-f ${name}=${valueOf(parentFields, name)}`)
|
|
211
|
+
.join(" ");
|
|
212
|
+
const example = actionParents.map((name) => `-f ${name}=${valueOf(parentFields, name) ?? "<value>"}`).join(" ");
|
|
213
|
+
const which = missing.length === actionParents.length
|
|
214
|
+
? "none were given"
|
|
215
|
+
: `${missing.join(", ")} ${missing.length === 1 ? "was" : "were"} not${given ? ` (given: ${given})` : ""}`;
|
|
216
|
+
return (`${PREFIX}note: this operation resolves its fields ` +
|
|
217
|
+
`from the VALUES of ${actionParents.join(", ")}` +
|
|
218
|
+
` (its schema action), and ${which}` +
|
|
219
|
+
", so the overlay above carries only the unresolved schema — a " +
|
|
220
|
+
"body field the object declares is not there yet and compile " +
|
|
221
|
+
"will refuse it as an unknown input. Re-run passing ALL of them " +
|
|
222
|
+
"as -f NAME=VALUE (a partial set is rejected):\n" +
|
|
223
|
+
` ${command} ${connectorKey} ${action} ${example}`);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* `_print_lookup_vocabulary`: the lookup table for the operation just
|
|
227
|
+
* prepared, or no lines at all when it has no lookups. The markdown catalog
|
|
228
|
+
* renders this for BAKED schemas; a schema that only exists because `prepare`
|
|
229
|
+
* ran has no page, so the command is where the vocabulary first becomes
|
|
230
|
+
* knowable.
|
|
231
|
+
*/
|
|
232
|
+
export function lookupVocabularyLines(connectorKey, action, objectName, specs, command = PREPARE_COMMAND) {
|
|
233
|
+
if (specs.size === 0)
|
|
234
|
+
return [];
|
|
235
|
+
const where = objectName ? ` (${objectName})` : "";
|
|
236
|
+
const lines = [
|
|
237
|
+
`${PREFIX}lookup fields on ${action}${where} — these hold ids; author lookup(...) and resolve, don't paste one:`,
|
|
238
|
+
];
|
|
239
|
+
const shown = [...specs.entries()].slice(0, 12);
|
|
240
|
+
for (const [field, spec] of shown) {
|
|
241
|
+
const by = (spec.by ?? []).join(" | ") || "?";
|
|
242
|
+
const target = `${pyOr(pyOr(spec.objectName, spec.path), "?")}.${pyOr(spec.value, "id")}`;
|
|
243
|
+
lines.push(` ${field} — matches on ${by}, sends ${target}`);
|
|
244
|
+
}
|
|
245
|
+
if (specs.size > shown.length)
|
|
246
|
+
lines.push(` … and ${specs.size - shown.length} more`);
|
|
247
|
+
const [field, spec] = shown[0];
|
|
248
|
+
const by = (pyTruthy(spec.by) ? spec.by : ["id"])[0];
|
|
249
|
+
const obj = objectName ? ` --object ${objectName}` : "";
|
|
250
|
+
lines.push(`${PREFIX}resolve with: ${command} ${connectorKey} ${action}${obj} --resolve ${field}:${by}=<value>`);
|
|
251
|
+
return lines;
|
|
252
|
+
}
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
// `--resolve`
|
|
255
|
+
/** A `--resolve` names a field with no lookup; `known` are the fields that have one, sorted. */
|
|
256
|
+
export function unknownLookupMessage(unknown, nodeType, known) {
|
|
257
|
+
return (`${PREFIX}no lookup is declared for ` +
|
|
258
|
+
`${unknown.map(pyRepr).join(", ")} on ${nodeType}. ` +
|
|
259
|
+
`Fields with a lookup: ${known.join(", ") || "(none)"}.`);
|
|
260
|
+
}
|
|
261
|
+
/** ` <field>: <by>=<value> -> <id>`, with ` (already resolved)` when the ledger answered. */
|
|
262
|
+
export function resolutionLine(field, by, value, got, alreadyResolved = false) {
|
|
263
|
+
return ` ${field}: ${by}=${value} -> ${pyStrAny(got)}${alreadyResolved ? " (already resolved)" : ""}`;
|
|
264
|
+
}
|
|
265
|
+
export function wroteResolutionsLine(count, ledgerPath) {
|
|
266
|
+
return ` wrote ${count} resolution(s) to ${ledgerPath}`;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* `generated N connector module(s) → <out>` — the descriptor generator's own
|
|
270
|
+
* last line (`generate_connectors_ts.py`), which `prepare` spawned between
|
|
271
|
+
* the resolution lines and its summary. `outDir` is `str(Path(args.output))`.
|
|
272
|
+
*/
|
|
273
|
+
export function generatedModulesLine(count, outDir) {
|
|
274
|
+
return `generated ${count} connector module(s) → ${outDir}`;
|
|
275
|
+
}
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// The retry across connections (flow-builder-sdk#744; `_retry_or_exit`)
|
|
278
|
+
/** `<Name> (<Folder>)` for a per-candidate report line (`_candidate_label`). */
|
|
279
|
+
export function candidateLabel(row) {
|
|
280
|
+
return `${pyStrAny(pyGet(row, "Name", null))} (${pyStrAny(pyGet(row, "Folder", null))})`;
|
|
281
|
+
}
|
|
282
|
+
/** ` <field>: not on this connection; trying N other <key> connection(s)`. */
|
|
283
|
+
export function tryingOtherConnectionsLine(field, count, connectorKey) {
|
|
284
|
+
return ` ${field}: not on this connection; trying ${count} other ${connectorKey} connection(s)`;
|
|
285
|
+
}
|
|
286
|
+
/** ` connection: <label> resolves <field>; re-running against it (--connection-id <id>)`. */
|
|
287
|
+
export function rerunAgainstLine(label, field, connectionId) {
|
|
288
|
+
return ` connection: ${label} resolves ${field}; re-running against it (--connection-id ${connectionId})`;
|
|
289
|
+
}
|
|
290
|
+
/** The terminal report's line for a candidate that resolved every requested lookup. */
|
|
291
|
+
export function candidateResolvesAllLine(label, lookups) {
|
|
292
|
+
return ` ${label} resolves all ${lookups} lookup(s)`;
|
|
293
|
+
}
|
|
294
|
+
/** The terminal report's line for a candidate whose collection was read to the end. */
|
|
295
|
+
export function candidateExhaustedLine(label, field, scanned) {
|
|
296
|
+
return ` ${label} ${field}: ${scanned} record(s) scanned, no match`;
|
|
297
|
+
}
|
|
298
|
+
/** The terminal report's line for a candidate whose lookup failed for another reason: the error, verbatim. */
|
|
299
|
+
export function candidateErrorLine(label, error) {
|
|
300
|
+
return ` ${label} ${messageOf(error)}`;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* An explicit `--connection-id` / `--connection` is the author's decision and
|
|
304
|
+
* is not overridden. The alternatives are still named, because the whole cost
|
|
305
|
+
* of this bug was not knowing they existed. `first` is the exhausted lookup's
|
|
306
|
+
* own message; `others` the pasteable listing of the other candidates.
|
|
307
|
+
*/
|
|
308
|
+
export function pinnedConnectionExhaustedMessage(first, connectorKey, othersCount, othersListing) {
|
|
309
|
+
let extra = "";
|
|
310
|
+
if (othersCount > 0) {
|
|
311
|
+
extra =
|
|
312
|
+
`\n${othersCount} other enabled ${connectorKey} connection${othersCount > 1 ? "s" : ""} exist${othersCount > 1 ? "" : "s"}:\n` +
|
|
313
|
+
`${othersListing}\n` +
|
|
314
|
+
"Re-run against one of those, or check the value. This run kept the connection you named.";
|
|
315
|
+
}
|
|
316
|
+
return `${PREFIX}${first}${extra}`;
|
|
317
|
+
}
|
|
318
|
+
/** The listing of other connections itself failed; the original lookup failure is what the author asked about. */
|
|
319
|
+
export function candidateListingFailedMessage(first, error) {
|
|
320
|
+
return `${PREFIX}${first}\n(could not list other connections: ${messageOf(error)})`;
|
|
321
|
+
}
|
|
322
|
+
export function onlyConnectionMessage(first, connectorKey) {
|
|
323
|
+
return (`${PREFIX}${first}\n` + `This is the only enabled ${connectorKey} connection on the tenant, so the value is what to check.`);
|
|
324
|
+
}
|
|
325
|
+
/** Two or more candidates resolve everything: reported, never chosen between. */
|
|
326
|
+
export function severalConnectionsResolveMessage(count, connectorKey, listing) {
|
|
327
|
+
return (`${PREFIX}${count} ${connectorKey} connections resolve ` +
|
|
328
|
+
"every requested lookup, and they are not interchangeable — the ids " +
|
|
329
|
+
"they return differ per connection. Pick the one you mean:\n" +
|
|
330
|
+
listing);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Nothing resolved anywhere: every candidate and what it held, one per line.
|
|
334
|
+
* HERE is where the collection listing finally belongs — every connection has
|
|
335
|
+
* been tried, so seeing what the collection actually holds is the next step
|
|
336
|
+
* rather than the detour it is while a retry is still available.
|
|
337
|
+
*/
|
|
338
|
+
export function noConnectionResolvesMessage(connectorKey, first, lines, connectionId) {
|
|
339
|
+
return (`${PREFIX}no ${connectorKey} connection could resolve ` +
|
|
340
|
+
`${first.field}: ${first.by}=${pyRepr(first.value)}\n` +
|
|
341
|
+
`${lines.join("\n")}\n` +
|
|
342
|
+
"Every candidate was read to the end, so the value is what to check now. " +
|
|
343
|
+
`To see what ${pyRepr(first.collection)} holds:\n` +
|
|
344
|
+
` uip is resources run list ${connectorKey} ${first.collection} ` +
|
|
345
|
+
`--connection-id ${connectionId}`);
|
|
346
|
+
}
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
// The event path
|
|
349
|
+
export function eventParentFieldsMessage() {
|
|
350
|
+
return `${PREFIX}-f parent fields apply to activities; a connector event has none.`;
|
|
351
|
+
}
|
|
352
|
+
export function eventAllObjectsMessage() {
|
|
353
|
+
return `${PREFIX}--all-objects applies to Generic activities; prepare a generic EVENT for one --object.`;
|
|
354
|
+
}
|
|
355
|
+
/** A Generic event fires for ONE object; name it. `rows` is the `triggers objects` catalog. */
|
|
356
|
+
export function genericEventNeedsObjectMessage(action, rows) {
|
|
357
|
+
const sample = rows
|
|
358
|
+
.slice(0, 8)
|
|
359
|
+
.filter((row) => pyTruthy(pyGet(row, "Name", null)))
|
|
360
|
+
.map((row) => pyStrAny(row.Name))
|
|
361
|
+
.join(", ");
|
|
362
|
+
return (`${PREFIX}${action} is a Generic event — it ` +
|
|
363
|
+
"fires for ONE object of the connection. Pass --object " +
|
|
364
|
+
`<name>. This connection's objects include: ${sample}` +
|
|
365
|
+
(rows.length > 8 ? " …" : ""));
|
|
366
|
+
}
|
|
367
|
+
export function objectOnCuratedEventMessage(action, builtInObject) {
|
|
368
|
+
return `${PREFIX}--object applies to Generic events; ${action}'s object is built in (${pyOr(builtInObject, "none")}).`;
|
|
369
|
+
}
|
|
370
|
+
/** ` WARN: <error>` — a listing failure a curated event survives. */
|
|
371
|
+
export function warnLine(error) {
|
|
372
|
+
return ` WARN: ${messageOf(error)}`;
|
|
373
|
+
}
|
|
374
|
+
/** The describe could not answer for a curated event; the definition's own sections stand in. */
|
|
375
|
+
export function describeFallbackWarning(error, bakedObject) {
|
|
376
|
+
return (` WARN: ${messageOf(error)}\n` + ` falling back to the registry definition's own schema sections for ${bakedObject}`);
|
|
377
|
+
}
|
|
378
|
+
export function byoaListingFailedMessage(connectionId, error) {
|
|
379
|
+
return (`${PREFIX}this event object requires a BYOA ` +
|
|
380
|
+
"(bring-your-own-app) connection, and the BYOA listing needed to " +
|
|
381
|
+
`check ${connectionId} against it failed: ${messageOf(error)}`);
|
|
382
|
+
}
|
|
383
|
+
export function byoaRequiredMessage(connectionId, connectorKey, listing) {
|
|
384
|
+
return (`${PREFIX}this event object requires a BYOA ` +
|
|
385
|
+
`(bring-your-own-app) connection, and ${connectionId} is not ` +
|
|
386
|
+
"one. A standard connection deploys and then faults at runtime with " +
|
|
387
|
+
`an unrelated-looking webhook error. BYOA ${connectorKey} connections:\n` +
|
|
388
|
+
listing);
|
|
389
|
+
}
|
|
390
|
+
export function eventSummaryLine(eventNodeType, out, operation, mode, whereParameters, filterFields) {
|
|
391
|
+
return (`${PREFIX}${eventNodeType} → ${out}/ (event overlay, ` +
|
|
392
|
+
`${operation}, ${mode}, ${whereParameters} where-parameter(s), ` +
|
|
393
|
+
`${filterFields} filter field(s))`);
|
|
394
|
+
}
|
|
395
|
+
/** The first eight filterable field names, or nothing when the event has none. */
|
|
396
|
+
export function filterableFieldsLine(filterFields) {
|
|
397
|
+
if (filterFields.length === 0)
|
|
398
|
+
return undefined;
|
|
399
|
+
const names = filterFields
|
|
400
|
+
.slice(0, 8)
|
|
401
|
+
.map((f) => pyStrAny(pyGet(f, "name", null)))
|
|
402
|
+
.join(", ");
|
|
403
|
+
return `${PREFIX}filterable fields (${filterFields.length}): ${names}${filterFields.length > 8 ? " …" : ""}`;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* The webhook note and the connector's own registration text, or nothing for a
|
|
407
|
+
* polling event. Registration is an act on the external service — the one step
|
|
408
|
+
* the loop cannot make deterministic — so the connector's words are handed over
|
|
409
|
+
* instead of leaving `uip is webhooks config` to be discovered.
|
|
410
|
+
*/
|
|
411
|
+
export function webhookNoteLines(mode, textBlocks) {
|
|
412
|
+
if (mode !== "webhooks")
|
|
413
|
+
return [];
|
|
414
|
+
return [
|
|
415
|
+
`${PREFIX}note: this event is WEBHOOK mode — it ` +
|
|
416
|
+
"cannot be debugged locally (deploy to test), and the webhook " +
|
|
417
|
+
"URL must be registered on the external service" +
|
|
418
|
+
(textBlocks.length > 0 ? ":" : " (retrieve it with `uip is webhooks config`)."),
|
|
419
|
+
...textBlocks.map((block) => ` ${block}`),
|
|
420
|
+
];
|
|
421
|
+
}
|
|
422
|
+
/** `{ connector: '…', event: '…'[, object: '…'], connection: '…', folder: '…' }`. */
|
|
423
|
+
export function eventSubscription(connectorKey, action, objectName, connectionName, folderName) {
|
|
424
|
+
return (`{ connector: '${connectorKey}', event: '${action}'` +
|
|
425
|
+
(objectName !== undefined && objectName !== null ? `, object: '${objectName}'` : "") +
|
|
426
|
+
`, connection: '${connectionName}', folder: '${folderName}' }`);
|
|
427
|
+
}
|
|
428
|
+
export function eventNextLine(subscription) {
|
|
429
|
+
return (`${PREFIX}next: .trigger(onEvent(${subscription})) to ` +
|
|
430
|
+
"start the flow on this event — or .step('…', " +
|
|
431
|
+
`waitForEvent(${subscription})) to pause mid-flow for it`);
|
|
432
|
+
}
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
// Repointing the source, and the descriptor handoff
|
|
435
|
+
export function sourceNotAFileLine(source) {
|
|
436
|
+
return `--source ${source} does not name a file; left the import alone`;
|
|
437
|
+
}
|
|
438
|
+
export function severalSourcesLine(names) {
|
|
439
|
+
return `${names.length} sources import this connector (${names.join(", ")}); name one with --source to have it repointed`;
|
|
440
|
+
}
|
|
441
|
+
export function repointedLine(sourceRelative, specifier) {
|
|
442
|
+
return `repointed ${sourceRelative} -> ${specifier}`;
|
|
443
|
+
}
|
|
444
|
+
/** The two-line fallback when the module could not be read or exports several descriptors. */
|
|
445
|
+
export function descriptorUsageFallbackLines(sourcePosix) {
|
|
446
|
+
return [
|
|
447
|
+
`open "${sourcePosix}" and import the descriptor for the object you need`,
|
|
448
|
+
"use that descriptor with connector(descriptor, inputs, opts); do not " +
|
|
449
|
+
"reconstruct its live operation/object identity from the baked catalog",
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
/** The module has one descriptor but its inputs interface could not be parsed. */
|
|
453
|
+
export function descriptorUsageUnparsedLines(sourcePosix, descriptorName) {
|
|
454
|
+
return [
|
|
455
|
+
`open "${sourcePosix}" and import ${descriptorName}`,
|
|
456
|
+
"read its generated inputs interface before calling connector; " + "the required-input skeleton is unavailable",
|
|
457
|
+
];
|
|
458
|
+
}
|
|
459
|
+
/** The ready-to-paste handoff for a module with exactly one descriptor. */
|
|
460
|
+
export function descriptorUsageLines(descriptorName, importPath, requiredFields, boundConnection, boundFolder) {
|
|
461
|
+
let inputs = "{}";
|
|
462
|
+
if (requiredFields.length > 0) {
|
|
463
|
+
const placeholders = requiredFields.map(([name, type]) => `${name}: /* ${type}, required */`).join(", ");
|
|
464
|
+
inputs = `{ ${placeholders} }`;
|
|
465
|
+
}
|
|
466
|
+
return [
|
|
467
|
+
`import { ${descriptorName} } from "${importPath}";`,
|
|
468
|
+
`connector(${descriptorName}, ${inputs}, { connection: "${boundConnection}", folder: "${boundFolder}" })`,
|
|
469
|
+
"use this descriptor as generated: it carries the resolved live " +
|
|
470
|
+
"operation and object identity even when the baked action id differs",
|
|
471
|
+
];
|
|
472
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Refusal } from "../errors.js";
|
|
2
|
+
import type { LedgerObject } from "../model/library.js";
|
|
3
|
+
/**
|
|
4
|
+
* Enough rows to choose from without dumping a catalog into the error. The
|
|
5
|
+
* most-selective-token fallback can legitimately produce dozens of rows; past
|
|
6
|
+
* this many the table stops being a choice and starts being the unfiltered
|
|
7
|
+
* list the ladder exists to avoid.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAX_CANDIDATES = 15;
|
|
10
|
+
/** One row of `uip is resources list`: `Name`, `DisplayName`, `Custom`, `Operations`, … */
|
|
11
|
+
export type CatalogueRow = Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* No unique object for the requested name, with the candidates a human (or an
|
|
14
|
+
* agent with the prompt in hand) needs to choose (`_objects.ObjectMatchError`).
|
|
15
|
+
*
|
|
16
|
+
* `retriable` marks every outcome that fell PAST the name rungs — zero
|
|
17
|
+
* matches, and the substring refusals (unique or ambiguous). All of them can be
|
|
18
|
+
* caused by a stale `uip is resources list` cache hiding a real object:
|
|
19
|
+
* `--object User` on a catalog that lacks `User` but carries `UserAlias` would
|
|
20
|
+
* otherwise refuse with a recovery message pointing at the WRONG object, and
|
|
21
|
+
* the cache never invalidates itself. The caller refreshes the catalog once and
|
|
22
|
+
* re-runs the whole ladder before believing any of them. Ambiguity WITHIN a
|
|
23
|
+
* name rung (two objects whose names are case-variants) is not retriable — a
|
|
24
|
+
* fresh catalog has the same two names in it.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ObjectMatchRefusal extends Refusal {
|
|
27
|
+
readonly retriable: boolean;
|
|
28
|
+
constructor(message: string, code: string, retriable?: boolean);
|
|
29
|
+
}
|
|
30
|
+
/** The ledger file exists but cannot be read back — refuse, don't clobber (`_objects.LedgerError`). */
|
|
31
|
+
export declare class LedgerRefusal extends Refusal {
|
|
32
|
+
constructor(message: string, code: string);
|
|
33
|
+
}
|
|
34
|
+
/** `str(row.get("Name") or "")`. */
|
|
35
|
+
export declare function objectName(row: CatalogueRow): string;
|
|
36
|
+
/** `str(row.get("DisplayName") or "")`. */
|
|
37
|
+
export declare function objectDisplayName(row: CatalogueRow): string;
|
|
38
|
+
/**
|
|
39
|
+
* True when the row's `Operations` include `operation`. A catalog that does
|
|
40
|
+
* not report operations cannot be narrowed by one; keep the row rather than
|
|
41
|
+
* hiding it on missing data.
|
|
42
|
+
*/
|
|
43
|
+
export declare function supportsOperation(row: CatalogueRow, operation?: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The table an ambiguous match prints: enough columns to choose with.
|
|
46
|
+
*
|
|
47
|
+
* `Custom: yes` is how a prompt's "custom" qualifier resolves — it is a facet
|
|
48
|
+
* column here precisely so nothing upstream has to parse it out of English.
|
|
49
|
+
*/
|
|
50
|
+
export declare function formatCandidates(rows: readonly CatalogueRow[]): string;
|
|
51
|
+
/** The outcome of {@link matchObject}: the row, and how the given name became the API name. */
|
|
52
|
+
export interface ObjectMatch {
|
|
53
|
+
row: CatalogueRow;
|
|
54
|
+
/** `undefined` on an exact match; the normalization sentence otherwise, for the caller to print. */
|
|
55
|
+
note: string | undefined;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The catalog row for `wanted`, or throw {@link ObjectMatchRefusal}.
|
|
59
|
+
*
|
|
60
|
+
* `operation` is the action's verb (`Create`, `Update`, …); rows that do not
|
|
61
|
+
* support it are dropped before any matching.
|
|
62
|
+
*/
|
|
63
|
+
export declare function matchObject(rows: readonly CatalogueRow[], wanted: string | undefined | null, operation?: string): ObjectMatch;
|
|
64
|
+
export declare const RESOLUTIONS_FILENAME = "resolutions.json";
|
|
65
|
+
/** `<outDir>/resolutions.json`. */
|
|
66
|
+
export declare function ledgerPath(outDir: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* The ledger after {@link recordObject}: the sections `prepare` maintains,
|
|
69
|
+
* plus whatever else the file carried. Under `schemaVersion: "1"` the sections
|
|
70
|
+
* are additive — readers ignore keys they do not know.
|
|
71
|
+
*/
|
|
72
|
+
export interface RecordedLedger extends Record<string, unknown> {
|
|
73
|
+
schemaVersion: "1";
|
|
74
|
+
objects: Record<string, Record<string, LedgerObject>>;
|
|
75
|
+
resolutions: Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The whole ledger at `path` as parsed, `{}` when there is no file, or throw
|
|
79
|
+
* {@link LedgerRefusal}.
|
|
80
|
+
*
|
|
81
|
+
* A file that will not parse is REFUSED rather than silently reset to `{}` —
|
|
82
|
+
* the same rule `bindings.json` follows: a corrupt-but-recoverable audit of
|
|
83
|
+
* every resolution prepare ever made is worth more than this run finishing,
|
|
84
|
+
* and overwriting it is the one way to make the corruption permanent.
|
|
85
|
+
* Parseable-but-not-an-object (a top-level array, a bare string) is corruption
|
|
86
|
+
* wearing valid JSON; sliding it past the refusal would overwrite whatever it
|
|
87
|
+
* used to be.
|
|
88
|
+
*/
|
|
89
|
+
export declare function readLedger(path: string): Record<string, unknown>;
|
|
90
|
+
/**
|
|
91
|
+
* `json.dump(ledger, indent=2, sort_keys=True)` + newline: the bytes the
|
|
92
|
+
* ledger is written in. Keys sort by code point at every level, so the order a
|
|
93
|
+
* key was inserted in never reaches the file — which is also why integer-like
|
|
94
|
+
* keys (which `JSON.parse` hoists) are accepted here: sorting puts them where
|
|
95
|
+
* Python would have.
|
|
96
|
+
*/
|
|
97
|
+
export declare function formatLedger(ledger: Record<string, unknown>): string;
|
|
98
|
+
/** Write the ledger at `path`, creating its directory. */
|
|
99
|
+
export declare function writeLedger(path: string, ledger: Record<string, unknown>): void;
|
|
100
|
+
/** The object decision `recordObject` writes, and what it was decided from. */
|
|
101
|
+
export interface ObjectDecision {
|
|
102
|
+
row: CatalogueRow;
|
|
103
|
+
/** The action's verb, recorded as `operation` when present. */
|
|
104
|
+
operation?: string;
|
|
105
|
+
}
|
|
106
|
+
/** The `objects[nodeType][requested]` row for a decision. */
|
|
107
|
+
export declare function ledgerObjectOf(decision: ObjectDecision): LedgerObject;
|
|
108
|
+
/**
|
|
109
|
+
* The ledger with the match recorded under `objects[nodeType][requested]`.
|
|
110
|
+
*
|
|
111
|
+
* Pure: returns a new ledger and leaves `ledger` untouched. The row lands in a
|
|
112
|
+
* sibling `objects` section rather than in `resolutions`: an object identity is
|
|
113
|
+
* not a field lookup, and it should not wear a fake `field` key. Keys NEST
|
|
114
|
+
* rather than encode a tuple: the lookup rows' tuple-string keys exist to match
|
|
115
|
+
* a JavaScript `JSON.stringify` that reads them back, and nothing in JavaScript
|
|
116
|
+
* reads this section.
|
|
117
|
+
*
|
|
118
|
+
* Unlike lookup rows, the current match is ALWAYS written, `--refresh` or not.
|
|
119
|
+
* A lookup row stands because re-resolving costs a network call and can
|
|
120
|
+
* silently change an artifact; this row costs nothing to recompute and MUST
|
|
121
|
+
* agree with the overlay written beside it in this same run. The ledger
|
|
122
|
+
* records the DECISION only; the schema it selected lives in the overlay.
|
|
123
|
+
*/
|
|
124
|
+
export declare function recordObject(ledger: Record<string, unknown>, nodeType: string, requested: string, decision: ObjectDecision): RecordedLedger;
|
|
125
|
+
/**
|
|
126
|
+
* Record the match in `<outDir>/resolutions.json` and return that path — the
|
|
127
|
+
* Python `record_object_resolution`: read (refusing a corrupt file before
|
|
128
|
+
* anything is created), record, write.
|
|
129
|
+
*/
|
|
130
|
+
export declare function recordObjectResolution(outDir: string, nodeType: string, requested: string, row: CatalogueRow, operation?: string): string;
|