@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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { LibraryEntry } from "../model/library.js";
|
|
2
|
+
import { type Dict } from "./py.js";
|
|
3
|
+
export declare const SCHEMA_VERSION = "1";
|
|
4
|
+
/**
|
|
5
|
+
* Ops the IS CLI exposes as a POST whose inputs are query-string params with a
|
|
6
|
+
* required (often empty) body. A dispatcher reads
|
|
7
|
+
* `operation.dispatchStyle == "query-post"` and dispatches
|
|
8
|
+
* `run create <obj> --query <inputs> --body '{}'` instead of the default
|
|
9
|
+
* create→--body. The registry does not expose per-parameter location, so this
|
|
10
|
+
* is a small explicit list rather than derived.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CURATED_DISPATCH_STYLE: ReadonlyMap<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Per-parameter value-grammar tags. A dispatcher validates the COMPUTED input
|
|
15
|
+
* against `operation.paramGrammars` at every dispatch boundary. "sql-where":
|
|
16
|
+
* Data Service query expressions (RHS string literals must be single-quoted).
|
|
17
|
+
*/
|
|
18
|
+
export declare const CURATED_PARAM_GRAMMARS: ReadonlyMap<string, Dict>;
|
|
19
|
+
/**
|
|
20
|
+
* A few connector ops the registry enumerates only in a form `uip is resources
|
|
21
|
+
* run` can't dispatch. The Outlook reply op is enumerated as the MULTIPART
|
|
22
|
+
* `ReplyToEmailV2`; the connector also exposes the non-multipart sibling
|
|
23
|
+
* `ReplyToEmail` that Flow v1 uses. Keyed by (connectorKey, registry
|
|
24
|
+
* objectName); deep-merged into `operation` AFTER enrichment so it wins over
|
|
25
|
+
* the `is resources describe` params.
|
|
26
|
+
*/
|
|
27
|
+
export declare const CURATED_OP_OVERRIDE: ReadonlyMap<string, Record<string, Dict>>;
|
|
28
|
+
/** The curated tables `extractCanonical` consults; tests can hand in their own. */
|
|
29
|
+
export interface CanonicalContext {
|
|
30
|
+
schemaVersion?: string;
|
|
31
|
+
dispatchStyles?: ReadonlyMap<string, string>;
|
|
32
|
+
paramGrammars?: ReadonlyMap<string, Dict>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `apply_curated_overrides`: deep-merge CURATED_OP_OVERRIDE into matching
|
|
36
|
+
* entries, AFTER enrichment, so the curated shape wins over the registry-
|
|
37
|
+
* described one. Matches on (connectorKey, objectName) using the entry's
|
|
38
|
+
* current (registry) objectName. Returns how many entries were touched.
|
|
39
|
+
*/
|
|
40
|
+
export declare function applyCuratedOverrides(entries: LibraryEntry[], overrides?: ReadonlyMap<string, Record<string, Dict>>): number;
|
|
41
|
+
/** The `value` of the context entry named `name`, or `null`. */
|
|
42
|
+
export declare function contextValue(context: unknown, name: string): unknown;
|
|
43
|
+
export declare function hasContext(context: unknown, name: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* `_parameter_metadata`: field-shaped metadata carried by each method
|
|
46
|
+
* parameter, keyed by name.
|
|
47
|
+
*
|
|
48
|
+
* A method parameter and a node field describe the same input in two
|
|
49
|
+
* different schemas. `displayName`, `reference` and the choice list exist on
|
|
50
|
+
* both sides, so they are the only ones worth moving across. A parameter's
|
|
51
|
+
* `enum` is deprecated in favour of `enhancedEnum`, whose `{name, value}`
|
|
52
|
+
* pairs flatten to the literals a field's `enum` holds.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parameterMetadata(connectorMethodInfo: unknown): Map<string, Dict>;
|
|
55
|
+
/** `Data.Node` from a parsed `flow registry get` response, or `null`. */
|
|
56
|
+
export declare function nodeOf(payload: unknown): unknown;
|
|
57
|
+
/**
|
|
58
|
+
* `extract_canonical`: the library entry for one registry `Data.Node`, or
|
|
59
|
+
* `undefined` when the node is filtered out (not enabled, not cross-platform,
|
|
60
|
+
* experimental, or without a `connectorDetail` block).
|
|
61
|
+
*/
|
|
62
|
+
export declare function extractCanonical(nodeData: unknown, ctx?: CanonicalContext): LibraryEntry | undefined;
|
|
63
|
+
/** Accept the boolean JSON contract and older Yes/No cache rows. */
|
|
64
|
+
export declare function asBool(value: unknown): boolean;
|
|
65
|
+
/** Read the connector tier across CLI response casing variants. */
|
|
66
|
+
export declare function connectorTier(record: unknown): string | null;
|
|
67
|
+
/** Normalize current and legacy connector tier spellings to `tier1`/`tier2`. */
|
|
68
|
+
export declare function normalizedConnectorTier(record: unknown): "tier1" | "tier2" | null;
|
|
69
|
+
/**
|
|
70
|
+
* Keep the node types whose connector metadata declares `tier` — actions and
|
|
71
|
+
* connector events both, per `scopedConnectorKey`. Returns the kept node types
|
|
72
|
+
* and the connector keys that matched.
|
|
73
|
+
*/
|
|
74
|
+
export declare function filterNodeTypesByTier(nodeTypes: readonly string[], connectorRecords: ReadonlyMap<string, unknown>, tier: string): [string[], Set<string>];
|
|
75
|
+
/** `merge_connector_record`: stamp connector discovery metadata and its derived flag. */
|
|
76
|
+
export declare function mergeConnectorRecord(entry: LibraryEntry, record: unknown): LibraryEntry;
|
|
77
|
+
/**
|
|
78
|
+
* The connector key for an ACTION node type, `uipath.connector.<key>.<op>`, or
|
|
79
|
+
* `null`. The event forms carry an extra segment and are answered by
|
|
80
|
+
* `eventConnectorKey` instead.
|
|
81
|
+
*/
|
|
82
|
+
export declare function actionConnectorKey(nodeType: string): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* The connector key for a WAIT-form event node type,
|
|
85
|
+
* `uipath.connector.event.<key>.<event>`, or `null`. Only the wait form is
|
|
86
|
+
* generated: `registry get` refuses the start form without a connection, and
|
|
87
|
+
* the SDK derives the start form from its wait sibling.
|
|
88
|
+
*/
|
|
89
|
+
export declare function eventConnectorKey(nodeType: string): string | null;
|
|
90
|
+
/** The connector a node type belongs to, for SCOPE filtering: action or wait event. */
|
|
91
|
+
export declare function scopedConnectorKey(nodeType: string): string | null;
|
|
92
|
+
/** The connector a node type belongs to, in ANY of its three forms — trigger included. */
|
|
93
|
+
export declare function catalogConnectorKey(nodeType: unknown): string | null;
|
|
94
|
+
export declare const FIRST_PARTY_PREFIX = "uipath-";
|
|
95
|
+
export declare const MOCK_MARKER = "mock";
|
|
96
|
+
/**
|
|
97
|
+
* Does this connector key belong in the PUBLISHED catalog? `custom-<org>-*` are
|
|
98
|
+
* one tenant's own connectors and `uipath-mock-*` are eval fixtures; neither
|
|
99
|
+
* resolves for another user.
|
|
100
|
+
*/
|
|
101
|
+
export declare function isFirstPartyConnector(key: unknown): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* `(kept node types, dropped connector keys)` for a FULL build. A node type
|
|
104
|
+
* with no connector key at all is not a connector node and is kept.
|
|
105
|
+
*/
|
|
106
|
+
export declare function selectPublishedConnectors(nodeTypes: readonly string[]): [string[], string[]];
|
|
107
|
+
/** The action segment of a `uipath.connector.<key>.<action>` node type. */
|
|
108
|
+
export declare function actionIdFromNodeType(nodeType: string): string;
|
|
109
|
+
export declare function isHttpPassthrough(entry: LibraryEntry): boolean;
|
|
110
|
+
/** Map a PascalCase key to camelCase (first character lowered). */
|
|
111
|
+
export declare function toLowerCamel<T>(s: T): T;
|
|
112
|
+
/**
|
|
113
|
+
* Recursively lower-camel-case every dict key in `value`. The IS CLI started
|
|
114
|
+
* returning PascalCase keys for parameters, fields and references; the library
|
|
115
|
+
* uses camelCase, so the on-disk schema stays stable across CLI versions.
|
|
116
|
+
*/
|
|
117
|
+
export declare function normalizeFieldKeys(value: unknown): unknown;
|
|
118
|
+
/** `_pick`: the first non-None value among `d[k]` for the given keys, else `null`. */
|
|
119
|
+
export declare function pick(d: unknown, ...keys: string[]): unknown;
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical extraction: one raw `flow registry get` node into one library entry.
|
|
3
|
+
*
|
|
4
|
+
* Everything here is a pure function of its arguments. The key ORDER of the
|
|
5
|
+
* entry it builds is the Python's dict order and is load-bearing: the entry is
|
|
6
|
+
* written with `json.dump(indent=2)` and the library's sha256 is published, so
|
|
7
|
+
* a key that moves is a different library.
|
|
8
|
+
*
|
|
9
|
+
* Also here: the connector-record stamping (`merge_connector_record`), the
|
|
10
|
+
* tier readers, the three node-type → connector-key parsers, the published-
|
|
11
|
+
* catalog filter, and the curated override tables the generator carries.
|
|
12
|
+
*/
|
|
13
|
+
import { pyStr, pyStrip, lowerFirst, startsWithUpper } from "../compat/pystr.js";
|
|
14
|
+
import { sortedStrings } from "../compat/compare.js";
|
|
15
|
+
import { get, isRecord, pyIter, pyOr, partition, truthy } from "./py.js";
|
|
16
|
+
import { schemaAction } from "./schema-action.js";
|
|
17
|
+
export const SCHEMA_VERSION = "1";
|
|
18
|
+
/** `(connectorKey, objectName)` as a Map key; `null`s stay distinct from `""`. */
|
|
19
|
+
function curatedKey(connectorKey, objectName) {
|
|
20
|
+
return JSON.stringify([connectorKey ?? null, objectName ?? null]);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Ops the IS CLI exposes as a POST whose inputs are query-string params with a
|
|
24
|
+
* required (often empty) body. A dispatcher reads
|
|
25
|
+
* `operation.dispatchStyle == "query-post"` and dispatches
|
|
26
|
+
* `run create <obj> --query <inputs> --body '{}'` instead of the default
|
|
27
|
+
* create→--body. The registry does not expose per-parameter location, so this
|
|
28
|
+
* is a small explicit list rather than derived.
|
|
29
|
+
*/
|
|
30
|
+
export const CURATED_DISPATCH_STYLE = new Map([
|
|
31
|
+
[curatedKey("uipath-uipath-dataservice", "QueryEntityRecords_V3"), "query-post"],
|
|
32
|
+
// The V2 curated sibling, and the one the REGISTRY node
|
|
33
|
+
// `uipath.connector.uipath-uipath-dataservice.query-entity-records` maps to:
|
|
34
|
+
// POST /v2/{entityName}/qer, every input a path or query parameter, body `{}`.
|
|
35
|
+
[curatedKey("uipath-uipath-dataservice", "QueryEntityRecordsCurated"), "query-post"],
|
|
36
|
+
]);
|
|
37
|
+
/**
|
|
38
|
+
* Per-parameter value-grammar tags. A dispatcher validates the COMPUTED input
|
|
39
|
+
* against `operation.paramGrammars` at every dispatch boundary. "sql-where":
|
|
40
|
+
* Data Service query expressions (RHS string literals must be single-quoted).
|
|
41
|
+
*/
|
|
42
|
+
export const CURATED_PARAM_GRAMMARS = new Map([
|
|
43
|
+
[curatedKey("uipath-uipath-dataservice", "QueryEntityRecords_V3"), { queryExpression: "sql-where" }],
|
|
44
|
+
[curatedKey("uipath-uipath-dataservice", "QueryEntityRecords"), { queryExpression: "sql-where" }],
|
|
45
|
+
[curatedKey("uipath-uipath-dataservice", "QueryEntityRecordsCurated"), { queryExpression: "sql-where" }],
|
|
46
|
+
]);
|
|
47
|
+
/**
|
|
48
|
+
* A few connector ops the registry enumerates only in a form `uip is resources
|
|
49
|
+
* run` can't dispatch. The Outlook reply op is enumerated as the MULTIPART
|
|
50
|
+
* `ReplyToEmailV2`; the connector also exposes the non-multipart sibling
|
|
51
|
+
* `ReplyToEmail` that Flow v1 uses. Keyed by (connectorKey, registry
|
|
52
|
+
* objectName); deep-merged into `operation` AFTER enrichment so it wins over
|
|
53
|
+
* the `is resources describe` params.
|
|
54
|
+
*/
|
|
55
|
+
export const CURATED_OP_OVERRIDE = new Map([
|
|
56
|
+
[
|
|
57
|
+
curatedKey("uipath-microsoft-outlook365", "ReplyToEmailV2"),
|
|
58
|
+
{
|
|
59
|
+
operation: {
|
|
60
|
+
objectName: "ReplyToEmail",
|
|
61
|
+
path: "/ReplyToEmail",
|
|
62
|
+
parameters: [
|
|
63
|
+
{
|
|
64
|
+
name: "id",
|
|
65
|
+
type: "query",
|
|
66
|
+
dataType: "string",
|
|
67
|
+
required: true,
|
|
68
|
+
displayName: "Message ID",
|
|
69
|
+
description: "The id of the email to reply to",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
]);
|
|
76
|
+
/**
|
|
77
|
+
* `apply_curated_overrides`: deep-merge CURATED_OP_OVERRIDE into matching
|
|
78
|
+
* entries, AFTER enrichment, so the curated shape wins over the registry-
|
|
79
|
+
* described one. Matches on (connectorKey, objectName) using the entry's
|
|
80
|
+
* current (registry) objectName. Returns how many entries were touched.
|
|
81
|
+
*/
|
|
82
|
+
export function applyCuratedOverrides(entries, overrides = CURATED_OP_OVERRIDE) {
|
|
83
|
+
let n = 0;
|
|
84
|
+
for (const e of entries) {
|
|
85
|
+
const key = curatedKey(get(pyOr(get(e, "connector", {}), {}), "key", ""), get(pyOr(get(e, "operation", {}), {}), "objectName", ""));
|
|
86
|
+
const override = overrides.get(key);
|
|
87
|
+
if (!truthy(override))
|
|
88
|
+
continue;
|
|
89
|
+
const target = e;
|
|
90
|
+
for (const [section, fields] of Object.entries(override)) {
|
|
91
|
+
if (!(section in target) || target[section] === undefined)
|
|
92
|
+
target[section] = {};
|
|
93
|
+
const sectionValue = target[section];
|
|
94
|
+
for (const [k, v] of Object.entries(fields))
|
|
95
|
+
sectionValue[k] = v;
|
|
96
|
+
}
|
|
97
|
+
n += 1;
|
|
98
|
+
}
|
|
99
|
+
return n;
|
|
100
|
+
}
|
|
101
|
+
/** The `value` of the context entry named `name`, or `null`. */
|
|
102
|
+
export function contextValue(context, name) {
|
|
103
|
+
for (const c of pyIter(context)) {
|
|
104
|
+
if (get(c, "name") === name)
|
|
105
|
+
return get(c, "value");
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
export function hasContext(context, name) {
|
|
110
|
+
return pyIter(context).some((c) => get(c, "name") === name);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `_parameter_metadata`: field-shaped metadata carried by each method
|
|
114
|
+
* parameter, keyed by name.
|
|
115
|
+
*
|
|
116
|
+
* A method parameter and a node field describe the same input in two
|
|
117
|
+
* different schemas. `displayName`, `reference` and the choice list exist on
|
|
118
|
+
* both sides, so they are the only ones worth moving across. A parameter's
|
|
119
|
+
* `enum` is deprecated in favour of `enhancedEnum`, whose `{name, value}`
|
|
120
|
+
* pairs flatten to the literals a field's `enum` holds.
|
|
121
|
+
*/
|
|
122
|
+
export function parameterMetadata(connectorMethodInfo) {
|
|
123
|
+
const out = new Map();
|
|
124
|
+
for (const p of pyIter(pyOr(get(pyOr(connectorMethodInfo, {}), "parameters"), []))) {
|
|
125
|
+
const name = get(p, "name");
|
|
126
|
+
if (typeof name !== "string" || !name)
|
|
127
|
+
continue;
|
|
128
|
+
const meta = {};
|
|
129
|
+
if (get(p, "displayName") !== null)
|
|
130
|
+
meta.displayName = get(p, "displayName");
|
|
131
|
+
if (get(p, "reference") !== null)
|
|
132
|
+
meta.reference = get(p, "reference");
|
|
133
|
+
if (isRecord(p) && "enum" in p) {
|
|
134
|
+
meta.enum = p.enum ?? null;
|
|
135
|
+
}
|
|
136
|
+
else if (truthy(get(p, "enhancedEnum"))) {
|
|
137
|
+
const values = pyIter(get(p, "enhancedEnum"))
|
|
138
|
+
.filter((e) => isRecord(e) && get(e, "value") !== null)
|
|
139
|
+
.map((e) => get(e, "value"));
|
|
140
|
+
if (values.length > 0)
|
|
141
|
+
meta.enum = values;
|
|
142
|
+
}
|
|
143
|
+
if (truthy(meta))
|
|
144
|
+
out.set(name, meta);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
/** `Data.Node` from a parsed `flow registry get` response, or `null`. */
|
|
149
|
+
export function nodeOf(payload) {
|
|
150
|
+
return get(get(payload, "Data", {}), "Node");
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* `extract_canonical`: the library entry for one registry `Data.Node`, or
|
|
154
|
+
* `undefined` when the node is filtered out (not enabled, not cross-platform,
|
|
155
|
+
* experimental, or without a `connectorDetail` block).
|
|
156
|
+
*/
|
|
157
|
+
export function extractCanonical(nodeData, ctx = {}) {
|
|
158
|
+
const node = nodeData;
|
|
159
|
+
if (!truthy(node))
|
|
160
|
+
return undefined;
|
|
161
|
+
const nodeType = get(node, "nodeType", "");
|
|
162
|
+
const model = pyOr(get(node, "model", {}), {});
|
|
163
|
+
const context = pyOr(get(model, "context", []), []);
|
|
164
|
+
// Activity-level metadata lives in the editor form: search
|
|
165
|
+
// `node.form.sections[*].fields[*].componentProps` for the `connectorDetail`
|
|
166
|
+
// block — isEnabled / targetPlatform / isExperimental / configuration.
|
|
167
|
+
let connectorDetail;
|
|
168
|
+
for (const section of pyIter(pyOr(get(pyOr(get(node, "form", {}), {}), "sections", []), []))) {
|
|
169
|
+
for (const f of pyIter(pyOr(get(section, "fields", []), []))) {
|
|
170
|
+
const cp = get(pyOr(get(f, "componentProps"), {}), "connectorDetail");
|
|
171
|
+
if (isRecord(cp) && "configuration" in cp) {
|
|
172
|
+
connectorDetail = cp;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (connectorDetail)
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
if (!truthy(connectorDetail))
|
|
180
|
+
return undefined;
|
|
181
|
+
if (!truthy(get(connectorDetail, "isEnabled", false)))
|
|
182
|
+
return undefined;
|
|
183
|
+
if (get(connectorDetail, "targetPlatform") !== "CrossPlatform")
|
|
184
|
+
return undefined;
|
|
185
|
+
if (truthy(get(connectorDetail, "isExperimental", true)))
|
|
186
|
+
return undefined;
|
|
187
|
+
const configuration = get(connectorDetail, "configuration", "{}");
|
|
188
|
+
if (typeof configuration !== "string") {
|
|
189
|
+
throw new TypeError(`the JSON object must be str, bytes or bytearray, not ${configuration === null ? "NoneType" : typeof configuration}`);
|
|
190
|
+
}
|
|
191
|
+
let config;
|
|
192
|
+
try {
|
|
193
|
+
config = JSON.parse(configuration);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
const activityType = get(config, "activityType", "");
|
|
199
|
+
// The human-readable verb lives on connectorMethodInfo when present; fall
|
|
200
|
+
// back to the model.context entry, then to the configuration blob (Generic
|
|
201
|
+
// CRUD actions carry it only there).
|
|
202
|
+
const cmi = pyOr(get(node, "connectorMethodInfo", {}), {});
|
|
203
|
+
const operationName = pyOr(get(cmi, "operation"), contextValue(context, "operation"), get(config, "operation"), "");
|
|
204
|
+
const path = pyOr(get(cmi, "path"), "");
|
|
205
|
+
const curated = pyOr(get(cmi, "curated"), {});
|
|
206
|
+
const objectDisplayName = pyOr(get(curated, "displayName"), "");
|
|
207
|
+
// Input fields: take `inputDefinition.fields` when they exist and fill their
|
|
208
|
+
// gaps from `connectorMethodInfo.parameters`; build them from the parameters
|
|
209
|
+
// when they do not. Gaps only — a value the node already states wins.
|
|
210
|
+
const paramMeta = parameterMetadata(cmi);
|
|
211
|
+
const declaredFields = pyOr(get(get(node, "inputDefinition", {}), "fields"), []);
|
|
212
|
+
const inputFields = [];
|
|
213
|
+
if (truthy(declaredFields)) {
|
|
214
|
+
for (const declared of pyIter(declaredFields)) {
|
|
215
|
+
if (!isRecord(declared))
|
|
216
|
+
throw new TypeError("cannot convert dictionary update sequence element to a dict");
|
|
217
|
+
const merged = { ...declared };
|
|
218
|
+
const meta = paramMeta.get(get(merged, "name")) ?? {};
|
|
219
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
220
|
+
if (!(key in merged) || merged[key] === undefined)
|
|
221
|
+
merged[key] = value;
|
|
222
|
+
}
|
|
223
|
+
inputFields.push(merged);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
for (const p of pyIter(pyOr(get(cmi, "parameters", []), []))) {
|
|
228
|
+
const f = {
|
|
229
|
+
name: get(p, "name", ""),
|
|
230
|
+
type: get(p, "dataType", "string"),
|
|
231
|
+
required: get(p, "required", false),
|
|
232
|
+
description: get(p, "description", ""),
|
|
233
|
+
};
|
|
234
|
+
Object.assign(f, paramMeta.get(f.name) ?? {});
|
|
235
|
+
inputFields.push(f);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
let outputFields = get(get(node, "outputDefinition", {}), "fields");
|
|
239
|
+
if (!truthy(outputFields))
|
|
240
|
+
outputFields = get(get(node, "outputResponseDefinition", {}), "fields");
|
|
241
|
+
if (!truthy(outputFields))
|
|
242
|
+
outputFields = [];
|
|
243
|
+
const display = pyOr(get(node, "display", {}), {});
|
|
244
|
+
const isTrigger = pyStr(activityType).endsWith("Trigger");
|
|
245
|
+
const operation = {
|
|
246
|
+
name: operationName,
|
|
247
|
+
objectName: get(config, "objectName", ""),
|
|
248
|
+
objectDisplayName,
|
|
249
|
+
httpMethod: get(config, "httpMethod", ""),
|
|
250
|
+
path,
|
|
251
|
+
subType: get(config, "subType", ""),
|
|
252
|
+
supportsStreaming: get(config, "supportsStreaming", false),
|
|
253
|
+
activityType,
|
|
254
|
+
};
|
|
255
|
+
// A connector EVENT's operation is not `operation.name` — that is the
|
|
256
|
+
// object's CRUD verb. The event's own operation is the `model.context` entry
|
|
257
|
+
// the platform stamps (`EMAIL_RECEIVED`), the key every trigger API takes.
|
|
258
|
+
if (isTrigger)
|
|
259
|
+
operation.eventOperation = pyOr(contextValue(context, "operation"), "");
|
|
260
|
+
if (isTrigger && truthy(get(node, "eventMode")))
|
|
261
|
+
operation.eventMode = pyOr(get(node, "eventMode"), "");
|
|
262
|
+
const entry = {
|
|
263
|
+
schemaVersion: ctx.schemaVersion ?? SCHEMA_VERSION,
|
|
264
|
+
nodeType,
|
|
265
|
+
version: pyOr(get(config, "version", ""), get(node, "version", "")),
|
|
266
|
+
category: get(node, "category", ""),
|
|
267
|
+
tags: get(node, "tags", []),
|
|
268
|
+
connector: {
|
|
269
|
+
key: get(config, "connectorKey", ""),
|
|
270
|
+
},
|
|
271
|
+
operation,
|
|
272
|
+
display: {
|
|
273
|
+
label: pyOr(get(display, "label", ""), get(node, "displayName", "")),
|
|
274
|
+
description: pyOr(get(display, "description", ""), get(node, "description", "")),
|
|
275
|
+
icon: get(display, "icon", ""),
|
|
276
|
+
iconBackground: get(display, "iconBackground", ""),
|
|
277
|
+
iconBackgroundDark: get(display, "iconBackgroundDark", ""),
|
|
278
|
+
},
|
|
279
|
+
runtime: {
|
|
280
|
+
bpmnType: get(model, "type", ""),
|
|
281
|
+
serviceType: get(model, "serviceType", ""),
|
|
282
|
+
activityConfigurationVersion: pyOr(contextValue(context, "activityConfigurationVersion"), ""),
|
|
283
|
+
requiresConnection: hasContext(context, "connection"),
|
|
284
|
+
requiresFolderKey: hasContext(context, "folderKey"),
|
|
285
|
+
// Connector discovery metadata replaces the old empty-schema heuristic
|
|
286
|
+
// later in mergeConnectorRecord().
|
|
287
|
+
requiresConnectionForSchema: false,
|
|
288
|
+
},
|
|
289
|
+
inputSchema: { fields: inputFields },
|
|
290
|
+
outputSchema: { fields: outputFields },
|
|
291
|
+
};
|
|
292
|
+
// Some operations resolve their real field set from the VALUES of a few
|
|
293
|
+
// parent fields; record what a caller has to do about it.
|
|
294
|
+
const action = schemaAction(cmi, inputFields);
|
|
295
|
+
if (truthy(action))
|
|
296
|
+
operation.schemaAction = action;
|
|
297
|
+
const tableKey = curatedKey(get(config, "connectorKey", ""), get(config, "objectName", ""));
|
|
298
|
+
const dispatchStyle = (ctx.dispatchStyles ?? CURATED_DISPATCH_STYLE).get(tableKey);
|
|
299
|
+
if (truthy(dispatchStyle))
|
|
300
|
+
operation.dispatchStyle = dispatchStyle;
|
|
301
|
+
const paramGrammars = (ctx.paramGrammars ?? CURATED_PARAM_GRAMMARS).get(tableKey);
|
|
302
|
+
if (truthy(paramGrammars))
|
|
303
|
+
operation.paramGrammars = paramGrammars;
|
|
304
|
+
return entry;
|
|
305
|
+
}
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
// Connector records (`is connectors metadata`)
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
/** Accept the boolean JSON contract and older Yes/No cache rows. */
|
|
310
|
+
export function asBool(value) {
|
|
311
|
+
if (typeof value === "boolean")
|
|
312
|
+
return value;
|
|
313
|
+
if (typeof value === "string")
|
|
314
|
+
return ["true", "yes", "1"].includes(pyStrip(value).toLowerCase());
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
/** Read the connector tier across CLI response casing variants. */
|
|
318
|
+
export function connectorTier(record) {
|
|
319
|
+
if (!isRecord(record))
|
|
320
|
+
return null;
|
|
321
|
+
let value = get(record, "Tier");
|
|
322
|
+
if (value === null)
|
|
323
|
+
value = get(record, "tier");
|
|
324
|
+
return value !== null ? pyStrip(pyStr(value)) : null;
|
|
325
|
+
}
|
|
326
|
+
/** Normalize current and legacy connector tier spellings to `tier1`/`tier2`. */
|
|
327
|
+
export function normalizedConnectorTier(record) {
|
|
328
|
+
const value = connectorTier(record);
|
|
329
|
+
if (value === null)
|
|
330
|
+
return null;
|
|
331
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]/gu, "");
|
|
332
|
+
if (["1", "t1", "tier1"].includes(normalized))
|
|
333
|
+
return "tier1";
|
|
334
|
+
if (["2", "t2", "tier2"].includes(normalized))
|
|
335
|
+
return "tier2";
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Keep the node types whose connector metadata declares `tier` — actions and
|
|
340
|
+
* connector events both, per `scopedConnectorKey`. Returns the kept node types
|
|
341
|
+
* and the connector keys that matched.
|
|
342
|
+
*/
|
|
343
|
+
export function filterNodeTypesByTier(nodeTypes, connectorRecords, tier) {
|
|
344
|
+
if (tier !== "tier1" && tier !== "tier2")
|
|
345
|
+
throw new Error(`unsupported connector tier: ${tier}`);
|
|
346
|
+
const matching = new Set();
|
|
347
|
+
for (const [key, record] of connectorRecords)
|
|
348
|
+
if (normalizedConnectorTier(record) === tier)
|
|
349
|
+
matching.add(key);
|
|
350
|
+
const kept = nodeTypes.filter((nt) => {
|
|
351
|
+
const key = scopedConnectorKey(nt);
|
|
352
|
+
return key !== null && matching.has(key);
|
|
353
|
+
});
|
|
354
|
+
return [kept, matching];
|
|
355
|
+
}
|
|
356
|
+
/** `merge_connector_record`: stamp connector discovery metadata and its derived flag. */
|
|
357
|
+
export function mergeConnectorRecord(entry, record) {
|
|
358
|
+
if (!truthy(record))
|
|
359
|
+
return entry;
|
|
360
|
+
const connector = entry.connector;
|
|
361
|
+
const name = get(record, "Name");
|
|
362
|
+
if (truthy(name))
|
|
363
|
+
connector.name = name;
|
|
364
|
+
const tier = connectorTier(record);
|
|
365
|
+
if (tier !== null)
|
|
366
|
+
connector.tier = tier;
|
|
367
|
+
const discovery = {
|
|
368
|
+
type: pyOr(get(record, "DiscoveryType"), ""),
|
|
369
|
+
hasObjectDiscovery: asBool(get(record, "HasObjectDiscovery")),
|
|
370
|
+
hasFieldDiscovery: asBool(get(record, "HasFieldDiscovery")),
|
|
371
|
+
hasCustomObjectDiscovery: asBool(get(record, "HasCustomObjectDiscovery")),
|
|
372
|
+
hasCustomFieldDiscovery: asBool(get(record, "HasCustomFieldDiscovery")),
|
|
373
|
+
};
|
|
374
|
+
connector.discovery = discovery;
|
|
375
|
+
entry.runtime.requiresConnectionForSchema = Object.entries(discovery).some(([key, value]) => key !== "type" && truthy(value));
|
|
376
|
+
return entry;
|
|
377
|
+
}
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
// Node types
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
const CONNECTOR_PREFIX = "uipath.connector.";
|
|
382
|
+
const EVENT_PREFIX = "uipath.connector.event.";
|
|
383
|
+
const TRIGGER_PREFIX = "uipath.connector.trigger.";
|
|
384
|
+
/**
|
|
385
|
+
* The connector key for an ACTION node type, `uipath.connector.<key>.<op>`, or
|
|
386
|
+
* `null`. The event forms carry an extra segment and are answered by
|
|
387
|
+
* `eventConnectorKey` instead.
|
|
388
|
+
*/
|
|
389
|
+
export function actionConnectorKey(nodeType) {
|
|
390
|
+
if (!nodeType.startsWith(CONNECTOR_PREFIX))
|
|
391
|
+
return null;
|
|
392
|
+
const remainder = nodeType.slice(CONNECTOR_PREFIX.length);
|
|
393
|
+
if (remainder.startsWith("trigger.") || remainder.startsWith("event."))
|
|
394
|
+
return null;
|
|
395
|
+
const [connectorKey, separator] = partition(remainder, ".");
|
|
396
|
+
return separator && connectorKey ? connectorKey : null;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* The connector key for a WAIT-form event node type,
|
|
400
|
+
* `uipath.connector.event.<key>.<event>`, or `null`. Only the wait form is
|
|
401
|
+
* generated: `registry get` refuses the start form without a connection, and
|
|
402
|
+
* the SDK derives the start form from its wait sibling.
|
|
403
|
+
*/
|
|
404
|
+
export function eventConnectorKey(nodeType) {
|
|
405
|
+
if (!nodeType.startsWith(EVENT_PREFIX))
|
|
406
|
+
return null;
|
|
407
|
+
const [connectorKey, separator] = partition(nodeType.slice(EVENT_PREFIX.length), ".");
|
|
408
|
+
return separator && connectorKey ? connectorKey : null;
|
|
409
|
+
}
|
|
410
|
+
/** The connector a node type belongs to, for SCOPE filtering: action or wait event. */
|
|
411
|
+
export function scopedConnectorKey(nodeType) {
|
|
412
|
+
return actionConnectorKey(nodeType) ?? eventConnectorKey(nodeType);
|
|
413
|
+
}
|
|
414
|
+
/** The connector a node type belongs to, in ANY of its three forms — trigger included. */
|
|
415
|
+
export function catalogConnectorKey(nodeType) {
|
|
416
|
+
const text = pyStr(pyOr(nodeType, ""));
|
|
417
|
+
const key = scopedConnectorKey(text);
|
|
418
|
+
if (key)
|
|
419
|
+
return key;
|
|
420
|
+
if (!text.startsWith(TRIGGER_PREFIX))
|
|
421
|
+
return null;
|
|
422
|
+
const [candidate, , event] = partition(text.slice(TRIGGER_PREFIX.length), ".");
|
|
423
|
+
return candidate && event ? candidate : null;
|
|
424
|
+
}
|
|
425
|
+
export const FIRST_PARTY_PREFIX = "uipath-";
|
|
426
|
+
export const MOCK_MARKER = "mock";
|
|
427
|
+
/**
|
|
428
|
+
* Does this connector key belong in the PUBLISHED catalog? `custom-<org>-*` are
|
|
429
|
+
* one tenant's own connectors and `uipath-mock-*` are eval fixtures; neither
|
|
430
|
+
* resolves for another user.
|
|
431
|
+
*/
|
|
432
|
+
export function isFirstPartyConnector(key) {
|
|
433
|
+
const lowered = pyStr(pyOr(key, "")).toLowerCase();
|
|
434
|
+
return lowered.startsWith(FIRST_PARTY_PREFIX) && !lowered.includes(MOCK_MARKER);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* `(kept node types, dropped connector keys)` for a FULL build. A node type
|
|
438
|
+
* with no connector key at all is not a connector node and is kept.
|
|
439
|
+
*/
|
|
440
|
+
export function selectPublishedConnectors(nodeTypes) {
|
|
441
|
+
const droppedSet = new Set();
|
|
442
|
+
for (const nt of nodeTypes) {
|
|
443
|
+
const key = catalogConnectorKey(nt);
|
|
444
|
+
if (key && !isFirstPartyConnector(key))
|
|
445
|
+
droppedSet.add(key);
|
|
446
|
+
}
|
|
447
|
+
const dropped = sortedStrings(droppedSet);
|
|
448
|
+
if (dropped.length === 0)
|
|
449
|
+
return [[...nodeTypes], []];
|
|
450
|
+
const kept = nodeTypes.filter((nt) => {
|
|
451
|
+
const key = catalogConnectorKey(nt);
|
|
452
|
+
return isFirstPartyConnector(key) || !key;
|
|
453
|
+
});
|
|
454
|
+
return [kept, dropped];
|
|
455
|
+
}
|
|
456
|
+
/** The action segment of a `uipath.connector.<key>.<action>` node type. */
|
|
457
|
+
export function actionIdFromNodeType(nodeType) {
|
|
458
|
+
const parts = nodeType.split(".");
|
|
459
|
+
return parts.length >= 4 ? parts[parts.length - 1] : nodeType.replaceAll(".", "-");
|
|
460
|
+
}
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
462
|
+
// Generic templates and HTTP passthroughs
|
|
463
|
+
// ---------------------------------------------------------------------------
|
|
464
|
+
/**
|
|
465
|
+
* HTTP escape hatches (`*-http-request`, objectName usually `httpRequest` but
|
|
466
|
+
* sometimes null) are kept as-is, not expanded — there is no object catalog to
|
|
467
|
+
* iterate.
|
|
468
|
+
*/
|
|
469
|
+
const HTTP_PASSTHROUGH_OBJECT_NAMES = new Set(["httpRequest", "HttpRequest", "http_request"]);
|
|
470
|
+
export function isHttpPassthrough(entry) {
|
|
471
|
+
if (HTTP_PASSTHROUGH_OBJECT_NAMES.has(get(entry.operation, "objectName")))
|
|
472
|
+
return true;
|
|
473
|
+
// Some connectors leave objectName null on the http-request action; fall
|
|
474
|
+
// back to recognising the nodeType suffix.
|
|
475
|
+
return actionIdFromNodeType(entry.nodeType).endsWith("http-request");
|
|
476
|
+
}
|
|
477
|
+
// ---------------------------------------------------------------------------
|
|
478
|
+
// IS payload key normalization
|
|
479
|
+
// ---------------------------------------------------------------------------
|
|
480
|
+
/** Map a PascalCase key to camelCase (first character lowered). */
|
|
481
|
+
export function toLowerCamel(s) {
|
|
482
|
+
if (typeof s !== "string" || !s)
|
|
483
|
+
return s;
|
|
484
|
+
return (startsWithUpper(s) ? lowerFirst(s) : s);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Recursively lower-camel-case every dict key in `value`. The IS CLI started
|
|
488
|
+
* returning PascalCase keys for parameters, fields and references; the library
|
|
489
|
+
* uses camelCase, so the on-disk schema stays stable across CLI versions.
|
|
490
|
+
*/
|
|
491
|
+
export function normalizeFieldKeys(value) {
|
|
492
|
+
if (isRecord(value)) {
|
|
493
|
+
const out = {};
|
|
494
|
+
for (const [k, v] of Object.entries(value))
|
|
495
|
+
out[toLowerCamel(k)] = normalizeFieldKeys(v);
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
if (Array.isArray(value))
|
|
499
|
+
return value.map(normalizeFieldKeys);
|
|
500
|
+
return value;
|
|
501
|
+
}
|
|
502
|
+
/** `_pick`: the first non-None value among `d[k]` for the given keys, else `null`. */
|
|
503
|
+
export function pick(d, ...keys) {
|
|
504
|
+
if (!isRecord(d))
|
|
505
|
+
return null;
|
|
506
|
+
for (const k of keys) {
|
|
507
|
+
const v = d[k];
|
|
508
|
+
if (v !== null && v !== undefined)
|
|
509
|
+
return v;
|
|
510
|
+
}
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Reporter } from "../reporter.js";
|
|
2
|
+
import { type UipClient } from "../uip/client.js";
|
|
3
|
+
import { type BuildOptions, type CatalogScope } from "./build.js";
|
|
4
|
+
export interface BuildLibraryCliOptions extends BuildOptions {
|
|
5
|
+
/** `--cache-dir`: raw `flow registry get` responses and the search result. */
|
|
6
|
+
cacheDir: string;
|
|
7
|
+
/** `--is-cache-dir`: `is resources describe/list` and `is triggers` payloads. */
|
|
8
|
+
isCacheDir: string;
|
|
9
|
+
/** `--connectors-cache-dir`: `is connectors metadata` payloads. */
|
|
10
|
+
connectorsCacheDir: string;
|
|
11
|
+
/** `--keep-temp`: leave the three cache directories behind. */
|
|
12
|
+
keepTemp: boolean;
|
|
13
|
+
/** `--concurrency`: parallel remote CLI calls. */
|
|
14
|
+
concurrency: number;
|
|
15
|
+
/**
|
|
16
|
+
* `--offline`: never run `uip`; treat an uncached call as a failed one.
|
|
17
|
+
*
|
|
18
|
+
* For replaying a build from the caches a previous run left behind, which is
|
|
19
|
+
* what the pipeline publishes when something looks wrong. See
|
|
20
|
+
* {@link OfflineUipClient} for why failing is the faithful answer.
|
|
21
|
+
*/
|
|
22
|
+
offline: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** argparse's defaults, verbatim. */
|
|
25
|
+
export declare const DEFAULTS: {
|
|
26
|
+
readonly outputDir: "./library";
|
|
27
|
+
readonly cacheDir: "./.registry-cache";
|
|
28
|
+
readonly isCacheDir: "./.is-cache";
|
|
29
|
+
readonly connectorsCacheDir: "./.is-connectors-cache";
|
|
30
|
+
readonly catalogScope: CatalogScope;
|
|
31
|
+
readonly concurrency: 10;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The options for this argv, or a {@link UsageError} carrying argparse's
|
|
35
|
+
* wording. Both `--flag value` and `--flag=value` are accepted, as argparse
|
|
36
|
+
* accepts them; anything that is neither a known flag nor a flag's value is an
|
|
37
|
+
* unrecognized argument, because the generator takes no positionals.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseArgs(argv: readonly string[]): BuildLibraryCliOptions;
|
|
40
|
+
/** What a test swaps in for the tenant and the terminal. */
|
|
41
|
+
export interface RunDeps {
|
|
42
|
+
client?: UipClient;
|
|
43
|
+
reporter?: Reporter;
|
|
44
|
+
}
|
|
45
|
+
export declare function run(argv: string[], deps?: RunDeps): Promise<number>;
|