@uipath/maestro-builder-sdk 6.4.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/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
|
@@ -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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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:
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
/**
|
|
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<
|
|
19
|
+
export declare function extractLibrary(options: ExtractLibraryOptions): Promise<{
|
|
20
|
+
written: number;
|
|
21
|
+
}>;
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extractArchive } from "./archive/extract.js";
|
|
2
2
|
export async function extractLibrary(options) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
options.
|
|
6
|
-
|
|
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;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The filesystem moves the registry makes, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Generators return strings and maps; these helpers put them on disk. The
|
|
5
|
+
* stage-and-swap dance exists because two `registry pull`s can race on the
|
|
6
|
+
* shared cache when a coding agent issues tool calls in parallel, and a
|
|
7
|
+
* converter that wipes and rewrites its output directory in place left one of
|
|
8
|
+
* them mid-write.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdirSync, renameSync, rmSync, writeFileSync, existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { messageOf } from "./errors.js";
|
|
13
|
+
export function ensureDir(path) {
|
|
14
|
+
mkdirSync(path, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
export function writeText(path, text) {
|
|
17
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
18
|
+
writeFileSync(path, text, "utf8");
|
|
19
|
+
}
|
|
20
|
+
export function writeBytes(path, bytes) {
|
|
21
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
22
|
+
writeFileSync(path, bytes);
|
|
23
|
+
}
|
|
24
|
+
export function readText(path) {
|
|
25
|
+
return readFileSync(path, "utf8");
|
|
26
|
+
}
|
|
27
|
+
export function readJson(path) {
|
|
28
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
29
|
+
}
|
|
30
|
+
export function isFile(path) {
|
|
31
|
+
try {
|
|
32
|
+
return statSync(path).isFile();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function isDirectory(path) {
|
|
39
|
+
try {
|
|
40
|
+
return statSync(path).isDirectory();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Every file under `root`, as paths relative to it with `/` separators, sorted. */
|
|
47
|
+
export function listFiles(root, filter) {
|
|
48
|
+
const out = [];
|
|
49
|
+
const walk = (dir, prefix) => {
|
|
50
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
51
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
52
|
+
if (entry.isDirectory())
|
|
53
|
+
walk(join(dir, entry.name), rel);
|
|
54
|
+
else if (entry.isFile() && (!filter || filter(rel)))
|
|
55
|
+
out.push(rel);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
if (existsSync(root))
|
|
59
|
+
walk(root, "");
|
|
60
|
+
return out.sort();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Build a directory beside its final name, then swap it into place.
|
|
64
|
+
*
|
|
65
|
+
* `fill` writes into `<target>.tmp-<pid>`. When it returns, any existing
|
|
66
|
+
* `target` is renamed to `<target>.old-<pid>`, the staging directory takes the
|
|
67
|
+
* final name, and the old tree is removed. A failure inside `fill` leaves the
|
|
68
|
+
* previous `target` untouched and cleans up the staging directory; a failure
|
|
69
|
+
* of the final rename puts the parked tree back, so the previous output
|
|
70
|
+
* survives as it did when Python's `Path.rename` raised.
|
|
71
|
+
*
|
|
72
|
+
* On overlayfs — every Docker RUN after the first — a directory written by an
|
|
73
|
+
* earlier layer cannot be renamed, so the parking step falls back to deleting
|
|
74
|
+
* it. See the comment on that branch for what the fallback gives up.
|
|
75
|
+
*/
|
|
76
|
+
export function stageAndSwap(target, fill) {
|
|
77
|
+
const staging = `${target}.tmp-${process.pid}`;
|
|
78
|
+
const previous = `${target}.old-${process.pid}`;
|
|
79
|
+
rmSync(staging, { recursive: true, force: true });
|
|
80
|
+
mkdirSync(staging, { recursive: true });
|
|
81
|
+
try {
|
|
82
|
+
fill(staging);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
rmSync(staging, { recursive: true, force: true });
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
89
|
+
// Which of the three states the target is in when the final rename runs, so
|
|
90
|
+
// a failure there can say honestly what is and is not still on disk.
|
|
91
|
+
let parked = "nothing-was-there";
|
|
92
|
+
if (existsSync(target)) {
|
|
93
|
+
try {
|
|
94
|
+
renameSync(target, previous);
|
|
95
|
+
parked = "parked";
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// EXDEV on a rename INSIDE one directory looks impossible, and on
|
|
99
|
+
// overlayfs it is routine: a directory that came from a lower layer
|
|
100
|
+
// cannot be renamed at all, because the kernel would have to copy the
|
|
101
|
+
// whole tree up first, so it answers "cross-device link not permitted".
|
|
102
|
+
// The e2e image hits this exactly — `registry pull` writes library-md in
|
|
103
|
+
// one RUN and `registry materialize` swaps it in the next.
|
|
104
|
+
//
|
|
105
|
+
// So: delete instead of parking. Be clear about what that costs. From
|
|
106
|
+
// here until the rename below succeeds, `target` does not exist, and if
|
|
107
|
+
// that rename fails there is no parked copy to put back — the PREVIOUS
|
|
108
|
+
// tree is gone for good. The NEW tree is not: it is complete in
|
|
109
|
+
// `staging`, which is left in place on that failure and named in the
|
|
110
|
+
// error, so an operator can finish the move by hand. The alternative
|
|
111
|
+
// that keeps the old tree, copying it aside first, costs a second pass
|
|
112
|
+
// over 7,000 files inside a Docker build where nothing races the swap;
|
|
113
|
+
// the window is accepted there and only there. The atomic path stays the
|
|
114
|
+
// default everywhere a rename works.
|
|
115
|
+
if (error?.code !== "EXDEV")
|
|
116
|
+
throw error;
|
|
117
|
+
rmSync(target, { recursive: true, force: true });
|
|
118
|
+
parked = "deleted";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
renameSync(staging, target);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
// The new tree is deliberately NOT cleaned up here: it is the only copy
|
|
126
|
+
// when the overlayfs branch ran, and finishing the move by hand is the
|
|
127
|
+
// recovery. It is not ALWAYS there, though — the rename can fail because
|
|
128
|
+
// the staging tree itself went missing, which is what a second run sharing
|
|
129
|
+
// this output with the same pid does, the race the `.tmp-<pid>` naming
|
|
130
|
+
// already admits. Sending an operator after a directory that is not there
|
|
131
|
+
// costs them the time it takes to distrust the message.
|
|
132
|
+
let outcome;
|
|
133
|
+
if (parked === "parked") {
|
|
134
|
+
try {
|
|
135
|
+
renameSync(previous, target);
|
|
136
|
+
outcome = `the previous tree was put back at ${target}`;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
outcome = `the previous tree could not be put back and is still parked at ${previous}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (parked === "deleted") {
|
|
143
|
+
outcome = "the previous tree was already deleted by the overlayfs fallback and is gone";
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
outcome = "there was no previous tree";
|
|
147
|
+
}
|
|
148
|
+
const remains = existsSync(staging)
|
|
149
|
+
? `the complete new tree is at ${staging}`
|
|
150
|
+
: `the new tree is NOT at ${staging} either, so this output has to be built again`;
|
|
151
|
+
throw new Error(`stageAndSwap: could not move ${staging} to ${target}: ${messageOf(error)}; ${outcome}, and ${remains}`, { cause: error });
|
|
152
|
+
}
|
|
153
|
+
if (parked === "parked")
|
|
154
|
+
rmSync(previous, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
/** Remove a directory tree if it exists, then create it empty. */
|
|
157
|
+
export function resetDir(path) {
|
|
158
|
+
rmSync(path, { recursive: true, force: true });
|
|
159
|
+
mkdirSync(path, { recursive: true });
|
|
160
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { LoadedLibrary } from "../library-reader.js";
|
|
2
|
+
import { type LookupSpecData, type LookupStrategy } from "../lookup-specs.js";
|
|
3
|
+
import { type LibraryEntry, type LibraryField, type V1Definition } from "../model/library.js";
|
|
4
|
+
import { NamedTypeRegistry } from "./type-registry.js";
|
|
5
|
+
import { type TreeField, type TypeNode, type TypeTree } from "./type-tree.js";
|
|
6
|
+
/** A field name is a valid identifier only if it has no dots, brackets, etc.; otherwise it is quoted. */
|
|
7
|
+
export declare function keyLiteral(name: string): string;
|
|
8
|
+
/** One sentence for a lookup field's own doc comment: what it sends, how to get one. */
|
|
9
|
+
export declare function lookupFieldNote(declared: TreeField): string;
|
|
10
|
+
/**
|
|
11
|
+
* One property line.
|
|
12
|
+
*
|
|
13
|
+
* An INPUT property is wrapped so it also accepts an expression, and a
|
|
14
|
+
* resolvable lookup additionally accepts an unresolved token. The wrapper goes
|
|
15
|
+
* here rather than on the leaf type so that `tags: input('tags')` — the whole
|
|
16
|
+
* collection as one expression — is legal alongside `tags: ['a', out(…)]`.
|
|
17
|
+
* An OUTPUT property is never wrapped: it came back from the connector.
|
|
18
|
+
*/
|
|
19
|
+
export declare function emitNestedProperty(name: string, node: TypeNode, indent: string, namedTypes: NamedTypeRegistry, allOptional: boolean, isInput?: boolean): string[];
|
|
20
|
+
/** `export interface <name> { … }` over the roots of one tree. */
|
|
21
|
+
export declare function emitInterface(name: string, allOptional: boolean, namedTypes: NamedTypeRegistry, fieldTree: TypeTree, isInput?: boolean): string[];
|
|
22
|
+
export declare const STRATEGY_PROSE: Readonly<Record<LookupStrategy, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* The grep-able header: the lookups stated in prose beside the data, for agents
|
|
25
|
+
* that read source with Grep and have no language server. Bare ` * ` body
|
|
26
|
+
* lines, to be spliced inside a `/** … *\/` block.
|
|
27
|
+
*/
|
|
28
|
+
export declare function emitLookupComment(specs: ReadonlyMap<string, LookupSpecData>, symbol?: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* The descriptor's `lookups:` literal — a RUNTIME value, not a type. `lookup()`
|
|
31
|
+
* reads `aliases` from it to build the `.byName()` family.
|
|
32
|
+
*/
|
|
33
|
+
export declare function emitLookupsBlock(specs: ReadonlyMap<string, LookupSpecData>): string[];
|
|
34
|
+
/**
|
|
35
|
+
* A connector EVENT as a `triggerDescriptor()`, the shape `onEvent()` takes.
|
|
36
|
+
*
|
|
37
|
+
* The payload type comes from `outputSchema`, and `where` from the entry's
|
|
38
|
+
* event parameters. An entry that declares none keeps a loose
|
|
39
|
+
* `Record<string, string>`: that is either an event which genuinely takes no
|
|
40
|
+
* parameters (HTTP Webhook) or one nothing has described yet, and an empty
|
|
41
|
+
* interface would refuse every key in both cases.
|
|
42
|
+
*/
|
|
43
|
+
export declare function emitEvent(entry: LibraryEntry, inFields: readonly LibraryField[], inputTree: TypeTree, outputTree: TypeTree, name: string, inputTypes: NamedTypeRegistry, outputTypes: NamedTypeRegistry, invariant?: ReadonlySet<string>): string[];
|
|
44
|
+
/** One operation: its `Inputs`/`Outputs` interfaces, doc comment and `descriptor()` const. */
|
|
45
|
+
export declare function emitOp(entry: LibraryEntry, v1def: V1Definition, inputTree: TypeTree, outputTree: TypeTree, name: string, inputTypes: NamedTypeRegistry, outputTypes: NamedTypeRegistry, invariant?: ReadonlySet<string>): string[];
|
|
46
|
+
/**
|
|
47
|
+
* The catalog names required inputs but the generated `Inputs` interface is
|
|
48
|
+
* empty: the descriptor would type-check a call the platform rejects. One line
|
|
49
|
+
* naming the operation and the fields, or `undefined` when there is no gap.
|
|
50
|
+
*/
|
|
51
|
+
export declare function requiredInputGap(entry: LibraryEntry, inputTree: TypeTree): string | undefined;
|
|
52
|
+
export interface DescriptorOptions {
|
|
53
|
+
/** Module specifier for `import { descriptor }`; {@link PACKAGE_NAME} by default. */
|
|
54
|
+
importSpecifier?: string;
|
|
55
|
+
/** Connector keys to emit; every connector when omitted or empty. */
|
|
56
|
+
connectors?: readonly string[];
|
|
57
|
+
/**
|
|
58
|
+
* Emit typed descriptors for `GenericExpanded` entries (and every per-object
|
|
59
|
+
* variant of a generic event). Off for the bulk library; on for the small
|
|
60
|
+
* connection-resolved overlays `prepare` writes.
|
|
61
|
+
*/
|
|
62
|
+
includeGenericExpanded?: boolean;
|
|
63
|
+
}
|
|
64
|
+
export interface DescriptorOutput {
|
|
65
|
+
/** `"<connector-key>.ts"` → module text, in emit order (connector keys sorted by code point). */
|
|
66
|
+
modules: Map<string, string>;
|
|
67
|
+
/** nodeType → exported const name, from `naming.symbolIndex`, for the whole index. */
|
|
68
|
+
symbols: Map<string, string>;
|
|
69
|
+
/** One line per operation whose catalog requires inputs its interface cannot carry. */
|
|
70
|
+
requiredInputGaps: string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Render every connector module of the library.
|
|
74
|
+
*
|
|
75
|
+
* The Python `main()` wrote every module and only then exited 1 on
|
|
76
|
+
* {@link DescriptorOutput.requiredInputGaps}; this returns both and the caller
|
|
77
|
+
* decides. A missing entry or sidecar throws, as the Python did.
|
|
78
|
+
*/
|
|
79
|
+
export declare function renderDescriptorModules(library: LoadedLibrary, options?: DescriptorOptions): DescriptorOutput;
|
|
80
|
+
/** The text the Python printed before exiting 1 on gaps; call only with a non-empty list. */
|
|
81
|
+
export declare function formatRequiredInputGaps(gaps: readonly string[]): string;
|