tubeless 0.1.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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/abort.d.ts +12 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +49 -0
- package/dist/abort.js.map +1 -0
- package/dist/batch.d.ts +22 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/batch.js +73 -0
- package/dist/batch.js.map +1 -0
- package/dist/checkpoint.d.ts +33 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +71 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/child-execution.d.ts +30 -0
- package/dist/child-execution.d.ts.map +1 -0
- package/dist/child-execution.js +270 -0
- package/dist/child-execution.js.map +1 -0
- package/dist/cli-command.d.ts +29 -0
- package/dist/cli-command.d.ts.map +1 -0
- package/dist/cli-command.js +314 -0
- package/dist/cli-command.js.map +1 -0
- package/dist/cli-parser.d.ts +28 -0
- package/dist/cli-parser.d.ts.map +1 -0
- package/dist/cli-parser.js +557 -0
- package/dist/cli-parser.js.map +1 -0
- package/dist/cli-pipeline-command.d.ts +84 -0
- package/dist/cli-pipeline-command.d.ts.map +1 -0
- package/dist/cli-pipeline-command.js +185 -0
- package/dist/cli-pipeline-command.js.map +1 -0
- package/dist/cli-types.d.ts +220 -0
- package/dist/cli-types.d.ts.map +1 -0
- package/dist/cli-types.js +19 -0
- package/dist/cli-types.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +8 -0
- package/dist/env.js.map +1 -0
- package/dist/file-utils.d.ts +4 -0
- package/dist/file-utils.d.ts.map +1 -0
- package/dist/file-utils.js +35 -0
- package/dist/file-utils.js.map +1 -0
- package/dist/interactive-reporter.d.ts +38 -0
- package/dist/interactive-reporter.d.ts.map +1 -0
- package/dist/interactive-reporter.js +308 -0
- package/dist/interactive-reporter.js.map +1 -0
- package/dist/lifecycle.d.ts +17 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +77 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/live-ticker.d.ts +21 -0
- package/dist/live-ticker.d.ts.map +1 -0
- package/dist/live-ticker.js +377 -0
- package/dist/live-ticker.js.map +1 -0
- package/dist/mapped-child-progress.d.ts +109 -0
- package/dist/mapped-child-progress.d.ts.map +1 -0
- package/dist/mapped-child-progress.js +133 -0
- package/dist/mapped-child-progress.js.map +1 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +5 -0
- package/dist/node.js.map +1 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +19 -0
- package/dist/paths.js.map +1 -0
- package/dist/pipeline-command-marker.d.ts +5 -0
- package/dist/pipeline-command-marker.d.ts.map +1 -0
- package/dist/pipeline-command-marker.js +21 -0
- package/dist/pipeline-command-marker.js.map +1 -0
- package/dist/pipeline-diagnostics.d.ts +4 -0
- package/dist/pipeline-diagnostics.d.ts.map +1 -0
- package/dist/pipeline-diagnostics.js +17 -0
- package/dist/pipeline-diagnostics.js.map +1 -0
- package/dist/pipeline-module.d.ts +62 -0
- package/dist/pipeline-module.d.ts.map +1 -0
- package/dist/pipeline-module.js +157 -0
- package/dist/pipeline-module.js.map +1 -0
- package/dist/pipeline.d.ts +606 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +1444 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/progress.d.ts +4 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +8 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-select.d.ts +81 -0
- package/dist/prompt-select.d.ts.map +1 -0
- package/dist/prompt-select.js +197 -0
- package/dist/prompt-select.js.map +1 -0
- package/dist/rate-limit.d.ts +7 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +23 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/render.d.ts +21 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +60 -0
- package/dist/render.js.map +1 -0
- package/dist/reporter-entry.d.ts +3 -0
- package/dist/reporter-entry.d.ts.map +1 -0
- package/dist/reporter-entry.js +3 -0
- package/dist/reporter-entry.js.map +1 -0
- package/dist/reporter.d.ts +50 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +167 -0
- package/dist/reporter.js.map +1 -0
- package/dist/retry.d.ts +28 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +59 -0
- package/dist/retry.js.map +1 -0
- package/dist/run-store-sqlite.d.ts +12 -0
- package/dist/run-store-sqlite.d.ts.map +1 -0
- package/dist/run-store-sqlite.js +187 -0
- package/dist/run-store-sqlite.js.map +1 -0
- package/dist/run-store-ui-http.d.ts +5 -0
- package/dist/run-store-ui-http.d.ts.map +1 -0
- package/dist/run-store-ui-http.js +19 -0
- package/dist/run-store-ui-http.js.map +1 -0
- package/dist/run-store-ui-page.d.ts +2 -0
- package/dist/run-store-ui-page.d.ts.map +1 -0
- package/dist/run-store-ui-page.js +759 -0
- package/dist/run-store-ui-page.js.map +1 -0
- package/dist/run-store-ui-protocol.d.ts +28 -0
- package/dist/run-store-ui-protocol.d.ts.map +1 -0
- package/dist/run-store-ui-protocol.js +84 -0
- package/dist/run-store-ui-protocol.js.map +1 -0
- package/dist/run-store-ui-state.d.ts +21 -0
- package/dist/run-store-ui-state.d.ts.map +1 -0
- package/dist/run-store-ui-state.js +65 -0
- package/dist/run-store-ui-state.js.map +1 -0
- package/dist/run-store-ui.d.ts +25 -0
- package/dist/run-store-ui.d.ts.map +1 -0
- package/dist/run-store-ui.js +217 -0
- package/dist/run-store-ui.js.map +1 -0
- package/dist/run-store.d.ts +109 -0
- package/dist/run-store.d.ts.map +1 -0
- package/dist/run-store.js +345 -0
- package/dist/run-store.js.map +1 -0
- package/dist/testing.d.ts +40 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +83 -0
- package/dist/testing.js.map +1 -0
- package/dist/tracing-internal.d.ts +21 -0
- package/dist/tracing-internal.d.ts.map +1 -0
- package/dist/tracing-internal.js +206 -0
- package/dist/tracing-internal.js.map +1 -0
- package/dist/tracing-json.d.ts +12 -0
- package/dist/tracing-json.d.ts.map +1 -0
- package/dist/tracing-json.js +8 -0
- package/dist/tracing-json.js.map +1 -0
- package/dist/tracing-otel.d.ts +36 -0
- package/dist/tracing-otel.d.ts.map +1 -0
- package/dist/tracing-otel.js +60 -0
- package/dist/tracing-otel.js.map +1 -0
- package/dist/tracing.d.ts +48 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +2 -0
- package/dist/tracing.js.map +1 -0
- package/dist/workbench-bin.d.ts +3 -0
- package/dist/workbench-bin.d.ts.map +1 -0
- package/dist/workbench-bin.js +8 -0
- package/dist/workbench-bin.js.map +1 -0
- package/dist/workbench-graph.d.ts +3 -0
- package/dist/workbench-graph.d.ts.map +1 -0
- package/dist/workbench-graph.js +64 -0
- package/dist/workbench-graph.js.map +1 -0
- package/dist/workbench-inspect.d.ts +3 -0
- package/dist/workbench-inspect.d.ts.map +1 -0
- package/dist/workbench-inspect.js +65 -0
- package/dist/workbench-inspect.js.map +1 -0
- package/dist/workbench-plan.d.ts +3 -0
- package/dist/workbench-plan.d.ts.map +1 -0
- package/dist/workbench-plan.js +72 -0
- package/dist/workbench-plan.js.map +1 -0
- package/dist/workbench-run.d.ts +13 -0
- package/dist/workbench-run.d.ts.map +1 -0
- package/dist/workbench-run.js +115 -0
- package/dist/workbench-run.js.map +1 -0
- package/dist/workbench-shared.d.ts +67 -0
- package/dist/workbench-shared.d.ts.map +1 -0
- package/dist/workbench-shared.js +163 -0
- package/dist/workbench-shared.js.map +1 -0
- package/dist/workbench-studio.d.ts +26 -0
- package/dist/workbench-studio.d.ts.map +1 -0
- package/dist/workbench-studio.js +57 -0
- package/dist/workbench-studio.js.map +1 -0
- package/dist/workbench-ui.d.ts +3 -0
- package/dist/workbench-ui.d.ts.map +1 -0
- package/dist/workbench-ui.js +262 -0
- package/dist/workbench-ui.js.map +1 -0
- package/dist/workbench.d.ts +5 -0
- package/dist/workbench.d.ts.map +1 -0
- package/dist/workbench.js +43 -0
- package/dist/workbench.js.map +1 -0
- package/docs/README.md +31 -0
- package/docs/agent-guide.md +168 -0
- package/docs/api-reference.md +269 -0
- package/docs/api-report.json +298 -0
- package/docs/child-pipeline-composition.md +185 -0
- package/docs/cli.md +118 -0
- package/docs/concepts.md +502 -0
- package/docs/getting-started.md +162 -0
- package/docs/llms.txt +62 -0
- package/docs/recipes.md +51 -0
- package/docs/studio.md +86 -0
- package/examples/best-effort.ts +42 -0
- package/examples/cancellation-and-testing.ts +45 -0
- package/examples/child-pipeline.ts +51 -0
- package/examples/cli-job.ts +86 -0
- package/examples/conditional-step.ts +41 -0
- package/examples/fan-out-progress.ts +53 -0
- package/examples/local-observability.ts +80 -0
- package/examples/publish-with-gates.ts +56 -0
- package/examples/rendering.ts +34 -0
- package/examples/resumable-enrichment.ts +75 -0
- package/examples/tracing.ts +35 -0
- package/examples/tsconfig.json +15 -0
- package/examples/typed-import.ts +54 -0
- package/examples/validated-boundaries.ts +75 -0
- package/package.json +156 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { createRunId } from "./pipeline.js";
|
|
6
|
+
import { executePipelineCommandValues, } from "./workbench-run.js";
|
|
7
|
+
import { commandContext, errorMessage, loadPipelineCommand, TUBELESS_WORKBENCH_EXIT_CODE, writeUsageError, } from "./workbench-shared.js";
|
|
8
|
+
const UI_USAGE = `Usage: tubeless ui [options] [studio-file]
|
|
9
|
+
|
|
10
|
+
Serve the local pipeline studio from an append-only SQLite run store. Register
|
|
11
|
+
definePipelineCommand modules directly or through one studio config file.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--command <path> Register a launchable pipeline command (repeatable)
|
|
15
|
+
-e, --export <name> Select the export when registering exactly one command
|
|
16
|
+
--store <path> SQLite database (default: .tubeless/runs.sqlite)
|
|
17
|
+
--host <value> Bind address (default: 127.0.0.1)
|
|
18
|
+
--port <number> HTTP port (default: 4317)
|
|
19
|
+
-h, --help Show this help
|
|
20
|
+
`;
|
|
21
|
+
function parseUiArgs(argv) {
|
|
22
|
+
return parseArgs({
|
|
23
|
+
args: [...argv],
|
|
24
|
+
allowPositionals: true,
|
|
25
|
+
options: {
|
|
26
|
+
command: { type: "string", multiple: true },
|
|
27
|
+
export: { type: "string", short: "e" },
|
|
28
|
+
help: { type: "boolean", short: "h" },
|
|
29
|
+
host: { type: "string" },
|
|
30
|
+
port: { type: "string" },
|
|
31
|
+
store: { type: "string" },
|
|
32
|
+
},
|
|
33
|
+
strict: true,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function loadPipelineStudioConfig(fileArgument, io) {
|
|
37
|
+
const filePath = path.resolve(io.cwd, fileArgument);
|
|
38
|
+
try {
|
|
39
|
+
const fileStat = await stat(filePath);
|
|
40
|
+
if (!fileStat.isFile())
|
|
41
|
+
throw new Error(`${filePath} is not a file.`);
|
|
42
|
+
const [{ isPipelineStudioConfig }, moduleExports] = await Promise.all([
|
|
43
|
+
import("./workbench-studio.js"),
|
|
44
|
+
// SAFETY: A dynamic import of an arbitrary user module yields an untyped
|
|
45
|
+
// export namespace; every value is validated by isPipelineStudioConfig
|
|
46
|
+
// before use, so the Record<string, unknown> shape is the module boundary.
|
|
47
|
+
import(pathToFileURL(filePath).href),
|
|
48
|
+
]);
|
|
49
|
+
const configs = Object.entries(moduleExports).filter((entry) => isPipelineStudioConfig(entry[1]));
|
|
50
|
+
const unique = configs.filter(([, value], index) => configs.findIndex(([, other]) => other === value) === index);
|
|
51
|
+
if (unique.length === 0) {
|
|
52
|
+
throw new Error("Module does not export a definePipelineStudio config.");
|
|
53
|
+
}
|
|
54
|
+
if (unique.length > 1) {
|
|
55
|
+
throw new Error(`Module exports multiple studio configs (${unique.map(([name]) => name).join(", ")}).`);
|
|
56
|
+
}
|
|
57
|
+
// SAFETY: unique[0] passed isPipelineStudioConfig, whose type guard
|
|
58
|
+
// establishes the PipelineStudioConfig shape before it is returned.
|
|
59
|
+
return { config: unique[0][1], filePath };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
io.stderr.write(`Error: ${errorMessage(error)}\n`);
|
|
63
|
+
return { exitCode: TUBELESS_WORKBENCH_EXIT_CODE.load };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function runUi(argv, io) {
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = parseUiArgs(argv);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return writeUsageError(io, errorMessage(error), UI_USAGE);
|
|
73
|
+
}
|
|
74
|
+
if (parsed.values.help) {
|
|
75
|
+
io.stdout.write(UI_USAGE);
|
|
76
|
+
return TUBELESS_WORKBENCH_EXIT_CODE.success;
|
|
77
|
+
}
|
|
78
|
+
if (parsed.positionals.length > 1) {
|
|
79
|
+
return writeUsageError(io, "Pass at most one studio config file.", UI_USAGE);
|
|
80
|
+
}
|
|
81
|
+
const directCommandFiles = parsed.values.command ?? [];
|
|
82
|
+
const studioFile = parsed.positionals[0];
|
|
83
|
+
if (parsed.values.export && (directCommandFiles.length !== 1 || studioFile)) {
|
|
84
|
+
return writeUsageError(io, "--export requires exactly one registered --command.", UI_USAGE);
|
|
85
|
+
}
|
|
86
|
+
const port = Number(parsed.values.port ?? "4317");
|
|
87
|
+
if (!Number.isInteger(port) || port < 0 || port > 65_535) {
|
|
88
|
+
return writeUsageError(io, "--port must be an integer from 0 to 65535.", UI_USAGE);
|
|
89
|
+
}
|
|
90
|
+
const specs = directCommandFiles.map((file) => {
|
|
91
|
+
const spec = {
|
|
92
|
+
cwd: io.cwd,
|
|
93
|
+
filePath: path.resolve(io.cwd, file),
|
|
94
|
+
};
|
|
95
|
+
if (parsed.values.export !== undefined) {
|
|
96
|
+
spec.exportName = parsed.values.export;
|
|
97
|
+
}
|
|
98
|
+
return spec;
|
|
99
|
+
});
|
|
100
|
+
if (studioFile) {
|
|
101
|
+
const loadedConfig = await loadPipelineStudioConfig(studioFile, io);
|
|
102
|
+
if ("exitCode" in loadedConfig)
|
|
103
|
+
return loadedConfig.exitCode;
|
|
104
|
+
const configDirectory = path.dirname(loadedConfig.filePath);
|
|
105
|
+
const configCwd = path.resolve(configDirectory, loadedConfig.config.cwd ?? ".");
|
|
106
|
+
specs.push(...loadedConfig.config.commands.map((command) => {
|
|
107
|
+
const spec = {
|
|
108
|
+
cwd: configCwd,
|
|
109
|
+
filePath: path.resolve(configDirectory, command.file),
|
|
110
|
+
};
|
|
111
|
+
if (command.export !== undefined) {
|
|
112
|
+
spec.exportName = command.export;
|
|
113
|
+
}
|
|
114
|
+
if (command.name !== undefined) {
|
|
115
|
+
spec.name = command.name;
|
|
116
|
+
}
|
|
117
|
+
return spec;
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
const identities = new Set();
|
|
121
|
+
for (const spec of specs) {
|
|
122
|
+
const identity = `${spec.filePath}\0${spec.exportName ?? ""}`;
|
|
123
|
+
if (identities.has(identity)) {
|
|
124
|
+
return writeUsageError(io, `Command module ${JSON.stringify(spec.filePath)} is duplicated.`, UI_USAGE);
|
|
125
|
+
}
|
|
126
|
+
identities.add(identity);
|
|
127
|
+
}
|
|
128
|
+
const host = (parsed.values.host ?? "127.0.0.1").toLowerCase();
|
|
129
|
+
const isLoopbackHost = host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
130
|
+
if (specs.length > 0 && !isLoopbackHost) {
|
|
131
|
+
return writeUsageError(io, "Browser-triggered execution requires a loopback --host.", UI_USAGE);
|
|
132
|
+
}
|
|
133
|
+
const launchController = new AbortController();
|
|
134
|
+
const activeLaunches = new Set();
|
|
135
|
+
const registrations = [];
|
|
136
|
+
for (const spec of specs) {
|
|
137
|
+
const commandIo = { ...io, cwd: spec.cwd };
|
|
138
|
+
const loaded = await loadPipelineCommand(spec.filePath, spec.exportName, commandIo);
|
|
139
|
+
if ("exitCode" in loaded)
|
|
140
|
+
return loaded.exitCode;
|
|
141
|
+
if (!("parseValues" in loaded.command) ||
|
|
142
|
+
typeof loaded.command.parseValues !== "function" ||
|
|
143
|
+
!("execute" in loaded.command) ||
|
|
144
|
+
typeof loaded.command.execute !== "function") {
|
|
145
|
+
io.stderr.write(`Error: ${spec.filePath} does not support structured studio launches.\n`);
|
|
146
|
+
return TUBELESS_WORKBENCH_EXIT_CODE.load;
|
|
147
|
+
}
|
|
148
|
+
// SAFETY: the guard above verified the cross-realm loaded command exposes
|
|
149
|
+
// both parseValues and execute as functions, matching the
|
|
150
|
+
// WorkbenchStructuredPipelineCommand runtime surface.
|
|
151
|
+
const launchableCommand = loaded.command;
|
|
152
|
+
const commandName = spec.name ?? loaded.command.descriptor.name;
|
|
153
|
+
const descriptor = {
|
|
154
|
+
canPlan: true,
|
|
155
|
+
id: `${spec.filePath}#${loaded.exportName}`,
|
|
156
|
+
name: commandName,
|
|
157
|
+
parameters: loaded.command.descriptor.parameters,
|
|
158
|
+
};
|
|
159
|
+
if (loaded.command.descriptor.description !== undefined) {
|
|
160
|
+
descriptor.description = loaded.command.descriptor.description;
|
|
161
|
+
}
|
|
162
|
+
registrations.push({
|
|
163
|
+
command: launchableCommand,
|
|
164
|
+
commandIo,
|
|
165
|
+
descriptor,
|
|
166
|
+
runIdPrefix: loaded.command.descriptor.name,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const filename = path.resolve(io.cwd, parsed.values.store ?? ".tubeless/runs.sqlite");
|
|
170
|
+
let store;
|
|
171
|
+
let server;
|
|
172
|
+
try {
|
|
173
|
+
const { openSqlitePipelineRunStore } = await import("./run-store-sqlite.js");
|
|
174
|
+
const { startPipelineRunStudio } = await import("./run-store-ui.js");
|
|
175
|
+
store = await openSqlitePipelineRunStore(filename);
|
|
176
|
+
const commandById = new Map(registrations.map((registration) => [registration.descriptor.id, registration]));
|
|
177
|
+
const launcher = registrations.length === 0
|
|
178
|
+
? undefined
|
|
179
|
+
: {
|
|
180
|
+
commands: registrations.map(({ descriptor }) => descriptor),
|
|
181
|
+
plan(commandId, input) {
|
|
182
|
+
const registration = commandById.get(commandId);
|
|
183
|
+
if (!registration?.command.plan) {
|
|
184
|
+
throw new Error("Pipeline planning is not available for this command.");
|
|
185
|
+
}
|
|
186
|
+
return registration.command.plan(input);
|
|
187
|
+
},
|
|
188
|
+
async launch(commandId, values) {
|
|
189
|
+
const registration = commandById.get(commandId);
|
|
190
|
+
if (!registration)
|
|
191
|
+
return { accepted: false, errors: ["Pipeline command not found."] };
|
|
192
|
+
const runId = createRunId(registration.runIdPrefix);
|
|
193
|
+
const pipelineContext = { runId, tracing: { exporter: store } };
|
|
194
|
+
let parsedCommand;
|
|
195
|
+
try {
|
|
196
|
+
parsedCommand = registration.command.parseValues(values, commandContext(registration.commandIo, launchController.signal, pipelineContext));
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
return { accepted: false, errors: [errorMessage(error)] };
|
|
200
|
+
}
|
|
201
|
+
if (parsedCommand.kind === "error") {
|
|
202
|
+
return { accepted: false, errors: parsedCommand.errors };
|
|
203
|
+
}
|
|
204
|
+
if (parsedCommand.kind === "help") {
|
|
205
|
+
return { accepted: false, errors: ["Help is not a launchable value set."] };
|
|
206
|
+
}
|
|
207
|
+
const execution = executePipelineCommandValues(registration.command, parsedCommand.values, registration.commandIo, launchController.signal, pipelineContext);
|
|
208
|
+
activeLaunches.add(execution);
|
|
209
|
+
void execution.finally(() => activeLaunches.delete(execution));
|
|
210
|
+
return { accepted: true, runId };
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
const studioOptions = {
|
|
214
|
+
host,
|
|
215
|
+
launcher,
|
|
216
|
+
port,
|
|
217
|
+
store,
|
|
218
|
+
};
|
|
219
|
+
if (isLoopbackHost) {
|
|
220
|
+
studioOptions.history = {
|
|
221
|
+
clear: () => store.clearHistory(),
|
|
222
|
+
isBusy: () => activeLaunches.size > 0,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
server = await startPipelineRunStudio(studioOptions);
|
|
226
|
+
io.stdout.write(`Tubeless local studio: ${server.url}\n`);
|
|
227
|
+
io.stdout.write(`Run store: ${filename}\n`);
|
|
228
|
+
if (registrations.length > 0) {
|
|
229
|
+
io.stdout.write(`Launchable commands: ${registrations.map(({ descriptor }) => descriptor.name).join(", ")}\n`);
|
|
230
|
+
}
|
|
231
|
+
io.stdout.write("Press Ctrl-C to stop.\n");
|
|
232
|
+
await new Promise((resolve) => {
|
|
233
|
+
if (io.signal?.aborted) {
|
|
234
|
+
resolve();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (io.signal) {
|
|
238
|
+
io.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const stop = () => {
|
|
242
|
+
process.removeListener("SIGINT", stop);
|
|
243
|
+
process.removeListener("SIGTERM", stop);
|
|
244
|
+
resolve();
|
|
245
|
+
};
|
|
246
|
+
process.once("SIGINT", stop);
|
|
247
|
+
process.once("SIGTERM", stop);
|
|
248
|
+
});
|
|
249
|
+
return TUBELESS_WORKBENCH_EXIT_CODE.success;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
io.stderr.write(`Error: ${errorMessage(error)}\n`);
|
|
253
|
+
return TUBELESS_WORKBENCH_EXIT_CODE.execution;
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
await server?.close();
|
|
257
|
+
launchController.abort(new DOMException("The local studio is stopping.", "AbortError"));
|
|
258
|
+
await Promise.allSettled(activeLaunches);
|
|
259
|
+
await store?.close();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=workbench-ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbench-ui.js","sourceRoot":"","sources":["../src/workbench-ui.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WorkbenchCliIo } from "./workbench-shared.js";
|
|
2
|
+
export { TUBELESS_WORKBENCH_EXIT_CODE, type WorkbenchCliIo } from "./workbench-shared.js";
|
|
3
|
+
/** Run the `tubeless` development workbench. Execution requires a definePipelineCommand export. */
|
|
4
|
+
export declare function runWorkbenchCli(argv: readonly string[], io: WorkbenchCliIo): Promise<number>;
|
|
5
|
+
//# sourceMappingURL=workbench.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbench.d.ts","sourceRoot":"","sources":["../src/workbench.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,4BAA4B,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAgB1F,mGAAmG;AACnG,wBAAsB,eAAe,CACnC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,cAAc,GACjB,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { runGraph } from "./workbench-graph.js";
|
|
2
|
+
import { runInspect } from "./workbench-inspect.js";
|
|
3
|
+
import { runPlan } from "./workbench-plan.js";
|
|
4
|
+
import { runCommand } from "./workbench-run.js";
|
|
5
|
+
import { TUBELESS_WORKBENCH_EXIT_CODE, writeUsageError, } from "./workbench-shared.js";
|
|
6
|
+
import { runUi } from "./workbench-ui.js";
|
|
7
|
+
export { TUBELESS_WORKBENCH_EXIT_CODE } from "./workbench-shared.js";
|
|
8
|
+
const WORKBENCH_USAGE = `Usage: tubeless <command> [options] <pipeline-file>
|
|
9
|
+
|
|
10
|
+
Inspect, plan, visualize, or safely run exported tubeless workflows.
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
tubeless inspect Show pipeline identity and the default structural plan
|
|
14
|
+
tubeless plan Preview step selection without executing the pipeline
|
|
15
|
+
tubeless graph Generate Mermaid flowchart source
|
|
16
|
+
tubeless run Execute an exported definePipelineCommand
|
|
17
|
+
tubeless ui Open the optional local run studio
|
|
18
|
+
|
|
19
|
+
Run tubeless <command> --help for command-specific options.
|
|
20
|
+
`;
|
|
21
|
+
/** Run the `tubeless` development workbench. Execution requires a definePipelineCommand export. */
|
|
22
|
+
export async function runWorkbenchCli(argv, io) {
|
|
23
|
+
const [command, ...commandArgs] = argv;
|
|
24
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
25
|
+
io.stdout.write(WORKBENCH_USAGE);
|
|
26
|
+
return TUBELESS_WORKBENCH_EXIT_CODE.success;
|
|
27
|
+
}
|
|
28
|
+
if (command === undefined) {
|
|
29
|
+
return writeUsageError(io, "Pass a command.", WORKBENCH_USAGE);
|
|
30
|
+
}
|
|
31
|
+
if (command === "inspect")
|
|
32
|
+
return runInspect(commandArgs, io);
|
|
33
|
+
if (command === "plan")
|
|
34
|
+
return runPlan(commandArgs, io);
|
|
35
|
+
if (command === "graph")
|
|
36
|
+
return runGraph(commandArgs, io);
|
|
37
|
+
if (command === "run")
|
|
38
|
+
return runCommand(commandArgs, io);
|
|
39
|
+
if (command === "ui")
|
|
40
|
+
return runUi(commandArgs, io);
|
|
41
|
+
return writeUsageError(io, `Unknown command ${JSON.stringify(command)}.`, WORKBENCH_USAGE);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=workbench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbench.js","sourceRoot":"","sources":["../src/workbench.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,4BAA4B,EAC5B,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,4BAA4B,EAAuB,MAAM,uBAAuB,CAAC;AAE1F,MAAM,eAAe,GAAG;;;;;;;;;;;;CAYvB,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAuB,EACvB,EAAkB;IAElB,MAAM,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,GAAG,IAAI,CAAC;IACvC,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACnE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACjC,OAAO,4BAA4B,CAAC,OAAO,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,eAAe,CAAC,EAAE,EAAE,iBAAiB,EAAE,eAAe,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACpD,OAAO,eAAe,CAAC,EAAE,EAAE,mBAAmB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAC7F,CAAC"}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# `tubeless` documentation
|
|
2
|
+
|
|
3
|
+
## Start here
|
|
4
|
+
|
|
5
|
+
| Document | Use it for |
|
|
6
|
+
| --------------------------------------- | ----------------------------------------------------- |
|
|
7
|
+
| [Package README](../README.md) | What it is, a first example, and a pattern table |
|
|
8
|
+
| [Getting started](./getting-started.md) | Building, running, and testing a first pipeline |
|
|
9
|
+
| [Recipe index](./recipes.md) | Choosing an executable example by intent |
|
|
10
|
+
| [Core concepts](./concepts.md) | Dependencies, skips, failures, dry runs, and contexts |
|
|
11
|
+
| [Agent guide](./agent-guide.md) | Rules for generating and modifying pipeline code |
|
|
12
|
+
|
|
13
|
+
## How to
|
|
14
|
+
|
|
15
|
+
| Document | Use it for |
|
|
16
|
+
| --------------------------- | ------------------------------------------------- |
|
|
17
|
+
| [CLI](./cli.md) | `inspect`, `plan`, `graph`, `run`, and exit codes |
|
|
18
|
+
| [Local studio](./studio.md) | Optional run history, `tubeless ui`, and catalogs |
|
|
19
|
+
|
|
20
|
+
## Deeper reference
|
|
21
|
+
|
|
22
|
+
| Document | Use it for |
|
|
23
|
+
| ------------------------------------------------------------- | ---------------------------------------------------- |
|
|
24
|
+
| [Child-pipeline composition](./child-pipeline-composition.md) | Opaque child and fan-out semantics |
|
|
25
|
+
| [Generated API inventory](./api-reference.md) | Entrypoints, exported symbols, and surface hashes |
|
|
26
|
+
| [Machine-readable API report](./api-report.json) | Automated public-surface review |
|
|
27
|
+
| [Agent evaluations](./agent-evaluations.md) | Forward-testing the guidance against realistic tasks |
|
|
28
|
+
| [LLM index](./llms.txt) | Compact machine-readable documentation map |
|
|
29
|
+
|
|
30
|
+
The generated API inventory is an audit artifact, not a tutorial. Prefer the
|
|
31
|
+
recipe index when deciding how to implement a workflow.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Agent guide for `tubeless`
|
|
2
|
+
|
|
3
|
+
Use this guide when generating or modifying pipelines, pipeline-backed scripts,
|
|
4
|
+
or shared helpers in this repository.
|
|
5
|
+
|
|
6
|
+
## Workflow
|
|
7
|
+
|
|
8
|
+
1. Read the [recipe index](./recipes.md) and open the smallest matching example.
|
|
9
|
+
2. Inspect a nearby production pipeline for domain conventions.
|
|
10
|
+
3. Declare stable IDs and operational descriptions. Add `name` only when printed
|
|
11
|
+
output needs a friendlier display name.
|
|
12
|
+
4. Model data dependencies before failure policy or CLI concerns.
|
|
13
|
+
5. Typecheck the example or consumer, then run focused tests.
|
|
14
|
+
6. Run `make check` from the package root after changing the package or its learning surface.
|
|
15
|
+
|
|
16
|
+
## Primitive selection
|
|
17
|
+
|
|
18
|
+
- Use `createSteps<TDomainOptions>()` once per pipeline and `definePipeline` once
|
|
19
|
+
after declaring its steps. Domain option types contain domain input only;
|
|
20
|
+
callers pass built-in controls alongside domain fields to `run(options)`, while
|
|
21
|
+
`plan(controls)` accepts controls alone.
|
|
22
|
+
- Treat a `PipelineDefinitionError` during module loading as an authoring bug;
|
|
23
|
+
static graph mistakes are rejected when `definePipeline` is called.
|
|
24
|
+
- Use `dependsOn` when the output is required, `optionalDependsOn` when absence
|
|
25
|
+
is expected, and `skipAfterFailureOf` for a failure gate that supplies no data.
|
|
26
|
+
- Use `createSteps(optionsSchema)` when external domain options need Standard
|
|
27
|
+
Schema validation or transformation. Add `outputSchema` only at step
|
|
28
|
+
boundaries that receive untrusted or independently checked values, and
|
|
29
|
+
`resultSchema` when the finalized public result must be checked. Core imports
|
|
30
|
+
no schema library.
|
|
31
|
+
- Set `dryRun: "skip"` on filesystem writes, database mutations, publication,
|
|
32
|
+
email, and other steps whose normal `run` must not execute in a dry run. Use a
|
|
33
|
+
typed `dryRun` handler when the step should produce a preview value instead.
|
|
34
|
+
- Use `step.skippable` only for an intentional successful outcome. Handle its
|
|
35
|
+
resulting `T | undefined` output type explicitly.
|
|
36
|
+
- Use `fromPipeline` for one independently useful child workflow and
|
|
37
|
+
`forEachPipeline` for runtime fan-out with stable keys and bounded concurrency.
|
|
38
|
+
Parent plans keep these steps opaque but expose `nestedPipeline` with the child
|
|
39
|
+
pipeline id, declared step ids, and single/fan-out mode for presentation.
|
|
40
|
+
- Use `runConcurrent` for bounded lightweight functions that do not need child
|
|
41
|
+
lifecycle events.
|
|
42
|
+
- Use `definePipelineCommand` for scripts centered on a pipeline. Do not parse
|
|
43
|
+
`process.argv` manually or redeclare built-in dry-run, step, or target flags.
|
|
44
|
+
Omit `mapOptions` when validated flags already satisfy same-name pipeline
|
|
45
|
+
options; provide it when names, types, defaults, or derived values differ.
|
|
46
|
+
The returned command exposes an immutable `descriptor`; UI adapters should
|
|
47
|
+
render that structured parameter contract instead of parsing help text.
|
|
48
|
+
Use `command.plan()` or `tubeless plan` for a selection-only preview. Do not
|
|
49
|
+
simulate planning with `--plan`.
|
|
50
|
+
- Use `pipeline.toMermaid()` when documentation needs the static graph; do not
|
|
51
|
+
duplicate dependency edges by hand.
|
|
52
|
+
- Use `tubeless inspect <pipeline-file>` for a module inventory, `tubeless plan` to
|
|
53
|
+
preview selection without domain options or execution, and `tubeless graph`
|
|
54
|
+
when generating documentation. `tubeless plan` accepts a pipeline or a marked
|
|
55
|
+
command and prefers the command when both are exported. Let the workbench
|
|
56
|
+
discover the sole matching export or pass `--export`.
|
|
57
|
+
- Use `tubeless run <command-file> -- <command-args>` only for modules exporting a
|
|
58
|
+
`definePipelineCommand`. Keep application flags after `--`; the command must
|
|
59
|
+
continue to own domain validation and option mapping.
|
|
60
|
+
|
|
61
|
+
## Runtime rules
|
|
62
|
+
|
|
63
|
+
- Library entrypoints are ESM-only and require Node.js 22 or later. The
|
|
64
|
+
`tubeless` CLI requires Bun 1.3.14 or later (`#!/usr/bin/env bun`).
|
|
65
|
+
- Use `context.log`, never direct `console` calls inside steps.
|
|
66
|
+
- Pass `context.signal` into network calls, batching, retry, rate limiting, and
|
|
67
|
+
long waits. Use `context.sleep` for retry-aware or testable delays.
|
|
68
|
+
- Call `context.reportProgress` for long loops and `context.reportAttempt` for
|
|
69
|
+
retries that operators should see.
|
|
70
|
+
- Resolve relative files from `context.cwd`; prefer helpers from
|
|
71
|
+
`tubeless/node`.
|
|
72
|
+
- Use `runOrThrow` when every step must succeed and the caller expects a value.
|
|
73
|
+
It always throws for an unsuccessful run, including `continueOnError` runs.
|
|
74
|
+
Use `run` when the caller must inspect failures, skips, timings, or best-effort
|
|
75
|
+
output. Its versioned `PipelineRun` exposes `runId`, terminal status and
|
|
76
|
+
timestamps, errors, and timestamped step reports with correlated attempt IDs;
|
|
77
|
+
structural skips have no attempt ID or start timestamp. Use hooks or tracing
|
|
78
|
+
for streaming logs and progress. Use `plan` when nothing may run.
|
|
79
|
+
- Declare supported downstream goals with `targets: [step]` on
|
|
80
|
+
`definePipeline`, then select their literal IDs through run controls. Required
|
|
81
|
+
inputs and failure gates are selected recursively. Use `stepIds` only when
|
|
82
|
+
exact low-level filtering of any step is intentional; never combine the two.
|
|
83
|
+
- Read `PipelinePlanStep.selectionReasons` when explaining selection. It already
|
|
84
|
+
includes originating targets and immediate dependents; do not reconstruct
|
|
85
|
+
provenance by walking dependency arrays in application or CLI code. Use
|
|
86
|
+
`renderPipelinePlan` from `tubeless/render` for shared human or JSON output
|
|
87
|
+
instead of maintaining another selection-reason formatter. Use
|
|
88
|
+
`createPipelineReporter` / `createRunReporter` from `tubeless/reporter` for
|
|
89
|
+
TTY presentation; do not import reporters from the root kernel.
|
|
90
|
+
- Use focused hooks for ordinary observation: `onStepStart`,
|
|
91
|
+
`onStepProgress`, `onStepComplete`, `onStepSkip`, `onStepCancel`, and
|
|
92
|
+
`onStepFail`. Their event metadata is already narrowed. Use additive
|
|
93
|
+
`onStepStatus` only when one consumer genuinely needs the whole discriminated
|
|
94
|
+
lifecycle, such as a status-aware renderer or event store.
|
|
95
|
+
- Use `createPipelineTestRuntime` from `tubeless/testing` for deterministic
|
|
96
|
+
pipeline tests. Inspect its structured logs, statuses, and latest progress;
|
|
97
|
+
keep test-framework matchers outside the package.
|
|
98
|
+
- Branch on `PipelineError.code`, `phase`, and `kind`, never message prose.
|
|
99
|
+
Underlying thrown codes live in `sourceCode`. Failed and cancelled reports
|
|
100
|
+
expose the structured error under `error`; skipped reports expose `reason`,
|
|
101
|
+
optional `message`, and optional `dependencyId`. `PipelineError.cause` is a
|
|
102
|
+
bounded JSON-safe snapshot; a thrown `PipelineExecutionError` retains the
|
|
103
|
+
original value through native `Error.cause`. Use `renderPipelineError` when a
|
|
104
|
+
diagnostic crosses a human or JSON presentation boundary.
|
|
105
|
+
- Standard Schema failures use `kind: "validation"`, retain normalized `issues`,
|
|
106
|
+
and have boundary-specific codes for options, step outputs, and final results.
|
|
107
|
+
Async schemas run during `run`; synchronous `plan()` previews graph and
|
|
108
|
+
selection only and never invokes schemas.
|
|
109
|
+
- Wrap normal finalizers in `requireOutputs` when a valid result requires
|
|
110
|
+
specific step outputs. Use a plain finalizer only when partial output is a
|
|
111
|
+
valid domain result.
|
|
112
|
+
- Preserve the dependency-free runtime. Keep application telemetry SDKs at the
|
|
113
|
+
exporter boundary.
|
|
114
|
+
- Keep durable local observation opt-in. Use the append-only adapter from
|
|
115
|
+
`tubeless/run-store/sqlite` or `tubeless run --store`; do not make pipeline
|
|
116
|
+
definitions depend on storage or the studio.
|
|
117
|
+
- Use `createPipelineRunProjector` from `tubeless/run-store` when a custom
|
|
118
|
+
reader pages `listEvents({ afterId })`. Append each newer page and call
|
|
119
|
+
`snapshot()`; a refresh with no new ids returns the cached view. Duplicate
|
|
120
|
+
and out-of-order ids are ignored; `0` is a valid first id. Use
|
|
121
|
+
`projectPipelineRunStore` only for a one-shot fold of a complete list. See
|
|
122
|
+
[`local-observability.ts`](../examples/local-observability.ts).
|
|
123
|
+
- Pass caller-owned `runId` and `parentRunId` values through `PipelineContext`
|
|
124
|
+
when joining an external execution tree. Do not derive correlation from step
|
|
125
|
+
IDs or timestamps.
|
|
126
|
+
- Treat `tubeless ui` as a local projection with no execution capability by
|
|
127
|
+
default. Use `definePipelineStudio` for a checked-in command catalog, and
|
|
128
|
+
register only explicit `definePipelineCommand` modules; never make execution
|
|
129
|
+
require the studio server or infer executable modules from observed history.
|
|
130
|
+
|
|
131
|
+
## Failure and safety rules
|
|
132
|
+
|
|
133
|
+
- Do not use `step.skippable` to swallow an exception.
|
|
134
|
+
- Do not treat dry run as rollback; external side effects require
|
|
135
|
+
`dryRun: "skip"` or a side-effect-free custom `dryRun` handler.
|
|
136
|
+
- Do not publish after a failed or cancelled validation step. Make validation
|
|
137
|
+
required, or use `skipAfterFailureOf` when its output is intentionally optional;
|
|
138
|
+
the gate blocks both unsuccessful terminal statuses.
|
|
139
|
+
- Do not hide absent required finalizer outputs behind defaults. Declare them
|
|
140
|
+
with `requireOutputs`; handle optional outputs explicitly.
|
|
141
|
+
- Keep step IDs stable because reports, hooks, traces, and CLI selection use them.
|
|
142
|
+
- Treat `name` as presentation only. Dependencies, outputs, traces, and CLI
|
|
143
|
+
selection continue to use the stable step ID.
|
|
144
|
+
|
|
145
|
+
## Required references
|
|
146
|
+
|
|
147
|
+
- Read [core concepts](./concepts.md) for skip, failure, or selection changes.
|
|
148
|
+
- Read [the CLI](./cli.md) for inspect, plan, graph, run, and exit codes.
|
|
149
|
+
- Read [the studio](./studio.md) before changing `tubeless ui` or
|
|
150
|
+
`definePipelineStudio`.
|
|
151
|
+
- Read [child composition](./child-pipeline-composition.md) before changing child
|
|
152
|
+
propagation, progress, or parent/child selection.
|
|
153
|
+
- Read the relevant executable example linked from the
|
|
154
|
+
[recipe index](./recipes.md) before writing new usage.
|
|
155
|
+
- Use the [generated API inventory](./api-reference.md) only to verify exports;
|
|
156
|
+
it is not implementation guidance.
|
|
157
|
+
|
|
158
|
+
## Validation
|
|
159
|
+
|
|
160
|
+
For package changes:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
make check
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
For a consumer-only change, run its focused tests and the repository typecheck.
|
|
167
|
+
If the public surface changes intentionally, regenerate the checked API artifacts
|
|
168
|
+
with `bun run api:generate` from the package root.
|