@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.
Files changed (161) hide show
  1. package/dist/bpmn/uipath-moddle.v1.json +21 -0
  2. package/dist/cli/commands/registry.js +68 -12
  3. package/dist/registry/archive/entry-policy.d.ts +36 -0
  4. package/dist/registry/archive/entry-policy.js +60 -0
  5. package/dist/registry/archive/extract.d.ts +31 -0
  6. package/dist/registry/archive/extract.js +92 -0
  7. package/dist/registry/archive/zip-reader.d.ts +38 -0
  8. package/dist/registry/archive/zip-reader.js +256 -0
  9. package/dist/registry/codecs/pyjson.d.ts +62 -0
  10. package/dist/registry/codecs/pyjson.js +208 -0
  11. package/dist/registry/compat/compare.d.ts +18 -0
  12. package/dist/registry/compat/compare.js +32 -0
  13. package/dist/registry/compat/paths.d.ts +4 -0
  14. package/dist/registry/compat/paths.js +17 -0
  15. package/dist/registry/compat/pystr.d.ts +50 -0
  16. package/dist/registry/compat/pystr.js +221 -0
  17. package/dist/registry/concurrency.d.ts +10 -0
  18. package/dist/registry/concurrency.js +34 -0
  19. package/dist/registry/curated-overlay.d.ts +36 -4
  20. package/dist/registry/curated-overlay.js +87 -14
  21. package/dist/registry/errors.d.ts +26 -0
  22. package/dist/registry/errors.js +38 -0
  23. package/dist/registry/extractor.d.ts +9 -10
  24. package/dist/registry/extractor.js +5 -15
  25. package/dist/registry/files.d.ts +26 -0
  26. package/dist/registry/files.js +160 -0
  27. package/dist/registry/generators/descriptors.d.ts +81 -0
  28. package/dist/registry/generators/descriptors.js +405 -0
  29. package/dist/registry/generators/fields.d.ts +46 -0
  30. package/dist/registry/generators/fields.js +111 -0
  31. package/dist/registry/generators/markdown/common.d.ts +21 -0
  32. package/dist/registry/generators/markdown/common.js +55 -0
  33. package/dist/registry/generators/markdown/index.d.ts +33 -0
  34. package/dist/registry/generators/markdown/index.js +37 -0
  35. package/dist/registry/generators/markdown/page.d.ts +8 -0
  36. package/dist/registry/generators/markdown/page.js +52 -0
  37. package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
  38. package/dist/registry/generators/markdown/pyvalue.js +106 -0
  39. package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
  40. package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
  41. package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
  42. package/dist/registry/generators/markdown/sections/example.js +295 -0
  43. package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
  44. package/dist/registry/generators/markdown/sections/filtering.js +76 -0
  45. package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
  46. package/dist/registry/generators/markdown/sections/header.js +37 -0
  47. package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
  48. package/dist/registry/generators/markdown/sections/inputs.js +151 -0
  49. package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
  50. package/dist/registry/generators/markdown/sections/lookups.js +150 -0
  51. package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
  52. package/dist/registry/generators/markdown/sections/outputs.js +9 -0
  53. package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
  54. package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
  55. package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
  56. package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
  57. package/dist/registry/generators/materialize.d.ts +30 -0
  58. package/dist/registry/generators/materialize.js +132 -0
  59. package/dist/registry/generators/md-symbols.d.ts +22 -0
  60. package/dist/registry/generators/md-symbols.js +58 -0
  61. package/dist/registry/generators/triggers.d.ts +15 -0
  62. package/dist/registry/generators/triggers.js +85 -0
  63. package/dist/registry/generators/type-registry.d.ts +50 -0
  64. package/dist/registry/generators/type-registry.js +154 -0
  65. package/dist/registry/generators/type-tree.d.ts +65 -0
  66. package/dist/registry/generators/type-tree.js +179 -0
  67. package/dist/registry/library-build/build.d.ts +57 -0
  68. package/dist/registry/library-build/build.js +286 -0
  69. package/dist/registry/library-build/canonical.d.ts +119 -0
  70. package/dist/registry/library-build/canonical.js +512 -0
  71. package/dist/registry/library-build/cli.d.ts +45 -0
  72. package/dist/registry/library-build/cli.js +182 -0
  73. package/dist/registry/library-build/context.d.ts +107 -0
  74. package/dist/registry/library-build/context.js +267 -0
  75. package/dist/registry/library-build/enrich.d.ts +36 -0
  76. package/dist/registry/library-build/enrich.js +158 -0
  77. package/dist/registry/library-build/events.d.ts +108 -0
  78. package/dist/registry/library-build/events.js +476 -0
  79. package/dist/registry/library-build/generic-expand.d.ts +77 -0
  80. package/dist/registry/library-build/generic-expand.js +363 -0
  81. package/dist/registry/library-build/plan.d.ts +71 -0
  82. package/dist/registry/library-build/plan.js +270 -0
  83. package/dist/registry/library-build/py.d.ts +45 -0
  84. package/dist/registry/library-build/py.js +167 -0
  85. package/dist/registry/library-build/schema-action.d.ts +38 -0
  86. package/dist/registry/library-build/schema-action.js +107 -0
  87. package/dist/registry/library-build/write.d.ts +6 -0
  88. package/dist/registry/library-build/write.js +26 -0
  89. package/dist/registry/library-reader.d.ts +20 -0
  90. package/dist/registry/library-reader.js +50 -0
  91. package/dist/registry/lookup-specs.d.ts +55 -0
  92. package/dist/registry/lookup-specs.js +140 -0
  93. package/dist/registry/materialize-cli.js +28 -14
  94. package/dist/registry/materializer.d.ts +25 -3
  95. package/dist/registry/materializer.js +45 -29
  96. package/dist/registry/model/library.d.ts +254 -0
  97. package/dist/registry/model/library.js +23 -0
  98. package/dist/registry/naming.d.ts +54 -0
  99. package/dist/registry/naming.js +142 -0
  100. package/dist/registry/orphans.d.ts +4 -0
  101. package/dist/registry/orphans.js +86 -0
  102. package/dist/registry/prepare/actions.d.ts +26 -0
  103. package/dist/registry/prepare/actions.js +94 -0
  104. package/dist/registry/prepare/connections.d.ts +181 -0
  105. package/dist/registry/prepare/connections.js +399 -0
  106. package/dist/registry/prepare/event.d.ts +96 -0
  107. package/dist/registry/prepare/event.js +268 -0
  108. package/dist/registry/prepare/handoff.d.ts +21 -0
  109. package/dist/registry/prepare/handoff.js +91 -0
  110. package/dist/registry/prepare/ledger.d.ts +104 -0
  111. package/dist/registry/prepare/ledger.js +216 -0
  112. package/dist/registry/prepare/messages.d.ts +190 -0
  113. package/dist/registry/prepare/messages.js +472 -0
  114. package/dist/registry/prepare/objects.d.ts +130 -0
  115. package/dist/registry/prepare/objects.js +318 -0
  116. package/dist/registry/prepare/overlay.d.ts +149 -0
  117. package/dist/registry/prepare/overlay.js +487 -0
  118. package/dist/registry/prepare/parents.d.ts +41 -0
  119. package/dist/registry/prepare/parents.js +122 -0
  120. package/dist/registry/prepare/pipeline.d.ts +91 -0
  121. package/dist/registry/prepare/pipeline.js +376 -0
  122. package/dist/registry/prepare/regenerate.d.ts +9 -0
  123. package/dist/registry/prepare/regenerate.js +50 -0
  124. package/dist/registry/prepare/resolve.d.ts +199 -0
  125. package/dist/registry/prepare/resolve.js +470 -0
  126. package/dist/registry/prepare/retry.d.ts +83 -0
  127. package/dist/registry/prepare/retry.js +109 -0
  128. package/dist/registry/prepare/source-import.d.ts +54 -0
  129. package/dist/registry/prepare/source-import.js +192 -0
  130. package/dist/registry/prepare/support.d.ts +41 -0
  131. package/dist/registry/prepare/support.js +178 -0
  132. package/dist/registry/prepare-cli.d.ts +16 -5
  133. package/dist/registry/prepare-cli.js +60 -53
  134. package/dist/registry/pull-cli.js +8 -7
  135. package/dist/registry/reporter.d.ts +26 -0
  136. package/dist/registry/reporter.js +41 -0
  137. package/dist/registry/uip/client.d.ts +99 -0
  138. package/dist/registry/uip/client.js +226 -0
  139. package/dist/registry/uip/commands.d.ts +40 -0
  140. package/dist/registry/uip/commands.js +80 -0
  141. package/dist/registry/uip/envelope.d.ts +37 -0
  142. package/dist/registry/uip/envelope.js +84 -0
  143. package/dist/registry/uip/response-cache.d.ts +28 -0
  144. package/dist/registry/uip/response-cache.js +57 -0
  145. package/lib/README.md +10 -8
  146. package/package.json +3 -3
  147. package/dist/generators/_connections.py +0 -331
  148. package/dist/generators/_events.py +0 -355
  149. package/dist/generators/_lookups.py +0 -114
  150. package/dist/generators/_naming.py +0 -185
  151. package/dist/generators/_objects.py +0 -299
  152. package/dist/generators/_resolve.py +0 -319
  153. package/dist/generators/convert_library_to_md.py +0 -1441
  154. package/dist/generators/extract_library.py +0 -125
  155. package/dist/generators/generate_connectors_ts.py +0 -936
  156. package/dist/generators/generate_library_json.py +0 -2527
  157. package/dist/generators/generate_triggers_ts.py +0 -110
  158. package/dist/generators/materialize_registry.py +0 -155
  159. package/dist/generators/prepare_connector.py +0 -1860
  160. package/dist/registry/python.d.ts +0 -26
  161. package/dist/registry/python.js +0 -46
