@standards/cli 1.0.0-alpha.391 → 1.0.0-alpha.393
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*! © 2025-2026 AND YOU CREATE SAS (RCS Paris 940 190 614). Proprietary — see LICENSE (Standards SDK License 1.0). */
|
|
3
|
+
import { t as discoverBundles } from "./discover-bundles-D5RNCkan.mjs";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import { styleText } from "node:util";
|
|
5
|
-
import { StandardsRequestError, createTransport } from "@standards/client";
|
|
6
|
+
import { BundleConflictError, StandardsRequestError, createTransport, syncBundle } from "@standards/client";
|
|
6
7
|
import { Command } from "commander";
|
|
7
|
-
import {
|
|
8
|
+
import { BUNDLE_LOCALES, COUNT_MODES, DEVICE_CHANNEL_WORKER_CLOSE_PROTOCOL_VIOLATION, MEETING_RESPONSES, RECORDING_OVERRIDES, RECORD_ACCESS_ROLES, RESOURCE_VISIBILITIES, SchemaError, SchemaErrorCode, ValidationError, canonicalStringify, computeReconnectDelay, computeRetryDelay, deviceChannelUrl, diffSchemaState, formatByteSize, parseRecordAccessRole, parseSchemaState, parseServerFrame, schemaStateSourcePayload } from "@standards/schema";
|
|
8
9
|
import Table from "cli-table3";
|
|
9
10
|
import { createAppClient } from "@standards/client/app";
|
|
10
|
-
import {
|
|
11
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
import { constants, createWriteStream } from "node:fs";
|
|
12
12
|
import { access, chmod, mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
|
|
13
13
|
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
14
|
+
import { homedir, hostname } from "node:os";
|
|
15
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
14
16
|
import { WebSocket } from "ws";
|
|
15
17
|
import { spawn } from "node:child_process";
|
|
16
|
-
import { constants, createWriteStream } from "node:fs";
|
|
17
18
|
import { StringDecoder } from "node:string_decoder";
|
|
18
19
|
import { Readable } from "node:stream";
|
|
19
20
|
import { pipeline } from "node:stream/promises";
|
|
@@ -193,100 +194,125 @@ function registerAuthCommand(program) {
|
|
|
193
194
|
});
|
|
194
195
|
}
|
|
195
196
|
//#endregion
|
|
196
|
-
//#region src/
|
|
197
|
+
//#region src/discovery/order-bundles.ts
|
|
197
198
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
199
|
+
* Orders discovered bundles so each comes after the bundles it `uses`.
|
|
200
|
+
* Bundles a `uses` names that were not discovered are already the host's concern.
|
|
200
201
|
*/
|
|
201
|
-
function
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
bundles.command("update").description("Apply assigned bundles — picks up newer bundle versions (additive sync)").action(async (_opts, cmd) => {
|
|
217
|
-
formatOutput(await getAppClientFromCommand(cmd).bundles.update({}), getFormat(cmd));
|
|
218
|
-
});
|
|
202
|
+
async function orderByUses(discovered) {
|
|
203
|
+
const byName = new Map(discovered.map((entry) => [entry.bundle.name, entry]));
|
|
204
|
+
const ordered = [];
|
|
205
|
+
const visited = /* @__PURE__ */ new Set();
|
|
206
|
+
async function visit(entry) {
|
|
207
|
+
if (visited.has(entry.bundle.name)) return;
|
|
208
|
+
visited.add(entry.bundle.name);
|
|
209
|
+
for (const used of (await entry.bundle.build()).uses) {
|
|
210
|
+
const dependency = byName.get(used);
|
|
211
|
+
if (dependency) await visit(dependency);
|
|
212
|
+
}
|
|
213
|
+
ordered.push(entry);
|
|
214
|
+
}
|
|
215
|
+
for (const entry of discovered) await visit(entry);
|
|
216
|
+
return ordered;
|
|
219
217
|
}
|
|
220
218
|
//#endregion
|
|
221
|
-
//#region src/commands/
|
|
222
|
-
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
219
|
+
//#region src/commands/bundles.ts
|
|
220
|
+
const HASH_PREFIX_LENGTH = 7;
|
|
221
|
+
function shortHash(hash) {
|
|
222
|
+
return hash?.slice(0, HASH_PREFIX_LENGTH) ?? "";
|
|
223
|
+
}
|
|
224
|
+
function describeFailure(name, error) {
|
|
225
|
+
if (error instanceof BundleConflictError && error.owner) return `${name}: ${messageOf(error)} (owned by ${error.owner})`;
|
|
226
|
+
return `${name}: ${messageOf(error)}`;
|
|
227
|
+
}
|
|
228
|
+
/** Runs a command body; any failure prints once on stderr and exits 1. */
|
|
229
|
+
async function run(body) {
|
|
230
|
+
try {
|
|
231
|
+
await body();
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error(styleText("red", `✗ ${messageOf(error)}`));
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function syncDiscovered(cmd, cwd) {
|
|
238
|
+
const transport = getClientFromCommand(cmd);
|
|
239
|
+
for (const { bundle } of await orderByUses(await discoverBundles({ cwd }))) try {
|
|
240
|
+
const { applied, hash } = await syncBundle(transport, bundle);
|
|
241
|
+
console.info(`${bundle.name} ${applied ? "applied" : "unchanged"} ${shortHash(hash)}`);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
throw new Error(describeFailure(bundle.name, error));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/** Reject a locale typo instead of letting the API answer a 400 on a bare enum. */
|
|
247
|
+
function parseLocale(value) {
|
|
248
|
+
const locale = BUNDLE_LOCALES.find((candidate) => candidate === value);
|
|
249
|
+
if (locale) return locale;
|
|
250
|
+
throw new ValidationError(`--locale must be one of: ${BUNDLE_LOCALES.join(", ")}`, []);
|
|
251
|
+
}
|
|
252
|
+
/** `<pieceId>=bind`, `<pieceId>=skip` or `<pieceId>=rename:<name>`, one per collision. */
|
|
236
253
|
function collectDecision(value, decisions) {
|
|
237
254
|
const separator = value.indexOf("=");
|
|
238
|
-
const
|
|
255
|
+
const pieceId = value.slice(0, separator);
|
|
239
256
|
const choice = value.slice(separator + 1);
|
|
240
257
|
if (separator > 0 && (choice === "bind" || choice === "skip")) return {
|
|
241
258
|
...decisions,
|
|
242
|
-
[
|
|
259
|
+
[pieceId]: choice
|
|
243
260
|
};
|
|
244
261
|
if (separator > 0 && choice.startsWith("rename:") && choice.length > 7) return {
|
|
245
262
|
...decisions,
|
|
246
|
-
[
|
|
263
|
+
[pieceId]: { rename: choice.slice(7) }
|
|
264
|
+
};
|
|
265
|
+
throw new ValidationError(`--decision must read <pieceId>=bind, <pieceId>=skip or <pieceId>=rename:<name> (received: ${value})`, []);
|
|
266
|
+
}
|
|
267
|
+
/** The input `preview` and `add` share: whole bundles, in one locale. */
|
|
268
|
+
function addInput(names, opts) {
|
|
269
|
+
return {
|
|
270
|
+
bundles: names,
|
|
271
|
+
locale: parseLocale(opts.locale),
|
|
272
|
+
withSamples: opts.withSamples === true
|
|
247
273
|
};
|
|
248
|
-
throw new ValidationError(`--decision must read <entryId>=bind, <entryId>=skip or <entryId>=rename:<name> (received: ${value})`, []);
|
|
249
274
|
}
|
|
250
|
-
/** The flags `preview` and `
|
|
251
|
-
function
|
|
252
|
-
return command.
|
|
275
|
+
/** The flags `preview` and `add` share. */
|
|
276
|
+
function withAddOptions(command) {
|
|
277
|
+
return command.argument("<names...>", "bundle names (the bundles each one uses come along)").option("--locale <locale>", `language to compile the pieces in (${BUNDLE_LOCALES.join(", ")})`, "en").option("--with-samples", "also add the bundles' sample records");
|
|
253
278
|
}
|
|
254
279
|
/**
|
|
255
|
-
* The
|
|
256
|
-
*
|
|
257
|
-
*
|
|
280
|
+
* The workspace's bundles: sync the ones the project declares, list what is
|
|
281
|
+
* installed, remove one, and add a bundle the workspace may add as a copy it
|
|
282
|
+
* owns (`preview`, then `add` with the preview's fingerprint).
|
|
258
283
|
*/
|
|
259
|
-
function
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
formatOutput(await getAppClientFromCommand(cmd).catalog.install({
|
|
283
|
-
selection: parseSelection(opts),
|
|
284
|
-
locale: parseChoice("--locale", opts.locale, CATALOG_LOCALES),
|
|
285
|
-
withSamples: opts.withSamples === true,
|
|
284
|
+
function registerBundlesCommand(program) {
|
|
285
|
+
const bundles = program.command("bundles").description("Sync and manage the workspace's bundles");
|
|
286
|
+
bundles.command("sync").description("Discover the project's bundles (standards/bundles/*.ts) and sync each one").option("--cwd <dir>", "project root to discover bundles in").action((opts, cmd) => run(() => syncDiscovered(cmd, opts.cwd)));
|
|
287
|
+
bundles.command("list").description("List the bundles installed in the workspace").action((_opts, cmd) => run(async () => {
|
|
288
|
+
const { installed } = await getAppClientFromCommand(cmd).bundles.list({});
|
|
289
|
+
formatOutput(installed.map((row) => ({
|
|
290
|
+
name: row.name,
|
|
291
|
+
mode: row.mode,
|
|
292
|
+
origin: row.origin ?? "",
|
|
293
|
+
hash: shortHash(row.hash),
|
|
294
|
+
installedAt: row.installedAt
|
|
295
|
+
})), getFormat(cmd));
|
|
296
|
+
}));
|
|
297
|
+
bundles.command("available").description("List the bundles the workspace may add, and whether each is installed").option("--locale <locale>", `language of the labels (${BUNDLE_LOCALES.join(", ")})`, "en").action((opts, cmd) => run(async () => {
|
|
298
|
+
const { available } = await getAppClientFromCommand(cmd).bundles.list({ locale: parseLocale(opts.locale) });
|
|
299
|
+
formatOutput(available, getFormat(cmd));
|
|
300
|
+
}));
|
|
301
|
+
withAddOptions(bundles.command("preview").description("Plan an add: what is new, what collides, and the plan's fingerprint")).action((names, opts, cmd) => run(async () => {
|
|
302
|
+
formatOutput(await getAppClientFromCommand(cmd).bundles.preview(addInput(names, opts)), getFormat(cmd));
|
|
303
|
+
}));
|
|
304
|
+
withAddOptions(bundles.command("add").description("Add previewed bundles as copies the workspace owns").requiredOption("--fingerprint <fingerprint>", "the fingerprint `bundles preview` returned").option("--decision <pieceId=choice>", "how to settle a collision: bind, skip or rename:<name> (repeatable)", collectDecision, {})).action((names, opts, cmd) => run(async () => {
|
|
305
|
+
formatOutput(await getAppClientFromCommand(cmd).bundles.add({
|
|
306
|
+
...addInput(names, opts),
|
|
286
307
|
decisions: opts.decision,
|
|
287
308
|
fingerprint: opts.fingerprint
|
|
288
309
|
}), getFormat(cmd));
|
|
289
|
-
});
|
|
310
|
+
}));
|
|
311
|
+
bundles.command("remove").description("Remove a synced bundle; its pieces are demoted, never deleted").argument("<name>", "bundle name").option("--yes", "remove without confirmation").action((name, opts, cmd) => run(async () => {
|
|
312
|
+
requireConfirmation(opts.yes, `Removing bundle "${name}" demotes its objects to workspace-owned. Re-run with "--yes" to confirm.`);
|
|
313
|
+
await getAppClientFromCommand(cmd).bundles.remove({ name });
|
|
314
|
+
console.info(`${name} removed`);
|
|
315
|
+
}));
|
|
290
316
|
}
|
|
291
317
|
//#endregion
|
|
292
318
|
//#region src/commands/codemod.ts
|
|
@@ -294,7 +320,7 @@ function registerCatalogCommand(program) {
|
|
|
294
320
|
function registerCodemodCommand(program) {
|
|
295
321
|
const codemod = program.command("codemod").description("Rewrite code for a breaking SDK change");
|
|
296
322
|
codemod.command("builder-shorthand").description("Rewrite text({ name, label }) builders to text(name, label)").argument("[paths...]", "files or directories to rewrite", ["."]).option("--dry-run", "report without writing").option("--relative-imports", "also treat relative imports as Standards builders — for the Standards monorepo itself").action(async (paths, opts) => {
|
|
297
|
-
const { runBuilderShorthand } = await import("./builder-shorthand-
|
|
323
|
+
const { runBuilderShorthand } = await import("./builder-shorthand-CPJ9hFiW.mjs");
|
|
298
324
|
const report = await runBuilderShorthand(paths, {
|
|
299
325
|
write: !opts.dryRun,
|
|
300
326
|
relativeImports: opts.relativeImports === true
|
|
@@ -2014,7 +2040,7 @@ function registerSchemaCommand(program) {
|
|
|
2014
2040
|
schema.command("inspect").description("Inspect an object (attributes, types, views)").argument("<object>", "object name or ID").action(async (objectName, _opts, cmd) => {
|
|
2015
2041
|
formatOutput(await getAppClientFromCommand(cmd).schemaObjects.get({ objectIdOrName: objectName }), getFormat(cmd));
|
|
2016
2042
|
});
|
|
2017
|
-
schema.command("groups").description("List the bundles
|
|
2043
|
+
schema.command("groups").description("List the bundles that own objects, with their label").action(async (_opts, cmd) => {
|
|
2018
2044
|
formatOutput(await getAppClientFromCommand(cmd).schemaGroups.list({}), getFormat(cmd));
|
|
2019
2045
|
});
|
|
2020
2046
|
}
|
|
@@ -2043,20 +2069,6 @@ function registerSearchCommand(program) {
|
|
|
2043
2069
|
});
|
|
2044
2070
|
}
|
|
2045
2071
|
//#endregion
|
|
2046
|
-
//#region src/commands/sources.ts
|
|
2047
|
-
/** Remote schema sources — definitions pushed by an external codebase through @standards/client. */
|
|
2048
|
-
function registerSourcesCommand(program) {
|
|
2049
|
-
const sources = program.command("sources").description("Inspect and remove remote schema sources on the current tenant");
|
|
2050
|
-
sources.command("list").description("List remote schema sources: id, hash, last apply, object names").action(async (_opts, cmd) => {
|
|
2051
|
-
formatOutput(await getAppClientFromCommand(cmd).schemaSources.list({}), getFormat(cmd));
|
|
2052
|
-
});
|
|
2053
|
-
sources.command("remove").description("Remove a remote schema source: its objects are demoted to custom, records are kept").argument("<sourceId>", "source id declared in code").option("--yes", "remove without confirmation").action(async (sourceId, opts, cmd) => {
|
|
2054
|
-
if (!opts.yes) throw new Error("Removing a schema source is explicit. Re-run with \"--yes\" to confirm.");
|
|
2055
|
-
await getAppClientFromCommand(cmd).schemaSources.remove({ sourceId });
|
|
2056
|
-
formatOutput({ removed: sourceId }, getFormat(cmd));
|
|
2057
|
-
});
|
|
2058
|
-
}
|
|
2059
|
-
//#endregion
|
|
2060
2072
|
//#region src/version.ts
|
|
2061
2073
|
/**
|
|
2062
2074
|
* The installed package's version, read from its own manifest: `src/` and the
|
|
@@ -2101,8 +2113,6 @@ function createProgram() {
|
|
|
2101
2113
|
registerInvitationsCommand(program);
|
|
2102
2114
|
registerAuthCommand(program);
|
|
2103
2115
|
registerBundlesCommand(program);
|
|
2104
|
-
registerCatalogCommand(program);
|
|
2105
|
-
registerSourcesCommand(program);
|
|
2106
2116
|
registerCodemodCommand(program);
|
|
2107
2117
|
registerConnectorsCommand(program);
|
|
2108
2118
|
registerMcpCommand(program);
|
|
@@ -24,7 +24,6 @@ const BUILDERS = /* @__PURE__ */ new Set([
|
|
|
24
24
|
"nativeObject"
|
|
25
25
|
]);
|
|
26
26
|
const WITH_ID = /* @__PURE__ */ new Set(["object", "nativeObject"]);
|
|
27
|
-
const SOURCE = "defineSchemaSource";
|
|
28
27
|
const STANDARDS_PACKAGE = /^@standards\/(schema|client)$/;
|
|
29
28
|
const RELATIVE_MODULE = /^\.{1,2}\//;
|
|
30
29
|
const COMMENT_IN_CONFIG = "comment in config, rewrite by hand";
|
|
@@ -46,7 +45,7 @@ function importedBuilders(clauses) {
|
|
|
46
45
|
if (!(bindings && ts.isNamedImports(bindings))) continue;
|
|
47
46
|
for (const element of bindings.elements) {
|
|
48
47
|
const exported = element.propertyName?.text ?? element.name.text;
|
|
49
|
-
if (BUILDERS.has(exported)
|
|
48
|
+
if (BUILDERS.has(exported)) found.set(element.name.text, exported);
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
return found;
|
|
@@ -109,33 +108,6 @@ function rewriteBuilder(call, exported, file) {
|
|
|
109
108
|
edits
|
|
110
109
|
};
|
|
111
110
|
}
|
|
112
|
-
function rewriteSource(call, file) {
|
|
113
|
-
const [id, config] = call.arguments;
|
|
114
|
-
if (call.arguments.length !== 2 || id === void 0 || config === void 0) return { kind: "none" };
|
|
115
|
-
if (!ts.isObjectLiteralExpression(config)) return {
|
|
116
|
-
kind: "skip",
|
|
117
|
-
reason: "config is not an object literal"
|
|
118
|
-
};
|
|
119
|
-
const idText = id.getText(file);
|
|
120
|
-
const hasProperties = config.properties.length > 0;
|
|
121
|
-
const open = config.getStart(file) + 1;
|
|
122
|
-
const spaces = /^[ \t]*/.exec(file.text.slice(open))?.[0].length ?? 0;
|
|
123
|
-
const multiline = /^[ \t]*[\r\n]/.test(file.text.slice(open));
|
|
124
|
-
const head = hasProperties ? `{ id: ${idText},${multiline ? "" : " "}` : `{ id: ${idText} }`;
|
|
125
|
-
const end = hasProperties ? multiline ? open : open + spaces : config.getEnd();
|
|
126
|
-
if (hasComment(file, id.getStart(file), end)) return {
|
|
127
|
-
kind: "skip",
|
|
128
|
-
reason: COMMENT_IN_CONFIG
|
|
129
|
-
};
|
|
130
|
-
return {
|
|
131
|
-
kind: "edit",
|
|
132
|
-
edits: [{
|
|
133
|
-
start: id.getStart(file),
|
|
134
|
-
end,
|
|
135
|
-
text: head
|
|
136
|
-
}]
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
111
|
function overlaps(x, y) {
|
|
140
112
|
if (x.start === x.end && y.start === y.end) return x.start === y.start;
|
|
141
113
|
return x.start < y.end && y.start < x.end;
|
|
@@ -161,10 +133,6 @@ function importedNamespaces(clauses) {
|
|
|
161
133
|
}
|
|
162
134
|
/** True when a call to `exported` still has the pre-shorthand argument shape. */
|
|
163
135
|
function isLegacyShape(call, exported) {
|
|
164
|
-
if (exported === SOURCE) {
|
|
165
|
-
const config = call.arguments[1];
|
|
166
|
-
return call.arguments.length === 2 && config !== void 0 && ts.isObjectLiteralExpression(config);
|
|
167
|
-
}
|
|
168
136
|
return BUILDERS.has(exported) && call.arguments.length === 1;
|
|
169
137
|
}
|
|
170
138
|
/** Every TS and JS module a schema can live in; declaration files carry no calls. */
|
|
@@ -190,7 +158,7 @@ function rewriteBuilderShorthand(text, fileName, options = {}) {
|
|
|
190
158
|
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
191
159
|
const exported = builders.get(node.expression.text);
|
|
192
160
|
if (exported !== void 0) {
|
|
193
|
-
const outcome =
|
|
161
|
+
const outcome = rewriteBuilder(node, exported, file);
|
|
194
162
|
if (outcome.kind === "edit") calls.push({
|
|
195
163
|
node,
|
|
196
164
|
edits: outcome.edits
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*! © 2025-2026 AND YOU CREATE SAS (RCS Paris 940 190 614). Proprietary — see LICENSE (Standards SDK License 1.0). */
|
|
3
|
+
import { DuplicateError, ValidationError, isBundle } from "@standards/schema";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { glob } from "node:fs/promises";
|
|
6
|
+
import { join, relative, resolve } from "node:path";
|
|
7
|
+
import { createJiti } from "jiti";
|
|
8
|
+
//#region src/discovery/discover-bundles.ts
|
|
9
|
+
const DEFAULT_PATTERNS = ["standards/bundles/*.ts"];
|
|
10
|
+
const CONFIG_FILE = "standards.config.ts";
|
|
11
|
+
function messageOf(error) {
|
|
12
|
+
return error instanceof Error ? error.message : String(error);
|
|
13
|
+
}
|
|
14
|
+
async function importModule(jiti, file, label) {
|
|
15
|
+
try {
|
|
16
|
+
return await jiti.import(file);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
throw new ValidationError(`Could not load ${label}: ${messageOf(error)}`, []);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function readConfigPatterns(jiti, cwd) {
|
|
22
|
+
const file = join(cwd, CONFIG_FILE);
|
|
23
|
+
if (!existsSync(file)) return void 0;
|
|
24
|
+
const config = (await importModule(jiti, file, CONFIG_FILE)).default;
|
|
25
|
+
if (config?.bundles === void 0) return void 0;
|
|
26
|
+
return Array.isArray(config.bundles) ? config.bundles : [config.bundles];
|
|
27
|
+
}
|
|
28
|
+
async function matchFiles(cwd, patterns) {
|
|
29
|
+
const files = /* @__PURE__ */ new Set();
|
|
30
|
+
for (const pattern of patterns) for await (const match of glob(pattern, { cwd })) files.add(resolve(cwd, match));
|
|
31
|
+
return [...files].sort();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Finds the bundles a project declares: every export, in every file the
|
|
35
|
+
* patterns match, that is a `BundleBuilder`. Files may hold anything else.
|
|
36
|
+
*/
|
|
37
|
+
async function discoverBundles(options = {}) {
|
|
38
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
39
|
+
const jiti = createJiti(import.meta.url, {
|
|
40
|
+
moduleCache: true,
|
|
41
|
+
fsCache: false
|
|
42
|
+
});
|
|
43
|
+
const patterns = options.patterns ?? await readConfigPatterns(jiti, cwd) ?? DEFAULT_PATTERNS;
|
|
44
|
+
const files = await matchFiles(cwd, patterns);
|
|
45
|
+
const found = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
const label = relative(cwd, file).split("\\").join("/");
|
|
48
|
+
const exports = await importModule(jiti, file, label);
|
|
49
|
+
for (const [exportName, value] of Object.entries(exports)) {
|
|
50
|
+
if (!isBundle(value)) continue;
|
|
51
|
+
const previous = found.get(value.name);
|
|
52
|
+
if (previous?.bundle === value) continue;
|
|
53
|
+
if (previous) {
|
|
54
|
+
const first = relative(cwd, previous.file).split("\\").join("/");
|
|
55
|
+
throw new DuplicateError("bundle", value.name, `Bundle "${value.name}" is exported by ${first} and ${label}`);
|
|
56
|
+
}
|
|
57
|
+
found.set(value.name, {
|
|
58
|
+
bundle: value,
|
|
59
|
+
file,
|
|
60
|
+
exportName
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (files.length > 0 && found.size === 0) throw new ValidationError(`No bundle found in ${files.length} file(s) matching ${patterns.join(", ")}`, []);
|
|
65
|
+
return [...found.values()].sort((a, b) => a.bundle.name.localeCompare(b.bundle.name));
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { discoverBundles as t };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import { BundleBuilder, ObjectBuilderLike } from "@standards/schema";
|
|
3
|
+
//#region src/discovery/define-config.d.ts
|
|
4
|
+
/** What `standards.config.ts` declares. Every key is optional: the conventions apply without a file. */
|
|
5
|
+
interface StandardsCliConfig {
|
|
6
|
+
/** Glob pattern(s), relative to the project root, that locate the bundle files. */
|
|
7
|
+
bundles?: string | string[];
|
|
8
|
+
}
|
|
9
|
+
/** Types a `standards.config.ts` default export. */
|
|
10
|
+
export declare function defineConfig(config: StandardsCliConfig): StandardsCliConfig;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/discovery/discover-bundles.d.ts
|
|
13
|
+
type AnyBundle = BundleBuilder<string, readonly ObjectBuilderLike[]>;
|
|
14
|
+
interface DiscoveredBundle {
|
|
15
|
+
bundle: AnyBundle;
|
|
16
|
+
/** Absolute path of the file that exports the bundle. */
|
|
17
|
+
file: string;
|
|
18
|
+
/** `default` or the name of the named export. */
|
|
19
|
+
exportName: string;
|
|
20
|
+
}
|
|
21
|
+
interface DiscoverBundlesOptions {
|
|
22
|
+
cwd?: string;
|
|
23
|
+
/** Overrides the config and the convention. */
|
|
24
|
+
patterns?: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Finds the bundles a project declares: every export, in every file the
|
|
28
|
+
* patterns match, that is a `BundleBuilder`. Files may hold anything else.
|
|
29
|
+
*/
|
|
30
|
+
export declare function discoverBundles(options?: DiscoverBundlesOptions): Promise<DiscoveredBundle[]>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export type { DiscoverBundlesOptions, DiscoveredBundle, StandardsCliConfig };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*! © 2025-2026 AND YOU CREATE SAS (RCS Paris 940 190 614). Proprietary — see LICENSE (Standards SDK License 1.0). */
|
|
3
|
+
import { t as discoverBundles } from "./discover-bundles-D5RNCkan.mjs";
|
|
4
|
+
//#region src/discovery/define-config.ts
|
|
5
|
+
/** Types a `standards.config.ts` default export. */
|
|
6
|
+
function defineConfig(config) {
|
|
7
|
+
return config;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { defineConfig, discoverBundles };
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@standards/cli",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.393",
|
|
4
4
|
"description": "CLI tool to interact with Standards API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"standards": "./dist/bin.mjs"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
9
17
|
"files": [
|
|
10
18
|
"dist",
|
|
11
19
|
"LICENSE",
|
|
@@ -17,11 +25,12 @@
|
|
|
17
25
|
"dependencies": {
|
|
18
26
|
"cli-table3": "^0.6.5",
|
|
19
27
|
"commander": "^13.1.0",
|
|
28
|
+
"jiti": "2.7.0",
|
|
20
29
|
"typescript": "^5.6.3",
|
|
21
30
|
"ws": "8.21.1",
|
|
22
|
-
"@standards/client": "1.0.0-alpha.
|
|
23
|
-
"@standards/constants": "1.0.0-alpha.
|
|
24
|
-
"@standards/schema": "1.0.0-alpha.
|
|
31
|
+
"@standards/client": "1.0.0-alpha.393",
|
|
32
|
+
"@standards/constants": "1.0.0-alpha.393",
|
|
33
|
+
"@standards/schema": "1.0.0-alpha.393"
|
|
25
34
|
},
|
|
26
35
|
"devDependencies": {
|
|
27
36
|
"@types/node": "^25.6.0",
|