@lotics/cli 0.88.0 → 0.89.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/render_page.js +100 -100
  2. package/dist/src/cli.js +162 -100
  3. package/dist/{client.d.ts → src/client.d.ts} +9 -1
  4. package/dist/src/package_content_types.d.ts +70 -0
  5. package/dist/src/package_content_types.js +12 -0
  6. package/package.json +2 -2
  7. package/dist/app_commands.d.ts +0 -401
  8. package/dist/app_commands.js +0 -1520
  9. package/dist/app_commands.test.d.ts +0 -1
  10. package/dist/app_commands.test.js +0 -898
  11. package/dist/app_workflow_check.d.ts +0 -77
  12. package/dist/app_workflow_check.js +0 -169
  13. package/dist/app_workflow_check.test.d.ts +0 -1
  14. package/dist/app_workflow_check.test.js +0 -166
  15. package/dist/args.d.ts +0 -42
  16. package/dist/args.js +0 -121
  17. package/dist/args.test.d.ts +0 -1
  18. package/dist/args.test.js +0 -88
  19. package/dist/child_env.d.ts +0 -13
  20. package/dist/child_env.js +0 -24
  21. package/dist/cli.d.ts +0 -2
  22. package/dist/cli.js +0 -1095
  23. package/dist/cli_dispatch.test.d.ts +0 -1
  24. package/dist/cli_dispatch.test.js +0 -89
  25. package/dist/client.test.d.ts +0 -1
  26. package/dist/client.test.js +0 -47
  27. package/dist/config.d.ts +0 -124
  28. package/dist/config.js +0 -315
  29. package/dist/config.test.d.ts +0 -1
  30. package/dist/config.test.js +0 -319
  31. package/dist/dev/file_relay.d.ts +0 -39
  32. package/dist/dev/file_relay.js +0 -87
  33. package/dist/dev/file_relay.test.d.ts +0 -1
  34. package/dist/dev/file_relay.test.js +0 -87
  35. package/dist/dev/rpc_handler.d.ts +0 -21
  36. package/dist/dev/rpc_handler.js +0 -166
  37. package/dist/dev/rpc_handler.test.d.ts +0 -1
  38. package/dist/dev/rpc_handler.test.js +0 -107
  39. package/dist/dev/server.d.ts +0 -75
  40. package/dist/dev/server.js +0 -443
  41. package/dist/dev/upload_relay.d.ts +0 -36
  42. package/dist/dev/upload_relay.js +0 -61
  43. package/dist/dev/upload_relay.test.d.ts +0 -1
  44. package/dist/dev/upload_relay.test.js +0 -76
  45. package/dist/dev/wrapper_page.d.ts +0 -40
  46. package/dist/dev/wrapper_page.js +0 -335
  47. package/dist/docx.d.ts +0 -2
  48. package/dist/docx.js +0 -341
  49. package/dist/docx.test.d.ts +0 -1
  50. package/dist/docx.test.js +0 -145
  51. package/dist/file_command_io.d.ts +0 -12
  52. package/dist/file_command_io.js +0 -34
  53. package/dist/generate_app_agents_dts.d.ts +0 -16
  54. package/dist/generate_app_agents_dts.js +0 -62
  55. package/dist/generate_app_agents_dts.test.d.ts +0 -1
  56. package/dist/generate_app_agents_dts.test.js +0 -45
  57. package/dist/generate_app_fields.d.ts +0 -56
  58. package/dist/generate_app_fields.js +0 -148
  59. package/dist/generate_app_fields.test.d.ts +0 -1
  60. package/dist/generate_app_fields.test.js +0 -108
  61. package/dist/generate_app_queries_dts.d.ts +0 -21
  62. package/dist/generate_app_queries_dts.js +0 -57
  63. package/dist/generate_app_workflows_dts.d.ts +0 -39
  64. package/dist/generate_app_workflows_dts.js +0 -229
  65. package/dist/generate_app_workflows_dts.test.d.ts +0 -1
  66. package/dist/generate_app_workflows_dts.test.js +0 -109
  67. package/dist/generate_package_fields.d.ts +0 -49
  68. package/dist/generate_package_fields.js +0 -157
  69. package/dist/generate_package_fields.test.d.ts +0 -1
  70. package/dist/generate_package_fields.test.js +0 -59
  71. package/dist/inputs.d.ts +0 -38
  72. package/dist/inputs.js +0 -50
  73. package/dist/inputs.test.d.ts +0 -1
  74. package/dist/inputs.test.js +0 -89
  75. package/dist/preview.d.ts +0 -3
  76. package/dist/preview.js +0 -233
  77. package/dist/starter_template.d.ts +0 -64
  78. package/dist/starter_template.js +0 -753
  79. package/dist/starter_template.test.d.ts +0 -1
  80. package/dist/starter_template.test.js +0 -115
  81. package/dist/version.d.ts +0 -1
  82. package/dist/version.js +0 -6
  83. package/dist/xlsx.d.ts +0 -2
  84. package/dist/xlsx.js +0 -489
  85. package/dist/xlsx.test.d.ts +0 -1
  86. package/dist/xlsx.test.js +0 -131
  87. /package/dist/{client.js → src/client.js} +0 -0
