@standards/cli 0.0.0-stage → 1.0.0-alpha.382

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.
@@ -0,0 +1,236 @@
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 { i as rewriteSourceFiles, n as parseSourceFile, t as applyEdits } from "./source-files-D2Bdfvhx.mjs";
4
+ import ts from "typescript";
5
+ //#region src/codemod/builder-shorthand.ts
6
+ const BUILDERS = /* @__PURE__ */ new Set([
7
+ "text",
8
+ "richtext",
9
+ "number",
10
+ "checkbox",
11
+ "date",
12
+ "phone",
13
+ "currency",
14
+ "status",
15
+ "select",
16
+ "multiselect",
17
+ "location",
18
+ "user",
19
+ "relation",
20
+ "formula",
21
+ "rollup",
22
+ "document",
23
+ "object",
24
+ "nativeObject"
25
+ ]);
26
+ const WITH_ID = /* @__PURE__ */ new Set(["object", "nativeObject"]);
27
+ const SOURCE = "defineSchemaSource";
28
+ const STANDARDS_PACKAGE = /^@standards\/(schema|client)$/;
29
+ const RELATIVE_MODULE = /^\.{1,2}\//;
30
+ const COMMENT_IN_CONFIG = "comment in config, rewrite by hand";
31
+ /** Import clauses whose module counts as Standards: the packages, plus relative paths on opt-in. */
32
+ function standardsImports(file, options) {
33
+ const clauses = [];
34
+ for (const statement of file.statements) {
35
+ if (!(ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier))) continue;
36
+ const specifier = statement.moduleSpecifier.text;
37
+ if ((STANDARDS_PACKAGE.test(specifier) || options.relativeImports === true && RELATIVE_MODULE.test(specifier)) && statement.importClause) clauses.push(statement.importClause);
38
+ }
39
+ return clauses;
40
+ }
41
+ /** Local identifier → exported builder name, for named imports from Standards. */
42
+ function importedBuilders(clauses) {
43
+ const found = /* @__PURE__ */ new Map();
44
+ for (const clause of clauses) {
45
+ const bindings = clause.namedBindings;
46
+ if (!(bindings && ts.isNamedImports(bindings))) continue;
47
+ for (const element of bindings.elements) {
48
+ const exported = element.propertyName?.text ?? element.name.text;
49
+ if (BUILDERS.has(exported) || exported === SOURCE) found.set(element.name.text, exported);
50
+ }
51
+ }
52
+ return found;
53
+ }
54
+ /** True when `[start, end)` of the file holds a comment, which a rewrite would drop. */
55
+ function hasComment(file, start, end) {
56
+ const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, file.languageVariant, file.text.slice(start, end));
57
+ for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) if (token === ts.SyntaxKind.SingleLineCommentTrivia || token === ts.SyntaxKind.MultiLineCommentTrivia) return true;
58
+ return false;
59
+ }
60
+ function readConfig(literal, file, allowed) {
61
+ const values = /* @__PURE__ */ new Map();
62
+ for (const property of literal.properties) {
63
+ if (ts.isSpreadAssignment(property)) return "spread in config";
64
+ if (ts.isShorthandPropertyAssignment(property)) {
65
+ values.set(property.name.text, property.name.text);
66
+ continue;
67
+ }
68
+ if (!(ts.isPropertyAssignment(property) && ts.isIdentifier(property.name))) return "unsupported property in config";
69
+ const key = property.name.text;
70
+ if (!allowed.has(key)) return `unsupported key "${key}"`;
71
+ values.set(key, property.initializer.getText(file));
72
+ }
73
+ if (!(values.has("name") && values.has("label"))) return "missing name or label";
74
+ return values;
75
+ }
76
+ function rewriteBuilder(call, exported, file) {
77
+ const [config] = call.arguments;
78
+ if (call.arguments.length !== 1 || config === void 0) return { kind: "none" };
79
+ if (!ts.isObjectLiteralExpression(config)) return {
80
+ kind: "skip",
81
+ reason: "config is not an object literal"
82
+ };
83
+ const values = readConfig(config, file, new Set(WITH_ID.has(exported) ? [
84
+ "name",
85
+ "label",
86
+ "id"
87
+ ] : ["name", "label"]));
88
+ if (typeof values === "string") return {
89
+ kind: "skip",
90
+ reason: values
91
+ };
92
+ if (hasComment(file, config.getStart(file), config.getEnd())) return {
93
+ kind: "skip",
94
+ reason: COMMENT_IN_CONFIG
95
+ };
96
+ const edits = [{
97
+ start: config.getStart(file),
98
+ end: config.getEnd(),
99
+ text: `${values.get("name")}, ${values.get("label")}`
100
+ }];
101
+ const id = values.get("id");
102
+ if (id !== void 0) edits.push({
103
+ start: call.getEnd(),
104
+ end: call.getEnd(),
105
+ text: `.id(${id})`
106
+ });
107
+ return {
108
+ kind: "edit",
109
+ edits
110
+ };
111
+ }
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
+ function overlaps(x, y) {
140
+ if (x.start === x.end && y.start === y.end) return x.start === y.start;
141
+ return x.start < y.end && y.start < x.end;
142
+ }
143
+ /** The widest call among those whose edits collide with another call's edits. */
144
+ function findOverlapVictim(calls) {
145
+ let victim;
146
+ for (const [i, first] of calls.entries()) for (const second of calls.slice(i + 1)) {
147
+ if (!first.edits.some((x) => second.edits.some((y) => overlaps(x, y)))) continue;
148
+ const wider = first.node.getWidth() >= second.node.getWidth() ? first : second;
149
+ if (victim === void 0 || wider.node.getWidth() > victim.node.getWidth()) victim = wider;
150
+ }
151
+ return victim;
152
+ }
153
+ /** Local names of `import * as x` from Standards. */
154
+ function importedNamespaces(clauses) {
155
+ const found = /* @__PURE__ */ new Set();
156
+ for (const clause of clauses) {
157
+ const bindings = clause.namedBindings;
158
+ if (bindings && ts.isNamespaceImport(bindings)) found.add(bindings.name.text);
159
+ }
160
+ return found;
161
+ }
162
+ /** True when a call to `exported` still has the pre-shorthand argument shape. */
163
+ 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
+ return BUILDERS.has(exported) && call.arguments.length === 1;
169
+ }
170
+ /** Every TS and JS module a schema can live in; declaration files carry no calls. */
171
+ /** Rewrites `fn({ name, label })` builders of one file to `fn(name, label)`. */
172
+ function rewriteBuilderShorthand(text, fileName, options = {}) {
173
+ const { file } = parseSourceFile(text, fileName);
174
+ const clauses = standardsImports(file, options);
175
+ const builders = importedBuilders(clauses);
176
+ const namespaces = importedNamespaces(clauses);
177
+ const calls = [];
178
+ const skipped = [];
179
+ function skip(node, callee, reason) {
180
+ const { line, character } = file.getLineAndCharacterOfPosition(node.getStart(file));
181
+ skipped.push({
182
+ file: fileName,
183
+ line: line + 1,
184
+ column: character + 1,
185
+ callee,
186
+ reason
187
+ });
188
+ }
189
+ function visit(node) {
190
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
191
+ const exported = builders.get(node.expression.text);
192
+ if (exported !== void 0) {
193
+ const outcome = exported === SOURCE ? rewriteSource(node, file) : rewriteBuilder(node, exported, file);
194
+ if (outcome.kind === "edit") calls.push({
195
+ node,
196
+ edits: outcome.edits
197
+ });
198
+ else if (outcome.kind === "skip") skip(node, node.expression.text, outcome.reason);
199
+ }
200
+ } else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression) && namespaces.has(node.expression.expression.text) && isLegacyShape(node, node.expression.name.text)) skip(node, node.expression.getText(file), "namespace import, rewrite by hand");
201
+ ts.forEachChild(node, visit);
202
+ }
203
+ visit(file);
204
+ let kept = calls;
205
+ for (;;) {
206
+ const victim = findOverlapVictim(kept);
207
+ if (victim === void 0) break;
208
+ skip(victim.node, victim.node.expression.text, "overlapping rewrite");
209
+ kept = kept.filter((call) => call !== victim);
210
+ }
211
+ skipped.sort((x, y) => x.line - y.line || x.column - y.column);
212
+ const changed = kept.length;
213
+ return {
214
+ output: applyEdits(text, kept.flatMap((call) => call.edits)),
215
+ changed,
216
+ skipped
217
+ };
218
+ }
219
+ /** Walks `paths`, rewrites every TS file, writes back when `write` is set. */
220
+ async function runBuilderShorthand(paths, options) {
221
+ const report = {
222
+ files: 0,
223
+ changed: 0,
224
+ skipped: []
225
+ };
226
+ const results = await rewriteSourceFiles(paths, options.write, (text, path) => rewriteBuilderShorthand(text, path, options));
227
+ for (const { result } of results) {
228
+ report.skipped.push(...result.skipped);
229
+ if (result.changed === 0) continue;
230
+ report.files += 1;
231
+ report.changed += result.changed;
232
+ }
233
+ return report;
234
+ }
235
+ //#endregion
236
+ export { runBuilderShorthand };
@@ -0,0 +1,173 @@
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 { i as rewriteSourceFiles, n as parseSourceFile, t as applyEdits } from "./source-files-D2Bdfvhx.mjs";
4
+ import { join, resolve } from "node:path";
5
+ import ts from "typescript";
6
+ //#region src/codemod/engine-imports.ts
7
+ const SCHEMA = "@standards/schema";
8
+ const ENGINE = "@standards/engine";
9
+ /** Subpath entries that moved whole: their specifier is renamed, nothing else. */
10
+ const MOVED_SUBPATHS = /* @__PURE__ */ new Set(["capabilities", "webhooks"]);
11
+ function isSchemaSpecifier(specifier) {
12
+ return specifier === SCHEMA || specifier.startsWith(`${SCHEMA}/`);
13
+ }
14
+ function schemaLiteralOf(node) {
15
+ const literal = node.moduleSpecifier;
16
+ return literal !== void 0 && ts.isStringLiteral(literal) && isSchemaSpecifier(literal.text) ? literal : void 0;
17
+ }
18
+ function namedElements(node) {
19
+ if (ts.isImportDeclaration(node)) {
20
+ const bindings = node.importClause?.namedBindings;
21
+ if (node.importClause?.name !== void 0) return void 0;
22
+ return bindings !== void 0 && ts.isNamedImports(bindings) ? bindings.elements : void 0;
23
+ }
24
+ return node.exportClause !== void 0 && ts.isNamedExports(node.exportClause) ? node.exportClause.elements : void 0;
25
+ }
26
+ function declaration(node, elements, specifier) {
27
+ return `${ts.isImportDeclaration(node) ? "import" : "export"}${(ts.isImportDeclaration(node) ? node.importClause?.isTypeOnly : node.isTypeOnly) ? " type" : ""} { ${elements.join(", ")} } from "${specifier}";`;
28
+ }
29
+ /**
30
+ * Moves every name `@standards/schema` no longer exports, and `@standards/engine`
31
+ * does, out of a schema import into an engine import beside it.
32
+ */
33
+ function rewriteEngineImports(source, fileName, exports) {
34
+ const parsed = parseSourceFile(source, fileName);
35
+ if (parsed.error !== void 0) return {
36
+ output: source,
37
+ changed: 0,
38
+ unresolved: [],
39
+ error: parsed.error
40
+ };
41
+ const edits = [];
42
+ const unresolved = [];
43
+ let moved = 0;
44
+ for (const node of parsed.file.statements) {
45
+ if (!(ts.isImportDeclaration(node) || ts.isExportDeclaration(node))) continue;
46
+ const literal = schemaLiteralOf(node);
47
+ if (literal === void 0) continue;
48
+ const specifier = literal.text;
49
+ const subpath = specifier.slice(18);
50
+ if (MOVED_SUBPATHS.has(subpath)) {
51
+ const quote = literal.getText().charAt(0);
52
+ edits.push({
53
+ start: literal.getStart(),
54
+ end: literal.getEnd(),
55
+ text: `${quote}${ENGINE}/${subpath}${quote}`
56
+ });
57
+ moved += 1;
58
+ continue;
59
+ }
60
+ const elements = namedElements(node);
61
+ if (elements === void 0) {
62
+ if (ts.isExportDeclaration(node) || node.importClause !== void 0) unresolved.push(node.getText());
63
+ continue;
64
+ }
65
+ const stay = [];
66
+ const go = [];
67
+ for (const element of elements) {
68
+ const name = (element.propertyName ?? element.name).text;
69
+ if (exports.schema(specifier).has(name)) stay.push(element.getText());
70
+ else if (exports.engine.has(name)) go.push(element.getText());
71
+ else {
72
+ unresolved.push(name);
73
+ stay.push(element.getText());
74
+ }
75
+ }
76
+ if (go.length === 0) continue;
77
+ moved += go.length;
78
+ const text = stay.length === 0 ? declaration(node, go, ENGINE) : `${declaration(node, stay, specifier)}\n${declaration(node, go, ENGINE)}`;
79
+ edits.push({
80
+ start: node.getStart(),
81
+ end: node.getEnd(),
82
+ text
83
+ });
84
+ }
85
+ function visit(node) {
86
+ if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal) && isSchemaSpecifier(node.argument.literal.text) && node.qualifier !== void 0) {
87
+ const literal = node.argument.literal;
88
+ const first = ts.isIdentifier(node.qualifier) ? node.qualifier : node.qualifier.left;
89
+ const name = ts.isIdentifier(first) ? first.text : void 0;
90
+ if (name !== void 0 && !exports.schema(literal.text).has(name) && exports.engine.has(name)) {
91
+ const quote = literal.getText().charAt(0);
92
+ edits.push({
93
+ start: literal.getStart(),
94
+ end: literal.getEnd(),
95
+ text: `${quote}${ENGINE}${quote}`
96
+ });
97
+ moved += 1;
98
+ }
99
+ }
100
+ ts.forEachChild(node, visit);
101
+ }
102
+ visit(parsed.file);
103
+ return {
104
+ output: applyEdits(source, edits),
105
+ changed: moved,
106
+ unresolved
107
+ };
108
+ }
109
+ /** The names a package's installed type declarations export, resolved from `root`. */
110
+ function exportedNames(packageName, root) {
111
+ const options = {
112
+ module: ts.ModuleKind.NodeNext,
113
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
114
+ skipLibCheck: true,
115
+ noEmit: true
116
+ };
117
+ const resolved = ts.resolveModuleName(packageName, join(root, "index.ts"), options, ts.sys).resolvedModule?.resolvedFileName;
118
+ if (resolved === void 0) return /* @__PURE__ */ new Set();
119
+ const program = ts.createProgram([resolved], options);
120
+ const file = program.getSourceFile(resolved);
121
+ const symbol = file && program.getTypeChecker().getSymbolAtLocation(file);
122
+ if (symbol === void 0) return /* @__PURE__ */ new Set();
123
+ return new Set(program.getTypeChecker().getExportsOfModule(symbol).map((exported) => exported.name));
124
+ }
125
+ /** Reads both packages as installed under `root`; fails when engine is not installed there. */
126
+ function installedExports(root) {
127
+ const bySpecifier = /* @__PURE__ */ new Map();
128
+ function schema(specifier) {
129
+ let names = bySpecifier.get(specifier);
130
+ if (names === void 0) {
131
+ names = exportedNames(specifier, root);
132
+ bySpecifier.set(specifier, names);
133
+ }
134
+ return names;
135
+ }
136
+ if (schema(SCHEMA).size === 0) return `${SCHEMA} is not installed under ${root}.`;
137
+ const engine = exportedNames(ENGINE, root);
138
+ if (engine.size === 0) return `Add ${ENGINE} to your dependencies and install it, then re-run.`;
139
+ return {
140
+ schema,
141
+ engine
142
+ };
143
+ }
144
+ async function runEngineImports(paths, options) {
145
+ const exports = installedExports(resolve(options.root));
146
+ if (typeof exports === "string") return exports;
147
+ const report = {
148
+ files: 0,
149
+ moved: 0,
150
+ unresolved: [],
151
+ failures: []
152
+ };
153
+ const results = await rewriteSourceFiles(paths, options.write, (text, path) => rewriteEngineImports(text, path, exports));
154
+ for (const { path, result } of results) {
155
+ if (result.error !== void 0) {
156
+ report.failures.push({
157
+ file: path,
158
+ message: result.error
159
+ });
160
+ continue;
161
+ }
162
+ for (const name of result.unresolved) report.unresolved.push({
163
+ file: path,
164
+ name
165
+ });
166
+ if (result.changed === 0) continue;
167
+ report.files += 1;
168
+ report.moved += result.changed;
169
+ }
170
+ return report;
171
+ }
172
+ //#endregion
173
+ export { runEngineImports };
@@ -0,0 +1,148 @@
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 { i as rewriteSourceFiles, n as parseSourceFile, t as applyEdits } from "./source-files-D2Bdfvhx.mjs";
4
+ import ts from "typescript";
5
+ import { PALETTE_HUES } from "@standards/constants";
6
+ //#region src/codemod/palette.ts
7
+ /** Previous Tailwind colour id → nearest Standards palette id (same table as the SQL migration). */
8
+ const OLD_TO_NEW = {
9
+ gray: "stone",
10
+ slate: "stone",
11
+ red: "brick",
12
+ rose: "brick",
13
+ orange: "terra",
14
+ amber: "ochre",
15
+ yellow: "ochre",
16
+ lime: "moss",
17
+ green: "sage",
18
+ emerald: "sage",
19
+ teal: "lagoon",
20
+ cyan: "lagoon",
21
+ blue: "ink",
22
+ sky: "ink",
23
+ indigo: "iris",
24
+ violet: "iris",
25
+ purple: "iris",
26
+ pink: "peony",
27
+ fuchsia: "peony"
28
+ };
29
+ const NEUTRAL = "stone";
30
+ /** Fallback order doubles as the tie-break: earlier wins. */
31
+ const HUE_IDS = Object.keys(PALETTE_HUES);
32
+ const PALETTE_IDS = /* @__PURE__ */ new Set([NEUTRAL, ...HUE_IDS]);
33
+ function isStringLike(node) {
34
+ return node !== void 0 && (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node));
35
+ }
36
+ function hueDistance(x, y) {
37
+ const delta = Math.abs(x - y) % 360;
38
+ return Math.min(delta, 360 - delta);
39
+ }
40
+ /** Smallest circular hue distance wins; a tie goes to the earlier id of the palette. */
41
+ function nearestFree(target, taken) {
42
+ const hue = PALETTE_HUES[target];
43
+ if (hue === void 0) return void 0;
44
+ let best;
45
+ let bestDistance = Number.POSITIVE_INFINITY;
46
+ for (const id of HUE_IDS) {
47
+ if (taken.has(id)) continue;
48
+ const distance = hueDistance(hue, PALETTE_HUES[id]);
49
+ if (distance < bestDistance) {
50
+ best = id;
51
+ bestDistance = distance;
52
+ }
53
+ }
54
+ return best;
55
+ }
56
+ function propertyName(property) {
57
+ if (!(ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property))) return void 0;
58
+ const { name } = property;
59
+ return ts.isIdentifier(name) || ts.isStringLiteral(name) ? name.text : void 0;
60
+ }
61
+ /** The string literal holding the `color` of an object literal, if it has one. */
62
+ function colorLiteral(object) {
63
+ if (!ts.isObjectLiteralExpression(object)) return void 0;
64
+ for (const property of object.properties) if (ts.isPropertyAssignment(property) && propertyName(property) === "color") return isStringLike(property.initializer) ? property.initializer : void 0;
65
+ }
66
+ /** Every string literal that carries a colour id: `color` values, `variant` attributes, `.color()` arguments. */
67
+ function colourLiterals(file) {
68
+ const found = [];
69
+ function visit(node) {
70
+ if (ts.isPropertyAssignment(node) && propertyName(node) === "color") {
71
+ if (isStringLike(node.initializer)) found.push(node.initializer);
72
+ } else if (ts.isJsxAttribute(node) && node.name.getText(file) === "variant") {
73
+ const value = node.initializer;
74
+ if (value && ts.isStringLiteral(value)) found.push(value);
75
+ else if (value && ts.isJsxExpression(value) && isStringLike(value.expression)) found.push(value.expression);
76
+ } else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "color" && isStringLike(node.arguments[0])) found.push(node.arguments[0]);
77
+ ts.forEachChild(node, visit);
78
+ }
79
+ visit(file);
80
+ return found;
81
+ }
82
+ /** Rewrites the colour ids of one source file to the Standards palette. */
83
+ function rewritePalette(text, fileName) {
84
+ const { file, error } = parseSourceFile(text, fileName);
85
+ if (error !== void 0) return {
86
+ output: text,
87
+ changed: 0,
88
+ error
89
+ };
90
+ const replacements = /* @__PURE__ */ new Map();
91
+ for (const literal of colourLiterals(file)) {
92
+ const mapped = OLD_TO_NEW[literal.text];
93
+ if (mapped !== void 0) replacements.set(literal, mapped);
94
+ }
95
+ function resolveArray(array) {
96
+ const options = array.elements.map(colorLiteral).filter((literal) => literal !== void 0);
97
+ const taken = new Set(options.map((o) => o.text).filter((id) => PALETTE_IDS.has(id)));
98
+ for (const option of options) {
99
+ const mapped = replacements.get(option);
100
+ if (mapped === void 0) continue;
101
+ const id = taken.has(mapped) ? nearestFree(mapped, taken) ?? mapped : mapped;
102
+ replacements.set(option, id);
103
+ taken.add(id);
104
+ }
105
+ }
106
+ function visitArrays(node) {
107
+ if (ts.isArrayLiteralExpression(node)) resolveArray(node);
108
+ ts.forEachChild(node, visitArrays);
109
+ }
110
+ visitArrays(file);
111
+ const edits = [...replacements.entries()].filter(([literal, id]) => literal.text !== id).map(([literal, id]) => ({
112
+ start: literal.getStart(file) + 1,
113
+ end: literal.getEnd() - 1,
114
+ text: id
115
+ }));
116
+ return {
117
+ output: applyEdits(text, edits),
118
+ changed: edits.length
119
+ };
120
+ }
121
+ /** Walks `paths`, rewrites every TS or JS file, writes back when `write` is set. */
122
+ async function runPalette(paths, options) {
123
+ const report = {
124
+ files: 0,
125
+ changed: 0,
126
+ rewritten: [],
127
+ failures: []
128
+ };
129
+ for (const { path, result } of await rewriteSourceFiles(paths, options.write, rewritePalette)) {
130
+ if (result.error !== void 0) {
131
+ report.failures.push({
132
+ file: path,
133
+ message: result.error
134
+ });
135
+ continue;
136
+ }
137
+ if (result.changed === 0) continue;
138
+ report.files += 1;
139
+ report.changed += result.changed;
140
+ report.rewritten.push({
141
+ file: path,
142
+ changed: result.changed
143
+ });
144
+ }
145
+ return report;
146
+ }
147
+ //#endregion
148
+ export { runPalette };
@@ -0,0 +1,122 @@
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 { r as rewriteFiles } from "./source-files-D2Bdfvhx.mjs";
4
+ import { readFile, writeFile } from "node:fs/promises";
5
+ import { basename, join } from "node:path";
6
+ //#region src/codemod/scopes.ts
7
+ /**
8
+ * Every package that left `@stndrds` for `@standards`: the restricted ones
9
+ * (now on the licensed registry only) and the public ones (now on npmjs and
10
+ * the licensed registry). A historical list: packages created later never had
11
+ * an `@stndrds` name.
12
+ */
13
+ const RENAMED_PACKAGES = [
14
+ "adapter-cache",
15
+ "adapter-kernel",
16
+ "adapter-meilisearch",
17
+ "adapter-nestjs",
18
+ "adapter-oidc-provider",
19
+ "adapter-postgres",
20
+ "adapter-posthog",
21
+ "adapter-redis",
22
+ "adapter-supabase",
23
+ "adapter-vercel-sandbox",
24
+ "auth",
25
+ "blocks",
26
+ "cli",
27
+ "client",
28
+ "constants",
29
+ "ds",
30
+ "engine",
31
+ "fonts",
32
+ "react",
33
+ "runtime",
34
+ "schema",
35
+ "ui"
36
+ ];
37
+ const SCOPE_REGISTRY_LINE = `@standards:registry=https://registry.standards.run/`;
38
+ /**
39
+ * The key line belongs in the user-level `~/.npmrc`, never the project one:
40
+ * pnpm does not expand environment variables in registry credentials read
41
+ * from a project `.npmrc`, so `${STANDARDS_TOKEN}` there reaches the
42
+ * registry as no key at all (401). Earlier runs of this codemod wrote it.
43
+ */
44
+ const PROJECT_AUTH_LINE = /^\s*\/\/registry\.standards\.run\/:_authToken=\$\{[^}]+\}\s*$/;
45
+ /** Public on npmjs too: a project using only these needs no licence and no registry line. */
46
+ const PUBLIC_PACKAGES = /* @__PURE__ */ new Set([
47
+ "cli",
48
+ "client",
49
+ "constants",
50
+ "schema"
51
+ ]);
52
+ const LICENSED_NAME = new RegExp(`@standards/(${RENAMED_PACKAGES.filter((name) => !PUBLIC_PACKAGES.has(name)).join("|")})(?![a-z0-9-])`);
53
+ const OLD_NAME = new RegExp(`@stndrds/(${RENAMED_PACKAGES.join("|")})(?![a-z0-9-])`, "g");
54
+ /** Code, styles, manifests, workspace config and agent guidance (AGENTS.md, CLAUDE.md). */
55
+ const REWRITTEN_FILE = /\.(?:[cm]?tsx?|[cm]?jsx?|css|scss|sass|less|json|jsonc|ya?ml|mdx?)$/;
56
+ /** Lockfiles are regenerated by the install that follows, never edited. */
57
+ const LOCKFILES = /* @__PURE__ */ new Set([
58
+ "pnpm-lock.yaml",
59
+ "package-lock.json",
60
+ "yarn.lock",
61
+ "bun.lock"
62
+ ]);
63
+ /** Renames every reference to one of our packages from `@stndrds/<name>` to `@standards/<name>`. */
64
+ function rewriteScopes(source) {
65
+ let changed = 0;
66
+ const output = source.replace(OLD_NAME, (_match, name) => {
67
+ changed += 1;
68
+ return `@standards/${name}`;
69
+ });
70
+ return {
71
+ output,
72
+ changed,
73
+ licensed: LICENSED_NAME.test(output)
74
+ };
75
+ }
76
+ /**
77
+ * Project `.npmrc` pointing `@standards` at the licensed registry: a different
78
+ * `@standards:registry` is replaced in place, an env-variable key line for the
79
+ * registry is removed (it never worked there), every other line is kept.
80
+ */
81
+ function ensureScopesNpmrc(text) {
82
+ const lines = text.split("\n");
83
+ if (lines.at(-1) === "") lines.pop();
84
+ const output = lines.filter((line) => !PROJECT_AUTH_LINE.test(line)).map((line) => line.trim().startsWith("@standards:registry=") ? SCOPE_REGISTRY_LINE : line);
85
+ if (!output.includes(SCOPE_REGISTRY_LINE)) output.push(SCOPE_REGISTRY_LINE);
86
+ return `${output.join("\n")}\n`;
87
+ }
88
+ function isRewritten(path) {
89
+ return REWRITTEN_FILE.test(path) && !LOCKFILES.has(basename(path));
90
+ }
91
+ async function readOptional(path) {
92
+ try {
93
+ return await readFile(path, "utf8");
94
+ } catch {
95
+ return "";
96
+ }
97
+ }
98
+ async function runScopes(paths, options) {
99
+ const report = {
100
+ files: 0,
101
+ changed: 0,
102
+ licensed: false
103
+ };
104
+ const results = await rewriteFiles(paths, isRewritten, options.write, rewriteScopes);
105
+ for (const { result } of results) {
106
+ if (result.licensed) report.licensed = true;
107
+ if (result.changed === 0) continue;
108
+ report.files += 1;
109
+ report.changed += result.changed;
110
+ }
111
+ if (!report.licensed) return report;
112
+ const npmrcPath = join(options.root, ".npmrc");
113
+ const current = await readOptional(npmrcPath);
114
+ const next = ensureScopesNpmrc(current);
115
+ if (next !== current) {
116
+ report.npmrc = npmrcPath;
117
+ if (options.write) await writeFile(npmrcPath, next);
118
+ }
119
+ return report;
120
+ }
121
+ //#endregion
122
+ export { runScopes };