@uipath/maestro-builder-sdk 6.3.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/api-index.md +6 -6
  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,182 @@
1
+ /**
2
+ * `registry build-library`: the Azure pipeline's library generator as a verb.
3
+ *
4
+ * This is `generate_library_json.py`'s `parse_args()` plus the part of `main()`
5
+ * that {@link buildLibrary} deliberately leaves out — the two banner lines, the
6
+ * cache lifecycle, and the exit code. The flag surface is the Python's one for
7
+ * one, because the pipeline that calls it is a YAML file nobody wants to edit
8
+ * twice; a flag argparse would have rejected is rejected here with exit 2.
9
+ *
10
+ * Exposed as `run(argv)` so the umbrella CLI can mount it the way it mounts
11
+ * every other registry verb. The optional `deps` argument exists for tests: a
12
+ * replay client and a recording reporter make the whole verb run without a
13
+ * tenant, which is the only way its exit codes can be pinned.
14
+ */
15
+ import { rmSync } from "node:fs";
16
+ import { UsageError, exitCodeOf, messageOf } from "../errors.js";
17
+ import { consoleReporter } from "../reporter.js";
18
+ import { OfflineUipClient, defaultUipClient } from "../uip/client.js";
19
+ import { FsResponseCache } from "../uip/response-cache.js";
20
+ import { buildLibrary, parseConnectorsFlag } from "./build.js";
21
+ /** argparse's defaults, verbatim. */
22
+ export const DEFAULTS = {
23
+ outputDir: "./library",
24
+ cacheDir: "./.registry-cache",
25
+ isCacheDir: "./.is-cache",
26
+ connectorsCacheDir: "./.is-connectors-cache",
27
+ catalogScope: "all",
28
+ concurrency: 10,
29
+ };
30
+ const CATALOG_SCOPES = ["all", "tier1", "tier2"];
31
+ const USAGE = [
32
+ "Usage: flow-sdk registry build-library [--output-dir <dir>] [--cache-dir <dir>] [--is-cache-dir <dir>]",
33
+ " [--connectors-cache-dir <dir>] [--triggers-output <path>]",
34
+ " [--connectors a,b] [--catalog-scope all|tier1|tier2] [--concurrency <n>]",
35
+ " [--skip-enrichment] [--skip-connector-enrichment]",
36
+ " [--skip-generic-expansion] [--keep-temp] [--offline]",
37
+ ].join("\n");
38
+ const VALUED = new Set([
39
+ "--output-dir",
40
+ "--cache-dir",
41
+ "--is-cache-dir",
42
+ "--connectors-cache-dir",
43
+ "--triggers-output",
44
+ "--connectors",
45
+ "--catalog-scope",
46
+ "--concurrency",
47
+ ]);
48
+ const SWITCHES = new Set(["--skip-enrichment", "--skip-connector-enrichment", "--skip-generic-expansion", "--keep-temp", "--offline"]);
49
+ /**
50
+ * `int(text)`, narrowed on purpose: an optionally signed run of ASCII digits,
51
+ * ASCII whitespace allowed around it. Python's `int()` also takes `_`
52
+ * separators (`1_0`) and non-ASCII decimal digits (`١٠`); a pipeline YAML
53
+ * never spells a worker count that way, and accepting it would only make a
54
+ * typo parse. What IS mirrored is the refusal of zero and negatives, below.
55
+ *
56
+ * The sign is only reachable as `--concurrency=-3`: the loop below reads any
57
+ * `-…` as the next flag, so `--concurrency -3` is "expected one argument"
58
+ * where argparse's negative-number special case gave `int("-3")`. Both are a
59
+ * usage error and exit 2 before the build starts, and the alternative is to
60
+ * change how every valued flag reads its value for a number that is then
61
+ * refused anyway.
62
+ */
63
+ const PY_INT = /^\s*[+-]?\d+\s*$/;
64
+ /**
65
+ * The options for this argv, or a {@link UsageError} carrying argparse's
66
+ * wording. Both `--flag value` and `--flag=value` are accepted, as argparse
67
+ * accepts them; anything that is neither a known flag nor a flag's value is an
68
+ * unrecognized argument, because the generator takes no positionals.
69
+ */
70
+ export function parseArgs(argv) {
71
+ const values = new Map();
72
+ const switches = new Set();
73
+ const unrecognized = [];
74
+ for (let i = 0; i < argv.length; i++) {
75
+ const arg = argv[i];
76
+ const eq = arg.startsWith("--") ? arg.indexOf("=") : -1;
77
+ const name = eq >= 0 ? arg.slice(0, eq) : arg;
78
+ if (VALUED.has(name)) {
79
+ if (eq >= 0) {
80
+ values.set(name, arg.slice(eq + 1));
81
+ continue;
82
+ }
83
+ const next = argv[i + 1];
84
+ // argparse refuses to read another flag as a value.
85
+ if (next === undefined || (next.startsWith("-") && next !== "-")) {
86
+ throw new UsageError(`argument ${name}: expected one argument`);
87
+ }
88
+ values.set(name, next);
89
+ i += 1;
90
+ }
91
+ else if (SWITCHES.has(name) && eq < 0) {
92
+ switches.add(name);
93
+ }
94
+ else {
95
+ unrecognized.push(arg);
96
+ }
97
+ }
98
+ if (unrecognized.length > 0)
99
+ throw new UsageError(`unrecognized arguments: ${unrecognized.join(" ")}`);
100
+ const catalogScope = values.get("--catalog-scope") ?? DEFAULTS.catalogScope;
101
+ if (!CATALOG_SCOPES.includes(catalogScope)) {
102
+ throw new UsageError(`argument --catalog-scope: invalid choice: '${catalogScope}' (choose from ${CATALOG_SCOPES.map((s) => `'${s}'`).join(", ")})`);
103
+ }
104
+ const concurrencyText = values.get("--concurrency");
105
+ let concurrency = DEFAULTS.concurrency;
106
+ if (concurrencyText !== undefined) {
107
+ if (!PY_INT.test(concurrencyText))
108
+ throw new UsageError(`argument --concurrency: invalid int value: '${concurrencyText}'`);
109
+ concurrency = parseInt(concurrencyText, 10);
110
+ // argparse accepted `0` and `-3`; the first `ThreadPoolExecutor(max_workers=…)`
111
+ // then raised `ValueError: max_workers must be greater than 0` — a traceback
112
+ // and exit 1, minutes into the build. The refusal is kept, moved to where
113
+ // the flag is read: a usage error (exit 2) before anything runs.
114
+ if (concurrency < 1)
115
+ throw new UsageError(`argument --concurrency: must be greater than 0, got ${concurrency}`);
116
+ }
117
+ // `if args.connectors:` tested the RAW string, so `--connectors ""` is a
118
+ // full build while `--connectors ,` is a curated subset of nothing. The
119
+ // split-and-strip happens only once the string has passed that gate.
120
+ const connectorsText = values.get("--connectors");
121
+ const connectors = connectorsText ? parseConnectorsFlag(connectorsText) : undefined;
122
+ return {
123
+ outputDir: values.get("--output-dir") ?? DEFAULTS.outputDir,
124
+ cacheDir: values.get("--cache-dir") ?? DEFAULTS.cacheDir,
125
+ isCacheDir: values.get("--is-cache-dir") ?? DEFAULTS.isCacheDir,
126
+ connectorsCacheDir: values.get("--connectors-cache-dir") ?? DEFAULTS.connectorsCacheDir,
127
+ ...(values.has("--triggers-output") ? { triggersOutput: values.get("--triggers-output") } : {}),
128
+ ...(connectors !== undefined ? { connectors } : {}),
129
+ catalogScope: catalogScope,
130
+ concurrency,
131
+ skipEnrichment: switches.has("--skip-enrichment"),
132
+ skipConnectorEnrichment: switches.has("--skip-connector-enrichment"),
133
+ skipGenericExpansion: switches.has("--skip-generic-expansion"),
134
+ keepTemp: switches.has("--keep-temp"),
135
+ offline: switches.has("--offline"),
136
+ };
137
+ }
138
+ export async function run(argv, deps = {}) {
139
+ if (argv.includes("--help") || argv.includes("-h")) {
140
+ console.log(USAGE);
141
+ return 0;
142
+ }
143
+ let options;
144
+ try {
145
+ options = parseArgs(argv);
146
+ }
147
+ catch (error) {
148
+ console.error(messageOf(error));
149
+ console.error(USAGE);
150
+ return 2;
151
+ }
152
+ const reporter = deps.reporter ?? consoleReporter;
153
+ reporter.info(`Cache directory: ${options.cacheDir}`);
154
+ reporter.info(`Output directory: ${options.outputDir}`);
155
+ try {
156
+ await buildLibrary(options, {
157
+ client: deps.client ?? (options.offline ? new OfflineUipClient() : defaultUipClient()),
158
+ caches: {
159
+ registry: new FsResponseCache(options.cacheDir),
160
+ is: new FsResponseCache(options.isCacheDir),
161
+ connectors: new FsResponseCache(options.connectorsCacheDir),
162
+ },
163
+ reporter,
164
+ concurrency: options.concurrency,
165
+ });
166
+ // The caches are a re-run aid, not a product: a build that got this far
167
+ // has no use for them unless asked to keep them. A build that failed above
168
+ // keeps them regardless, as the Python did, so the retry is cheap.
169
+ if (!options.keepTemp) {
170
+ reporter.info("Cleaning up caches...");
171
+ for (const dir of [options.cacheDir, options.isCacheDir, options.connectorsCacheDir]) {
172
+ rmSync(dir, { recursive: true, force: true });
173
+ }
174
+ }
175
+ reporter.info("Done!");
176
+ return 0;
177
+ }
178
+ catch (error) {
179
+ console.error(messageOf(error));
180
+ return exitCodeOf(error);
181
+ }
182
+ }
@@ -0,0 +1,107 @@
1
+ import type { Reporter } from "../reporter.js";
2
+ import type { UipClient } from "../uip/client.js";
3
+ import { type ResponseCache } from "../uip/response-cache.js";
4
+ export interface BuildCaches {
5
+ /** `--cache-dir`: raw `flow registry get` stdout plus `_search_results.json`. */
6
+ registry: ResponseCache;
7
+ /** `--is-cache-dir`: `is resources describe/list` and `is triggers` payloads. */
8
+ is: ResponseCache;
9
+ /** `--connectors-cache-dir`: `is connectors metadata` payloads. */
10
+ connectors: ResponseCache;
11
+ }
12
+ export interface BuildPorts {
13
+ client: UipClient;
14
+ caches: BuildCaches;
15
+ reporter: Reporter;
16
+ /** Parallel remote calls, as `--concurrency`. */
17
+ concurrency: number;
18
+ /** Kill a `uip` call after this long. Unset means no limit, as the Python had none. */
19
+ timeoutMs?: number;
20
+ }
21
+ /** One describe that failed while parent fields were set: `(connector, object, operation, message)`. */
22
+ export type FailedFieldDescribe = readonly [
23
+ connectorKey: string,
24
+ objectName: string,
25
+ operation: string,
26
+ message: string
27
+ ];
28
+ /**
29
+ * What the Python kept in module globals, plus the ports.
30
+ *
31
+ * `connectionId` routes every registry get / describe / list through
32
+ * `--connection-id`; `parentFields` are forwarded as `-f name=value` on
33
+ * describes and discriminate their cache key; `failedFieldDescribes` collects
34
+ * the CLI's own rejection message for each describe that failed under `-f`,
35
+ * because the pipeline otherwise swallows it (see `isDescribeOperation`).
36
+ */
37
+ export interface BuildContext extends BuildPorts {
38
+ connectionId?: string;
39
+ /** Insertion order is the `-f` order on the command line. */
40
+ parentFields: Map<string, string>;
41
+ failedFieldDescribes: FailedFieldDescribe[];
42
+ }
43
+ export interface BuildScope {
44
+ connectionId?: string;
45
+ parentFields?: ReadonlyMap<string, string> | Readonly<Record<string, string>>;
46
+ }
47
+ export declare function createBuildContext(ports: BuildPorts, scope?: BuildScope): BuildContext;
48
+ /** `_schema_cache_scope()`: static and per-connection responses never share a cache. */
49
+ export declare function schemaCacheScope(ctx: BuildContext): string;
50
+ /**
51
+ * `_field_scope()`: `f-` + the first 12 hex digits of the sha256 over
52
+ * `name=value` pairs sorted by name and joined with NUL; empty when no parent
53
+ * field is set. The describe answer is a function of these values, so a cache
54
+ * keyed only on (connection, object, operation) would serve the first variant
55
+ * for every later one.
56
+ */
57
+ export declare function fieldScope(parentFields: ReadonlyMap<string, string>): string;
58
+ /** Cache file for one `flow registry get`: the node type with `.` → `_`. */
59
+ export declare function registryCacheName(nodeType: string): string;
60
+ export declare const SEARCH_RESULTS_NAME = "_search_results.json";
61
+ /** `json.dump(payload, f)`: cache bytes shaped as Python wrote them. */
62
+ export declare function writeCache(cache: ResponseCache, name: string, payload: unknown): void;
63
+ /**
64
+ * `registry_get`: cached `uip maestro flow registry get <nodeType>`.
65
+ *
66
+ * Returns the cache name the raw stdout lives under, or `undefined` when the
67
+ * call failed. `quiet` suppresses the failure WARN — for a caller probing which
68
+ * of several node-type namespaces an action lives in, where a miss is an
69
+ * expected answer rather than a problem to report.
70
+ */
71
+ export declare function registryGet(ctx: BuildContext, nodeType: string, quiet?: boolean): Promise<string | undefined>;
72
+ /**
73
+ * `is_describe_operation`: cached `uip is resources describe <connector>
74
+ * <object> --operation <op>` (plus `--connection-id` and `-f` when set).
75
+ *
76
+ * Returns the payload's `Data` on success, or `undefined` on any failure. A
77
+ * failure is cached too, with a sentinel that keeps the CLI's `Message`: an
78
+ * incomplete parent set is rejected with a helpful message the pipeline would
79
+ * otherwise swallow, so it is recorded on the context while parent fields are
80
+ * set — from a fresh call and from a cached failure alike.
81
+ */
82
+ export declare function isDescribeOperation(ctx: BuildContext, connectorKey: string, objectName: string, operation: string): Promise<unknown>;
83
+ /**
84
+ * `is_describe_object`: cached `uip is resources describe <connector> <object>`
85
+ * with no `--operation`. Its `Data` carries `AvailableOperations`.
86
+ */
87
+ export declare function isDescribeObject(ctx: BuildContext, connectorKey: string, objectName: string): Promise<unknown>;
88
+ /**
89
+ * `is_resources_list`: cached `uip is resources list <connector>` — the object
90
+ * catalog, each row `Name`/`DisplayName`/`Path`/`Type`/`SubType`. The list on
91
+ * success (possibly empty), `undefined` on failure.
92
+ *
93
+ * `refresh` bypasses BOTH caches — this one and the CLI's own, which never
94
+ * invalidates itself: measured on uipath-salesforce-sfdc, 18 objects stale and
95
+ * 864 with `--refresh`.
96
+ */
97
+ export declare function isResourcesList(ctx: BuildContext, connectorKey: string, options?: {
98
+ operation?: string | null;
99
+ refresh?: boolean;
100
+ }): Promise<unknown[] | undefined>;
101
+ /**
102
+ * `is_connector_get`: cached `uip is connectors metadata <key>`, answering the
103
+ * first element of `Data` or `undefined`. The legacy `connectors get` cache
104
+ * files are deliberately not reused: that output omitted the discovery
105
+ * capabilities and would silently mark every connector static.
106
+ */
107
+ export declare function isConnectorGet(ctx: BuildContext, connectorKey: string): Promise<unknown>;
@@ -0,0 +1,267 @@
1
+ /**
2
+ * The build's connection to the tenant: one `uip` client, three response caches,
3
+ * and the scope every call is made under.
4
+ *
5
+ * The Python generator kept `_CONNECTION_ID`, `_PARENT_FIELDS` and
6
+ * `_FAILED_FIELD_DESCRIBES` as module globals that `prepare_connector` set before
7
+ * calling in. Here they ride on a {@link BuildContext} that every remote call
8
+ * takes as its first argument, so two builds in one process cannot see each
9
+ * other's connection, and a test can hand in a context that never reaches the
10
+ * network.
11
+ *
12
+ * Cache file names and the `Result: Failure` sentinels are the Python's exactly:
13
+ * a `--keep-temp` cache the Python wrote drives this code to the same bytes,
14
+ * which is how the port is proven.
15
+ */
16
+ import { createHash } from "node:crypto";
17
+ import { pyJsonStringify } from "../codecs/pyjson.js";
18
+ import { sortedByKeys } from "../compat/compare.js";
19
+ import { isConnectorsMetadata, isResourcesDescribeObject, isResourcesDescribeOperation, isResourcesList as isResourcesListArgv, registryGet as registryGetArgv, } from "../uip/commands.js";
20
+ import { CACHED_FAILURE, isCachedFailure, safeCacheName } from "../uip/response-cache.js";
21
+ import { pyStrip } from "../compat/pystr.js";
22
+ import { get, isRecord, pyOr, truthy } from "./py.js";
23
+ export function createBuildContext(ports, scope = {}) {
24
+ const parentFields = scope.parentFields instanceof Map
25
+ ? new Map(scope.parentFields)
26
+ : new Map(Object.entries(scope.parentFields ?? {}));
27
+ return {
28
+ ...ports,
29
+ ...(scope.connectionId ? { connectionId: scope.connectionId } : {}),
30
+ parentFields,
31
+ failedFieldDescribes: [],
32
+ };
33
+ }
34
+ /** `_schema_cache_scope()`: static and per-connection responses never share a cache. */
35
+ export function schemaCacheScope(ctx) {
36
+ return ctx.connectionId || "_static";
37
+ }
38
+ /**
39
+ * `_field_scope()`: `f-` + the first 12 hex digits of the sha256 over
40
+ * `name=value` pairs sorted by name and joined with NUL; empty when no parent
41
+ * field is set. The describe answer is a function of these values, so a cache
42
+ * keyed only on (connection, object, operation) would serve the first variant
43
+ * for every later one.
44
+ */
45
+ export function fieldScope(parentFields) {
46
+ if (parentFields.size === 0)
47
+ return "";
48
+ const pairs = sortedByKeys([...parentFields.entries()], ([k]) => k, ([, v]) => v);
49
+ const flat = pairs.map(([k, v]) => `${k}=${v}`).join("\0");
50
+ return `f-${createHash("sha256").update(flat, "utf8").digest("hex").slice(0, 12)}`;
51
+ }
52
+ /** Cache file for one `flow registry get`: the node type with `.` → `_`. */
53
+ export function registryCacheName(nodeType) {
54
+ return `${nodeType.replaceAll(".", "_")}.json`;
55
+ }
56
+ export const SEARCH_RESULTS_NAME = "_search_results.json";
57
+ function runUip(ctx, argv) {
58
+ return ctx.client.run(argv, ctx.timeoutMs === undefined ? undefined : { timeoutMs: ctx.timeoutMs });
59
+ }
60
+ /**
61
+ * `json.loads(result.stdout)`: the whole of stdout as one document, or
62
+ * `undefined` when it is not one. Deliberately not the lenient banner-skipping
63
+ * parser — the Python parsed the full stream and treated anything else as a
64
+ * failed call, and a call that fails there must fail here.
65
+ */
66
+ function parseStdout(stdout) {
67
+ try {
68
+ return JSON.parse(stdout);
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ }
74
+ /** `json.dump(payload, f)`: cache bytes shaped as Python wrote them. */
75
+ export function writeCache(cache, name, payload) {
76
+ cache.write(name, payload, pyJsonStringify(payload));
77
+ }
78
+ /**
79
+ * True when the call never ran, so nothing about it may be written down.
80
+ *
81
+ * Each fetcher below remembers a FAILURE as a `Result: Failure` sentinel, and
82
+ * a failure is only ever the tenant's answer — the Python had no offline mode,
83
+ * so every failure it cached was one. Under `--offline` the client refuses
84
+ * instead, and caching that as a failure would make the next ONLINE build over
85
+ * the same `--keep-temp` caches read the refusal as "this object has no
86
+ * schema" and carry on, silently.
87
+ *
88
+ * This is checked EXPLICITLY, before the parse, rather than resting on the
89
+ * parse failing: the four writes below are gated on `parseStdout` succeeding,
90
+ * and they stay safe only because `OfflineUipClient` happens to emit empty
91
+ * output. That accident is exactly what the offline work set out to remove, and
92
+ * `events.ts` already refuses to rest on it for the two trigger caches.
93
+ */
94
+ function refused(result) {
95
+ return result.refused === true;
96
+ }
97
+ /**
98
+ * `"__".join(parts)` raises `TypeError` on a non-str part, and the Python
99
+ * generator relied on that: an entry whose `objectName` is `null` never
100
+ * reached the CLI (its enrichment failed and was reported). `Array.join`
101
+ * would coerce `null` to `""` and issue a describe the Python never made.
102
+ */
103
+ function cacheNameOf(...parts) {
104
+ parts.forEach((part, i) => {
105
+ if (typeof part !== "string") {
106
+ const kind = part === null || part === undefined ? "NoneType" : typeof part === "number" ? "int" : typeof part;
107
+ throw new TypeError(`sequence item ${i}: expected str instance, ${kind} found`);
108
+ }
109
+ });
110
+ return `${safeCacheName(...parts)}.json`;
111
+ }
112
+ /** `payload.get("Result")` on something that is not a dict raises in Python. */
113
+ function envelope(payload) {
114
+ if (!isRecord(payload))
115
+ throw new TypeError(`'${Array.isArray(payload) ? "list" : typeof payload}' object has no attribute 'get'`);
116
+ return payload;
117
+ }
118
+ /**
119
+ * `registry_get`: cached `uip maestro flow registry get <nodeType>`.
120
+ *
121
+ * Returns the cache name the raw stdout lives under, or `undefined` when the
122
+ * call failed. `quiet` suppresses the failure WARN — for a caller probing which
123
+ * of several node-type namespaces an action lives in, where a miss is an
124
+ * expected answer rather than a problem to report.
125
+ */
126
+ export async function registryGet(ctx, nodeType, quiet = false) {
127
+ const name = registryCacheName(nodeType);
128
+ if (ctx.caches.registry.has(name))
129
+ return name;
130
+ const result = await runUip(ctx, registryGetArgv(nodeType, ctx.connectionId));
131
+ if (result.code !== 0) {
132
+ if (!quiet)
133
+ ctx.reporter.warn(` WARN: registry get failed for ${nodeType}: ${pyStrip(result.stderr)}`);
134
+ return undefined;
135
+ }
136
+ ctx.caches.registry.write(name, undefined, result.stdout);
137
+ return name;
138
+ }
139
+ function noteFailedFieldDescribe(ctx, connectorKey, objectName, operation, message) {
140
+ if (ctx.parentFields.size > 0) {
141
+ ctx.failedFieldDescribes.push([connectorKey, objectName, operation, typeof message === "string" && message ? message : ""]);
142
+ }
143
+ }
144
+ /**
145
+ * `is_describe_operation`: cached `uip is resources describe <connector>
146
+ * <object> --operation <op>` (plus `--connection-id` and `-f` when set).
147
+ *
148
+ * Returns the payload's `Data` on success, or `undefined` on any failure. A
149
+ * failure is cached too, with a sentinel that keeps the CLI's `Message`: an
150
+ * incomplete parent set is rejected with a helpful message the pipeline would
151
+ * otherwise swallow, so it is recorded on the context while parent fields are
152
+ * set — from a fresh call and from a cached failure alike.
153
+ */
154
+ export async function isDescribeOperation(ctx, connectorKey, objectName, operation) {
155
+ const parts = [schemaCacheScope(ctx), connectorKey, objectName, operation];
156
+ const scope = fieldScope(ctx.parentFields);
157
+ if (scope)
158
+ parts.push(scope);
159
+ const name = cacheNameOf(...parts);
160
+ if (ctx.caches.is.has(name)) {
161
+ const cached = ctx.caches.is.read(name);
162
+ if (isCachedFailure(cached))
163
+ noteFailedFieldDescribe(ctx, connectorKey, objectName, operation, get(cached, "Message"));
164
+ return isRecord(cached) && cached.Data !== undefined ? cached.Data : undefined;
165
+ }
166
+ const result = await runUip(ctx, isResourcesDescribeOperation(connectorKey, objectName, operation, ctx.connectionId, ctx.parentFields));
167
+ if (refused(result))
168
+ return undefined;
169
+ const parsed = parseStdout(result.stdout);
170
+ if (parsed === undefined)
171
+ return undefined;
172
+ const payload = envelope(parsed);
173
+ if (get(payload, "Result") !== "Success") {
174
+ noteFailedFieldDescribe(ctx, connectorKey, objectName, operation, get(payload, "Message"));
175
+ writeCache(ctx.caches.is, name, { Result: "Failure", [CACHED_FAILURE]: true, Message: get(payload, "Message") });
176
+ return undefined;
177
+ }
178
+ writeCache(ctx.caches.is, name, payload);
179
+ return payload.Data;
180
+ }
181
+ /**
182
+ * `is_describe_object`: cached `uip is resources describe <connector> <object>`
183
+ * with no `--operation`. Its `Data` carries `AvailableOperations`.
184
+ */
185
+ export async function isDescribeObject(ctx, connectorKey, objectName) {
186
+ const name = cacheNameOf(schemaCacheScope(ctx), connectorKey, objectName, "_operations");
187
+ if (ctx.caches.is.has(name)) {
188
+ const cached = ctx.caches.is.read(name);
189
+ return isRecord(cached) && cached.Data !== undefined ? cached.Data : undefined;
190
+ }
191
+ const result = await runUip(ctx, isResourcesDescribeObject(connectorKey, objectName, ctx.connectionId));
192
+ if (refused(result))
193
+ return undefined;
194
+ const parsed = parseStdout(result.stdout);
195
+ if (parsed === undefined)
196
+ return undefined;
197
+ const payload = envelope(parsed);
198
+ if (get(payload, "Result") !== "Success") {
199
+ writeCache(ctx.caches.is, name, { Result: "Failure", [CACHED_FAILURE]: true });
200
+ return undefined;
201
+ }
202
+ writeCache(ctx.caches.is, name, payload);
203
+ return payload.Data;
204
+ }
205
+ /**
206
+ * `is_resources_list`: cached `uip is resources list <connector>` — the object
207
+ * catalog, each row `Name`/`DisplayName`/`Path`/`Type`/`SubType`. The list on
208
+ * success (possibly empty), `undefined` on failure.
209
+ *
210
+ * `refresh` bypasses BOTH caches — this one and the CLI's own, which never
211
+ * invalidates itself: measured on uipath-salesforce-sfdc, 18 objects stale and
212
+ * 864 with `--refresh`.
213
+ */
214
+ export async function isResourcesList(ctx, connectorKey, options = {}) {
215
+ const operation = options.operation ?? undefined;
216
+ const name = `${safeCacheName(schemaCacheScope(ctx), connectorKey, pyOr(operation, "_all_operations"), "_resources_list")}.json`;
217
+ if (!options.refresh && ctx.caches.is.has(name)) {
218
+ const cached = ctx.caches.is.read(name);
219
+ const data = get(cached, "Data");
220
+ return Array.isArray(data) ? data : undefined;
221
+ }
222
+ const result = await runUip(ctx, isResourcesListArgv(connectorKey, {
223
+ ...(truthy(operation) ? { operation } : {}),
224
+ ...(options.refresh ? { refresh: true } : {}),
225
+ connectionId: ctx.connectionId,
226
+ }));
227
+ if (refused(result))
228
+ return undefined;
229
+ const parsed = parseStdout(result.stdout);
230
+ if (parsed === undefined)
231
+ return undefined;
232
+ const payload = envelope(parsed);
233
+ if (get(payload, "Result") !== "Success") {
234
+ writeCache(ctx.caches.is, name, { Result: "Failure", [CACHED_FAILURE]: true });
235
+ return undefined;
236
+ }
237
+ writeCache(ctx.caches.is, name, payload);
238
+ return Array.isArray(payload.Data) ? payload.Data : undefined;
239
+ }
240
+ /**
241
+ * `is_connector_get`: cached `uip is connectors metadata <key>`, answering the
242
+ * first element of `Data` or `undefined`. The legacy `connectors get` cache
243
+ * files are deliberately not reused: that output omitted the discovery
244
+ * capabilities and would silently mark every connector static.
245
+ */
246
+ export async function isConnectorGet(ctx, connectorKey) {
247
+ const name = `${safeCacheName(connectorKey, "_connector_metadata")}.json`;
248
+ const firstRow = (payload) => {
249
+ const data = pyOr(get(payload, "Data"), []);
250
+ return Array.isArray(data) && data.length > 0 ? data[0] : undefined;
251
+ };
252
+ if (ctx.caches.connectors.has(name))
253
+ return firstRow(ctx.caches.connectors.read(name));
254
+ const result = await runUip(ctx, isConnectorsMetadata(connectorKey));
255
+ if (refused(result))
256
+ return undefined;
257
+ const parsed = parseStdout(result.stdout);
258
+ if (parsed === undefined)
259
+ return undefined;
260
+ const payload = envelope(parsed);
261
+ if (get(payload, "Result") !== "Success") {
262
+ writeCache(ctx.caches.connectors, name, { Result: "Failure", [CACHED_FAILURE]: true });
263
+ return undefined;
264
+ }
265
+ writeCache(ctx.caches.connectors, name, payload);
266
+ return firstRow(payload);
267
+ }
@@ -0,0 +1,36 @@
1
+ import type { LibraryEntry } from "../model/library.js";
2
+ import { type BuildContext } from "./context.js";
3
+ /**
4
+ * Registry `httpMethod` (e.g. `GETBYID`) → a guess at the `is resources`
5
+ * operation name, for an entry whose canonical `operation.name` is empty.
6
+ */
7
+ export declare const HTTP_METHOD_TO_OPERATION: ReadonlyMap<string, string>;
8
+ /**
9
+ * `discover_operation_name`: the `is resources` operation whose HTTP method
10
+ * matches the entry's, from the object's `AvailableOperations`; `null` when
11
+ * the object cannot be described or nothing matches.
12
+ */
13
+ export declare function discoverOperationName(ctx: BuildContext, entry: LibraryEntry): Promise<unknown>;
14
+ /**
15
+ * The pure half of `enrich_entry`: merge one describe `Data` payload into the
16
+ * entry, in place. `operation.name` is filled only when the registry left it
17
+ * empty; `pathTemplate`, `parameters` and the two schemas come from the
18
+ * payload; `objectDisplayName` and `display.operationLabel` fill gaps only.
19
+ */
20
+ export declare function applyDescribe(entry: LibraryEntry, data: unknown): LibraryEntry;
21
+ /**
22
+ * `enrich_entry`: augment a canonical entry with `is resources describe` data.
23
+ * On any failure the entry is returned as it was, so the pipeline degrades
24
+ * gracefully.
25
+ *
26
+ * The registry's verb and the IS CLI's can DISAGREE for the same operation
27
+ * (Data Service `QueryEntityRecordsCurated`: registry `List`, CLI `Create`).
28
+ * When the registry's name does not resolve, the OBJECT is asked which
29
+ * operations it has and the one whose HTTP method matches is retried.
30
+ * `operation.name` deliberately keeps the REGISTRY's verb even then — it maps
31
+ * to a CRUD verb the offline fixtures are synthesised from; the discovered
32
+ * name is only for reaching the schema.
33
+ */
34
+ export declare function enrichEntry(ctx: BuildContext, entry: LibraryEntry): Promise<LibraryEntry>;
35
+ /** `enrich_all`: `enrichEntry` over every entry, results in input order. */
36
+ export declare function enrichAll(ctx: BuildContext, entries: readonly LibraryEntry[]): Promise<LibraryEntry[]>;