@uipath/maestro-builder-sdk 6.12.1 → 6.13.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/README.md CHANGED
@@ -128,9 +128,9 @@ reports its lookups as unresolved no matter how many times you prepare.
128
128
 
129
129
  It does three things, all of them on disk:
130
130
 
131
- - finds the connection and writes its ids into `bindings.json`, so the symbolic
131
+ - finds the connection and writes its ids into `.flow-sdk/bindings.json`, so the symbolic
132
132
  `slack` and `shared` names in the code resolve;
133
- - records each answered lookup in `connectors-local/`, so compiling stays
133
+ - records each answered lookup in `.flow-sdk/connectors-local/`, so compiling stays
134
134
  offline and repeatable;
135
135
  - writes the connection-resolved field schema for that operation, which is how
136
136
  a field the static library does not know about becomes compilable at all.
@@ -265,11 +265,51 @@ Exact signatures travel with the package, as TSDoc on the declarations in
265
265
  symbol, surfaced by an editor on hover. The authoring guides carry the same
266
266
  reference as Markdown.
267
267
 
268
+ ## Where authoring files go
269
+
270
+ Authoring writes files that Studio Web never reads. By default they all go in
271
+ one hidden folder, `.flow-sdk/`, in the directory you run the command from:
272
+
273
+ | File | Written by |
274
+ | --- | --- |
275
+ | `.flow-sdk/<Name>.flow.ts` | `decompile` without `-o` |
276
+ | `.flow-sdk/<Name>.pipeline.mjs`, `<Name>.baseline.flow` / `.edited.flow` / `.merged.flow` | `decompile` (the brownfield helper and the candidates it writes) |
277
+ | `.flow-sdk/bindings.json` | `registry prepare` (read by `check` and `compile`) |
278
+ | `.flow-sdk/connectors/` | `registry pull` |
279
+ | `.flow-sdk/connectors-local/` | `registry prepare` (read by `check` and `compile`) |
280
+
281
+ The product files are written where `compile -o` points, as before: the
282
+ `.flow`, plus `bindings_v2.json` and an inline agent's folder beside it.
283
+
284
+ The reason is packaging. `uip solution pack` copies every root-level file of a
285
+ Flow project into the package, but no subfolder other than `content/`. So a
286
+ project authored in place used to ship its `.flow.ts`, `bindings.json` and
287
+ connector modules. Under `.flow-sdk/` they stay out.
288
+
289
+ For example, authoring inside a project folder:
290
+
291
+ ```
292
+ MySolution/MyFlow/
293
+ ├── MyFlow.flow ← compile -o MyFlow.flow (shipped)
294
+ ├── project.uiproj, operate.json, bindings_v2.json (shipped)
295
+ └── .flow-sdk/ (not packed)
296
+ ├── MyFlow.flow.ts, MyFlow.pipeline.mjs
297
+ ├── bindings.json
298
+ └── connectors/, connectors-local/
299
+ ```
300
+
301
+ A source kept in `.flow-sdk/` imports its connectors as `./connectors/<key>.ts`,
302
+ the same relative path as before. An explicit flag (`-o`, `--bindings`,
303
+ `--connectors`, `--connectors-local`, `--out`) still wins. A workspace that
304
+ already has a root-level `bindings.json`, `connectors/` or `connectors-local/`
305
+ keeps using it, so nothing moves under an existing project.
306
+
268
307
  ## Integration Service connectors
269
308
 
270
- A flow that calls a connector needs a `bindings.json` alongside it, mapping the
309
+ A flow that calls a connector needs a `bindings.json` mapping the
271
310
  symbolic `connection` / `folder` names in the code — `slack` and `shared` in the
