@uipath/maestro-builder-sdk 6.56.0 → 6.57.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/decompile.js CHANGED
@@ -112,6 +112,38 @@ let runAbsorbed = new Map();
112
112
  let runFlowId = 'flow';
113
113
  /** `DecompileOptions.readSidecar` for the current run. */
114
114
  let runReadSidecar;
115
+ /**
116
+ * A host's own say on decompile's default script form, beside the TypeScript
117
+ * hand-over in core/typescript-module.ts:
118
+ * `globalThis[Symbol.for('uipath.maestro-builder-sdk.scripts')] = 'string'`.
119
+ */
120
+ const HOST_SCRIPTS = Symbol.for('uipath.maestro-builder-sdk.scripts');
121
+ /**
122
+ * Function scripts by default only under Node. The exact text a function form
123
+ * stores is what the loader's `Function.prototype.toString()` returns, which is
124
+ * the author's own text under Node's type stripping (what `compile` and `uip`
125
+ * run) and a reprint under other loaders. A host that runs the SDK in a page has
126
+ * not been measured, so there a decompiled script stays a string unless asked.
127
+ */
128
+ function defaultScripts() {
129
+ const host = globalThis[HOST_SCRIPTS];
130
+ if (host === 'function' || host === 'string')
131
+ return host;
132
+ // Real Node, tested as core/typescript-module.ts does: a page bundle may shim
133
+ // `process.versions.node` (cli-browser's banner sets it) but does not define
134
+ // `process.getBuiltinModule`.
135
+ return typeof globalThis.process?.getBuiltinModule === 'function' ? 'function' : 'string';
136
+ }
137
+ /** How this run prints scripts (`DecompileOptions.scripts`). */
138
+ let runScripts = 'function';
139
+ /** The module-level functions print hooks declared this run, in order. */
140
+ let runModuleFunctions = [];
141
+ /**
142
+ * Each step's reserved function name, by node: keyed by the node itself, not its
143
+ * id, because a root step and a subflow step may share an id, and two
144
+ * `function script_<id>` declarations would make the module unloadable.
145
+ */
146
+ let runFunctionNames = new Map();
115
147
  function warnOnce(message) {
116
148
  if (runWarned.has(message))
117
149
  return;
@@ -336,6 +368,8 @@ class Imports {
336
368
  locals = new Set(['flow']);
337
369
  /** Names a package may not take bare: the SDK's helpers and every built-in factory. */
338
370
  reserved;
371
+ /** The SDK's type-only names (`FlowVars`): `import { type X }`, erased under type stripping. */
372
+ types = new Set();
339
373
  constructor() {
340
374
  this.reserved = new Set([
341
375
  ...SDK_HELPER_NAMES,
@@ -368,14 +402,25 @@ class Imports {
368
402
  this.locals.add(local);
369
403
  return local;
370
404
  }
405
+ /**
406
+ * A type-only name from the SDK: `import { type X }`, erased under type
407
+ * stripping, so it never needs a runtime export. Taken as a local, so no
408
+ * package factory binds the same name.
409
+ */
410
+ useType(name) {
411
+ this.types.add(name);
412
+ this.locals.add(name);
413
+ return name;
414
+ }
371
415
  /** One import per module: the SDK first, then the packages by name. */
372
416
  line(specifier) {
373
417
  const lines = [];
374
418
  for (const [module, names] of [...this.used].sort(([a], [b]) => (a === undefined ? -1 : b === undefined ? 1 : a.localeCompare(b)))) {
375
- if (names.size === 0)
419
+ const types = module === undefined ? [...this.types].filter((t) => !names.has(t)).sort().map((t) => `type ${t}`) : [];
420
+ if (names.size === 0 && types.length === 0)
376
421
  continue;
377
422
  const bindings = [...names].sort(([a], [b]) => a.localeCompare(b)).map(([name, local]) => (local === name ? name : `${name} as ${local}`));
378
- lines.push(`import { ${bindings.join(', ')} } from ${tsLiteral(module ?? specifier)};`);
423
+ lines.push(`import { ${[...bindings, ...types].join(', ')} } from ${tsLiteral(module ?? specifier)};`);
379
424
  }
380
425
  return lines.join('\n');
381
426
  }
@@ -826,6 +871,11 @@ function emitRegisteredSpec(node, imp, inputNames, graph) {
826
871
  json: (v) => tsLiteral(v),
827
872
  key: (k) => key(k),
828
873
  helper: (name) => imp.use(name),
874
+ typeHelper: (name) => imp.useType(name),
875
+ warn: (message) => warnOnce(`step ${tsLiteral(node.id)}: ${message}`),
876
+ functionName: () => moduleFunctionName(node),
877
+ declareFunction: (source) => { runModuleFunctions.push(source); },
878
+ scripts: runScripts,
829
879
  };
830
880
  const call = impl.print?.(spec, print) ?? {
831
881
  factory: impl.factory,
@@ -2838,6 +2888,9 @@ function foldOutputBacking(flow) {
2838
2888
  }
2839
2889
  export function decompile(flow, options = {}) {
2840
2890
  flow = foldOutputBacking(flow);
2891
+ runScripts = options.scripts ?? defaultScripts();
2892
+ runModuleFunctions = [];
2893
+ runFunctionNames = new Map();
2841
2894
  const importSpecifier = options.importSpecifier ?? './flow-sdk.js';
2842
2895
  runStrict = options.bestEffort === false;
2843
2896
  runWarn = options.warn;
@@ -2991,7 +3044,27 @@ export function decompile(flow, options = {}) {
2991
3044
  const hoisted = [...carriedConsts, ...runRawManifests]
2992
3045
  .map(([name, json]) => `const ${name} = ${json};`)
2993
3046
  .join('\n\n');
2994
- return [imp.line(importSpecifier), hoisted, chain].filter((p) => p.length > 0).join('\n\n') + '\n';
3047
+ const functions = runModuleFunctions.join('\n\n');
3048
+ return [imp.line(importSpecifier), hoisted, chain, functions].filter((p) => p.length > 0).join('\n\n') + '\n';
3049
+ }
3050
+ /**
3051
+ * A step's module-level function name: `script_<id>`, with what an identifier
3052
+ * cannot hold replaced by `_`, and a numeric suffix when two steps (in different
3053
+ * scopes, or with ids that sanitize alike) would get the same one.
3054
+ * The `script_` prefix keeps it clear of every SDK export (all camelCase) and of
3055
+ * the hoisted constants.
3056
+ */
3057
+ function moduleFunctionName(node) {
3058
+ const have = runFunctionNames.get(node);
3059
+ if (have)
3060
+ return have;
3061
+ const base = `script_${node.id.replace(/[^A-Za-z0-9_$]/g, '_')}`;
3062
+ const taken = new Set(runFunctionNames.values());
3063
+ let name = base;
3064
+ for (let i = 2; taken.has(name); i++)
3065
+ name = `${base}_${i}`;
3066
+ runFunctionNames.set(node, name);
3067
+ return name;
2995
3068
  }
2996
3069
  /**
2997
3070
  * Top-level nodes (no `parentId`) that no root trigger reaches over ANY edge —
@@ -327,6 +327,27 @@ export interface FlowPrintContext {
327
327
  key(name: string): string;
328
328
  /** An authoring helper (`lit`, `input`, …) by name: imported into the printed source, and its identifier. */
329
329
  helper(name: string): string;
330
+ /** An exported TYPE (`FlowVars`) by name: imported as `type <name>`, and its identifier. */
331
+ typeHelper(name: string): string;
332
+ /**
333
+ * Report a finding about this step, printed once with the step named.
334
+ * Informational: it never fails `--strict`, so it is for a choice of spelling
335
+ * that loses nothing, not for something recompiling would drop.
336
+ */
337
+ warn(message: string): void;
338
+ /**
339
+ * The identifier of this step's module-level function: unique in the printed
340
+ * module, the same on every call for the step. Reserving it declares nothing;
341
+ * {@link declareFunction} does.
342
+ */
343
+ functionName(): string;
344
+ /** Add a declaration to the end of the printed module, after the flow, verbatim. */
345
+ declareFunction(source: string): void;
346
+ /**
347
+ * How scripts print: `function` (the default) as a TypeScript function where
348
+ * the body allows it, `string` always as `script({ code })`.
349
+ */
350
+ readonly scripts: 'function' | 'string';
330
351
  }
331
352
  /** What a node-local `check` may ask of the checker. */
332
353
  export interface FlowCheckContext {
package/dist/load.js CHANGED
@@ -3,6 +3,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
3
3
  import { basename, dirname, join, resolve } from 'node:path';
4
4
  import { statSync } from 'node:fs';
5
5
  import { useThisSdkForAuthoredSource } from './sdk-resolve.js';
6
+ import { scriptFunctionLowerings } from './core/script-function.js';
7
+ import { scriptFunctionScopeProblem } from './script-function-scope.js';
6
8
  /** Resolve a user argument (a `.ts` path or a BaseName) to a flow source file. */
7
9
  export function resolveFlowFile(arg) {
8
10
  const candidates = [arg, `${arg}.flow.ts`, `${arg}.ts`];
@@ -21,6 +23,7 @@ export function resolveFlowFile(arg) {
21
23
  }
22
24
  export async function loadBuiltFlow(file) {
23
25
  useThisSdkForAuthoredSource();
26
+ const firstLowering = scriptFunctionLowerings().length;
24
27
  let mod;
25
28
  try {
26
29
  mod = await import(pathToFileURL(file).href);
@@ -34,6 +37,16 @@ export async function loadBuiltFlow(file) {
34
37
  const built = typeof exp.build === 'function' ? exp.build() : exp;
35
38
  if (!built || !Array.isArray(built.steps))
36
39
  throw new Error(`${file} default export is not a built flow. Did you call .build()?`);
40
+ // A `script(fn)` function was lowered from its own text, which cannot show a
41
+ // name it takes from its module; this source can (script-function-scope.ts).
42
+ // Only a flow that lowered one pays for the program this builds.
43
+ // The functions this import lowered; see scriptFunctionLowerings.
44
+ const lowered = scriptFunctionLowerings().slice(firstLowering);
45
+ if (lowered.length > 0) {
46
+ const problem = scriptFunctionScopeProblem(file, lowered);
47
+ if (problem)
48
+ throw new Error(problem);
49
+ }
37
50
  return built;
38
51
  }
39
52
  /**
@@ -3,7 +3,48 @@
3
3
  * docs/NODE_TYPE_ARCHITECTURE_DESIGN.md; the package entry re-exports it.
4
4
  */
5
5
  import { type ActionSpec } from '../../../core/actions.js';
6
- import type { ScriptInputs } from './spec.js';
6
+ import type { ScriptReturns } from '../../../core/script-return.js';
7
+ import type { ScriptInputs, ScriptValue } from './spec.js';
8
+ /**
9
+ * Declare an inline script action, as a TypeScript function the compiler checks.
10
+ *
11
+ * The function is never called. Its body, exactly as written, becomes the
12
+ * script; its parameter is an authoring-time name for the Flow runtime's own
13
+ * `$vars` global and is renamed to it. `returns` is the declaration that reaches
14
+ * the `.flow`, and tsc checks the function's return against it.
15
+ *
16
+ * The function may read only what the script runtime has (the ES2020 built-ins
17
+ * and `console`): a module constant, an import or a library throws here, because
18
+ * in the run it would be a `ReferenceError`.
19
+ *
20
+ * ```ts
21
+ * .step('post', script(parseMessage, { returns: { channel: 'string', threadTs: 'string' } }))
22
+ *
23
+ * function parseMessage(vars: FlowVars) {
24
+ * const m = /archives\/([A-Z0-9]+)\/p(\d{10})(\d{6})/.exec(String(vars.start.output.message));
25
+ * if (!m) throw new Error('not a Slack permalink');
26
+ * return { channel: m[1], threadTs: m[2] + '.' + m[3] };
27
+ * }
28
+ * ```
29
+ *
30
+ * @param fn - The script, as a function of the variable scope (or of nothing).
31
+ * @param options - What the function returns (`returns`), as `script({ returns })` takes it.
32
+ * @returns An action spec to pass to `.step(name, …)`.
33
+ */
34
+ export declare function script<V, const R extends ScriptReturns>(fn: (vars: V) => ScriptValue<R>, options: {
35
+ returns: R;
36
+ }): ActionSpec;
37
+ /**
38
+ * Declare an inline script action, as a TypeScript function, with no declared
39
+ * return: the body's type is inferred as it is for a string body.
40
+ *
41
+ * @param fn - The script, as a function of the variable scope (or of nothing).
42
+ * @param options - No `returns`; pass one to have tsc check the function against it.
43
+ * @returns An action spec to pass to `.step(name, …)`.
44
+ */
45
+ export declare function script<V>(fn: (vars: V) => unknown, options?: {
46
+ returns?: never;
47
+ }): ActionSpec;
7
48
  /**
8
49
  * Declare an inline script action.
9
50
  *
@@ -3,13 +3,12 @@
3
3
  * docs/NODE_TYPE_ARCHITECTURE_DESIGN.md; the package entry re-exports it.
4
4
  */
5
5
  import { builtinActionSpec } from '../../../core/actions.js';
6
- /**
7
- * Declare an inline script action.
8
- *
9
- * @param inputs - The JavaScript body (`code`, which must `return`) and what it
10
- * returns (`returns`).
11
- * @returns An action spec to pass to `.step(name, …)`.
12
- */
13
- export function script(inputs) {
14
- return builtinActionSpec({ kind: 'script', inputs });
6
+ import { lowerScriptFunction } from '../../../core/script-function.js';
7
+ import { FLOW_SCRIPT_RUNTIME } from './runtime.js';
8
+ export function script(arg, options) {
9
+ if (typeof arg !== 'function')
10
+ return builtinActionSpec({ kind: 'script', inputs: arg });
11
+ const code = lowerScriptFunction(arg, FLOW_SCRIPT_RUNTIME, `script(${arg.name || 'function'})`);
12
+ const returns = options?.returns;
13
+ return builtinActionSpec({ kind: 'script', inputs: { code, ...(returns !== undefined ? { returns } : {}) } });
15
14
  }
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import { declaredScriptShape, inferScriptShape } from '../../../core/script-return.js';
8
8
  import { jsonClone } from '../../../core/json-clone.js';
9
+ import { scriptBodyEarlyError, scriptBodySyntaxError, scriptBodyTypeScriptSyntax, tryLowerScriptFunction } from '../../../core/script-function.js';
10
+ import { FLOW_SCRIPT_RUNTIME } from './runtime.js';
9
11
  import { checkScript } from './check.js';
10
12
  /**
11
13
  * Declare what a `core.action.script` node actually publishes.
@@ -83,6 +85,72 @@ function liftReturns(node, code, definitionType) {
83
85
  }
84
86
  return declared;
85
87
  }
88
+ /** The fallback reason for a body that parses alone but not as the printed function, or comes back changed. */
89
+ const UNCHANGED = 'its text would not come back unchanged as a function body (a line comment on its last line, for one).';
90
+ /**
91
+ * The function a script step prints as, declared after the flow, or undefined
92
+ * when the step keeps the string form (`script({ code })`), with a warning.
93
+ *
94
+ * A designer-edited flow may hold a half-written script, and decompile still has
95
+ * to produce a source that builds, so a body that cannot become a function is a
96
+ * reason to keep the string, never to fail. It becomes one only when all of
97
+ * these hold (docs/SCRIPT_FUNCTION_DESIGN.md, "Decompile"):
98
+ *
99
+ * 1. it parses;
100
+ * 2. it has no TypeScript-only syntax, which the loader would erase or reread
101
+ * (`const n: number`, or `f<T>(x)`, which JavaScript reads as comparisons);
102
+ * 3. `script(fn)` would accept it: it reads only names on the runtime's list
103
+ * (a function that failed this would throw when the source loads);
104
+ * 4. it comes back from compile byte for byte. The function is printed with
105
+ * the body verbatim between its braces, which lowering returns exactly, so
106
+ * this holds for nearly every body that passes the others; it is checked
107
+ * because it is cheap, and a line comment on the last line is the case that
108
+ * fails it (it would swallow the closing brace);
109
+ * 5. the engine compiles it as a strict function of `$vars`: an early error
110
+ * (a duplicate `let`, a `const $vars`, an octal literal) that TypeScript's
111
+ * parser does not report would make the whole module unloadable.
112
+ *
113
+ * The parameter is named `$vars`, so nothing is renamed and the body's text is
114
+ * the node's text.
115
+ */
116
+ function printAsFunction(code, ctx) {
117
+ if (ctx.scripts !== 'function')
118
+ return undefined;
119
+ const keep = (why) => {
120
+ ctx.warn(`script kept as a string: ${why} Fix the script and decompile again to get a typed function.`);
121
+ return undefined;
122
+ };
123
+ const syntax = scriptBodySyntaxError(code);
124
+ if (syntax)
125
+ return keep(`its body does not parse (${syntax}).`);
126
+ const typed = scriptBodyTypeScriptSyntax(code);
127
+ if (typed)
128
+ return keep(`it contains ${typed}, which a TypeScript loader would erase or reread, changing the script.`);
129
+ const name = ctx.functionName();
130
+ // With authoring's own rules: the printed function is lowered by `script(fn)`
131
+ // when the source is compiled back, so a body that rule refuses (`$vars?.x`,
132
+ // `$vars['x']`, an alias) must stay a string here, or compile would refuse it.
133
+ const lowered = tryLowerScriptFunction(`function ${name}($vars) {${code}}`, FLOW_SCRIPT_RUNTIME, { scopeReadsOnly: true });
134
+ if (!lowered.ok) {
135
+ // Worded as the SDK's list, not as a fault: whether a string body that reads
136
+ // the name really fails in the run is what #1411 has yet to establish.
137
+ if (lowered.free.length > 0) {
138
+ return keep(`it reads ${lowered.free.join(', ')}, which is not on the SDK's list of names the Flow script runtime provides.`);
139
+ }
140
+ // The body parses on its own (checked above) but not between the braces.
141
+ if (lowered.syntax)
142
+ return keep(UNCHANGED);
143
+ return keep(lowered.problems.join(' '));
144
+ }
145
+ if (lowered.code !== code)
146
+ return keep(UNCHANGED);
147
+ // Last, on what will actually be printed: an early error makes the module unloadable.
148
+ const early = scriptBodyEarlyError(code, FLOW_SCRIPT_RUNTIME);
149
+ if (early)
150
+ return keep(`as a function in an ES module (strict code) it would not load: ${early}.`);
151
+ ctx.declareFunction(`function ${name}($vars: ${ctx.typeHelper('FlowVars')}) {${code}}`);
152
+ return name;
153
+ }
86
154
  /** Key-sorted copy, so a property map compares by content and not by order. */
87
155
  function sortKeys(o) {
88
156
  return Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b)));
@@ -119,6 +187,9 @@ function scriptAt(version) {
119
187
  const returns = r === undefined ? undefined
120
188
  : typeof r === 'string' ? ctx.string(r)
121
189
  : `{ ${Object.entries(r).map(([k, t]) => `${ctx.key(k)}: ${ctx.string(String(t))}`).join(', ')} }`;
190
+ const fn = printAsFunction(inputs.code, ctx);
191
+ if (fn !== undefined)
192
+ return { factory: 'script', positional: [fn], options: [['returns', returns]] };
122
193
  return { factory: 'script', options: [['code', ctx.string(inputs.code)], ['returns', returns]] };
123
194
  },
124
195
  check: ({ inputs }, ctx) => checkScript(inputs, ctx),
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The Flow script runtime, as `script(fn)` lowers for it. Not part of the public
3
+ * surface: `index.ts` does not re-export this file.
4
+ */
5
+ import type { ScriptRuntime } from '../../../core/script-function.js';
6
+ /**
7
+ * What a Flow script body can read: the language's ES2020 built-ins, `console`,
8
+ * and the runtime's `$vars`. From the Flow skill's `references/script.md` ("The
9
+ * JavaScript runtime"), which also lists what reads `undefined` in a real run and
10
+ * is therefore absent here: `Intl`, `URL`, `TextEncoder`, `Buffer`, `fetch`,
11
+ * `require`, `structuredClone`. Change this when the runtime changes.
12
+ *
13
+ * Annex B's `escape`/`unescape` are left off on purpose: they URL-encode, and
14
+ * `escape` is the likeliest name for an author's own HTML-escape helper, which
15
+ * the runtime's global would silently replace.
16
+ *
17
+ * Only `script(fn)` checks against it. A string body is not checked, until
18
+ * there is evidence the list is complete (#1411).
19
+ */
20
+ export declare const FLOW_SCRIPT_RUNTIME: ScriptRuntime;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * What a Flow script body can read: the language's ES2020 built-ins, `console`,
3
+ * and the runtime's `$vars`. From the Flow skill's `references/script.md` ("The
4
+ * JavaScript runtime"), which also lists what reads `undefined` in a real run and
5
+ * is therefore absent here: `Intl`, `URL`, `TextEncoder`, `Buffer`, `fetch`,
6
+ * `require`, `structuredClone`. Change this when the runtime changes.
7
+ *
8
+ * Annex B's `escape`/`unescape` are left off on purpose: they URL-encode, and
9
+ * `escape` is the likeliest name for an author's own HTML-escape helper, which
10
+ * the runtime's global would silently replace.
11
+ *
12
+ * Only `script(fn)` checks against it. A string body is not checked, until
13
+ * there is evidence the list is complete (#1411).
14
+ */
15
+ export const FLOW_SCRIPT_RUNTIME = {
16
+ scopeGlobal: '$vars',
17
+ name: 'the Flow script runtime',
18
+ globals: new Set([
19
+ 'undefined', 'NaN', 'Infinity', 'globalThis', 'console',
20
+ 'Object', 'Function', 'Array', 'Number', 'Boolean', 'String', 'Symbol', 'BigInt', 'Date', 'RegExp', 'Math', 'JSON',
21
+ 'Promise', 'Proxy', 'Reflect', 'Map', 'Set', 'WeakMap', 'WeakSet',
22
+ 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError',
23
+ 'ArrayBuffer', 'SharedArrayBuffer', 'DataView', 'Atomics',
24
+ 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
25
+ 'Float32Array', 'Float64Array', 'BigInt64Array', 'BigUint64Array',
26
+ 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
27
+ 'encodeURI', 'encodeURIComponent', 'decodeURI', 'decodeURIComponent', 'eval',
28
+ ]),
29
+ };
@@ -3,7 +3,7 @@
3
3
  * in `FlowActionSpecs`. Moved from core/actions.ts in Phase 4 of
4
4
  * docs/NODE_TYPE_ARCHITECTURE_DESIGN.md.
5
5
  */
6
- import type { ScriptReturns } from '../../../core/script-return.js';
6
+ import type { ScriptReturns, ScriptReturnType } from '../../../core/script-return.js';
7
7
  export interface ScriptInputs {
8
8
  /** JavaScript body. Must `return` its result. Referenced via out('<step>'). */
9
9
  code: string;
@@ -42,6 +42,37 @@ export interface ScriptInputs {
42
42
  */
43
43
  returns?: ScriptReturns;
44
44
  }
45
+ /**
46
+ * The runtime's variable scope, as a script function's parameter sees it.
47
+ *
48
+ * `$vars` is the Flow script runtime's own global, not something the SDK
49
+ * provides: the SDK never calls a script function, and `script(fn)` renames the
50
+ * parameter to `$vars` in the body it emits. This type is a loose name for what
51
+ * that global holds, so `vars.start.output.message` type-checks. Any other
52
+ * parameter type works too; the SDK cannot check it against the runtime.
53
+ *
54
+ * ```ts
55
+ * .step('parse', script(parse, { returns: { amount: 'number' } }))
56
+ *
57
+ * function parse(vars: FlowVars) {
58
+ * return { amount: Number(vars.start.output.amount) };
59
+ * }
60
+ * ```
61
+ */
62
+ export type FlowVars = Record<string, any>;
63
+ /** The TypeScript type one of {@link ScriptReturnType}'s names stands for. */
64
+ export type ScriptValueOf<T extends ScriptReturnType> = T extends 'string' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : T extends 'array' ? readonly unknown[] : T extends 'object' ? object : unknown;
65
+ /**
66
+ * What a script function must return for a given `returns`: the type a name
67
+ * stands for, or, for a field map, an object with those fields.
68
+ *
69
+ * This is how `script(fn, { returns })` makes tsc check the function against the
70
+ * declaration that reaches the `.flow`: `returns: { total: 'number' }` over a
71
+ * function that returns a string `total` does not compile.
72
+ */
73
+ export type ScriptValue<R extends ScriptReturns> = R extends ScriptReturnType ? ScriptValueOf<R> : {
74
+ [F in keyof R]: R[F] extends ScriptReturnType ? ScriptValueOf<R[F]> : never;
75
+ };
45
76
  declare module '../../../core/actions.js' {
46
77
  interface FlowActionSpecs {
47
78
  /** The spec {@link script} builds. */
@@ -0,0 +1,57 @@
1
+ /** One name a script function reads from outside itself. */
2
+ export interface ScriptFunctionCapture {
3
+ /** How the call is named in messages: `script(renderSafe)`. */
4
+ call: string;
5
+ /** The name read. */
6
+ name: string;
7
+ /** Where the binding it resolves to is declared, `file:line`. */
8
+ declaredAt: string;
9
+ /** Where the read is, `file:line`. */
10
+ readAt: string;
11
+ }
12
+ /** What the pass found in a source: the captures, and what it could not check. */
13
+ export interface ScriptFunctionScope {
14
+ captures: ScriptFunctionCapture[];
15
+ /** `script(<arg>)` calls whose function-typed argument it could not follow, `file:line` each. */
16
+ unfollowed: string[];
17
+ /**
18
+ * `script(<arg>)` calls whose argument it could not follow and whose type is
19
+ * `any` or `unknown` (`fns.check` on an `any` object, a function from a `.js`
20
+ * file): a function or a string script's options, the pass cannot tell which.
21
+ */
22
+ untyped: string[];
23
+ /** How many distinct script functions it followed to a declaration. */
24
+ followed: number;
25
+ /**
26
+ * Each followed function's text as Node's type stripping leaves it, whitespace
27
+ * removed: what `Function.prototype.toString()` returns for it under that
28
+ * loader, so a lowered function can be matched to its declaration. Empty when
29
+ * this Node has no `module.stripTypeScriptTypes`.
30
+ */
31
+ followedTexts: Set<string>;
32
+ }
33
+ /**
34
+ * Every name a `script(fn)` function in `file` (or a file it imports) reads from
35
+ * outside itself, and every call whose function it could not follow.
36
+ *
37
+ * It follows a bare identifier (through `const`, a function declaration and
38
+ * imports between the author's files) and an inline function. Any other
39
+ * function-typed argument (`script(h.check)`, `script(fns.check)`, a wrapper's
40
+ * parameter) is `unfollowed`, and the caller refuses it rather than let a capture
41
+ * through unchecked. An argument that is not a function (`script({ code })`, or
42
+ * options passed by name) is the string form and is not a script function.
43
+ */
44
+ export declare function scriptFunctionScope(file: string): ScriptFunctionScope;
45
+ /** A whitespace-free key for comparing function texts. */
46
+ export declare function functionTextKey(text: string): string;
47
+ /**
48
+ * Why a loaded flow cannot be trusted with its script functions, or undefined.
49
+ *
50
+ * Fails closed: a function the pass could not follow, or fewer functions found in
51
+ * the source than `script(fn)` lowered (a call made through `const sc = script`,
52
+ * a re-exporting module, or a function from a file outside the program), is
53
+ * refused like a capture, because what it reads was not checked.
54
+ */
55
+ export declare function scriptFunctionScopeProblem(file: string, lowered: readonly string[]): string | undefined;
56
+ /** The message a load refuses with, one line per capture. */
57
+ export declare function scriptFunctionCaptureMessage(captures: readonly ScriptFunctionCapture[]): string;