@@ -0,0 +1,221 @@
1
+ /**
2
+ * The few string operations that must behave like Python's, because their
3
+ * result reaches a file the Python generators used to write.
4
+ *
5
+ * Everything else in the registry is ordinary TypeScript. This module exists
6
+ * so the places where JavaScript and Python disagree — `strip()`'s whitespace
7
+ * set, `str()` of a bool, `title()` after a digit, slicing by code point — are
8
+ * spelled once and tested against a table Python itself produced
9
+ * (`typescript/tests/fixtures/registry/pycompat.json`).
10
+ */
11
+ import { pyFloatRepr } from "../codecs/pyjson.js";
12
+ /**
13
+ * Characters `str.isspace()` is true for. Not the same set as JavaScript's
14
+ * `\s`: Python includes the C1 separators `\x1c`–`\x1f` and `\x85`, and
15
+ * excludes the BOM ``.
16
+ */
17
+ const PY_SPACE = "\t\n\u000b\u000c\r\u001c\u001d\u001e\u001f \u0085\u00a0\u1680" +
18
+ "\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a" +
19
+ "\u2028\u2029\u202f\u205f\u3000";
20
+ function isPySpace(ch) {
21
+ return PY_SPACE.includes(ch);
22
+ }
23
+ /** `str.strip()` with no argument. */
24
+ export function pyStrip(s) {
25
+ let start = 0;
26
+ let end = s.length;
27
+ while (start < end && isPySpace(s[start]))
28
+ start++;
29
+ while (end > start && isPySpace(s[end - 1]))
30
+ end--;
31
+ return s.slice(start, end);
32
+ }
33
+ /** `str.rstrip()` with no argument. */
34
+ export function pyRstrip(s) {
35
+ let end = s.length;
36
+ while (end > 0 && isPySpace(s[end - 1]))
37
+ end--;
38
+ return s.slice(0, end);
39
+ }
40
+ /** `str.strip(chars)`: strip any of the given characters from both ends. */
41
+ export function pyStripChars(s, chars) {
42
+ let start = 0;
43
+ let end = s.length;
44
+ while (start < end && chars.includes(s[start]))
45
+ start++;
46
+ while (end > start && chars.includes(s[end - 1]))
47
+ end--;
48
+ return s.slice(start, end);
49
+ }
50
+ /**
51
+ * `str(value)` for the JSON-shaped values the generators coerce: `True`,
52
+ * `None`, `1.0`, never `true`, `null`, `1`.
53
+ */
54
+ export function pyStr(value) {
55
+ if (value === null || value === undefined)
56
+ return "None";
57
+ if (typeof value === "boolean")
58
+ return value ? "True" : "False";
59
+ if (typeof value === "number") {
60
+ return Number.isInteger(value) && !Object.is(value, -0) ? String(value) : pyFloatRepr(value);
61
+ }
62
+ if (typeof value === "string")
63
+ return value;
64
+ if (typeof value === "bigint")
65
+ return value.toString();
66
+ // Python would print a repr of a dict or list here; the generators never do
67
+ // that on purpose, so make it loud rather than plausible.
68
+ throw new TypeError(`pyStr: refusing to stringify a ${Array.isArray(value) ? "list" : typeof value}`);
69
+ }
70
+ const UPPER = /\p{Lu}/u;
71
+ /**
72
+ * `str.title()`'s word-boundary test is the Unicode `Cased` property, not "a
73
+ * letter with a case category": a modifier letter such as `ʰ`, a circled `Ⓐ`
74
+ * or a Roman numeral `ⅷ` is cased and continues a word, while the apostrophe
75
+ * `ʼ` in `ʼn`'s titlecase is not and starts one.
76
+ */
77
+ const CASED = /\p{Cased}/u;
78
+ /** `Case_Ignorable`: what the final-sigma rule looks past on either side. */
79
+ const CASE_IGNORABLE = /\p{Case_Ignorable}/u;
80
+ const TITLECASE_LETTER = /\p{Lt}/u;
81
+ /**
82
+ * Code points whose titlecase is neither themselves nor their `toUpperCase()`,
83
+ * from Unicode 15.1 (CPython 3.13). `ß` and the ligatures titlecase to a
84
+ * capital followed by lowercase, the digraphs to their dedicated titlecase
85
+ * code point, and the Greek forms with iota subscript to the base capital plus
86
+ * COMBINING GREEK YPOGEGRAMMENI (U+0345), where uppercasing gives a full
87
+ * capital iota instead. (`ʼn` → `ʼN` and `ǰ` → `J̌` are NOT here: there the
88
+ * titlecase equals the uppercase, which `toUpperCase()` already spells.)
89
+ */
90
+ const TITLECASE_SPECIAL = new Map([
91
+ ["ß", "Ss"],
92
+ ["DŽ", "Dž"], ["dž", "Dž"], ["LJ", "Lj"], ["lj", "Lj"], ["NJ", "Nj"], ["nj", "Nj"], ["DZ", "Dz"], ["dz", "Dz"],
93
+ ["և", "Եւ"],
94
+ ["ᾲ", "Ὰͅ"], ["ᾳ", "ᾼ"], ["ᾴ", "Άͅ"], ["ᾷ", "ᾼ͂"],
95
+ ["ῂ", "Ὴͅ"], ["ῃ", "ῌ"], ["ῄ", "Ήͅ"], ["ῇ", "ῌ͂"],
96
+ ["ῲ", "Ὼͅ"], ["ῳ", "ῼ"], ["ῴ", "Ώͅ"], ["ῷ", "ῼ͂"],
97
+ ["ff", "Ff"], ["fi", "Fi"], ["fl", "Fl"], ["ffi", "Ffi"], ["ffl", "Ffl"], ["ſt", "St"], ["st", "St"],
98
+ ["ﬓ", "Մն"], ["ﬔ", "Մե"], ["ﬕ", "Մի"], ["ﬖ", "Վն"], ["ﬗ", "Մխ"],
99
+ ]);
100
+ /**
101
+ * `_PyUnicode_ToTitleFull`: Python's titlecase of one code point, which
102
+ * JavaScript lacks. UnicodeData gives most code points no titlecase of their
103
+ * own, in which case it is their uppercase — and `toUpperCase()` already
104
+ * applies SpecialCasing there — so only the exceptions need spelling: a
105
+ * titlecase letter is its own titlecase; Georgian Mkhedruli titlecases to
106
+ * itself although it uppercases to Mtavruli; the Greek small letters with
107
+ * iota subscript (`ᾀ`…`ᾧ`) titlecase to the titlecase letter eight code points
108
+ * up; and the table above.
109
+ */
110
+ function titlecaseChar(ch) {
111
+ const special = TITLECASE_SPECIAL.get(ch);
112
+ if (special !== undefined)
113
+ return special;
114
+ if (TITLECASE_LETTER.test(ch))
115
+ return ch;
116
+ const cp = ch.codePointAt(0);
117
+ if (cp >= 0x10d0 && cp <= 0x10ff)
118
+ return ch;
119
+ if ((cp >= 0x1f80 && cp <= 0x1f87) || (cp >= 0x1f90 && cp <= 0x1f97) || (cp >= 0x1fa0 && cp <= 0x1fa7)) {
120
+ return String.fromCodePoint(cp + 8);
121
+ }
122
+ return ch.toUpperCase();
123
+ }
124
+ /**
125
+ * Whether the `Σ` at `chars[i]` is in Final_Sigma context: preceded, past any
126
+ * case-ignorables, by a cased character, and not followed, past any
127
+ * case-ignorables, by one. CPython applies this inside `title()` and `lower()`;
128
+ * `toLowerCase()` applies it only when given the whole string, which lowering
129
+ * one character at a time forfeits.
130
+ */
131
+ function isFinalSigma(chars, i) {
132
+ let j = i - 1;
133
+ while (j >= 0 && CASE_IGNORABLE.test(chars[j]))
134
+ j--;
135
+ if (j < 0 || !CASED.test(chars[j]))
136
+ return false;
137
+ j = i + 1;
138
+ while (j < chars.length && CASE_IGNORABLE.test(chars[j]))
139
+ j++;
140
+ return j === chars.length || !CASED.test(chars[j]);
141
+ }
142
+ /**
143
+ * `str.title()`: each character after a non-cased one (or at the start) is
144
+ * titlecased, every other one lowercased — so digits and apostrophes start a
145
+ * new word (`"a1b"` → `"A1B"`, `"o'brien"` → `"O'Brien"`), unlike a
146
+ * split-on-space capitaliser, and a capital sigma ending a word becomes `ς`
147
+ * (`"ΟΔΟΣ"` → `"Οδος"`).
148
+ *
149
+ * Checked against CPython 3.13 (Unicode 15.1) over all 1,112,064 code points,
150
+ * each as `chr(cp)` — which exercises the titlecase branch — and again as
151
+ * `"A" + chr(cp)`, `"AΣ" + chr(cp)` and `chr(cp) + "Σ"`, which exercise the
152
+ * lowercase branch and the final-sigma rule in both directions. Every
153
+ * disagreement was a code point Node's ICU 16 cases and Unicode 15.1 does not:
154
+ * U+019B, U+0264, U+1C89, U+1C8A, U+A7CB–A7CD, U+A7DA–A7DC and the Garay block
155
+ * (U+10D50–10D65, U+10D70–10D85) — 54 code points in total. So this matches
156
+ * `str.title()` exactly on the repertoire the two share, and an engine newer
157
+ * than the Python it is compared with will case a handful of letters Python
158
+ * leaves alone.
159
+ *
160
+ * None of the 54 appears in the committed library's slugs, labels or field
161
+ * names, which is where it matters — but that is an observation about how the
162
+ * registry names things today, not a rule anything enforces, so a future
163
+ * connector could in principle carry one. The curated cases that guard the
164
+ * behaviour are in `registry-compat.test.ts`, via the fixture Python wrote.
165
+ */
166
+ export function pyTitle(s) {
167
+ const chars = [...s];
168
+ let out = "";
169
+ let previousCased = false;
170
+ for (let i = 0; i < chars.length; i++) {
171
+ const ch = chars[i];
172
+ if (!previousCased)
173
+ out += titlecaseChar(ch);
174
+ else if (ch === "Σ")
175
+ out += isFinalSigma(chars, i) ? "ς" : "σ";
176
+ else
177
+ out += ch.toLowerCase();
178
+ previousCased = CASED.test(ch);
179
+ }
180
+ return out;
181
+ }
182
+ /** `s[:1].isupper()`: the first code point is an uppercase letter. */
183
+ export function startsWithUpper(s) {
184
+ const first = s.codePointAt(0);
185
+ return first !== undefined && UPPER.test(String.fromCodePoint(first));
186
+ }
187
+ /** `s[:1].upper() + s[1:]`, slicing by code point. */
188
+ export function upperFirst(s) {
189
+ const first = s.codePointAt(0);
190
+ if (first === undefined)
191
+ return s;
192
+ const head = String.fromCodePoint(first);
193
+ return head.toUpperCase() + s.slice(head.length);
194
+ }
195
+ /** `s[:1].lower() + s[1:]`, slicing by code point. */
196
+ export function lowerFirst(s) {
197
+ const first = s.codePointAt(0);
198
+ if (first === undefined)
199
+ return s;
200
+ const head = String.fromCodePoint(first);
201
+ return head.toLowerCase() + s.slice(head.length);
202
+ }
203
+ /** `s.rsplit(sep, 1)[-1]`. */
204
+ export function afterLast(s, sep) {
205
+ const at = s.lastIndexOf(sep);
206
+ return at < 0 ? s : s.slice(at + sep.length);
207
+ }
208
+ /** `s.rsplit(sep, 1)[0]` when `sep` occurs, else `s`. */
209
+ export function beforeLast(s, sep) {
210
+ const at = s.lastIndexOf(sep);
211
+ return at < 0 ? s : s.slice(0, at);
212
+ }
213
+ /** `s.split(sep, 1)` as a pair; the second element is `undefined` when absent. */
214
+ export function splitOnce(s, sep) {
215
+ const at = s.indexOf(sep);
216
+ return at < 0 ? [s, undefined] : [s.slice(0, at), s.slice(at + sep.length)];
217
+ }
218
+ /** `str.isdigit()` for ASCII digits, which is all the generators ever test. */
219
+ export function isAsciiDigits(s) {
220
+ return /^[0-9]+$/.test(s);
221
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Bounded parallelism with results in input order.
3
+ *
4
+ * The library builder used seven thread pools; four of them appended results as
5
+ * they completed, so the order of expanded entries — and, on a tie, which of two
6
+ * colliding entries won — depended on scheduling. This helper runs at most
7
+ * `limit` tasks at once and always returns `results[i]` for `items[i]`, so the
8
+ * output of a build is a function of its inputs alone.
9
+ */
10
+ export declare function mapConcurrent<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Bounded parallelism with results in input order.
3
+ *
4
+ * The library builder used seven thread pools; four of them appended results as
5
+ * they completed, so the order of expanded entries — and, on a tie, which of two
6
+ * colliding entries won — depended on scheduling. This helper runs at most
7
+ * `limit` tasks at once and always returns `results[i]` for `items[i]`, so the
8
+ * output of a build is a function of its inputs alone.
9
+ */
10
+ export async function mapConcurrent(items, limit, fn) {
11
+ // `ThreadPoolExecutor(max_workers=0)` raised `ValueError: max_workers must
12
+ // be greater than 0`, and so does this, even for no work: a limit that is
13
+ // not a positive number is a caller's mistake, and clamping it to one
14
+ // worker would turn a pipeline typo into a build that runs serially for
15
+ // hours instead of failing at once.
16
+ if (!(Number.isFinite(limit) && limit >= 1)) {
17
+ throw new RangeError(`mapConcurrent: limit must be a positive number, got ${limit}`);
18
+ }
19
+ const results = new Array(items.length);
20
+ if (items.length === 0)
21
+ return results;
22
+ const workers = Math.min(Math.floor(limit), items.length);
23
+ let next = 0;
24
+ const run = async () => {
25
+ for (;;) {
26
+ const index = next++;
27
+ if (index >= items.length)
28
+ return;
29
+ results[index] = await fn(items[index], index);
30
+ }
31
+ };
32
+ await Promise.all(Array.from({ length: workers }, run));
33
+ return results;
34
+ }
@@ -1,7 +1,20 @@
1
+ import { type Reporter } from "./reporter.js";
1
2
  /** The corrections and definitions that the raw registry dump does not carry. */
2
3
  export declare function bundledOverlayPath(): string;
3
4
  /** Stable identity of the curated data, so a changed overlay gets a new cache entry. */
4
5
  export declare function curatedOverlayIdentity(overlayPath?: string): Promise<string>;
6
+ export interface CuratedOverlayResult {
7
+ /** Overlay files copied into the library. */
8
+ files: number;
9
+ /** Index rows added for identities the library did not already carry. */
10
+ entries: number;
11
+ /**
12
+ * Overlay entry files NOT copied, because the library resolves their
13
+ * identity to a different path. POSIX-relative to the overlay, sorted. Each
14
+ * one is also reported through the reporter — see {@link applyCuratedOverlay}.
15
+ */
16
+ skipped: string[];
17
+ }
5
18
  /**
6
19
  * Apply the SDK's curated corrections after extracting the raw registry dump.
7
20
  *
@@ -9,8 +22,27 @@ export declare function curatedOverlayIdentity(overlayPath?: string): Promise<st
9
22
  * wins collisions. This is the same contract as the former unpack script: a
10
23
  * correction changes the referenced file, and a definition absent from the
11
24
  * dump (notably connector triggers) is made resolvable by its added index row.
25
+ *
26
+ * WHEN THE LIBRARY ALREADY RESOLVES THE IDENTITY ELSEWHERE, the file is not
27
+ * copied at all and the skip is reported. That case is not hypothetical: four
28
+ * of the bundled `event-*@1.0.0.json` files carry a (nodeType, version) the
29
+ * raw dump lacks TODAY but a refreshed catalog serves at its own path — the
30
+ * per-object event variants land under `<connector>/events/<object>/`, keeping
31
+ * the template's nodeType. Since the union keeps the library's row, copying
32
+ * anyway left a file no index row pointed at, which `--check-orphans` (the
33
+ * image build passes it) rejects. The three ways out are not equal:
34
+ *
35
+ * - copying and teaching the orphan check to tolerate the file would keep
36
+ * the build green and hide the real loss, which is that the hand-verified
37
+ * CORRECTION never takes effect once the index points somewhere else;
38
+ * - redirecting the correction onto the indexed path cannot be done safely
39
+ * here, because ONE event identity maps to MANY per-object paths in a
40
+ * refreshed library, and stamping the flat curated definition over each
41
+ * per-object variant would destroy the per-object schema the library just
42
+ * served — a worse regression than the one being fixed;
43
+ * - so the copy is skipped and said out loud. The library keeps the
44
+ * definition it resolves, the tree gains no unreachable file, and the
45
+ * operator is told which overlay row has been overtaken and needs
46
+ * re-verifying. The image build is then green before AND after a refresh.
12
47
  */
13
- export declare function applyCuratedOverlay(libraryPath: string, overlayPath?: string): Promise<{
14
- files: number;
15
- entries: number;
16
- }>;
48
+ export declare function applyCuratedOverlay(libraryPath: string, overlayPath?: string, reporter?: Reporter): Promise<CuratedOverlayResult>;
@@ -1,7 +1,9 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { cp, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
3
- import { dirname, join, relative } from "node:path";
3
+ import { dirname, join, relative, sep } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
+ import { sortedStrings } from "./compat/compare.js";
6
+ import { consoleReporter } from "./reporter.js";
5
7
  /** The corrections and definitions that the raw registry dump does not carry. */
6
8
  export function bundledOverlayPath() {
7
9
  return fileURLToPath(new URL("../../lib/library-json.samples", import.meta.url));
@@ -30,6 +32,10 @@ export async function curatedOverlayIdentity(overlayPath = bundledOverlayPath())
30
32
  function entryIdentity(entry) {
31
33
  return `${String(entry.nodeType)}\0${String(entry.version)}`;
32
34
  }
35
+ /** The sidecar that travels with an entry file, and has no index row of its own. */
36
+ function sidecarOf(rel) {
37
+ return `${rel.slice(0, -".json".length)}.v1def.json`;
38
+ }
33
39
  /**
34
40
  * Apply the SDK's curated corrections after extracting the raw registry dump.
35
41
  *
@@ -37,26 +43,93 @@ function entryIdentity(entry) {
37
43
  * wins collisions. This is the same contract as the former unpack script: a
38
44
  * correction changes the referenced file, and a definition absent from the
39
45
  * dump (notably connector triggers) is made resolvable by its added index row.
46
+ *
47
+ * WHEN THE LIBRARY ALREADY RESOLVES THE IDENTITY ELSEWHERE, the file is not
48
+ * copied at all and the skip is reported. That case is not hypothetical: four
49
+ * of the bundled `event-*@1.0.0.json` files carry a (nodeType, version) the
50
+ * raw dump lacks TODAY but a refreshed catalog serves at its own path — the
51
+ * per-object event variants land under `<connector>/events/<object>/`, keeping
52
+ * the template's nodeType. Since the union keeps the library's row, copying
53
+ * anyway left a file no index row pointed at, which `--check-orphans` (the
54
+ * image build passes it) rejects. The three ways out are not equal:
55
+ *
56
+ * - copying and teaching the orphan check to tolerate the file would keep
57
+ * the build green and hide the real loss, which is that the hand-verified
58
+ * CORRECTION never takes effect once the index points somewhere else;
59
+ * - redirecting the correction onto the indexed path cannot be done safely
60
+ * here, because ONE event identity maps to MANY per-object paths in a
61
+ * refreshed library, and stamping the flat curated definition over each
62
+ * per-object variant would destroy the per-object schema the library just
63
+ * served — a worse regression than the one being fixed;
64
+ * - so the copy is skipped and said out loud. The library keeps the
65
+ * definition it resolves, the tree gains no unreachable file, and the
66
+ * operator is told which overlay row has been overtaken and needs
67
+ * re-verifying. The image build is then green before AND after a refresh.
40
68
  */
41
- export async function applyCuratedOverlay(libraryPath, overlayPath = bundledOverlayPath()) {
42
- const overlayFiles = await jsonFiles(overlayPath);
43
- for (const source of overlayFiles) {
44
- const rel = relative(overlayPath, source);
45
- if (rel === "index.json")
46
- continue;
47
- const destination = join(libraryPath, rel);
48
- await mkdir(dirname(destination), { recursive: true });
49
- await cp(source, destination);
50
- }
69
+ export async function applyCuratedOverlay(libraryPath, overlayPath = bundledOverlayPath(),
70
+ // The public materializer API does not forward one, and a warning nobody
71
+ // sees would defeat the point, so the default is the real terminal.
72
+ reporter = consoleReporter) {
51
73
  const indexPath = join(libraryPath, "index.json");
52
74
  const destination = JSON.parse(await readFile(indexPath, "utf8"));
53
75
  const overlay = JSON.parse(await readFile(join(overlayPath, "index.json"), "utf8"));
54
76
  const entries = destination.entries ?? [];
55
- const seen = new Set(entries.map(entryIdentity));
56
- const additions = (overlay.entries ?? []).filter((entry) => !seen.has(entryIdentity(entry)));
77
+ // Every path the library resolves each identity to. A `GenericExpanded` or
78
+ // per-object entry shares its nodeType with its siblings, so an identity
79
+ // legitimately has more than one.
80
+ const resolvedPaths = new Map();
81
+ for (const entry of entries) {
82
+ if (typeof entry.path !== "string")
83
+ continue;
84
+ const identity = entryIdentity(entry);
85
+ const known = resolvedPaths.get(identity);
86
+ if (known === undefined)
87
+ resolvedPaths.set(identity, [entry.path]);
88
+ else
89
+ known.push(entry.path);
90
+ }
91
+ const additions = [];
92
+ const skipped = new Set();
93
+ for (const row of overlay.entries ?? []) {
94
+ const identity = entryIdentity(row);
95
+ if (!resolvedPaths.has(identity)) {
96
+ additions.push(row);
97
+ continue;
98
+ }
99
+ const paths = resolvedPaths.get(identity);
100
+ if (typeof row.path !== "string" || paths.includes(row.path))
101
+ continue;
102
+ skipped.add(row.path);
103
+ reporter.warn([
104
+ ` WARN: curated overlay SKIPPED ${row.path}`,
105
+ ` ${String(row.nodeType)}@${String(row.version)} is already in the library's index, at`,
106
+ ...paths.map((path) => ` ${path}`),
107
+ " so the copied file would resolve for nobody. The library's own definition stands and",
108
+ " this correction does NOT take effect. Re-verify it against the current library and drop",
109
+ " its row from typescript/sdk/lib/library-json.samples/index.json once the library carries it.",
110
+ ].join("\n"));
111
+ }
112
+ // A skipped entry's sidecar goes with it: it is only ever read beside the
113
+ // entry file, so leaving it behind would state a correction nothing reads.
114
+ const notCopied = new Set(["index.json"]);
115
+ for (const rel of skipped) {
116
+ notCopied.add(rel);
117
+ notCopied.add(sidecarOf(rel));
118
+ }
119
+ const overlayFiles = await jsonFiles(overlayPath);
120
+ let copied = 0;
121
+ for (const source of overlayFiles) {
122
+ const rel = relative(overlayPath, source).split(sep).join("/");
123
+ if (notCopied.has(rel))
124
+ continue;
125
+ const target = join(libraryPath, rel);
126
+ await mkdir(dirname(target), { recursive: true });
127
+ await cp(source, target);
128
+ copied += 1;
129
+ }
57
130
  if (additions.length > 0) {
58
131
  destination.entries = [...entries, ...additions];
59
132
  await writeFile(indexPath, `${JSON.stringify(destination, null, 2)}\n`);
60
133
  }
61
- return { files: overlayFiles.length - 1, entries: additions.length };
134
+ return { files: copied, entries: additions.length, skipped: sortedStrings([...skipped]) };
62
135
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The two ways a registry verb ends badly, and what each means for the exit code.
3
+ *
4
+ * A {@link Refusal} is the tool declining to proceed for a reason the author
5
+ * can act on — no connection matched, the object is ambiguous, a parent field
6
+ * was rejected. The message is the product: agents read it and follow it, so it
7
+ * is written for a reader and kept verbatim from the Python it replaces. It
8
+ * exits 1. A {@link UsageError} is a malformed invocation and exits 2, as
9
+ * argparse did. Anything else is a bug and propagates as itself.
10
+ */
11
+ export declare class Refusal extends Error {
12
+ /** A stable, grep-able name for the refusal, e.g. `CONNECTION_AMBIGUOUS`. */
13
+ readonly code: string;
14
+ readonly exitCode: 1;
15
+ constructor(message: string,
16
+ /** A stable, grep-able name for the refusal, e.g. `CONNECTION_AMBIGUOUS`. */
17
+ code?: string);
18
+ }
19
+ export declare class UsageError extends Error {
20
+ readonly exitCode: 2;
21
+ constructor(message: string);
22
+ }
23
+ /** The exit code a CLI verb should return for a thrown value. */
24
+ export declare function exitCodeOf(error: unknown): number;
25
+ /** The text a CLI verb should print to stderr for a thrown value. */
26
+ export declare function messageOf(error: unknown): string;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The two ways a registry verb ends badly, and what each means for the exit code.
3
+ *
4
+ * A {@link Refusal} is the tool declining to proceed for a reason the author
5
+ * can act on — no connection matched, the object is ambiguous, a parent field
6
+ * was rejected. The message is the product: agents read it and follow it, so it
7
+ * is written for a reader and kept verbatim from the Python it replaces. It
8
+ * exits 1. A {@link UsageError} is a malformed invocation and exits 2, as
9
+ * argparse did. Anything else is a bug and propagates as itself.
10
+ */
11
+ export class Refusal extends Error {
12
+ code;
13
+ exitCode = 1;
14
+ constructor(message,
15
+ /** A stable, grep-able name for the refusal, e.g. `CONNECTION_AMBIGUOUS`. */
16
+ code = "REFUSED") {
17
+ super(message);
18
+ this.code = code;
19
+ this.name = "Refusal";
20
+ }
21
+ }
22
+ export class UsageError extends Error {
23
+ exitCode = 2;
24
+ constructor(message) {
25
+ super(message);
26
+ this.name = "UsageError";
27
+ }
28
+ }
29
+ /** The exit code a CLI verb should return for a thrown value. */
30
+ export function exitCodeOf(error) {
31
+ if (error instanceof Refusal || error instanceof UsageError)
32
+ return error.exitCode;
33
+ return 1;
34
+ }
35
+ /** The text a CLI verb should print to stderr for a thrown value. */
36
+ export function messageOf(error) {
37
+ return error instanceof Error ? error.message : String(error);
38
+ }
@@ -1,22 +1,21 @@
1
1
  /**
2
2
  * Unpack a downloaded connector-library archive.
3
3
  *
4
- * Python does the unpacking: `zipfile` is in its standard library, and this is
5
- * the only step of the pull that needs an archive reader. Adding the SDK's first
6
- * zip dependency to avoid one subprocess would be the worse trade, especially
7
- * for code whose whole job is refusing hostile paths — `extract_library.py`
8
- * carries that guard and its tests.
9
- *
10
- * Shaped like `materializer.ts` on purpose: same generator-path
11
- * resolution, same `--python` override, same failure translation.
4
+ * In-process since the TypeScript port: `archive/` reads the zip with
5
+ * `node:zlib` and refuses anything hostile (path traversal, symlinks, features
6
+ * the self-produced archive never uses). The Python `extract_library.py` this
7
+ * replaced carried the same rules; its tests are ported beside the reader.
12
8
  */
13
9
  export interface ExtractLibraryOptions {
14
10
  archivePath: string;
15
11
  destinationPath: string;
16
12
  /** Only entries under this zip prefix are taken. */
17
13
  root?: string;
14
+ /** @deprecated No interpreter is involved any more; accepted and ignored. */
18
15
  pythonExecutable?: string;
19
- /** Override used by tests and source checkouts. */
16
+ /** @deprecated The extractor is part of the package; accepted and ignored. */
20
17
  extractorPath?: string;
21
18
  }
22
- export declare function extractLibrary(options: ExtractLibraryOptions): Promise<void>;
19
+ export declare function extractLibrary(options: ExtractLibraryOptions): Promise<{
20
+ written: number;
21
+ }>;
@@ -1,18 +1,8 @@
1
- import { packagedGenerator, runGenerator } from "./python.js";
1
+ import { extractArchive } from "./archive/extract.js";
2
2
  export async function extractLibrary(options) {
3
- const args = [
4
- "--archive",
5
- options.archivePath,
6
- "--into",
7
- options.destinationPath,
8
- ];
9
- if (options.root) {
10
- args.push("--root", options.root);
11
- }
12
- await runGenerator({
13
- script: options.extractorPath ?? packagedGenerator("extract_library.py"),
14
- args,
15
- pythonExecutable: options.pythonExecutable,
16
- what: "Unpacking the connector library",
3
+ return extractArchive({
4
+ archivePath: options.archivePath,
5
+ destination: options.destinationPath,
6
+ root: options.root,
17
7
  });
18
8
  }
@@ -0,0 +1,26 @@
1
+ export declare function ensureDir(path: string): void;
2
+ export declare function writeText(path: string, text: string): void;
3
+ export declare function writeBytes(path: string, bytes: Uint8Array): void;
4
+ export declare function readText(path: string): string;
5
+ export declare function readJson(path: string): unknown;
6
+ export declare function isFile(path: string): boolean;
7
+ export declare function isDirectory(path: string): boolean;
8
+ /** Every file under `root`, as paths relative to it with `/` separators, sorted. */
9
+ export declare function listFiles(root: string, filter?: (relative: string) => boolean): string[];
10
+ /**
11
+ * Build a directory beside its final name, then swap it into place.
12
+ *
13
+ * `fill` writes into `<target>.tmp-<pid>`. When it returns, any existing
14
+ * `target` is renamed to `<target>.old-<pid>`, the staging directory takes the
15
+ * final name, and the old tree is removed. A failure inside `fill` leaves the
16
+ * previous `target` untouched and cleans up the staging directory; a failure
17
+ * of the final rename puts the parked tree back, so the previous output
18
+ * survives as it did when Python's `Path.rename` raised.
19
+ *
20
+ * On overlayfs — every Docker RUN after the first — a directory written by an
21
+ * earlier layer cannot be renamed, so the parking step falls back to deleting
22
+ * it. See the comment on that branch for what the fallback gives up.
23
+ */
24
+ export declare function stageAndSwap(target: string, fill: (staging: string) => void): void;
25
+ /** Remove a directory tree if it exists, then create it empty. */
26
+ export declare function resetDir(path: string): void;