272
- quick start — to real ids. `registry prepare` writes it; this is the shape, if
311
+ quick start — to real ids. `registry prepare` writes it to `.flow-sdk/bindings.json`
312
+ (see [Where authoring files go](#where-authoring-files-go)); this is the shape, if
273
313
  you would rather fill it from
274
314
  `uip is connections list --all-folders --output json`:
275
315
 
@@ -20,6 +20,7 @@ import { Bindings } from '../core/bindings.js';
20
20
  import { runWhenInvokedDirectly } from '../cli-run.js';
21
21
  import { ensureTypeScriptRuntime } from '../node-runtime.js';
22
22
  import { resolveLibraryDir } from '../registry/cache.js';
23
+ import { defaultBindingsFile } from '../workdir.js';
23
24
  function opt(argv, name) {
24
25
  const i = argv.indexOf(name);
25
26
  return i >= 0 ? argv[i + 1] : undefined;
@@ -49,7 +50,7 @@ export async function run(argv) {
49
50
  // compile-cli); load it only when present, and bindings.json alongside the .ts.
50
51
  const libDir = resolveLibraryDir(opt(argv, '--library'));
51
52
  const library = libDir && existsSync(join(libDir, 'index.json')) ? new Library(libDir) : undefined;
52
- const bindFile = opt(argv, '--bindings') ?? (existsSync('bindings.json') ? 'bindings.json' : undefined);
53
+ const bindFile = opt(argv, '--bindings') ?? defaultBindingsFile();
53
54
  const bindings = new Bindings(bindFile);
54
55
  const xml = await serialize(built, { library, bindings });
55
56
  const base = basename(file).replace(/\.bpmn\.ts$|\.ts$/, '');
@@ -25,6 +25,7 @@ import { reportResult } from '../cli-result.js';
25
25
  import { typeStripDecision } from '../node-runtime.js';
26
26
  import { readFileSync, writeFileSync } from 'node:fs';
27
27
  import { basename, resolve } from 'node:path';
28
+ import { defaultSourceOut } from '../workdir.js';
28
29
  import { runWhenInvokedDirectly } from '../cli-run.js';
29
30
  import { decompile } from './decompile.js';
30
31
  import { sameGraph } from './lift-verify.js';
@@ -98,7 +99,7 @@ export async function run(argv) {
98
99
  }
99
100
  }
100
101
  const base = basename(input).replace(/\.bpmn$/, '');
101
- const out = opt(argv, '-o') ?? `${base}.bpmn.ts`;
102
+ const out = opt(argv, '-o') ?? defaultSourceOut(`${base}.bpmn.ts`);
102
103
  writeFileSync(out, source);
103
104
  console.log(`decompile: wrote ${out}`);
104
105
  if (lifted) {
@@ -20,6 +20,7 @@ import { entryPointIO } from './entry-points.js';
20
20
  import { Library } from '../core/library.js';
21
21
  import { Bindings } from '../core/bindings.js';
22
22
  import { resolveLibraryDir } from '../registry/cache.js';
23
+ import { defaultBindingsFile, defaultOverlayDir } from '../workdir.js';
23
24
  import { runWhenInvokedDirectly } from '../cli-run.js';
24
25
  import { ensureTypeScriptRuntime } from '../node-runtime.js';
25
26
  import { CASE_FORMAT_PROFILE } from './format-profile.js';
@@ -57,9 +58,9 @@ export async function run(argv) {
57
58
  const libDir = resolveLibraryDir(opt(argv, '--library'));
58
59
  // A `prepare-connector` overlay, auto-detected next to the case (like bindings.json).
59
60
  const overlayDir = opt(argv, '--connectors-local')
60
- ?? (existsSync(join('connectors-local', 'index.json')) ? 'connectors-local' : undefined);
61
+ ?? defaultOverlayDir();
61
62
  const library = libDir && existsSync(join(libDir, 'index.json')) ? new Library(libDir, { overlayDir }) : undefined;
62
- const bindFile = opt(argv, '--bindings') ?? (existsSync('bindings.json') ? 'bindings.json' : undefined);
63
+ const bindFile = opt(argv, '--bindings') ?? defaultBindingsFile();
63
64
  const bindings = new Bindings(bindFile);
64
65
  const caseplan = serialize(built, { library, bindings });
65
66
  const out = opt(argv, '-o') ?? 'caseplan.json';
@@ -16,9 +16,10 @@
16
16
  * through the public Case CLI. Pass `--no-pipeline` to skip it.
17
17
  */
18
18
  import { reportResult } from '../cli-result.js';
19
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
19
+ import { mkdirSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
20
20
  import { resolveLibraryDir } from '../registry/cache.js';
21
- import { basename, resolve } from 'node:path';
21
+ import { basename, join, resolve } from 'node:path';
22
+ import { defaultSourceOut, workDirBeside } from '../workdir.js';
22
23
  import { runWhenInvokedDirectly } from '../cli-run.js';
23
24
  import { Library } from '../core/library.js';
24
25
  import { decompileWithDiagnostics } from './decompile.js';
@@ -64,7 +65,7 @@ export function run(argv) {
64
65
  library,
65
66
  bestEffort,
66
67
  });
67
- const out = opt(argv, '-o') ?? `${baseName(plan)}.case.ts`;
68
+ const out = opt(argv, '-o') ?? defaultSourceOut(`${baseName(plan)}.case.ts`);
68
69
  writeFileSync(out, result.source);
69
70
  console.log(`decompile: wrote ${basename(out)}`);
70
71
  reportResult({ path: resolve(out), diagnostics: result.diagnostics.map((d) => ({ code: d.code, location: d.location, message: d.message })) });
@@ -72,8 +73,11 @@ export function run(argv) {
72
73
  console.error(`decompile: [${diagnostic.code}] ${diagnostic.location}: ${diagnostic.message}`);
73
74
  }
74
75
  if (!argv.includes('--no-pipeline')) {
75
- const pipelinePath = out.replace(/\.case\.ts$|\.ts$/, '') + '.pipeline.mjs';
76
- writeFileSync(pipelinePath, pipelineScript(resolve(pos), resolve(out), importSpecifier, bestEffort, libraryDir ? resolve(libraryDir) : undefined));
76
+ // The helper and the candidate caseplan it writes are never shipped.
77
+ const workDir = workDirBeside(out);
78
+ mkdirSync(workDir, { recursive: true });
79
+ const pipelinePath = join(workDir, basename(out).replace(/\.case\.ts$|\.ts$/, '') + '.pipeline.mjs');
80
+ writeFileSync(pipelinePath, pipelineScript(resolve(pos), resolve(out), resolve(pipelinePath), importSpecifier, bestEffort, libraryDir ? resolve(libraryDir) : undefined));
77
81
  console.log(`decompile: wrote ${basename(pipelinePath)}`);
78
82
  reportResult({ pipeline: resolve(pipelinePath) });
79
83
  }
@@ -85,8 +89,8 @@ export function run(argv) {
85
89
  * feeding the original caseplan back as the binding registry; this preserves
86
90
  * the resource keys that the typed source deliberately names symbolically.
87
91
  */
88
- function pipelineScript(originalPath, tsPath, importSpecifier, bestEffort, libraryDir) {
89
- const base = tsPath.replace(/\.case\.ts$|\.ts$/, '');
92
+ function pipelineScript(originalPath, tsPath, pipelinePath, importSpecifier, bestEffort, libraryDir) {
93
+ const base = pipelinePath.replace(/\.pipeline\.mjs$/, '');
90
94
  return `#!/usr/bin/env node
91
95
  // Generated by case decompile — edit ${basename(tsPath)}, then run this file.
92
96
  import { execFileSync, spawnSync } from 'node:child_process';
package/dist/check-cli.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * Prints diagnostics; exits non-zero if any errors.
7
7
  */
8
8
  import { reportDiagnostics } from './cli-result.js';
9
- import { existsSync } from 'node:fs';
9
+ import { defaultBindingsFile } from './workdir.js';
10
10
  import { resolveFlowFile, loadBuiltFlow } from './load.js';
11
11
  import { check } from './check.js';
12
12
  import { byoaCheckOpts, eventResolveErrorFor, openCliLibrary } from './cli-library.js';
@@ -28,10 +28,10 @@ const VALUE_FLAGS = new Set(['--library', '--bindings', '--connectors-local']);
28
28
  * through the same `openCliLibrary`.
29
29
  */
30
30
  function checkOpts(argv) {
31
- // bindings.json beside the source, exactly as compile-cli finds it — so the
31
+ // The default bindings.json (workdir.ts), exactly as compile-cli finds it — so the
32
32
  // own-name refusal compile would raise is reported here first.
33
33
  const bindFlag = argv.indexOf('--bindings');
34
- const bindFile = bindFlag >= 0 ? argv[bindFlag + 1] : (existsSync('bindings.json') ? 'bindings.json' : undefined);
34
+ const bindFile = bindFlag >= 0 ? argv[bindFlag + 1] : defaultBindingsFile();
35
35
  const bindings = bindFile ? new Bindings(bindFile) : undefined;
36
36
  // The overlay matters here as much as it does for compile: `resolutions.json`
37
37
  // lives in it, and without it every already-resolved lookup would be reported
@@ -19,7 +19,7 @@ export function registerRegistryCommands(program) {
19
19
  .description('Download the published connector library and generate its Markdown and TypeScript views.')
20
20
  .option('--library <dir>', 'where library-json lands (default: ./library)')
21
21
  .option('--library-md <dir>', 'markdown output (default: ./library-md)')
22
- .option('--connectors <dir>', 'TypeScript descriptor output (default: ./connectors)')
22
+ .option('--connectors <dir>', 'TypeScript descriptor output (default: ./.flow-sdk/connectors)')
23
23
  .option('--connector <key>', 'only generate this connector (repeatable; default: whatever the project references)', collect, [])
24
24
  .option('--url <url>', 'archive URL to pull from')
25
25
  .option('--archive <zip>', 'use a zip already on disk instead of downloading')
@@ -122,7 +122,7 @@ export function registerRegistryCommands(program) {
122
122
  await invoke('registry build-library', () => loadTool(import.meta.url, '../../registry/library-build/cli.js'), argv);
123
123
  });
124
124
  family.command('prepare')
125
- .description("Resolve one operation's schema against a live connection into ./connectors-local.")
125
+ .description("Resolve one operation's schema against a live connection into ./.flow-sdk/connectors-local.")
126
126
  .argument('<connector-key>', 'connector key, e.g. uipath-salesforce')
127
127
  .option('--action <name>', '(required) operation to resolve, e.g. InsertRecord')
128
128
  .allowExcessArguments()
@@ -131,7 +131,7 @@ export function registerRegistryCommands(program) {
131
131
  .option('--connection-id <id>', 'connection to read the schema through')
132
132
  .option('--connection <name>', 'pick the connection by name when several match')
133
133
  .option('--connection-folder <folder>', 'pick the connection in the folder the flow runs in (name or key) when several match')
134
- .option('--bindings <file>', 'bindings file to update (default: ./bindings.json)')
134
+ .option('--bindings <file>', 'bindings file to update (default: ./.flow-sdk/bindings.json)')
135
135
  .option('--bind-connection <name>', 'symbolic connection name (default: the one label the source uses for this connector, else the connector suffix)')
136
136
  .option('--bind-folder <name>', 'symbolic folder name (default: the one folder label the source uses, else shared)')
137
137
  // A SEPARATE key, not commander's negation of `--bindings`: sharing one key
@@ -142,7 +142,7 @@ export function registerRegistryCommands(program) {
142
142
  .option('--object <name>', 'one connection-discovered object')
143
143
  .option('--all-objects', 'every connection-discovered object')
144
144
  .option('-f, --field <name=value>', 'parent field that selects the schema (repeatable)', collect, [])
145
- .option('--out <dir>', 'overlay directory (default: ./connectors-local)')
145
+ .option('--out <dir>', 'overlay directory (default: ./.flow-sdk/connectors-local)')
146
146
  // Not `--version`: the program's own version option shadows a subcommand's,
147
147
  // so `prepare --version 2.0` printed the package version instead of parsing.
148
148
  .option('--connector-version <version>', 'connector version to resolve against')
@@ -5,7 +5,7 @@ import { Library } from './library.js';
5
5
  *
6
6
  * `--library <dir>`, else `$FLOW_SDK_LIBRARY_JSON`, else the cache `registry
7
7
  * pull` filled (`resolveLibraryDir`). The overlay is `--connectors-local <dir>`,
8
- * else `./connectors-local` when it has an `index.json` — the directory
8
+ * else `.flow-sdk/connectors-local` (or a legacy `./connectors-local`) when it has an `index.json` — the directory
9
9
  * `registry prepare` writes by default.
10
10
  */
11
11
  export declare function openCliLibrary(argv: readonly string[]): Library | undefined;
@@ -13,6 +13,7 @@ import { existsSync } from 'node:fs';
13
13
  import { join } from 'node:path';
14
14
  import { Library } from './library.js';
15
15
  import { resolveLibraryDir } from './registry/cache.js';
16
+ import { defaultOverlayDir } from './workdir.js';
16
17
  /** The value after `name` in `argv`, or `undefined`. */
17
18
  function flagValue(argv, name) {
18
19
  const i = argv.indexOf(name);
@@ -23,7 +24,7 @@ function flagValue(argv, name) {
23
24
  *
24
25
  * `--library <dir>`, else `$FLOW_SDK_LIBRARY_JSON`, else the cache `registry
25
26
  * pull` filled (`resolveLibraryDir`). The overlay is `--connectors-local <dir>`,
26
- * else `./connectors-local` when it has an `index.json` — the directory
27
+ * else `.flow-sdk/connectors-local` (or a legacy `./connectors-local`) when it has an `index.json` — the directory
27
28
  * `registry prepare` writes by default.
28
29
  */
29
30
  export function openCliLibrary(argv) {
@@ -31,7 +32,7 @@ export function openCliLibrary(argv) {
31
32
  if (!libDir || !existsSync(join(libDir, 'index.json')))
32
33
  return undefined;
33
34
  const overlayDir = flagValue(argv, '--connectors-local')
34
- ?? (existsSync(join('connectors-local', 'index.json')) ? 'connectors-local' : undefined);
35
+ ?? defaultOverlayDir();
35
36
  return new Library(libDir, { overlayDir });
36
37
  }
37
38
  /**
@@ -8,7 +8,7 @@
8
8
  * [--no-check]
9
9
  *
10
10
  * --library defaults to $FLOW_SDK_LIBRARY_JSON; --bindings defaults to
11
- * ./bindings.json when present. Connector actions require a library.
11
+ * .flow-sdk/bindings.json (or a legacy ./bindings.json) when present. Connector actions require a library.
12
12
  *
13
13
  * `--no-check` skips the SDK's static `check()` pass and emits whatever
14
14
  * `.build()` produced. It exists for loops whose verification is somewhere else
@@ -28,6 +28,7 @@ import { Bindings } from './bindings.js';
28
28
  import { runWhenInvokedDirectly } from './cli-run.js';
29
29
  import { connectionResources, mergeBindingsV2, readBindingsV2 } from './bindings-v2.js';
30
30
  import { ensureTypeScriptRuntime } from './node-runtime.js';
31
+ import { defaultBindingsFile } from './workdir.js';
31
32
  import { BindingRefusal } from './core/binding-messages.js';
32
33
  import { ByoaRefusal } from './core/byoa.js';
33
34
  function opt(argv, name) {
@@ -75,7 +76,7 @@ export async function run(argv) {
75
76
  // filled. Resolving through one helper is what stops `pull` and `compile`
76
77
  // disagreeing about where the library is — they did, and a pulled library was
77
78
  // invisible to this command. The `prepare-connector`/`prepare-trigger` overlay
78
- // is `--connectors-local`, else `./connectors-local` (like bindings.json); it
79
+ // is `--connectors-local`, else `.flow-sdk/connectors-local` (like bindings.json); it
79
80
  // shadows the baked library with full, connection-resolved field schemas. See
80
81
  // core/library.ts (LibraryOpts.overlayDir). `check-cli` opens it the same way.
81
82
  //
@@ -88,7 +89,7 @@ export async function run(argv) {
88
89
  // library, and answering it from an assumption instead is what made
89
90
  // EVENT_NO_SCOPE an error for HTTP Webhook, whose only correct `where` is empty.
90
91
  const library = openCliLibrary(argv);
91
- const bindFile = opt(argv, '--bindings') ?? (existsSync('bindings.json') ? 'bindings.json' : undefined);
92
+ const bindFile = opt(argv, '--bindings') ?? defaultBindingsFile();
92
93
  const bindings = new Bindings(bindFile);
93
94
  if (!noCheck) {
94
95
  const diags = check(built, library
@@ -42,6 +42,6 @@ export function connectorRawNodeRefusal(nodeType) {
42
42
  + `Author it as connector('${key}', '${action}', { ...inputs }, { connection: '<binding>', folder: '<binding>' }). `
43
43
  + `If compile then refuses an input the static library does not carry, run `
44
44
  + `\`${prepareCommand(key, action)}\` — it discovers the connection, writes bindings.json `
45
- + `and loads the connection-resolved fields into ./connectors-local/ (add \`-f <parent>=<value>\`, e.g. `
45
+ + `and loads the connection-resolved fields into ./.flow-sdk/connectors-local/ (add \`-f <parent>=<value>\`, e.g. `
46
46
  + `\`-f entityName=<Entity>\`, when the operation's fields depend on a parent input).`);
47
47
  }
@@ -18,8 +18,9 @@
18
18
  * `--no-pipeline` to skip it.
19
19
  */
20
20
  import { reportResult } from './cli-result.js';
21
- import { readFileSync, writeFileSync } from 'node:fs';
22
- import { basename, resolve } from 'node:path';
21
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
22
+ import { basename, join, resolve } from 'node:path';
23
+ import { defaultSourceOut, workDirBeside } from './workdir.js';
23
24
  import { decompile } from './decompile.js';
24
25
  import { resolveFlowRefs } from './ref-resolve.js';
25
26
  import { runWhenInvokedDirectly } from './cli-run.js';
@@ -65,13 +66,18 @@ export function run(argv) {
65
66
  // reads them before compile refuses.
66
67
  warn: (message) => console.error(`flow-decompile: warning: ${message}`),
67
68
  });
68
- const out = opt(argv, '-o') ?? `${baseName(flow)}.flow.ts`;
69
+ const out = opt(argv, '-o') ?? defaultSourceOut(`${baseName(flow)}.flow.ts`);
69
70
  writeFileSync(out, ts);
70
71
  console.log(`flow-decompile: wrote ${basename(out)}`);
71
72
  reportResult({ path: resolve(out) });
72
73
  if (!argv.includes('--no-pipeline')) {
73
- const pipelinePath = out.replace(/\.flow\.ts$|\.ts$/, '') + '.pipeline.mjs';
74
- writeFileSync(pipelinePath, pipelineScript(resolve(pos), resolve(out)));
74
+ // The helper and the baseline/edited/merged candidates it writes are never
75
+ // shipped, so they go in the work directory even when `-o` put the source
76
+ // somewhere visible.
77
+ const workDir = workDirBeside(out);
78
+ mkdirSync(workDir, { recursive: true });
79
+ const pipelinePath = join(workDir, basename(out).replace(/\.flow\.ts$|\.ts$/, '') + '.pipeline.mjs');
80
+ writeFileSync(pipelinePath, pipelineScript(resolve(pos), resolve(out), resolve(pipelinePath)));
75
81
  console.log(`flow-decompile: wrote ${basename(pipelinePath)}`);
76
82
  reportResult({ pipeline: resolve(pipelinePath) });
77
83
  }
@@ -86,8 +92,8 @@ export function run(argv) {
86
92
  * CLI carrying those preview commands is installed, it falls back to the SDK's
87
93
  * retained `flow-sdk` bin from this workspace.
88
94
  */
89
- function pipelineScript(originalPath, tsPath) {
90
- const base = tsPath.replace(/\.flow\.ts$|\.ts$/, '');
95
+ function pipelineScript(originalPath, tsPath, pipelinePath) {
96
+ const base = pipelinePath.replace(/\.pipeline\.mjs$/, '');
91
97
  return `#!/usr/bin/env node
92
98
  // Generated by flow-decompile — the brownfield read/modify/write pipeline for
93
99
  // ${basename(originalPath)}. Run it, edit ${basename(tsPath)}, run it again.
package/dist/emit.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { SerializeOpts } from './serialize.js';
3
3
  export interface EmitFlowOpts extends SerializeOpts {
4
4
  /** Connector library dir; defaults to `$FLOW_SDK_LIBRARY_JSON`. */
5
5
  libraryDir?: string;
6
- /** bindings.json path; defaults to `./bindings.json` when it exists. */
6
+ /** bindings.json path; defaults to `.flow-sdk/bindings.json` (or a legacy `./bindings.json`) when it exists. */
7
7
  bindingsFile?: string;
8
8
  }
9
9
  /** Serialize `built` and write it to `path`; returns the `.flow` JSON. */
package/dist/emit.js CHANGED
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * `emitFlow(built, path)` is that, with the two resolutions defaulted the same
16
16
  * way `compile-cli` defaults them — library from `$FLOW_SDK_LIBRARY_JSON`,
17
- * bindings from `./bindings.json` when it exists:
17
+ * bindings from `.flow-sdk/bindings.json` (or a legacy `./bindings.json`) when it exists:
18
18
  *
19
19
  * ```ts
20
20
  * export default emitFlow(flow('release-rollup')…build(), 'ReleaseRollup/ReleaseRollup.flow');
@@ -31,6 +31,7 @@ import { serialize } from './serialize.js';
31
31
  import { Library } from './library.js';
32
32
  import { Bindings } from './bindings.js';
33
33
  import { resolveLibraryDir } from './registry/cache.js';
34
+ import { defaultBindingsFile } from './workdir.js';
34
35
  /**
35
36
  * Resolve the connector library the same way `compile-cli` does: only load it
36
37
  * when it is actually there, so a flow with no connectors emits fine without a
@@ -45,7 +46,7 @@ function defaultLibrary(dir) {
45
46
  }
46
47
  /** Serialize `built` and write it to `path`; returns the `.flow` JSON. */
47
48
  export function emitFlow(built, path, opts = {}) {
48
- const bindFile = opts.bindingsFile ?? (existsSync('bindings.json') ? 'bindings.json' : undefined);
49
+ const bindFile = opts.bindingsFile ?? defaultBindingsFile();
49
50
  const flowJson = serialize(built, {
50
51
  library: opts.library ?? defaultLibrary(opts.libraryDir),
51
52
  bindings: opts.bindings ?? new Bindings(bindFile),
@@ -206,7 +206,7 @@ export function exampleLines(j, fields, symbol, invariant, v1def) {
206
206
  if (schemaDynamic && symbol) {
207
207
  // ONE ordering per page: the import fact stays; authoring comes first.
208
208
  if (pyTruthy(operation.objectFromDiscovery)) {
209
- out.push("The descriptor comes from `./connectors-local/` and this " +
209
+ out.push("The descriptor comes from `./.flow-sdk/connectors-local/` and this " +
210
210
  "import resolves only after " +
211
211
  `\`npx flow-sdk registry prepare ${connectorKey} --action ${action} ` +
212
212
  "--object <name>` runs. That never blocks authoring: write " +
@@ -215,7 +215,7 @@ export function exampleLines(j, fields, symbol, invariant, v1def) {
215
215
  "names this exact prepare — then switch to this import.");
216
216
  }
217
217
  else {
218
- out.push("The descriptor comes from `./connectors-local/` and this " +
218
+ out.push("The descriptor comes from `./.flow-sdk/connectors-local/` and this " +
219
219
  "import resolves only after " +
220
220
  `\`npx flow-sdk registry prepare ${connectorKey} --action ${action}\` ` +
221
221
  "runs. That never blocks authoring: the fields in this " +
@@ -65,7 +65,7 @@ export function schemaDynamicLines(j, nodeType) {
65
65
  }
66
66
  out.push("> ```");
67
67
  out.push(">");
68
- let overlay = "> It writes a `./connectors-local/` overlay the compiler " +
68
+ let overlay = "> It writes a `./.flow-sdk/connectors-local/` overlay the compiler " +
69
69
  "auto-detects (its fields are unioned onto this entry).";
70
70
  if (!discovery) {
71
71
  overlay +=
@@ -21,7 +21,7 @@
21
21
  * a {@link UipClient} and hand the rows to them. Every refusal's text is kept
22
22
  * verbatim — agents read these messages and follow them.
23
23
  */
24
- import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
24
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
25
25
  import { dirname, join, resolve as resolvePath } from "node:path";
26
26
  import { pyJsonStringify } from "../codecs/pyjson.js";
27
27
  import { Refusal, messageOf } from "../errors.js";
@@ -473,6 +473,8 @@ export function mergeBindings(path, symbolicConnection, symbolicFolder, connecti
473
473
  }
474
474
  }
475
475
  const merged = mergeBindingsDocument(loaded, symbolicConnection, symbolicFolder, connection);
476
+ // The default lives in `.flow-sdk/`, which a fresh workspace does not have yet.
477
+ mkdirSync(dirname(path), { recursive: true });
476
478
  writeFileSync(path, formatBindings(merged.document), "utf8");
477
479
  return [merged.wroteConnection, merged.wroteFolder];
478
480
  }
@@ -17,6 +17,7 @@ import { readFileSync } from "node:fs";
17
17
  import { join, resolve } from "node:path";
18
18
  import { relativePosix, toPosix } from "../compat/paths.js";
19
19
  import { pyStrip } from "../compat/pystr.js";
20
+ import { isRelativeSpecifier } from "./source-import.js";
20
21
  import { descriptorUsageFallbackLines, descriptorUsageLines, descriptorUsageUnparsedLines } from "./messages.js";
21
22
  /**
22
23
  * `ConnectorValue<string>` → `string`, for the handoff skeleton. The generated
@@ -77,7 +78,7 @@ export function descriptorUsage(outDir, connectorKey, boundConnection = "<bindin
77
78
  exports = [];
78
79
  }
79
80
  let importPath = relativePosix(cwd, resolve(cwd, source));
80
- if (!importPath.startsWith("."))
81
+ if (!isRelativeSpecifier(importPath))
81
82
  importPath = `./${importPath}`;
82
83
  const sourcePosix = toPosix(source);
83
84
  if (exports.length === 1) {
@@ -16,7 +16,7 @@ export interface PrepareOptions {
16
16
  * already records are tried before the ambiguity refusal.
17
17
  */
18
18
  connectionFolder?: string;
19
- /** `--bindings` (default `./bindings.json`). */
19
+ /** `--bindings` (default `./.flow-sdk/bindings.json`, see {@link defaultBindingsOut}). */
20
20
  bindings?: string;
21
21
  /**
22
22
  * `--bind-connection`. Default: the one `connection:` label the source already
@@ -31,7 +31,7 @@ export interface PrepareOptions {
31
31
  allObjects?: boolean;
32
32
  /** `-f NAME=VALUE`, repeatable, in the order given. */
33
33
  fields?: readonly string[];
34
- /** `--out` (default `./connectors-local`). */
34
+ /** `--out` (default `./.flow-sdk/connectors-local`, see {@link defaultOverlayOut}). */
35
35
  out?: string;
36
36
  /** `--source`: the `.flow.ts` whose import is repointed. */
37
37
  source?: string;
@@ -56,8 +56,17 @@ export interface PreparePorts {
56
56
  /** How the pasteable `registry prepare` is spelled in the notes. */
57
57
  prepareCommand?: string;
58
58
  }
59
- /** Where prepare writes its overlay when `--out` is not given. */
60
- export declare const DEFAULT_OVERLAY_DIR = "./connectors-local";
59
+ /**
60
+ * Where prepare writes its overlay when `--out` is not given, as shown to the
61
+ * author: `./.flow-sdk/connectors-local`, or a legacy `./connectors-local` the
62
+ * workspace already has (workdir.ts).
63
+ */
64
+ export declare function defaultOverlayOut(cwd: string): string;
65
+ /**
66
+ * Where prepare records connection bindings when `--bindings` is not given:
67
+ * `./.flow-sdk/bindings.json`, or a legacy `./bindings.json` (workdir.ts).
68
+ */
69
+ export declare function defaultBindingsOut(cwd: string): string;
61
70
  export interface PrepareReport {
62
71
  kind: "action" | "event";
63
72
  /** The node type the overlay entry is stored under (the WAIT form for an event). */
@@ -25,7 +25,7 @@
25
25
  * file system is reached through paths resolved against `cwd`.
26
26
  */
27
27
  import { existsSync, readFileSync } from "node:fs";
28
- import { resolve } from "node:path";
28
+ import { relative, resolve } from "node:path";
29
29
  import { GUID_RE } from "../../core/binding-messages.js";
30
30
  import { PACKAGE_NAME } from "../../package-name.js";
31
31
  import { afterLast } from "../compat/pystr.js";
@@ -49,8 +49,22 @@ import { retryOtherConnections } from "./retry.js";
49
49
  import { repointSource } from "./source-import.js";
50
50
  import { sourceBindingLabels } from "./source-labels.js";
51
51
  import { isPlainObject, pyGet, pyOr, pyPathJoin, pyPathlibStr, pyStrAny, pyTruthy } from "./support.js";
52
- /** Where prepare writes its overlay when `--out` is not given. */
53
- export const DEFAULT_OVERLAY_DIR = "./connectors-local";
52
+ import { workPath } from "../../workdir.js";
53
+ /**
54
+ * Where prepare writes its overlay when `--out` is not given, as shown to the
55
+ * author: `./.flow-sdk/connectors-local`, or a legacy `./connectors-local` the
56
+ * workspace already has (workdir.ts).
57
+ */
58
+ export function defaultOverlayOut(cwd) {
59
+ return `./${relative(cwd, workPath("connectors-local", { cwd }))}`;
60
+ }
61
+ /**
62
+ * Where prepare records connection bindings when `--bindings` is not given:
63
+ * `./.flow-sdk/bindings.json`, or a legacy `./bindings.json` (workdir.ts).
64
+ */
65
+ export function defaultBindingsOut(cwd) {
66
+ return `./${relative(cwd, workPath("bindings.json", { cwd }))}`;
67
+ }
54
68
  function memoryCaches() {
55
69
  return { registry: new MemoryResponseCache(), is: new MemoryResponseCache(), connectors: new MemoryResponseCache() };
56
70
  }
@@ -107,7 +121,7 @@ function chooseLabels(options, ports) {
107
121
  const command = ports.prepareCommand ?? PREPARE_COMMAND;
108
122
  const labels = sourceBindingLabels({
109
123
  cwd: ports.cwd ?? process.cwd(),
110
- out: options.out ?? DEFAULT_OVERLAY_DIR,
124
+ out: options.out ?? defaultOverlayOut(ports.cwd ?? process.cwd()),
111
125
  connectorKey: options.connectorKey,
112
126
  source: options.source,
113
127
  });
@@ -222,7 +236,7 @@ async function connect(options, ports) {
222
236
  return { connectionId, row };
223
237
  // Recorded on BOTH routes. Until 3.30.5 only discovery wrote it, and the
224
238
  // explicit-id route left the author hand-writing the id and folder key.
225
- const bindingsShown = options.bindings ?? "./bindings.json";
239
+ const bindingsShown = options.bindings ?? defaultBindingsOut(ports.cwd ?? process.cwd());
226
240
  const bindingsPath = resolve(ports.cwd ?? process.cwd(), bindingsShown);
227
241
  if (labels.connectionFrom) {
228
242
  await refuseLabelOfAnotherConnector(ports.client, options.connectorKey, labels.connection, connectionId, bindingsPath, bindingsShown);
@@ -321,7 +335,7 @@ async function prepareOnConnection(options, ports, connected) {
321
335
  const { connectorKey, action } = options;
322
336
  const fieldPairs = options.fields ?? [];
323
337
  const parentFields = parseFields(fieldPairs);
324
- const outShown = options.out ?? DEFAULT_OVERLAY_DIR;
338
+ const outShown = options.out ?? defaultOverlayOut(cwd);
325
339
  const out = resolve(cwd, outShown);
326
340
  const importSpecifier = options.importSpecifier ?? PACKAGE_NAME;
327
341
  const jsonOnly = options.jsonOnly ?? false;
@@ -3,7 +3,11 @@
3
3
  * Anchored to a real import statement on purpose (see the module comment).
4
4
  */
5
5
  export declare const IMPORT_PATTERN: RegExp;
6
- /** Where a `.flow.ts` will not be: generated output and installed packages. */
6
+ /**
7
+ * Where a `.flow.ts` will not be: generated output and installed packages.
8
+ * Matched by name at any depth, so `.flow-sdk/connectors/` is skipped too while
9
+ * a source kept in `.flow-sdk/` itself is found.
10
+ */
7
11
  export declare const SOURCE_SKIP_DIRS: ReadonlySet<string>;
8
12
  /**
9
13
  * `Path(p).resolve()` with `strict=False`: absolute, `..` collapsed, and
@@ -11,6 +15,8 @@ export declare const SOURCE_SKIP_DIRS: ReadonlySet<string>;
11
15
  * go through this, so `/var/...` and `/private/var/...` name one file.
12
16
  */
13
17
  export declare function canonicalPath(p: string): string;
18
+ /** `./x`, `../x`, `.` or `..`: a path relative to the importing file, as Node reads one. */
19
+ export declare function isRelativeSpecifier(specifier: string): boolean;
14
20
  /**
15
21
  * `_resolves_to`: does `specifier`, read from `source`, name the file `target`?
16
22
  * A bare package specifier never does — it is not our overlay.
@@ -23,13 +23,18 @@ import { readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from
23
23
  import { basename, dirname, join, resolve } from "node:path";
24
24
  import { compareCodePoints } from "../compat/compare.js";
25
25
  import { relativePosix } from "../compat/paths.js";
26
+ import { workPath } from "../../workdir.js";
26
27
  import { repointedLine, severalSourcesLine, sourceNotAFileLine } from "./messages.js";
27
28
  /**
28
29
  * `import … from "<specifier>"` — the specifier is group 3, quotes preserved.
29
30
  * Anchored to a real import statement on purpose (see the module comment).
30
31
  */
31
32
  export const IMPORT_PATTERN = /(^\s*import\s[^;'"]*?\sfrom\s*)(['"])([^'"]+)(\2)/gm;
32
- /** Where a `.flow.ts` will not be: generated output and installed packages. */
33
+ /**
34
+ * Where a `.flow.ts` will not be: generated output and installed packages.
35
+ * Matched by name at any depth, so `.flow-sdk/connectors/` is skipped too while
36
+ * a source kept in `.flow-sdk/` itself is found.
37
+ */
33
38
  export const SOURCE_SKIP_DIRS = new Set(["node_modules", "connectors", "connectors-local", ".git"]);
34
39
  /**
35
40
  * `Path(p).resolve()` with `strict=False`: absolute, `..` collapsed, and
@@ -48,12 +53,16 @@ export function canonicalPath(p) {
48
53
  return join(canonicalPath(parent), basename(absolute));
49
54
  }
50
55
  }
56
+ /** `./x`, `../x`, `.` or `..`: a path relative to the importing file, as Node reads one. */
57
+ export function isRelativeSpecifier(specifier) {
58
+ return /^\.\.?(\/|$)/.test(specifier);
59
+ }
51
60
  /**
52
61
  * `_resolves_to`: does `specifier`, read from `source`, name the file `target`?
53
62
  * A bare package specifier never does — it is not our overlay.
54
63
  */
55
64
  export function resolvesTo(specifier, source, target) {
56
- if (!specifier.startsWith("."))
65
+ if (!isRelativeSpecifier(specifier))
57
66
  return false;
58
67
  return canonicalPath(resolve(dirname(source), specifier)) === canonicalPath(target);
59
68
  }
@@ -134,7 +143,9 @@ export function repointImport(source, baked, prepared) {
134
143
  return undefined;
135
144
  }
136
145
  let specifier = relativePosix(dirname(source), prepared);
137
- if (!specifier.startsWith("."))
146
+ // `.flow-sdk/…` starts with a dot but is not relative: without `./` Node
147
+ // resolves it as a package name.
148
+ if (!isRelativeSpecifier(specifier))
138
149
  specifier = `./${specifier}`;
139
150
  let changed = false;
140
151
  const rewritten = text.replace(IMPORT_PATTERN, (whole, head, quote, spec, tail) => {
@@ -169,7 +180,8 @@ export function repointSource(options) {
169
180
  if (options.noSourceRewrite || !isFile(prepared))
170
181
  return [];
171
182
  const root = canonicalPath(cwd);
172
- const baked = canonicalPath(join(root, "connectors", `${options.connectorKey}.ts`));
183
+ // Where `registry pull` put the module: `.flow-sdk/connectors/`, or a legacy `connectors/`.
184
+ const baked = canonicalPath(join(workPath("connectors", { cwd: root }), `${options.connectorKey}.ts`));
173
185
  let candidates;
174
186
  if (options.source) {
175
187
  candidates = [canonicalPath(resolve(cwd, options.source))];
@@ -44,7 +44,8 @@ import { basename, dirname, extname, resolve } from "node:path";
44
44
  import * as ts from "typescript";
45
45
  import { GUID_RE } from "../../core/binding-messages.js";
46
46
  import { relativePosix } from "../compat/paths.js";
47
- import { canonicalPath, flowSources } from "./source-import.js";
47
+ import { canonicalPath, flowSources, isRelativeSpecifier } from "./source-import.js";
48
+ import { inWorkDir } from "../../workdir.js";
48
49
  const CALLEES = new Set(["connector", "onEvent", "waitForEvent"]);
49
50
  function isFile(path) {
50
51
  try {
@@ -58,7 +59,12 @@ function isFile(path) {
58
59
  export function sourceBindingLabels(options) {
59
60
  const result = { connection: new Map(), folder: new Map(), otherConnectors: new Map(), unreadable: { connection: [], folder: [] } };
60
61
  const root = canonicalPath(options.cwd);
61
- const moduleDirs = [canonicalPath(resolve(root, "connectors")), canonicalPath(resolve(options.cwd, options.out))];
62
+ // `registry pull`'s modules in either layout (workdir.ts), and the overlay.
63
+ const moduleDirs = [
64
+ canonicalPath(inWorkDir("connectors", root)),
65
+ canonicalPath(resolve(root, "connectors")),
66
+ canonicalPath(resolve(options.cwd, options.out)),
67
+ ];
62
68
  let files;
63
69
  if (options.source) {
64
70
  const one = canonicalPath(resolve(options.cwd, options.source));
@@ -81,7 +87,7 @@ export function sourceBindingLabels(options) {
81
87
  }
82
88
  /** The connector key a module specifier names, when it is one of the generated connector modules. */
83
89
  function connectorModuleKey(specifier, from, moduleDirs) {
84
- if (!specifier.startsWith("."))
90
+ if (!isRelativeSpecifier(specifier))
85
91
  return undefined;
86
92
  let target = canonicalPath(resolve(dirname(from), specifier));
87
93
  if (extname(target) === "")
@@ -3,7 +3,7 @@ import { reportResult } from '../cli-result.js';
3
3
  import { PACKAGE_NAME } from "../package-name.js";
4
4
  import { exitCodeOf } from "./errors.js";
5
5
  import { exitText } from "./prepare/messages.js";
6
- import { DEFAULT_OVERLAY_DIR, prepareConnector as runPipeline } from "./prepare/pipeline.js";
6
+ import { defaultOverlayOut, prepareConnector as runPipeline } from "./prepare/pipeline.js";
7
7
  import { consoleReporter } from "./reporter.js";
8
8
  import { defaultUipClient } from "./uip/client.js";
9
9
  // This mirrors the generator's own flags one for one, so a `--version` here is
@@ -187,7 +187,7 @@ export async function run(argv) {
187
187
  }
188
188
  try {
189
189
  const report = await prepareConnector(options);
190
- reportResult({ connector: options.connectorKey, action: options.action, overlay: resolve(options.outPath ?? DEFAULT_OVERLAY_DIR), ...report });
190
+ reportResult({ connector: options.connectorKey, action: options.action, overlay: resolve(options.outPath ?? defaultOverlayOut(process.cwd())), ...report });
191
191
  return 0;
192
192
  }
193
193
  catch (error) {
@@ -6,6 +6,7 @@ import { CURRENT_LINK, LIBRARY_JSON_DIR, LIBRARY_MARKDOWN_DIR, cacheEntry, libra
6
6
  import { extractLibrary } from "./extractor.js";
7
7
  import { materializeRegistry } from "./materializer.js";
8
8
  import { curatedOverlayIdentity } from "./curated-overlay.js";
9
+ import { WORK_DIR, workPath } from "../workdir.js";
9
10
  /**
10
11
  * Fetch the published connector library into the shared cache, and generate the
11
12
  * typed descriptors a project imports.
@@ -244,7 +245,8 @@ export async function run(argv) {
244
245
  console.error(`registry pull: ${flag} is ignored — the generators run in-process now and the flag will be removed.`);
245
246
  }
246
247
  }
247
- const connectorsPath = value(argv, "--connectors") ?? "connectors";
248
+ // `.flow-sdk/connectors`, or a legacy `connectors/` the workspace already has.
249
+ const connectorsPath = value(argv, "--connectors") ?? workPath("connectors");
248
250
  try {
249
251
  const archive = await resolveArchive(argv, cacheDir, force);
250
252
  const archiveSha256 = await archiveIdentity(archive);
@@ -277,7 +279,10 @@ export async function run(argv) {
277
279
  // Which descriptors this project will actually import. Generating all 30
278
280
  // costs 1.1 MB in someone's repository for operations they never call.
279
281
  const named = repeated(argv, "--connector");
280
- const referenced = named.length > 0 ? named : referencedConnectorKeys(".");
282
+ // Sources sit in the project root or in the hidden work directory.
283
+ const referenced = named.length > 0
284
+ ? named
285
+ : [...new Set([...referencedConnectorKeys("."), ...referencedConnectorKeys(WORK_DIR)])].sort();
281
286
  // Generate only what is missing. The Markdown is 2,472 pages and a pure
282
287
  // function of the library JSON, so regenerating it to add one descriptor is
283
288
  // pure waste — and it is what the first run of this design did.
package/dist/serialize.js CHANGED
@@ -6417,7 +6417,7 @@ function buildConnectorInputs(rc, inputs, rename = NO_RENAME, resolutions = {},
6417
6417
  `static library does not contain this input. Run ` +
6418
6418
  `\`${prepareCommand(rc.connectorKey, action)}\` ` +
6419
6419
  `to load them ` +
6420
- `(writes ./connectors-local/, which the compiler auto-detects). If that still ` +
6420
+ `(writes ./.flow-sdk/connectors-local/, which the compiler auto-detects). If that still ` +
6421
6421
  `does not carry this input, the operation resolves its fields from the VALUES of ` +
6422
6422
  `parent inputs: resolve every parent and re-run with ` +
6423
6423
  `\`-f <parent-field>=<value>\` for each — too few parents is refused, and a ` +
@@ -0,0 +1,35 @@
1
+ /** The hidden directory, relative to the current directory. */
2
+ export declare const WORK_DIR = ".flow-sdk";
3
+ /** `name` under {@link WORK_DIR}, relative to `cwd` when one is given. */
4
+ export declare function inWorkDir(name: string, cwd?: string): string;
5
+ /**
6
+ * Where the default `name` lives for this workspace, for reading or writing.
7
+ *
8
+ * `.flow-sdk/<name>` when it exists; else the legacy `./<name>` when THAT exists
9
+ * (a workspace laid out before the work directory); else `.flow-sdk/<name>`,
10
+ * where a new file goes. Pass `exists` to require more than presence (an
11
+ * overlay counts only once it has its `index.json`).
12
+ */
13
+ export declare function workPath(name: string, options?: {
14
+ cwd?: string;
15
+ exists?: (path: string) => boolean;
16
+ }): string;
17
+ /** {@link workPath} for a file that is only read: `undefined` when neither exists. */
18
+ export declare function existingWorkPath(name: string, options?: {
19
+ cwd?: string;
20
+ exists?: (path: string) => boolean;
21
+ }): string | undefined;
22
+ /** The default author-side `bindings.json`, when there is one. */
23
+ export declare function defaultBindingsFile(cwd?: string): string | undefined;
24
+ /** The default `connectors-local/` overlay, once `registry prepare` has written its index. */
25
+ export declare function defaultOverlayDir(cwd?: string): string | undefined;
26
+ /** Create the work directory under `cwd` (default: the current directory). */
27
+ export declare function ensureWorkDir(cwd?: string): void;
28
+ /**
29
+ * The work directory that goes with an authored `path`: the directory `path` is
30
+ * already in when that is a work directory, else `.flow-sdk/` beside it. A
31
+ * decompile's `.pipeline.mjs` helper and the candidates it writes go here.
32
+ */
33
+ export declare function workDirBeside(path: string): string;
34
+ /** The default path of a decompiled source named `file`, creating the work directory. */
35
+ export declare function defaultSourceOut(file: string): string;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The SDK's hidden work directory: where authoring files that never ship live.
3
+ *
4
+ * Authoring writes files the product never reads: the decompiled `<Name>.flow.ts`
5
+ * and its `.pipeline.mjs` helper, the `baseline`/`edited`/`merged` candidates, the
6
+ * author-side `bindings.json`, and the generated `connectors/` and
7
+ * `connectors-local/` modules. Studio Web needs none of them. The product files
8
+ * (`<Name>.flow`, `project.uiproj`, `operate.json`, `bindings_v2.json`, an inline
9
+ * agent's folder) are written where `compile -o` says, exactly as before.
10
+ *
11
+ * Every one of those defaults used to be a bare name in the current directory,
12
+ * so a project authored in place carried them into its package: `uip solution
13
+ * pack` copies every root-level file of a Flow project. Keeping them under one
14
+ * dot-folder keeps them out: pack copies no project subfolder but `content/`.
15
+ *
16
+ * Paths stay relative to the current directory, as the old defaults were, so
17
+ * `uip` (which spawns the SDK with the caller's cwd) and a direct `flow-sdk`
18
+ * call land in the same place. An explicit flag (`-o`, `--bindings`,
19
+ * `--connectors`, `--connectors-local`, `--out`) still wins.
20
+ *
21
+ * A workspace laid out before this directory existed keeps working: when only
22
+ * the old root-level entry exists, reads and writes use it (see
23
+ * {@link workPath}), so an existing `./connectors-local/` overlay is extended
24
+ * rather than split across two places. When both exist, `.flow-sdk/` wins and
25
+ * stderr says the root one is ignored.
26
+ */
27
+ import { existsSync, mkdirSync } from 'node:fs';
28
+ import { basename, dirname, join } from 'node:path';
29
+ /** The hidden directory, relative to the current directory. */
30
+ export const WORK_DIR = '.flow-sdk';
31
+ /** `name` under {@link WORK_DIR}, relative to `cwd` when one is given. */
32
+ export function inWorkDir(name, cwd) {
33
+ return cwd === undefined ? join(WORK_DIR, name) : join(cwd, WORK_DIR, name);
34
+ }
35
+ /**
36
+ * Where the default `name` lives for this workspace, for reading or writing.
37
+ *
38
+ * `.flow-sdk/<name>` when it exists; else the legacy `./<name>` when THAT exists
39
+ * (a workspace laid out before the work directory); else `.flow-sdk/<name>`,
40
+ * where a new file goes. Pass `exists` to require more than presence (an
41
+ * overlay counts only once it has its `index.json`).
42
+ */
43
+ export function workPath(name, options = {}) {
44
+ const exists = options.exists ?? existsSync;
45
+ const current = inWorkDir(name, options.cwd);
46
+ const legacy = options.cwd === undefined ? name : join(options.cwd, name);
47
+ if (exists(current)) {
48
+ // Both present is how an edit goes missing: a guide still names the root
49
+ // file, the author edits it, and every command keeps reading this one.
50
+ if (exists(legacy))
51
+ noteShadowed(current, legacy);
52
+ return current;
53
+ }
54
+ return exists(legacy) ? legacy : current;
55
+ }
56
+ const noted = new Set();
57
+ /** Say once per process that a root-level `legacy` is being ignored. */
58
+ function noteShadowed(current, legacy) {
59
+ if (noted.has(legacy))
60
+ return;
61
+ noted.add(legacy);
62
+ console.error(`flow-sdk: using ${current}; ${legacy} is also present and is ignored. Move its contents into ${current}, or pass the path explicitly.`);
63
+ }
64
+ /** {@link workPath} for a file that is only read: `undefined` when neither exists. */
65
+ export function existingWorkPath(name, options = {}) {
66
+ const exists = options.exists ?? existsSync;
67
+ const path = workPath(name, options);
68
+ return exists(path) ? path : undefined;
69
+ }
70
+ /** The default author-side `bindings.json`, when there is one. */
71
+ export function defaultBindingsFile(cwd) {
72
+ return existingWorkPath('bindings.json', { cwd });
73
+ }
74
+ /** The default `connectors-local/` overlay, once `registry prepare` has written its index. */
75
+ export function defaultOverlayDir(cwd) {
76
+ const exists = (dir) => existsSync(join(dir, 'index.json'));
77
+ return existingWorkPath('connectors-local', { cwd, exists });
78
+ }
79
+ /** Create the work directory under `cwd` (default: the current directory). */
80
+ export function ensureWorkDir(cwd) {
81
+ mkdirSync(cwd === undefined ? WORK_DIR : join(cwd, WORK_DIR), { recursive: true });
82
+ }
83
+ /**
84
+ * The work directory that goes with an authored `path`: the directory `path` is
85
+ * already in when that is a work directory, else `.flow-sdk/` beside it. A
86
+ * decompile's `.pipeline.mjs` helper and the candidates it writes go here.
87
+ */
88
+ export function workDirBeside(path) {
89
+ const dir = dirname(path);
90
+ return basename(dir) === WORK_DIR ? dir : join(dir, WORK_DIR);
91
+ }
92
+ /** The default path of a decompiled source named `file`, creating the work directory. */
93
+ export function defaultSourceOut(file) {
94
+ ensureWorkDir();
95
+ return inWorkDir(file);
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.12.1",
3
+ "version": "6.13.0",
4
4
  "description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.uipath.com/maestro",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "9b83c4da2e3512512d5b128f43cc8f8f03b74555"
95
+ "gitref": "bf85d17e1d820f584329913c9ac836a2fbc23dec"
96
96
  }