@uipath/maestro-builder-sdk 6.4.0 → 6.5.1
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/bpmn/uipath-moddle.v1.json +21 -0
- 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,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Match the object a Generic connector action addresses, deterministically.
|
|
3
|
+
*
|
|
4
|
+
* `prepare --object Company__C` used to mean EXACTLY `Company__C`: the catalog
|
|
5
|
+
* is matched by equality, the user's casing is not the API's, and the failure
|
|
6
|
+
* was a dead end — "object was not found" with no candidates, against a catalog
|
|
7
|
+
* the command had just fetched.
|
|
8
|
+
*
|
|
9
|
+
* THE LADDER, strictest first, stopping at the first rung with EXACTLY one hit:
|
|
10
|
+
*
|
|
11
|
+
* 1. exact `Name`;
|
|
12
|
+
* 2. case-insensitive `Name`;
|
|
13
|
+
* 3. case-insensitive `DisplayName`.
|
|
14
|
+
*
|
|
15
|
+
* A unique case-insensitive match proceeds LOUDLY (the caller prints the
|
|
16
|
+
* normalization) — casing is presentation, so those rungs cannot pick a thing
|
|
17
|
+
* the author did not name. SUBSTRING MATCHING NEVER PROCEEDS, unique or not:
|
|
18
|
+
* "unique among what exists" is not "unique among what the author meant", and
|
|
19
|
+
* the two diverge exactly when it hurts — `--object Company` on a tenant whose
|
|
20
|
+
* only Create-capable hit is `DandBCompany` would silently prepare an object
|
|
21
|
+
* the author never named, and the same source line would prepare DIFFERENT
|
|
22
|
+
* objects on different tenants. So substring hits are how the REFUSAL finds its
|
|
23
|
+
* candidates, nothing more.
|
|
24
|
+
*
|
|
25
|
+
* Several matches refuse with the candidate table — a wrong field value is one
|
|
26
|
+
* wrong input, a wrong object is a wrong flow, so nothing here ever picks
|
|
27
|
+
* first. Zero matches on a multi-word input refuse with the candidates of the
|
|
28
|
+
* most selective word, because qualifier words are facets, not name material.
|
|
29
|
+
* Candidates are narrowed to objects whose `Operations` include this action's
|
|
30
|
+
* verb before any matching.
|
|
31
|
+
*
|
|
32
|
+
* Ported from `integrations/scripts/_objects.py`; the refusal text is verbatim.
|
|
33
|
+
*/
|
|
34
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
35
|
+
import { dirname, join } from "node:path";
|
|
36
|
+
import { pyJsonStringify } from "../codecs/pyjson.js";
|
|
37
|
+
import { pyStrip } from "../compat/pystr.js";
|
|
38
|
+
import { Refusal, messageOf } from "../errors.js";
|
|
39
|
+
import { isPlainObject, pyOr, pyRepr, pySplitWhitespace, pyStrAny, pyTruthy, pyTypeName } from "./support.js";
|
|
40
|
+
/**
|
|
41
|
+
* Enough rows to choose from without dumping a catalog into the error. The
|
|
42
|
+
* most-selective-token fallback can legitimately produce dozens of rows; past
|
|
43
|
+
* this many the table stops being a choice and starts being the unfiltered
|
|
44
|
+
* list the ladder exists to avoid.
|
|
45
|
+
*/
|
|
46
|
+
export const MAX_CANDIDATES = 15;
|
|
47
|
+
/**
|
|
48
|
+
* No unique object for the requested name, with the candidates a human (or an
|
|
49
|
+
* agent with the prompt in hand) needs to choose (`_objects.ObjectMatchError`).
|
|
50
|
+
*
|
|
51
|
+
* `retriable` marks every outcome that fell PAST the name rungs — zero
|
|
52
|
+
* matches, and the substring refusals (unique or ambiguous). All of them can be
|
|
53
|
+
* caused by a stale `uip is resources list` cache hiding a real object:
|
|
54
|
+
* `--object User` on a catalog that lacks `User` but carries `UserAlias` would
|
|
55
|
+
* otherwise refuse with a recovery message pointing at the WRONG object, and
|
|
56
|
+
* the cache never invalidates itself. The caller refreshes the catalog once and
|
|
57
|
+
* re-runs the whole ladder before believing any of them. Ambiguity WITHIN a
|
|
58
|
+
* name rung (two objects whose names are case-variants) is not retriable — a
|
|
59
|
+
* fresh catalog has the same two names in it.
|
|
60
|
+
*/
|
|
61
|
+
export class ObjectMatchRefusal extends Refusal {
|
|
62
|
+
retriable;
|
|
63
|
+
constructor(message, code, retriable = false) {
|
|
64
|
+
super(message, code);
|
|
65
|
+
this.retriable = retriable;
|
|
66
|
+
this.name = "ObjectMatchRefusal";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** The ledger file exists but cannot be read back — refuse, don't clobber (`_objects.LedgerError`). */
|
|
70
|
+
export class LedgerRefusal extends Refusal {
|
|
71
|
+
constructor(message, code) {
|
|
72
|
+
super(message, code);
|
|
73
|
+
this.name = "LedgerRefusal";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** `str(row.get("Name") or "")`. */
|
|
77
|
+
export function objectName(row) {
|
|
78
|
+
return pyStrAny(pyOr(row.Name, ""));
|
|
79
|
+
}
|
|
80
|
+
/** `str(row.get("DisplayName") or "")`. */
|
|
81
|
+
export function objectDisplayName(row) {
|
|
82
|
+
return pyStrAny(pyOr(row.DisplayName, ""));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* True when the row's `Operations` include `operation`. A catalog that does
|
|
86
|
+
* not report operations cannot be narrowed by one; keep the row rather than
|
|
87
|
+
* hiding it on missing data.
|
|
88
|
+
*/
|
|
89
|
+
export function supportsOperation(row, operation) {
|
|
90
|
+
if (!operation)
|
|
91
|
+
return true;
|
|
92
|
+
const operations = row.Operations;
|
|
93
|
+
if (!Array.isArray(operations))
|
|
94
|
+
return true;
|
|
95
|
+
return operations.includes(operation);
|
|
96
|
+
}
|
|
97
|
+
/** `len(s)` in code points, as Python counts. */
|
|
98
|
+
function width(s) {
|
|
99
|
+
return [...s].length;
|
|
100
|
+
}
|
|
101
|
+
/** `s.ljust(w)`, padding by code-point width. */
|
|
102
|
+
function ljust(s, w) {
|
|
103
|
+
return s + " ".repeat(Math.max(0, w - width(s)));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The table an ambiguous match prints: enough columns to choose with.
|
|
107
|
+
*
|
|
108
|
+
* `Custom: yes` is how a prompt's "custom" qualifier resolves — it is a facet
|
|
109
|
+
* column here precisely so nothing upstream has to parse it out of English.
|
|
110
|
+
*/
|
|
111
|
+
export function formatCandidates(rows) {
|
|
112
|
+
const shown = rows.slice(0, MAX_CANDIDATES);
|
|
113
|
+
const nameW = Math.max(width("Name"), ...shown.map((r) => width(objectName(r))));
|
|
114
|
+
const dispW = Math.max(width("DisplayName"), ...shown.map((r) => width(objectDisplayName(r))));
|
|
115
|
+
const lines = [` ${ljust("Name", nameW)} ${ljust("DisplayName", dispW)} Custom Operations`];
|
|
116
|
+
for (const row of shown) {
|
|
117
|
+
const operations = row.Operations;
|
|
118
|
+
const ops = Array.isArray(operations) ? operations.map(pyStrAny).join(",") : "?";
|
|
119
|
+
lines.push(` ${ljust(objectName(row), nameW)} ${ljust(objectDisplayName(row), dispW)} ` +
|
|
120
|
+
`${ljust(pyStrAny(pyOr(row.Custom, "?")), 6)} ${ops}`);
|
|
121
|
+
}
|
|
122
|
+
if (rows.length > MAX_CANDIDATES) {
|
|
123
|
+
lines.push(` … and ${rows.length - MAX_CANDIDATES} more`);
|
|
124
|
+
}
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
function containing(rows, needle) {
|
|
128
|
+
const lowered = needle.toLowerCase();
|
|
129
|
+
return rows.filter((row) => objectName(row).toLowerCase().includes(lowered) || objectDisplayName(row).toLowerCase().includes(lowered));
|
|
130
|
+
}
|
|
131
|
+
function ambiguous(wanted, hits, operation) {
|
|
132
|
+
return (`"${wanted}" matches ${hits.length} objects` +
|
|
133
|
+
(operation ? ` that support ${operation}` : "") +
|
|
134
|
+
". Nothing here picks one for you: a wrong object is a wrong flow." +
|
|
135
|
+
" Pick by the task's wording (Custom: yes marks a tenant-only object)," +
|
|
136
|
+
" or present these and let the user choose, then re-run with" +
|
|
137
|
+
" --object <Name>:\n" +
|
|
138
|
+
formatCandidates(hits));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The catalog row for `wanted`, or throw {@link ObjectMatchRefusal}.
|
|
142
|
+
*
|
|
143
|
+
* `operation` is the action's verb (`Create`, `Update`, …); rows that do not
|
|
144
|
+
* support it are dropped before any matching.
|
|
145
|
+
*/
|
|
146
|
+
export function matchObject(rows, wanted, operation) {
|
|
147
|
+
wanted = pyStrip(wanted ?? "");
|
|
148
|
+
if (!wanted) {
|
|
149
|
+
throw new ObjectMatchRefusal("--object needs a name to match.", "OBJECT_NAME_REQUIRED");
|
|
150
|
+
}
|
|
151
|
+
const pool = rows.filter((row) => supportsOperation(row, operation));
|
|
152
|
+
const exact = pool.filter((row) => objectName(row) === wanted);
|
|
153
|
+
if (exact.length === 1)
|
|
154
|
+
return { row: exact[0], note: undefined };
|
|
155
|
+
const lowered = wanted.toLowerCase();
|
|
156
|
+
const rungs = [
|
|
157
|
+
["name", objectName],
|
|
158
|
+
["display name", objectDisplayName],
|
|
159
|
+
];
|
|
160
|
+
for (const [rung, key] of rungs) {
|
|
161
|
+
const hits = pool.filter((row) => key(row).toLowerCase() === lowered);
|
|
162
|
+
if (hits.length === 1) {
|
|
163
|
+
return { row: hits[0], note: `matched "${wanted}" -> ${objectName(hits[0])} (by ${rung})` };
|
|
164
|
+
}
|
|
165
|
+
if (hits.length > 1) {
|
|
166
|
+
throw new ObjectMatchRefusal(ambiguous(wanted, hits, operation), "OBJECT_AMBIGUOUS");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Substring hits only ever feed the refusal. A unique one is still a guess
|
|
170
|
+
// — the pool it is unique within is tenant state, not the author's intent —
|
|
171
|
+
// so it is offered back as the one candidate to confirm, never taken.
|
|
172
|
+
// Everything from here down is `retriable`: a stale catalog can hide the
|
|
173
|
+
// real object the author named, and these refusals would then recover
|
|
174
|
+
// toward the wrong one.
|
|
175
|
+
let hits = containing(pool, wanted);
|
|
176
|
+
if (hits.length === 1) {
|
|
177
|
+
throw new ObjectMatchRefusal(`no object is NAMED "${wanted}"` +
|
|
178
|
+
(operation ? ` (of those that support ${operation})` : "") +
|
|
179
|
+
`. One object contains it — if ${pyRepr(objectName(hits[0]))} is the one ` +
|
|
180
|
+
"the task means, re-run with --object " +
|
|
181
|
+
`${objectName(hits[0])}:\n` +
|
|
182
|
+
formatCandidates(hits), "OBJECT_SUBSTRING_ONLY", true);
|
|
183
|
+
}
|
|
184
|
+
if (hits.length > 1) {
|
|
185
|
+
throw new ObjectMatchRefusal(ambiguous(wanted, hits, operation), "OBJECT_SUBSTRING_AMBIGUOUS", true);
|
|
186
|
+
}
|
|
187
|
+
// Nothing contains the whole input. A multi-word input usually mixes name
|
|
188
|
+
// material with qualifiers; the most selective word's candidates are the
|
|
189
|
+
// smallest non-empty table the author can actually choose from.
|
|
190
|
+
const tokens = pySplitWhitespace(wanted);
|
|
191
|
+
if (tokens.length > 1) {
|
|
192
|
+
const nonempty = tokens.map((t) => [t, containing(pool, t)]).filter(([, h]) => h.length > 0);
|
|
193
|
+
if (nonempty.length > 0) {
|
|
194
|
+
// `min(..., key=len)`: the FIRST of the smallest, on ties.
|
|
195
|
+
let [token, best] = nonempty[0];
|
|
196
|
+
for (const pair of nonempty)
|
|
197
|
+
if (pair[1].length < best.length)
|
|
198
|
+
[token, best] = pair;
|
|
199
|
+
hits = best;
|
|
200
|
+
throw new ObjectMatchRefusal(`no object matches "${wanted}" as one name` +
|
|
201
|
+
(operation ? ` (of those that support ${operation})` : "") +
|
|
202
|
+
`. Its word "${token}" matches ${hits.length}` +
|
|
203
|
+
' — qualifiers like "custom" are the Custom column below,' +
|
|
204
|
+
" not part of the name. Pick one and re-run with" +
|
|
205
|
+
" --object <Name>:\n" +
|
|
206
|
+
formatCandidates(hits), "OBJECT_WORD_CANDIDATES", true);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
throw new ObjectMatchRefusal(`no object in the connected catalog matches "${wanted}"` +
|
|
210
|
+
(operation ? ` and supports ${operation}` : "") +
|
|
211
|
+
` (${pool.length} object(s) searched by name, display name and` +
|
|
212
|
+
" substring). Check the value against the words the task used," +
|
|
213
|
+
" or list the catalog to see what is there.", "OBJECT_NOT_FOUND", true);
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// The ledger: `connectors-local/resolutions.json`.
|
|
217
|
+
export const RESOLUTIONS_FILENAME = "resolutions.json";
|
|
218
|
+
/** `<outDir>/resolutions.json`. */
|
|
219
|
+
export function ledgerPath(outDir) {
|
|
220
|
+
return join(outDir, RESOLUTIONS_FILENAME);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The whole ledger at `path` as parsed, `{}` when there is no file, or throw
|
|
224
|
+
* {@link LedgerRefusal}.
|
|
225
|
+
*
|
|
226
|
+
* A file that will not parse is REFUSED rather than silently reset to `{}` —
|
|
227
|
+
* the same rule `bindings.json` follows: a corrupt-but-recoverable audit of
|
|
228
|
+
* every resolution prepare ever made is worth more than this run finishing,
|
|
229
|
+
* and overwriting it is the one way to make the corruption permanent.
|
|
230
|
+
* Parseable-but-not-an-object (a top-level array, a bare string) is corruption
|
|
231
|
+
* wearing valid JSON; sliding it past the refusal would overwrite whatever it
|
|
232
|
+
* used to be.
|
|
233
|
+
*/
|
|
234
|
+
export function readLedger(path) {
|
|
235
|
+
if (!existsSync(path))
|
|
236
|
+
return {};
|
|
237
|
+
let ledger;
|
|
238
|
+
try {
|
|
239
|
+
ledger = JSON.parse(readFileSync(path, "utf8"));
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
throw new LedgerRefusal(`${path} exists but cannot be read back (${messageOf(error)}); refusing to ` +
|
|
243
|
+
"overwrite it. Fix or remove the file and re-run.", "LEDGER_CORRUPT");
|
|
244
|
+
}
|
|
245
|
+
if (!isPlainObject(ledger)) {
|
|
246
|
+
throw new LedgerRefusal(`${path} exists but is not a JSON object ` +
|
|
247
|
+
`(found ${pyTypeName(ledger)}); refusing to overwrite it. ` +
|
|
248
|
+
"Fix or remove the file and re-run.", "LEDGER_NOT_OBJECT");
|
|
249
|
+
}
|
|
250
|
+
return ledger;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* `json.dump(ledger, indent=2, sort_keys=True)` + newline: the bytes the
|
|
254
|
+
* ledger is written in. Keys sort by code point at every level, so the order a
|
|
255
|
+
* key was inserted in never reaches the file — which is also why integer-like
|
|
256
|
+
* keys (which `JSON.parse` hoists) are accepted here: sorting puts them where
|
|
257
|
+
* Python would have.
|
|
258
|
+
*/
|
|
259
|
+
export function formatLedger(ledger) {
|
|
260
|
+
return `${pyJsonStringify(ledger, { indent: 2, sortKeys: true, allowIntegerKeys: true })}\n`;
|
|
261
|
+
}
|
|
262
|
+
/** Write the ledger at `path`, creating its directory. */
|
|
263
|
+
export function writeLedger(path, ledger) {
|
|
264
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
265
|
+
writeFileSync(path, formatLedger(ledger), "utf8");
|
|
266
|
+
}
|
|
267
|
+
/** The `objects[nodeType][requested]` row for a decision. */
|
|
268
|
+
export function ledgerObjectOf(decision) {
|
|
269
|
+
const custom = pyStrip(pyStrAny(pyOr(decision.row.Custom, ""))).toLowerCase();
|
|
270
|
+
return {
|
|
271
|
+
value: objectName(decision.row),
|
|
272
|
+
displayName: objectDisplayName(decision.row),
|
|
273
|
+
custom: custom === "yes" || custom === "true" || custom === "1",
|
|
274
|
+
...(decision.operation ? { operation: decision.operation } : {}),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The ledger with the match recorded under `objects[nodeType][requested]`.
|
|
279
|
+
*
|
|
280
|
+
* Pure: returns a new ledger and leaves `ledger` untouched. The row lands in a
|
|
281
|
+
* sibling `objects` section rather than in `resolutions`: an object identity is
|
|
282
|
+
* not a field lookup, and it should not wear a fake `field` key. Keys NEST
|
|
283
|
+
* rather than encode a tuple: the lookup rows' tuple-string keys exist to match
|
|
284
|
+
* a JavaScript `JSON.stringify` that reads them back, and nothing in JavaScript
|
|
285
|
+
* reads this section.
|
|
286
|
+
*
|
|
287
|
+
* Unlike lookup rows, the current match is ALWAYS written, `--refresh` or not.
|
|
288
|
+
* A lookup row stands because re-resolving costs a network call and can
|
|
289
|
+
* silently change an artifact; this row costs nothing to recompute and MUST
|
|
290
|
+
* agree with the overlay written beside it in this same run. The ledger
|
|
291
|
+
* records the DECISION only; the schema it selected lives in the overlay.
|
|
292
|
+
*/
|
|
293
|
+
export function recordObject(ledger, nodeType, requested, decision) {
|
|
294
|
+
const objectsIn = pyOr(ledger.objects, {});
|
|
295
|
+
const objects = isPlainObject(objectsIn) ? { ...objectsIn } : {};
|
|
296
|
+
const perTypeIn = pyOr(objects[nodeType], {});
|
|
297
|
+
const perType = isPlainObject(perTypeIn) ? { ...perTypeIn } : {};
|
|
298
|
+
perType[requested] = ledgerObjectOf(decision);
|
|
299
|
+
objects[nodeType] = perType;
|
|
300
|
+
const resolutions = pyOr(ledger.resolutions, {});
|
|
301
|
+
return {
|
|
302
|
+
...ledger,
|
|
303
|
+
objects: objects,
|
|
304
|
+
resolutions: (pyTruthy(resolutions) ? resolutions : {}),
|
|
305
|
+
schemaVersion: "1",
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Record the match in `<outDir>/resolutions.json` and return that path — the
|
|
310
|
+
* Python `record_object_resolution`: read (refusing a corrupt file before
|
|
311
|
+
* anything is created), record, write.
|
|
312
|
+
*/
|
|
313
|
+
export function recordObjectResolution(outDir, nodeType, requested, row, operation) {
|
|
314
|
+
const path = ledgerPath(outDir);
|
|
315
|
+
const ledger = recordObject(readLedger(path), nodeType, requested, { row, operation });
|
|
316
|
+
writeLedger(path, ledger);
|
|
317
|
+
return path;
|
|
318
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { type LookupStrategy } from "../lookup-specs.js";
|
|
2
|
+
export declare const INDEX_FILENAME = "index.json";
|
|
3
|
+
export declare const DESCRIPTORS_ROOT = "descriptors";
|
|
4
|
+
/**
|
|
5
|
+
* `_variant_key`: identity for one resolvable activity schema variant. Keying
|
|
6
|
+
* only by nodeType made the last prepared object overwrite every earlier one.
|
|
7
|
+
*/
|
|
8
|
+
export declare function variantKey(entry: Record<string, unknown>): string;
|
|
9
|
+
/** `_descriptor_overlay_path`: `<key>/op@1.json` → `descriptors/<key>/op@1.json`; already-rooted paths pass through. */
|
|
10
|
+
export declare function descriptorOverlayPath(path: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* `_migrate_legacy_descriptor_roots`: move `<dst>/<key>/**` written by an
|
|
13
|
+
* earlier `prepare` under `<dst>/descriptors/<key>/**`, bottom-up, leaving no
|
|
14
|
+
* empty directory behind. Only roots the index still names are touched.
|
|
15
|
+
*/
|
|
16
|
+
export declare function migrateLegacyDescriptorRoots(dst: string, entries: readonly unknown[]): void;
|
|
17
|
+
/**
|
|
18
|
+
* The overlay index rows after merging `srcEntries` over `dstEntries`: every
|
|
19
|
+
* row's `path` under `descriptors/`, a same-variant row refreshed, a new
|
|
20
|
+
* variant added, and the whole list sorted by
|
|
21
|
+
* `(connectorKey, nodeType, version, objectName, path)`. Pure — the inputs
|
|
22
|
+
* are copied, not rewritten.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergeIndexEntries(dstEntries: readonly unknown[], srcEntries: readonly unknown[]): Record<string, unknown>[];
|
|
25
|
+
/**
|
|
26
|
+
* The bytes `connectors-local/index.json` is written in.
|
|
27
|
+
*
|
|
28
|
+
* INTENTIONAL DIVERGENCE from the Python, which wrote `json.dump(idx, indent=2)`
|
|
29
|
+
* with no trailing newline — the one file in the overlay without one, since
|
|
30
|
+
* `write_library` and the ledger both appended it. Every other JSON document
|
|
31
|
+
* the registry writes ends in a newline, and the overlay index now does too,
|
|
32
|
+
* so a diff of a re-prepared overlay stops showing a "\ No newline at end of
|
|
33
|
+
* file" marker on the index alone.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatOverlayIndex(index: unknown): string;
|
|
36
|
+
/** Text or bytes for one file of a freshly written single-op library, keyed by POSIX-relative path. */
|
|
37
|
+
export type LibraryFiles = ReadonlyMap<string, string | Uint8Array>;
|
|
38
|
+
/**
|
|
39
|
+
* `merge_into` over an in-memory library: the shape `planLibrary` produces.
|
|
40
|
+
* `files` must carry `index.json`; every other file is copied under
|
|
41
|
+
* `descriptors/` (a nested `index.json` is skipped, as the Python's walk did).
|
|
42
|
+
*/
|
|
43
|
+
export declare function mergeFilesInto(files: LibraryFiles, dst: string): void;
|
|
44
|
+
/** `merge_into(src, dst)`: merge a freshly written single-op library directory into the overlay. */
|
|
45
|
+
export declare function mergeInto(src: string, dst: string): void;
|
|
46
|
+
/** `_overlay_has_generic_expanded`: does the overlay hold ANY `GenericExpanded` entry, from any prepare? */
|
|
47
|
+
export declare function overlayHasGenericExpanded(out: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* How the read-back functions reach a JSON file by its index-relative path.
|
|
50
|
+
* Throws when the file is missing or not JSON — every caller treats a throw as
|
|
51
|
+
* "unavailable", the way the Python's `except (OSError, ValueError, …)` did.
|
|
52
|
+
*/
|
|
53
|
+
export type JsonReader = (relativePath: string) => unknown;
|
|
54
|
+
/** A reader over a directory on disk. */
|
|
55
|
+
export declare function directoryReader(dir: string): JsonReader;
|
|
56
|
+
/** A reader over an in-memory library (`planLibrary`'s `files`), for the staged half of {@link keepPreparedSchema}. */
|
|
57
|
+
export declare function filesReader(files: LibraryFiles): JsonReader;
|
|
58
|
+
/**
|
|
59
|
+
* `_count_input_fields`: input-field count across the matching overlay
|
|
60
|
+
* variants, or `undefined` when nothing matched or a file could not be read
|
|
61
|
+
* — never `0` for "unavailable", which the summary line would print as a
|
|
62
|
+
* plausible success.
|
|
63
|
+
*/
|
|
64
|
+
export declare function countInputFieldsFrom(read: JsonReader, nodeType: string, objectName?: string | null): number | undefined;
|
|
65
|
+
/** {@link countInputFieldsFrom} over an overlay directory. */
|
|
66
|
+
export declare function countInputFields(out: string, nodeType: string, objectName?: string | null): number | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* `_parent_fields`: `[parent field names, whether the prepared schema is parent-ONLY]`.
|
|
69
|
+
*
|
|
70
|
+
* A reference `path` carrying a `{placeholder}` is the declaration that an
|
|
71
|
+
* operation is parent-driven: `/project/{fields.project.key}/issuetypes` on
|
|
72
|
+
* `fields.issuetype.id` names the field whose value must be resolved first.
|
|
73
|
+
* The parent set is both halves of that relation — the placeholder names AND
|
|
74
|
+
* the fields that declare them — because both must be passed as `-f`.
|
|
75
|
+
* "Parent-only" then means every prepared field is a parent: the expected
|
|
76
|
+
* shape with no `-f`, and a silent failure WITH `-f`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function parentFieldsFrom(read: JsonReader, nodeType: string, objectName?: string | null): [parents: string[], parentOnly: boolean];
|
|
79
|
+
/** {@link parentFieldsFrom} over an overlay directory. */
|
|
80
|
+
export declare function parentFields(out: string, nodeType: string, objectName?: string | null): [parents: string[], parentOnly: boolean];
|
|
81
|
+
/**
|
|
82
|
+
* `_schema_action_parents`: the parent inputs the PREPARED operation's own
|
|
83
|
+
* `operation.schemaAction.parents` names — the one declaration that is a
|
|
84
|
+
* PRECONDITION rather than a symptom: with any of these missing from `-f` the
|
|
85
|
+
* action cannot have run, whatever the describe returned. Declaration order,
|
|
86
|
+
* which is also the resolution order.
|
|
87
|
+
*/
|
|
88
|
+
export declare function schemaActionParentsFrom(read: JsonReader, nodeType: string, objectName?: string | null): string[];
|
|
89
|
+
/** {@link schemaActionParentsFrom} over an overlay directory. */
|
|
90
|
+
export declare function schemaActionParents(out: string, nodeType: string, objectName?: string | null): string[];
|
|
91
|
+
type ParentValues = ReadonlyMap<string, string> | Readonly<Record<string, string>>;
|
|
92
|
+
/**
|
|
93
|
+
* `_keep_prepared_schema`: `[kept, discarded]` when this run must NOT
|
|
94
|
+
* overwrite the overlay, else `undefined`.
|
|
95
|
+
*
|
|
96
|
+
* `--resolve` records a lookup; `-f` supplies the value a schema action reads.
|
|
97
|
+
* A run carrying only the former cannot have run the action, so what it has
|
|
98
|
+
* to write is the UNRESOLVED entry — and the merge replaces by variant key, so
|
|
99
|
+
* it lands on top of a schema an earlier `-f` run resolved (Jira
|
|
100
|
+
* `create-issue`: 21 fields down to 3, #666). Three conditions, all necessary:
|
|
101
|
+
* the EXISTING overlay declares `schemaAction.parents`; none of them arrived
|
|
102
|
+
* as `-f`; and the staged entry has STRICTLY fewer input fields than the one
|
|
103
|
+
* on disk — equal or richer still merges, so a fresher entry is never pinned
|
|
104
|
+
* by a stale overlay, and a first prepare (no overlay) writes normally.
|
|
105
|
+
*
|
|
106
|
+
* `staged` is the freshly built library: a directory, or `planLibrary`'s
|
|
107
|
+
* in-memory files.
|
|
108
|
+
*/
|
|
109
|
+
export declare function keepPreparedSchema(out: string, nodeType: string, staged: string | LibraryFiles, parentFieldValues: ParentValues, objectName?: string | null): [kept: number, discarded: number] | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* One lookup spec as `prepare` needs it: what the generated descriptor carries
|
|
112
|
+
* plus `filterPattern`, which is a retrieval detail (not an authoring one) that
|
|
113
|
+
* only the resolver reads. A type literal rather than an interface so it is
|
|
114
|
+
* assignable to the resolver's loosely typed `ResolveSpec`.
|
|
115
|
+
*/
|
|
116
|
+
export type OverlayLookupSpec = {
|
|
117
|
+
path: string;
|
|
118
|
+
by: string[];
|
|
119
|
+
value: string;
|
|
120
|
+
aliases: Map<string, string>;
|
|
121
|
+
strategy: LookupStrategy;
|
|
122
|
+
objectName?: string;
|
|
123
|
+
dependsOn?: string[];
|
|
124
|
+
filterPattern?: string;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* `_lookup_specs_from_overlay`: the prepared operation's lookup specs, read
|
|
128
|
+
* back from the overlay just written. Generic object variants share a
|
|
129
|
+
* nodeType, so `objectName` narrows to the one just prepared (a variant with
|
|
130
|
+
* no object name still answers, as the Python's `not in ("", None, name)` did);
|
|
131
|
+
* the FIRST matching entry answers.
|
|
132
|
+
*
|
|
133
|
+
* An EVENT's resolvable fields are its subscription parameters, and
|
|
134
|
+
* `v1def.eventParameters.fields` is the ONE authority for those — the same
|
|
135
|
+
* list the SDK, `check` and the descriptor generator read.
|
|
136
|
+
*
|
|
137
|
+
* INTENTIONAL DIVERGENCE from the Python. It selected `eventParameters` as the
|
|
138
|
+
* `fields_source` but then handed that source to `lookup_specs`, which reads
|
|
139
|
+
* `v1def.inputDefinition.fields` FIRST and falls back to the source only when
|
|
140
|
+
* the sidecar's input list is empty AND the source has a required field. On a
|
|
141
|
+
* prepared event whose trigger definition carries message-body inputs, the
|
|
142
|
+
* specs therefore came from the BODY fields — the exact wrong list — and an
|
|
143
|
+
* optional event parameter produced no spec at all. Here an event's specs are
|
|
144
|
+
* built from `eventParameters.fields` directly, which is what the descriptor
|
|
145
|
+
* generator emits `lookups` from (`descriptorInputFields`), so `--resolve`,
|
|
146
|
+
* the vocabulary print and the generated `.by*` helpers agree.
|
|
147
|
+
*/
|
|
148
|
+
export declare function lookupSpecsFromOverlay(out: string, nodeType: string, objectName?: string | null): Map<string, OverlayLookupSpec>;
|
|
149
|
+
export {};
|