@@ -1,77 +0,0 @@
1
- import type ts from "typescript";
2
- /**
3
- * The compiler options the SERVER uses at set-time verify — a copy of
4
- * `backend/features/workflows/typecheck_with_typescript.ts:COMPILER_OPTIONS`,
5
- * expressed against the project-resolved `ts` namespace so the local verdict
6
- * matches the server's. The CLI can't import backend code, so this is duplicated
7
- * and pinned to the same literal by tests on BOTH sides (this package's
8
- * `app_workflow_check.test.ts` + the backend's `typecheck_with_typescript.test.ts`
9
- * — same pattern as the `__workflow` envelope). If the server changes a flag, both
10
- * pins break and force this to follow. lib `es2022` with NO DOM (a body runs on
11
- * the server, not a browser); `types: []` so no `@types/*` ambient leaks in;
12
- * `skipLibCheck` keeps lib-typecheck off the hot path.
13
- */
14
- export declare function workflowCheckCompilerOptions(tsApi: typeof ts): ts.CompilerOptions;
15
- /** One type error, mapped back to the author's body coordinates. */
16
- export interface WorkflowCheckIssue {
17
- /** 1-indexed line in the author's body (the envelope-prefix offset removed). */
18
- line: number;
19
- /** 1-indexed column in the author's body. */
20
- col: number;
21
- /** TS-prefixed diagnostic code, e.g. "TS2339". */
22
- code: string;
23
- /** Human-readable message; multi-line TS messages joined with `\n`. */
24
- message: string;
25
- }
26
- /** The verdict for one alias's body. `issues: []` ⇒ clean. */
27
- export interface WorkflowCheckAliasResult {
28
- alias: string;
29
- bodyPath: string;
30
- issues: WorkflowCheckIssue[];
31
- }
32
- export interface WorkflowCheckInput {
33
- /** Absolute path to the wrapped body file (`src/workflows/<alias>.ts`). */
34
- bodyPath: string;
35
- /** Absolute path to the per-alias ambient globals (`.lotics/workflows/<alias>.globals.d.ts`). */
36
- globalsPath: string;
37
- }
38
- /**
39
- * Count the lines the envelope prefix adds ABOVE the author's body, so a
40
- * diagnostic on body line K reports as the author's line K, not K+offset. The
41
- * wrapper opener (`async function __workflow(...) {`) and the header above it
42
- * (the `/// <reference>`, the `//` comments, the `export {};` marker, blank
43
- * lines) all sit before the first author line. The author's body is everything
44
- * between the wrapper opener line and the trailing `}` — so the offset is the
45
- * count of lines up to and including the opener.
46
- *
47
- * Mirrors the body shape `writeWorkflowFile` produces; recognized structurally
48
- * (the `__workflow` opener) so a future header tweak can't silently desync the
49
- * line mapping. A file with no recognizable opener (degraded) maps with a zero
50
- * offset rather than guessing.
51
- */
52
- export declare function bodyLineOffset(wrappedSource: string): number;
53
- /**
54
- * Resolve the app's own `typescript` from `projectDir`. Loud, actionable error
55
- * when the app has no compiler installed (never a silent skip — a skipped check
56
- * reads as a clean check). The dynamic import is the boundary adapter the file
57
- * header explains: the only place we load a project-local peer tool.
58
- */
59
- export declare function loadProjectTypescript(projectDir: string): Promise<typeof ts>;
60
- /**
61
- * Type-check one alias's body in an ISOLATED program built from exactly that
62
- * alias's `{body, globals}` pair — so the ambient `trigger` is unambiguous and
63
- * `trigger.app_workflow.inputs` is checked against THIS alias's inputs. Returns
64
- * every diagnostic that lands in the body file, mapped back to the author's
65
- * coordinates.
66
- */
67
- export declare function checkOneWorkflowBody(tsApi: typeof ts, input: WorkflowCheckInput): WorkflowCheckIssue[];
68
- /**
69
- * Type-check every requested alias in ONE process (one isolated program each).
70
- * Pure over its inputs (the resolved `ts` + the `{body, globals}` paths) so it's
71
- * unit-testable without the CLI/manifest plumbing. Aliases are returned in the
72
- * order given.
73
- */
74
- export declare function checkWorkflowBodies(tsApi: typeof ts, aliases: {
75
- alias: string;
76
- input: WorkflowCheckInput;
77
- }[]): WorkflowCheckAliasResult[];
@@ -1,169 +0,0 @@
1
- /**
2
- * `lotics app workflow check [alias]` — local TypeScript type check of editable
3
- * workflow bodies, ONE isolated program per bound alias (GAP-59 fix).
4
- *
5
- * The bug it replaces: a single tsc program over `src/workflows/*.ts` +
6
- * `.lotics/workflows/**` compiles ALL aliases' ambient globals together. Each
7
- * `.globals.d.ts` declares its OWN `declare const trigger: AppWorkflowTrigger`
8
- * (where `app_workflow.inputs` is THAT alias's input shape) at top level — so N
9
- * aliases means N colliding ambient `trigger` declarations. tsc resolves ONE, and
10
- * every body then type-checks `trigger.app_workflow.inputs` against the WRONG
11
- * alias's inputs (a 22-workflow app emitted ~237 false TS2339 errors).
12
- *
13
- * The fix mirrors the SERVER, which verifies one body at a time and never
14
- * collides: build a SEPARATE `ts.Program` per alias from exactly that alias's
15
- * `{body, globals}` pair, so its ambient `trigger` is unambiguous. All aliases
16
- * run in ONE node process (N in-memory programs, not N `tsc` spawns).
17
- *
18
- * `typescript` is resolved from the APP's own `node_modules` (it ships `tsc` as a
19
- * devDependency), not bundled into the CLI bin: the verdict then tracks the exact
20
- * compiler version the app's own `npm run typecheck` / CI uses, and the bin stays
21
- * lean (the compiler is ~9MB). This is the one place the CLI loads a project-local
22
- * peer tool — the path is only knowable at runtime and the version must match the
23
- * app's, so there is no static-import alternative.
24
- */
25
- import fs from "node:fs";
26
- import path from "node:path";
27
- import { createRequire } from "node:module";
28
- /**
29
- * The compiler options the SERVER uses at set-time verify — a copy of
30
- * `backend/features/workflows/typecheck_with_typescript.ts:COMPILER_OPTIONS`,
31
- * expressed against the project-resolved `ts` namespace so the local verdict
32
- * matches the server's. The CLI can't import backend code, so this is duplicated
33
- * and pinned to the same literal by tests on BOTH sides (this package's
34
- * `app_workflow_check.test.ts` + the backend's `typecheck_with_typescript.test.ts`
35
- * — same pattern as the `__workflow` envelope). If the server changes a flag, both
36
- * pins break and force this to follow. lib `es2022` with NO DOM (a body runs on
37
- * the server, not a browser); `types: []` so no `@types/*` ambient leaks in;
38
- * `skipLibCheck` keeps lib-typecheck off the hot path.
39
- */
40
- export function workflowCheckCompilerOptions(tsApi) {
41
- return {
42
- target: tsApi.ScriptTarget.ES2022,
43
- module: tsApi.ModuleKind.ESNext,
44
- lib: ["lib.es2022.d.ts"],
45
- strict: true,
46
- noImplicitAny: true,
47
- strictNullChecks: true,
48
- noImplicitReturns: false,
49
- noEmit: true,
50
- allowJs: false,
51
- isolatedModules: false,
52
- skipLibCheck: true,
53
- moduleResolution: tsApi.ModuleResolutionKind.NodeNext,
54
- types: [],
55
- declaration: false,
56
- };
57
- }
58
- /**
59
- * Count the lines the envelope prefix adds ABOVE the author's body, so a
60
- * diagnostic on body line K reports as the author's line K, not K+offset. The
61
- * wrapper opener (`async function __workflow(...) {`) and the header above it
62
- * (the `/// <reference>`, the `//` comments, the `export {};` marker, blank
63
- * lines) all sit before the first author line. The author's body is everything
64
- * between the wrapper opener line and the trailing `}` — so the offset is the
65
- * count of lines up to and including the opener.
66
- *
67
- * Mirrors the body shape `writeWorkflowFile` produces; recognized structurally
68
- * (the `__workflow` opener) so a future header tweak can't silently desync the
69
- * line mapping. A file with no recognizable opener (degraded) maps with a zero
70
- * offset rather than guessing.
71
- */
72
- export function bodyLineOffset(wrappedSource) {
73
- const lines = wrappedSource.split("\n");
74
- for (let i = 0; i < lines.length; i++) {
75
- if (/^\s*async\s+function\s+__workflow\s*\(/.test(lines[i]))
76
- return i + 1;
77
- }
78
- return 0;
79
- }
80
- /**
81
- * Resolve the app's own `typescript` from `projectDir`. Loud, actionable error
82
- * when the app has no compiler installed (never a silent skip — a skipped check
83
- * reads as a clean check). The dynamic import is the boundary adapter the file
84
- * header explains: the only place we load a project-local peer tool.
85
- */
86
- export async function loadProjectTypescript(projectDir) {
87
- const requireFromProject = createRequire(path.join(projectDir, "package.json"));
88
- let resolved;
89
- try {
90
- resolved = requireFromProject.resolve("typescript");
91
- }
92
- catch {
93
- throw new Error(`Could not resolve 'typescript' from ${projectDir}. ` +
94
- `'lotics app workflow check' type-checks bodies with the app's own compiler — ` +
95
- `run 'npm install' (the scaffold ships typescript as a devDependency).`);
96
- }
97
- const mod = (await import(pathToImportUrl(resolved)));
98
- // The CJS typescript module loads under an ESM `import()` as `{ default: ts }`;
99
- // a future ESM build would expose the namespace directly. Accept both.
100
- return mod.default ?? mod;
101
- }
102
- /** A filesystem path → a `file://` URL `import()` accepts on every platform. */
103
- function pathToImportUrl(p) {
104
- const resolved = path.resolve(p);
105
- const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
106
- return `file://${prefixed.split(path.sep).join("/")}`;
107
- }
108
- /**
109
- * Type-check one alias's body in an ISOLATED program built from exactly that
110
- * alias's `{body, globals}` pair — so the ambient `trigger` is unambiguous and
111
- * `trigger.app_workflow.inputs` is checked against THIS alias's inputs. Returns
112
- * every diagnostic that lands in the body file, mapped back to the author's
113
- * coordinates.
114
- */
115
- export function checkOneWorkflowBody(tsApi, input) {
116
- const wrapped = fs.readFileSync(input.bodyPath, "utf-8");
117
- const offset = bodyLineOffset(wrapped);
118
- const program = tsApi.createProgram({
119
- rootNames: [input.globalsPath, input.bodyPath],
120
- options: workflowCheckCompilerOptions(tsApi),
121
- });
122
- const bodyFile = program.getSourceFile(input.bodyPath);
123
- if (!bodyFile) {
124
- // The program failed to ingest the body file — surface loudly rather than
125
- // returning a false "clean" result.
126
- return [
127
- {
128
- line: 1,
129
- col: 1,
130
- code: "INTERNAL",
131
- message: `workflow check: body file was not loaded (${input.bodyPath})`,
132
- },
133
- ];
134
- }
135
- const issues = [];
136
- for (const d of tsApi.getPreEmitDiagnostics(program)) {
137
- if (d.file !== bodyFile)
138
- continue;
139
- let line = 1;
140
- let col = 1;
141
- if (typeof d.start === "number") {
142
- const lc = bodyFile.getLineAndCharacterOfPosition(d.start);
143
- // Map the wrapped-source line back to the author's body line. A diagnostic
144
- // that lands on a synthetic wrapper/header line (rare) clamps to line 1.
145
- line = Math.max(1, lc.line + 1 - offset);
146
- col = lc.character + 1;
147
- }
148
- issues.push({
149
- line,
150
- col,
151
- code: `TS${d.code}`,
152
- message: tsApi.flattenDiagnosticMessageText(d.messageText, "\n"),
153
- });
154
- }
155
- return issues;
156
- }
157
- /**
158
- * Type-check every requested alias in ONE process (one isolated program each).
159
- * Pure over its inputs (the resolved `ts` + the `{body, globals}` paths) so it's
160
- * unit-testable without the CLI/manifest plumbing. Aliases are returned in the
161
- * order given.
162
- */
163
- export function checkWorkflowBodies(tsApi, aliases) {
164
- return aliases.map(({ alias, input }) => ({
165
- alias,
166
- bodyPath: input.bodyPath,
167
- issues: checkOneWorkflowBody(tsApi, input),
168
- }));
169
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,166 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { tmpdir } from "node:os";
5
- import ts from "typescript";
6
- import { bodyLineOffset, checkOneWorkflowBody, checkWorkflowBodies, workflowCheckCompilerOptions, } from "./app_workflow_check.js";
7
- import { writeWorkflowFile, writeWorkflowGlobals, FALLBACK_ENVELOPE_PREFIX, FALLBACK_ENVELOPE_SUFFIX, } from "./app_commands.js";
8
- /**
9
- * The collision regression (GAP-59 fix). The shipped GAP-59 attempt compiled
10
- * every alias's body + per-alias ambient globals into ONE tsc program. Each
11
- * `<alias>.globals.d.ts` declares its OWN top-level `declare const trigger:
12
- * AppWorkflowTrigger` whose `app_workflow.inputs` is THAT alias's input shape —
13
- * so N aliases collided on the ambient `trigger`, tsc resolved one, and every
14
- * body checked `trigger.app_workflow.inputs` against the wrong alias's inputs.
15
- *
16
- * `checkWorkflowBodies` builds an ISOLATED program per alias, so each body's
17
- * `trigger` is unambiguous. The decisive case: alias A's globals type
18
- * `inputs.foo`, alias B's type `inputs.bar`; A's body reads `.foo` (clean), B's
19
- * reads `.bar` (clean) — both pass ONLY because each program sees only its own
20
- * globals. A body reading a field that isn't in its own inputs is an error for
21
- * THAT alias only.
22
- */
23
- /**
24
- * A faithful per-alias ambient globals `.d.ts`, matching the shape
25
- * `generateWorkspaceDts` emits for an `app_workflow` trigger: a top-level
26
- * `interface AppWorkflowTrigger` whose `app_workflow.inputs` carries this alias's
27
- * typed inputs, `declare const trigger`, and the `__WorkflowReturn` alias the
28
- * envelope's return type references.
29
- */
30
- function globalsDts(inputFields) {
31
- const fields = Object.entries(inputFields)
32
- .map(([k, t]) => ` readonly ${k}: ${t};`)
33
- .join("\n");
34
- return [
35
- "interface AppWorkflowTrigger {",
36
- ' readonly type: "app_workflow";',
37
- " readonly app_workflow: {",
38
- " readonly inputs: {",
39
- fields,
40
- " };",
41
- " };",
42
- "}",
43
- "declare const trigger: AppWorkflowTrigger;",
44
- "type __WorkflowReturn = { data?: unknown; message?: string; field_errors?: unknown };",
45
- ].join("\n");
46
- }
47
- describe("checkWorkflowBodies — isolated program per alias (GAP-59 collision regression)", () => {
48
- let workDir;
49
- beforeEach(() => {
50
- workDir = fs.mkdtempSync(path.join(tmpdir(), "lotics-wf-check-test-"));
51
- });
52
- afterEach(() => {
53
- fs.rmSync(workDir, { recursive: true, force: true });
54
- });
55
- /** Write one alias's body (wrapped via the real writer) + its globals; return the check input. */
56
- function writeAlias(alias, body, inputs) {
57
- const bodyPath = writeWorkflowFile(workDir, alias, body);
58
- const globalsPath = writeWorkflowGlobals(workDir, alias, globalsDts(inputs));
59
- return { alias, input: { bodyPath, globalsPath } };
60
- }
61
- it("each alias type-checks against its OWN inputs — A.foo clean, B.bar clean, no cross-talk", () => {
62
- const a = writeAlias("aliasA", "const x: string = trigger.app_workflow.inputs.foo;\nreturn({ data: { x } });", { foo: "string" });
63
- const b = writeAlias("aliasB", "const y: string = trigger.app_workflow.inputs.bar;\nreturn({ data: { y } });", { bar: "string" });
64
- const results = checkWorkflowBodies(ts, [a, b]);
65
- const byAlias = Object.fromEntries(results.map((r) => [r.alias, r]));
66
- // Both clean — proving each program saw ONLY its own globals. Under the old
67
- // all-in-one program, one of A/B would read a `.foo`/`.bar` that the OTHER
68
- // alias's surviving ambient `trigger` doesn't declare → a false TS2339.
69
- expect(byAlias.aliasA.issues).toEqual([]);
70
- expect(byAlias.aliasB.issues).toEqual([]);
71
- });
72
- it("a body reading an input that isn't in its OWN schema is an error for THAT alias only", () => {
73
- // aliasA's inputs = { foo }, but its body reads `.bar` (which only aliasB has).
74
- // With isolated programs, aliasA must fail (it has no `bar`) while aliasB,
75
- // reading its own `.bar`, stays clean.
76
- const a = writeAlias("aliasA", "const x: string = trigger.app_workflow.inputs.bar;\nreturn({ data: { x } });", { foo: "string" });
77
- const b = writeAlias("aliasB", "const y: string = trigger.app_workflow.inputs.bar;\nreturn({ data: { y } });", { bar: "string" });
78
- const results = checkWorkflowBodies(ts, [a, b]);
79
- const byAlias = Object.fromEntries(results.map((r) => [r.alias, r]));
80
- expect(byAlias.aliasA.issues.length).toBeGreaterThan(0);
81
- expect(byAlias.aliasA.issues[0].code).toBe("TS2339"); // Property 'bar' does not exist
82
- expect(byAlias.aliasA.issues[0].message).toContain("bar");
83
- // aliasB is unaffected — the error is scoped to the offending alias.
84
- expect(byAlias.aliasB.issues).toEqual([]);
85
- });
86
- it("an isolated program proves a single combined program WOULD collide", () => {
87
- // Build ONE program over BOTH aliases' bodies + globals (the OLD approach) and
88
- // show it reports a false error that the per-alias programs do NOT — pinning
89
- // exactly the bug the fix removes.
90
- const a = writeAlias("aliasA", "const x: string = trigger.app_workflow.inputs.foo;\nreturn({ data: { x } });", { foo: "string" });
91
- const b = writeAlias("aliasB", "const y: string = trigger.app_workflow.inputs.bar;\nreturn({ data: { y } });", { bar: "string" });
92
- // Per-alias: both clean.
93
- const perAlias = checkWorkflowBodies(ts, [a, b]);
94
- expect(perAlias.every((r) => r.issues.length === 0)).toBe(true);
95
- // All-in-one: one program over all four files. The two ambient `trigger`
96
- // declarations + two `AppWorkflowTrigger`/`__WorkflowReturn` interfaces
97
- // collide, so the combined program reports errors the isolated ones don't.
98
- const combined = ts.createProgram({
99
- rootNames: [a.input.globalsPath, a.input.bodyPath, b.input.globalsPath, b.input.bodyPath],
100
- options: workflowCheckCompilerOptions(ts),
101
- });
102
- const combinedDiagnostics = ts.getPreEmitDiagnostics(combined);
103
- // The collision is a TS2339 on a BODY file — the alias whose ambient `trigger`
104
- // lost the merge reads a field that isn't on the surviving alias's inputs.
105
- // Assert THAT specific diagnostic, not a bare count (an unrelated options-level
106
- // TS5110, which has no `.file`, would also satisfy length > 0 — a false green).
107
- const collisions = combinedDiagnostics.filter((d) => d.file !== undefined && d.code === 2339);
108
- expect(collisions.length).toBeGreaterThan(0);
109
- const text = collisions.map((d) => ts.flattenDiagnosticMessageText(d.messageText, "\n")).join("\n");
110
- expect(text).toMatch(/\b(foo|bar)\b/);
111
- });
112
- it("maps a diagnostic line back to the author's body (envelope offset removed)", () => {
113
- // Body line 2 has the error; the report must point at line 2, not the wrapped
114
- // line (the envelope + header push it down several lines).
115
- const a = writeAlias("aliasA", "const ok: string = trigger.app_workflow.inputs.foo;\nconst bad: number = trigger.app_workflow.inputs.foo;\nreturn({ data: { ok, bad } });", { foo: "string" });
116
- const issues = checkOneWorkflowBody(ts, a.input);
117
- expect(issues.length).toBeGreaterThan(0);
118
- // The string→number assignment is on the author's line 2.
119
- expect(issues[0].line).toBe(2);
120
- });
121
- });
122
- describe("bodyLineOffset", () => {
123
- it("counts the lines up to and including the __workflow opener", () => {
124
- // Two header lines, then the opener on line 3 → offset 3 (author's line 1
125
- // is the next line).
126
- const wrapped = ["// header a", "// header b", FALLBACK_ENVELOPE_PREFIX.trimEnd(), "return({});", FALLBACK_ENVELOPE_SUFFIX.trim()].join("\n");
127
- expect(bodyLineOffset(wrapped)).toBe(3);
128
- });
129
- it("returns 0 when there is no recognizable wrapper opener", () => {
130
- expect(bodyLineOffset("return({});\n")).toBe(0);
131
- });
132
- });
133
- /**
134
- * Pin the local compiler options to the SERVER's set-time options
135
- * (`COMPILER_OPTIONS` in typecheck_with_typescript.ts). The check command's
136
- * verdict only matches set-time if these stay in lockstep — if the server
137
- * changes a flag, this breaks and forces the local options to follow.
138
- */
139
- describe("workflowCheckCompilerOptions pins the server's set-time options", () => {
140
- it("matches the server's FULL pinned literal — every flag, so no flag can silently drift", () => {
141
- expect(workflowCheckCompilerOptions(ts)).toEqual({
142
- target: ts.ScriptTarget.ES2022,
143
- module: ts.ModuleKind.ESNext,
144
- lib: ["lib.es2022.d.ts"],
145
- strict: true,
146
- noImplicitAny: true,
147
- strictNullChecks: true,
148
- noImplicitReturns: false,
149
- noEmit: true,
150
- allowJs: false,
151
- isolatedModules: false,
152
- skipLibCheck: true,
153
- moduleResolution: ts.ModuleResolutionKind.NodeNext,
154
- types: [],
155
- declaration: false,
156
- });
157
- });
158
- it("uses the SAME __workflow envelope the server compiles bodies within", () => {
159
- // The body writer wraps in this envelope; the server compiles within the
160
- // identical one (pinned to the backend's SOURCE_PREFIX/SUFFIX by
161
- // app_commands.test.ts + apps.test.ts). If they diverge, the local line
162
- // mapping and the parse of top-level await / return({...}) break.
163
- expect(FALLBACK_ENVELOPE_PREFIX).toBe("async function __workflow(): Promise<__WorkflowReturn | void> {\n");
164
- expect(FALLBACK_ENVELOPE_SUFFIX).toBe("\n}");
165
- });
166
- });
package/dist/args.d.ts DELETED
@@ -1,42 +0,0 @@
1
- /**
2
- * CLI argument parser.
3
- *
4
- * Splits `process.argv.slice(2)` into a command / subcommand / positional
5
- * (`toolArgs`) / remaining-positionals (`restArgs`) shape plus typed `flags`.
6
- * Value-taking flags (`-m`, `--api-key`, …) consume the next token; boolean
7
- * flags (`--json`, `--local`, …) toggle. Anything not matching a known flag is
8
- * positional.
9
- *
10
- * Lives apart from `cli.ts` so it can be unit-tested — `cli.ts` runs `main()`
11
- * on import, so importing the parser from there would execute the CLI.
12
- */
13
- export declare function parseArgs(argv: string[]): {
14
- command?: string;
15
- subcommand?: string;
16
- toolArgs?: string;
17
- restArgs: string[];
18
- flags: {
19
- json: boolean;
20
- timeout?: number;
21
- output?: string;
22
- as?: string;
23
- apiKey?: string;
24
- workspace?: string;
25
- viewAs?: string;
26
- /** `--ui-src <path>`: absolute packages/ui/src for `ui link` in an external app. */
27
- uiSrc?: string;
28
- name?: string;
29
- timezone?: string;
30
- message?: string;
31
- local: boolean;
32
- all: boolean;
33
- /** `--yes` (alias `-y`): confirm a destructive command (e.g. `workspace delete`) non-interactively. */
34
- yes: boolean;
35
- /** `--print-created` (alias `--report-effects`): print the honest post-run side-effect harvest. */
36
- printCreated: boolean;
37
- /** `--cleanup`: also delete the harvested created records (records only). */
38
- cleanup: boolean;
39
- version: boolean;
40
- help: boolean;
41
- };
42
- };
package/dist/args.js DELETED
@@ -1,121 +0,0 @@
1
- /**
2
- * CLI argument parser.
3
- *
4
- * Splits `process.argv.slice(2)` into a command / subcommand / positional
5
- * (`toolArgs`) / remaining-positionals (`restArgs`) shape plus typed `flags`.
6
- * Value-taking flags (`-m`, `--api-key`, …) consume the next token; boolean
7
- * flags (`--json`, `--local`, …) toggle. Anything not matching a known flag is
8
- * positional.
9
- *
10
- * Lives apart from `cli.ts` so it can be unit-tested — `cli.ts` runs `main()`
11
- * on import, so importing the parser from there would execute the CLI.
12
- */
13
- export function parseArgs(argv) {
14
- const flags = {
15
- json: false,
16
- timeout: undefined,
17
- output: undefined,
18
- as: undefined,
19
- apiKey: undefined,
20
- workspace: undefined,
21
- viewAs: undefined,
22
- uiSrc: undefined,
23
- name: undefined,
24
- timezone: undefined,
25
- message: undefined,
26
- local: false,
27
- all: false,
28
- yes: false,
29
- printCreated: false,
30
- cleanup: false,
31
- version: false,
32
- help: false,
33
- };
34
- let command;
35
- let subcommand;
36
- let toolArgs;
37
- const restArgs = [];
38
- let i = 0;
39
- while (i < argv.length) {
40
- const arg = argv[i];
41
- switch (arg) {
42
- case "--json":
43
- flags.json = true;
44
- break;
45
- case "--timeout":
46
- flags.timeout = parseInt(argv[++i], 10);
47
- break;
48
- case "--output":
49
- case "-o":
50
- flags.output = argv[++i];
51
- break;
52
- case "--as":
53
- flags.as = argv[++i];
54
- break;
55
- case "--api-key":
56
- flags.apiKey = argv[++i];
57
- break;
58
- case "--workspace":
59
- case "-w":
60
- flags.workspace = argv[++i];
61
- break;
62
- case "--view-as":
63
- flags.viewAs = argv[++i];
64
- break;
65
- case "--ui-src":
66
- flags.uiSrc = argv[++i];
67
- break;
68
- case "--name":
69
- flags.name = argv[++i];
70
- break;
71
- case "--timezone":
72
- flags.timezone = argv[++i];
73
- break;
74
- case "-m":
75
- case "--message":
76
- flags.message = argv[++i];
77
- break;
78
- case "--local":
79
- flags.local = true;
80
- break;
81
- case "--all":
82
- flags.all = true;
83
- break;
84
- case "--yes":
85
- case "-y":
86
- flags.yes = true;
87
- break;
88
- case "--print-created":
89
- case "--report-effects":
90
- flags.printCreated = true;
91
- break;
92
- case "--cleanup":
93
- flags.cleanup = true;
94
- break;
95
- case "--version":
96
- case "-v":
97
- flags.version = true;
98
- break;
99
- case "--help":
100
- case "-h":
101
- flags.help = true;
102
- break;
103
- default:
104
- if (!command) {
105
- command = arg;
106
- }
107
- else if (!subcommand) {
108
- subcommand = arg;
109
- }
110
- else if (!toolArgs) {
111
- toolArgs = arg;
112
- }
113
- else {
114
- restArgs.push(arg);
115
- }
116
- break;
117
- }
118
- i++;
119
- }
120
- return { command, subcommand, toolArgs, restArgs, flags };
121
- }
@@ -1 +0,0 @@
1
- export {};
package/dist/args.test.js DELETED
@@ -1,88 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { parseArgs } from "./args.js";
3
- describe("parseArgs", () => {
4
- it("splits command / subcommand / positional", () => {
5
- const r = parseArgs(["app", "deploy", "my message"]);
6
- expect(r.command).toBe("app");
7
- expect(r.subcommand).toBe("deploy");
8
- expect(r.toolArgs).toBe("my message");
9
- expect(r.restArgs).toEqual([]);
10
- });
11
- // `app deploy` regression: `-m` must be parsed as a flag, not silently
12
- // consumed as the positional message.
13
- it("parses -m as the message flag", () => {
14
- const r = parseArgs(["app", "deploy", "-m", "a message"]);
15
- expect(r.flags.message).toBe("a message");
16
- expect(r.toolArgs).toBeUndefined();
17
- });
18
- it("parses --message as the message flag", () => {
19
- const r = parseArgs(["app", "deploy", "--message", "a message"]);
20
- expect(r.flags.message).toBe("a message");
21
- });
22
- it("defaults message to undefined for a bare deploy", () => {
23
- const r = parseArgs(["app", "deploy"]);
24
- expect(r.flags.message).toBeUndefined();
25
- });
26
- it("treats a bare --version as the boolean CLI-version flag", () => {
27
- const r = parseArgs(["--version"]);
28
- expect(r.flags.version).toBe(true);
29
- });
30
- it("treats -v as the boolean CLI-version flag", () => {
31
- const r = parseArgs(["-v"]);
32
- expect(r.flags.version).toBe(true);
33
- });
34
- it("parses --workspace as a value flag", () => {
35
- const r = parseArgs(["run", "query_tables", "{}", "--workspace", "wsp_123"]);
36
- expect(r.flags.workspace).toBe("wsp_123");
37
- expect(r.command).toBe("run");
38
- expect(r.subcommand).toBe("query_tables");
39
- expect(r.toolArgs).toBe("{}");
40
- });
41
- it("parses -w as the workspace alias", () => {
42
- const r = parseArgs(["run", "query_tables", "-w", "wsp_123"]);
43
- expect(r.flags.workspace).toBe("wsp_123");
44
- });
45
- it("parses --view-as as a value flag", () => {
46
- const r = parseArgs(["app", "dev", "--view-as", "mbr_abc"]);
47
- expect(r.flags.viewAs).toBe("mbr_abc");
48
- expect(r.command).toBe("app");
49
- expect(r.subcommand).toBe("dev");
50
- });
51
- it("parses --all as a boolean flag", () => {
52
- const r = parseArgs(["auth", "logout", "--all"]);
53
- expect(r.flags.all).toBe(true);
54
- expect(r.command).toBe("auth");
55
- expect(r.subcommand).toBe("logout");
56
- });
57
- it("defaults workspace to undefined and all to false", () => {
58
- const r = parseArgs(["org"]);
59
- expect(r.flags.workspace).toBeUndefined();
60
- expect(r.flags.all).toBe(false);
61
- });
62
- it("parses --print-created and --report-effects to the same boolean flag", () => {
63
- expect(parseArgs(["app", "workflow", "run", "wf", "--print-created"]).flags.printCreated).toBe(true);
64
- expect(parseArgs(["app", "workflow", "run", "wf", "--report-effects"]).flags.printCreated).toBe(true);
65
- // The alias does not consume the next token (it's a boolean flag).
66
- const r = parseArgs(["app", "workflow", "run", "wf", "--print-created"]);
67
- expect(r.restArgs).toEqual(["wf"]);
68
- });
69
- it("parses --cleanup as a boolean flag (default false)", () => {
70
- expect(parseArgs(["app", "workflow", "run", "wf", "--cleanup"]).flags.cleanup).toBe(true);
71
- expect(parseArgs(["app", "workflow", "run", "wf"]).flags.cleanup).toBe(false);
72
- expect(parseArgs(["app", "workflow", "run", "wf"]).flags.printCreated).toBe(false);
73
- });
74
- it("parses --yes and -y as the same boolean confirmation flag (default false)", () => {
75
- expect(parseArgs(["workspace", "delete", "wsp_1", "--yes"]).flags.yes).toBe(true);
76
- expect(parseArgs(["workspace", "delete", "wsp_1", "-y"]).flags.yes).toBe(true);
77
- const r = parseArgs(["workspace", "delete", "wsp_1"]);
78
- expect(r.flags.yes).toBe(false);
79
- // The boolean flag does not consume the id positional.
80
- expect(parseArgs(["workspace", "delete", "wsp_1", "--yes"]).toolArgs).toBe("wsp_1");
81
- });
82
- it("treats `org use <name>` as command / subcommand / positional", () => {
83
- const r = parseArgs(["org", "use", "Acme Corp"]);
84
- expect(r.command).toBe("org");
85
- expect(r.subcommand).toBe("use");
86
- expect(r.toolArgs).toBe("Acme Corp");
87
- });
88
- });