@metaobjectsdev/cli 1.0.9-rc.3 → 1.0.9-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +15 -0
  2. package/dist/src/commands/eject.d.ts +17 -0
  3. package/dist/src/commands/eject.d.ts.map +1 -1
  4. package/dist/src/commands/eject.js +87 -2
  5. package/dist/src/commands/eject.js.map +1 -1
  6. package/dist/src/commands/gen.js +3 -1
  7. package/dist/src/commands/gen.js.map +1 -1
  8. package/dist/src/commands/generator-help.d.ts +21 -0
  9. package/dist/src/commands/generator-help.d.ts.map +1 -0
  10. package/dist/src/commands/generator-help.js +43 -0
  11. package/dist/src/commands/generator-help.js.map +1 -0
  12. package/dist/src/commands/generator.d.ts +36 -0
  13. package/dist/src/commands/generator.d.ts.map +1 -0
  14. package/dist/src/commands/generator.js +333 -0
  15. package/dist/src/commands/generator.js.map +1 -0
  16. package/dist/src/commands/init.d.ts.map +1 -1
  17. package/dist/src/commands/init.js +10 -1
  18. package/dist/src/commands/init.js.map +1 -1
  19. package/dist/src/commands/verify.d.ts +13 -1
  20. package/dist/src/commands/verify.d.ts.map +1 -1
  21. package/dist/src/commands/verify.js +69 -14
  22. package/dist/src/commands/verify.js.map +1 -1
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/index.js +12 -0
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/lib/advisory.d.ts +14 -0
  27. package/dist/src/lib/advisory.d.ts.map +1 -1
  28. package/dist/src/lib/advisory.js +64 -5
  29. package/dist/src/lib/advisory.js.map +1 -1
  30. package/dist/src/lib/args.d.ts +12 -0
  31. package/dist/src/lib/args.d.ts.map +1 -1
  32. package/dist/src/lib/args.js +6 -0
  33. package/dist/src/lib/args.js.map +1 -1
  34. package/dist/src/lib/catalog-listing.d.ts +15 -0
  35. package/dist/src/lib/catalog-listing.d.ts.map +1 -1
  36. package/dist/src/lib/catalog-listing.js +38 -3
  37. package/dist/src/lib/catalog-listing.js.map +1 -1
  38. package/dist/src/lib/codegen-drift.d.ts +10 -0
  39. package/dist/src/lib/codegen-drift.d.ts.map +1 -1
  40. package/dist/src/lib/codegen-drift.js +8 -0
  41. package/dist/src/lib/codegen-drift.js.map +1 -1
  42. package/dist/src/lib/install-set.d.ts +12 -2
  43. package/dist/src/lib/install-set.d.ts.map +1 -1
  44. package/dist/src/lib/install-set.js +14 -2
  45. package/dist/src/lib/install-set.js.map +1 -1
  46. package/dist/src/lib/runtime-eject.d.ts +80 -0
  47. package/dist/src/lib/runtime-eject.d.ts.map +1 -0
  48. package/dist/src/lib/runtime-eject.js +212 -0
  49. package/dist/src/lib/runtime-eject.js.map +1 -0
  50. package/package.json +10 -10
  51. package/src/commands/eject.ts +103 -3
  52. package/src/commands/gen.ts +3 -1
  53. package/src/commands/generator-help.ts +47 -0
  54. package/src/commands/generator.ts +390 -0
  55. package/src/commands/init.ts +10 -1
  56. package/src/commands/verify.ts +71 -14
  57. package/src/index.ts +12 -0
  58. package/src/lib/advisory.ts +71 -5
  59. package/src/lib/args.ts +16 -0
  60. package/src/lib/catalog-listing.ts +68 -3
  61. package/src/lib/codegen-drift.ts +17 -0
  62. package/src/lib/install-set.ts +19 -2
  63. package/src/lib/runtime-eject.ts +263 -0
@@ -0,0 +1,80 @@
1
+ import { type HttpRuntimeModule } from "@metaobjectsdev/codegen-ts";
2
+ import { type OwnedComparison } from "./owned-copy.js";
3
+ /**
4
+ * The ejectable generators whose OUTPUT imports the adapter tier, and the adapter modules
5
+ * it imports. `entity` is here for its allowlist TYPES only, which is one file.
6
+ */
7
+ export declare const RUNTIME_ENTRIES: Readonly<Record<string, readonly HttpRuntimeModule[]>>;
8
+ /** True iff ejecting `name` also copies adapter source. */
9
+ export declare function ejectsRuntime(name: string): boolean;
10
+ interface RuntimePackage {
11
+ /** `<root>/src` — the source the copies are taken from and compared against. */
12
+ srcRoot: string;
13
+ manifest: {
14
+ dependencies?: Record<string, string>;
15
+ peerDependencies?: Record<string, string>;
16
+ devDependencies?: Record<string, string>;
17
+ };
18
+ }
19
+ /**
20
+ * The installed `@metaobjectsdev/runtime-ts`: its `src/` and its manifest.
21
+ *
22
+ * Resolves the package ENTRY and walks up to the manifest that names it, never
23
+ * `"<pkg>/package.json"`: the exports map does not export that subpath, and under real Node
24
+ * asking for it throws ERR_PACKAGE_PATH_NOT_EXPORTED (see `install-set.ts`). `src/` ships
25
+ * in the published package (`files: ["dist", "src", ...]`).
26
+ */
27
+ export declare function resolveRuntimePackage(): RuntimePackage;
28
+ export interface RuntimeClosure {
29
+ /** Source files, POSIX paths relative to the package's `src/`, sorted. */
30
+ files: string[];
31
+ /** Packages the closure imports (never `node:` builtins), sorted. */
32
+ packages: string[];
33
+ }
34
+ /** Every file the given adapter modules reach through relative imports, and what else they import. */
35
+ export declare function runtimeClosure(srcRoot: string, modules: readonly HttpRuntimeModule[]): RuntimeClosure;
36
+ export type RuntimeFileStatus = "created" | "preserved" | "replaced";
37
+ export interface RuntimeFileResult {
38
+ /** Project-relative path of the copy. */
39
+ path: string;
40
+ status: RuntimeFileStatus;
41
+ /** How the copy ALREADY on disk compared to the package's source. Absent when there was none. */
42
+ comparison?: OwnedComparison | undefined;
43
+ }
44
+ export interface RuntimeEjectResult {
45
+ files: RuntimeFileResult[];
46
+ /** Packages the copied source imports — what the adopter now installs directly. */
47
+ packages: string[];
48
+ }
49
+ /**
50
+ * Copy the adapter source `names` emit imports of into `<cwd>/codegen/runtime/`.
51
+ * Never overwrites a file without `force`, the same rule the generator copy keeps.
52
+ */
53
+ export declare function ejectRuntime(cwd: string, names: readonly string[], force: boolean): Promise<RuntimeEjectResult>;
54
+ /**
55
+ * The install lines the copied source needs, with ranges read from the runtime package's
56
+ * own manifest (never restated here). A `@metaobjectsdev/*` package moves with the CLI.
57
+ * A package that ships no types of its own gets its `@types/*` sibling as a dev dependency
58
+ * when the runtime package itself develops against one.
59
+ */
60
+ export declare function runtimeInstall(packages: readonly string[]): {
61
+ runtime: Map<string, string | undefined>;
62
+ dev: Map<string, string | undefined>;
63
+ };
64
+ /** One owned adapter file, compared to the package it came from. */
65
+ export interface RuntimeCopyRow {
66
+ path: string;
67
+ verdict: OwnedComparison["verdict"] | "not-in-package";
68
+ localOnly: number;
69
+ referenceOnly: number;
70
+ }
71
+ /**
72
+ * Every file under `<cwd>/codegen/runtime/`, compared to the installed package's source.
73
+ * Empty when the project owns no adapter copy. `not-in-package` is a file the package does
74
+ * not ship — your own addition, or one upstream deleted or renamed.
75
+ */
76
+ export declare function runtimeCopyStatus(cwd: string): Promise<RuntimeCopyRow[]>;
77
+ /** The command that shows exactly how one owned file differs from the installed package's. */
78
+ export declare function runtimeDiffCommand(path: string): string;
79
+ export {};
80
+ //# sourceMappingURL=runtime-eject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-eject.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime-eject.ts"],"names":[],"mappings":"AAuBA,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGzE;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC,CAIlF,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,UAAU,cAAc;IACtB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1C,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC1C,CAAC;CACH;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,cAAc,CAoBtD;AA4BD,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,iBAAiB,EAAE,GAAG,cAAc,CAsBrG;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAErE,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,iGAAiG;IACjG,UAAU,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;CAC1C;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,mFAAmF;IACnF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqBrH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG;IAC3D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CACtC,CAYA;AAED,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,gBAAgB,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAYD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAsB9E;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD"}
@@ -0,0 +1,212 @@
1
+ // ADR-0034 Amendment 3 (2026-09-24 ruling) — ejecting a helper hands over ALL of its code.
2
+ //
3
+ // `meta eject routes` used to copy a generator whose OUTPUT still imported the HTTP adapter
4
+ // from `@metaobjectsdev/runtime-ts` — the mount helpers, filter parser, error envelopes and
5
+ // pagination, which is where most route defects have lived. Owning the generator while the
6
+ // code it emits calls into a package is half of owning it: a defect in the adapter still
7
+ // waits on an upstream release.
8
+ //
9
+ // So eject also copies the adapter SOURCE the output depends on — the transitive closure of
10
+ // relative imports from the entry module each generator's output names — into
11
+ // `codegen/runtime/`, laid out exactly as the package's own `src/` so every relative import
12
+ // in it resolves unchanged. Package imports are left as they are: what the closure reaches
13
+ // outside the package is `@metaobjectsdev/metadata` (core: the filter-op vocabulary and the
14
+ // canonical serializer) and third-party libraries (drizzle-orm, fastify, hono, zod, qs).
15
+ //
16
+ // The copies are VERBATIM. That is what makes "is my copy still the package's?" a byte
17
+ // comparison and "what did upstream change?" a plain `diff -r` against the installed
18
+ // package's `src/`, with no header to strip first.
19
+ import { createRequire } from "node:module";
20
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
21
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
22
+ import { dirname, join, relative, sep } from "node:path";
23
+ import { HTTP_RUNTIME_MODULES, HTTP_RUNTIME_PACKAGE, OWNED_RUNTIME_DIR, } from "@metaobjectsdev/codegen-ts";
24
+ import { compareOwnedCopy } from "./owned-copy.js";
25
+ import { cliVersion } from "./version.js";
26
+ /**
27
+ * The ejectable generators whose OUTPUT imports the adapter tier, and the adapter modules
28
+ * it imports. `entity` is here for its allowlist TYPES only, which is one file.
29
+ */
30
+ export const RUNTIME_ENTRIES = {
31
+ routes: ["drizzle-fastify"],
32
+ "routes-hono": ["hono"],
33
+ entity: ["allowlists"],
34
+ };
35
+ /** True iff ejecting `name` also copies adapter source. */
36
+ export function ejectsRuntime(name) {
37
+ return Object.hasOwn(RUNTIME_ENTRIES, name);
38
+ }
39
+ /**
40
+ * The installed `@metaobjectsdev/runtime-ts`: its `src/` and its manifest.
41
+ *
42
+ * Resolves the package ENTRY and walks up to the manifest that names it, never
43
+ * `"<pkg>/package.json"`: the exports map does not export that subpath, and under real Node
44
+ * asking for it throws ERR_PACKAGE_PATH_NOT_EXPORTED (see `install-set.ts`). `src/` ships
45
+ * in the published package (`files: ["dist", "src", ...]`).
46
+ */
47
+ export function resolveRuntimePackage() {
48
+ const req = createRequire(import.meta.url);
49
+ let dir = dirname(req.resolve(HTTP_RUNTIME_PACKAGE));
50
+ for (let hops = 0; hops < 8; hops++) {
51
+ const candidate = join(dir, "package.json");
52
+ if (existsSync(candidate)) {
53
+ const manifest = JSON.parse(readFileSync(candidate, "utf8"));
54
+ if (manifest.name === HTTP_RUNTIME_PACKAGE) {
55
+ const srcRoot = join(dir, "src");
56
+ if (!existsSync(srcRoot)) {
57
+ throw new Error(`${HTTP_RUNTIME_PACKAGE} at ${dir} ships no src/ — cannot copy the adapter source.`);
58
+ }
59
+ return { srcRoot, manifest };
60
+ }
61
+ }
62
+ const up = dirname(dir);
63
+ if (up === dir)
64
+ break;
65
+ dir = up;
66
+ }
67
+ throw new Error(`could not locate the installed ${HTTP_RUNTIME_PACKAGE} package.`);
68
+ }
69
+ /** Relative (`./`, `../`) and bare module specifiers a source file imports or re-exports. */
70
+ function specifiersOf(source) {
71
+ const out = [];
72
+ // `import … from "x"` and the re-export forms (`export { … } from`, `export * from`) only —
73
+ // a bare `export function f() { return Array.from("…") }` must not read as an import.
74
+ const STATEMENT = /(?:^|\n)\s*(?:import\s+[^;"']*?\s+from|export\s+(?:type\s+)?(?:\*(?:\s+as\s+\w+)?|\{[^}]*\})\s*from)\s*["']([^"']+)["']/g;
75
+ for (const m of source.matchAll(STATEMENT)) {
76
+ if (m[1] !== undefined)
77
+ out.push(m[1]);
78
+ }
79
+ for (const m of source.matchAll(/(?:^|\n)\s*import\s*["']([^"']+)["']/g)) {
80
+ if (m[1] !== undefined)
81
+ out.push(m[1]);
82
+ }
83
+ return out;
84
+ }
85
+ /** `@scope/name/sub` → `@scope/name`; `name/sub` → `name`. */
86
+ function packageNameOf(spec) {
87
+ const parts = spec.split("/");
88
+ return spec.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
89
+ }
90
+ function toPosix(p) {
91
+ return p.split(sep).join("/");
92
+ }
93
+ /** Every file the given adapter modules reach through relative imports, and what else they import. */
94
+ export function runtimeClosure(srcRoot, modules) {
95
+ const files = new Set();
96
+ const packages = new Set();
97
+ const queue = modules.map((m) => `${HTTP_RUNTIME_MODULES[m].file}.ts`);
98
+ while (queue.length > 0) {
99
+ const rel = queue.shift();
100
+ if (files.has(rel))
101
+ continue;
102
+ const abs = join(srcRoot, rel);
103
+ if (!existsSync(abs)) {
104
+ throw new Error(`${HTTP_RUNTIME_PACKAGE}: ${rel} is imported but not shipped in src/.`);
105
+ }
106
+ files.add(rel);
107
+ for (const spec of specifiersOf(readFileSync(abs, "utf8"))) {
108
+ if (spec.startsWith("./") || spec.startsWith("../")) {
109
+ const target = toPosix(join(dirname(rel), spec)).replace(/\.js$/, ".ts");
110
+ queue.push(target.endsWith(".ts") ? target : `${target}.ts`);
111
+ }
112
+ else if (!spec.startsWith("node:")) {
113
+ packages.add(packageNameOf(spec));
114
+ }
115
+ }
116
+ }
117
+ return { files: [...files].sort(), packages: [...packages].sort() };
118
+ }
119
+ /**
120
+ * Copy the adapter source `names` emit imports of into `<cwd>/codegen/runtime/`.
121
+ * Never overwrites a file without `force`, the same rule the generator copy keeps.
122
+ */
123
+ export async function ejectRuntime(cwd, names, force) {
124
+ const modules = [...new Set(names.flatMap((n) => RUNTIME_ENTRIES[n] ?? []))];
125
+ if (modules.length === 0)
126
+ return { files: [], packages: [] };
127
+ const { srcRoot } = resolveRuntimePackage();
128
+ const closure = runtimeClosure(srcRoot, modules);
129
+ const files = [];
130
+ for (const rel of closure.files) {
131
+ const source = await readFile(join(srcRoot, rel), "utf8");
132
+ const path = `${OWNED_RUNTIME_DIR}/${rel}`;
133
+ const abs = join(cwd, path);
134
+ const existing = existsSync(abs) ? await readFile(abs, "utf8") : undefined;
135
+ const comparison = existing === undefined ? undefined : await compareOwnedCopy(existing, source);
136
+ if (existing !== undefined && !force) {
137
+ files.push({ path, status: "preserved", comparison });
138
+ continue;
139
+ }
140
+ await mkdir(dirname(abs), { recursive: true });
141
+ await writeFile(abs, source, "utf8");
142
+ files.push({ path, status: existing === undefined ? "created" : "replaced", comparison });
143
+ }
144
+ return { files, packages: closure.packages };
145
+ }
146
+ /**
147
+ * The install lines the copied source needs, with ranges read from the runtime package's
148
+ * own manifest (never restated here). A `@metaobjectsdev/*` package moves with the CLI.
149
+ * A package that ships no types of its own gets its `@types/*` sibling as a dev dependency
150
+ * when the runtime package itself develops against one.
151
+ */
152
+ export function runtimeInstall(packages) {
153
+ const { manifest } = resolveRuntimePackage();
154
+ const known = { ...manifest.peerDependencies, ...manifest.dependencies };
155
+ const runtime = new Map();
156
+ const dev = new Map();
157
+ for (const p of packages) {
158
+ runtime.set(p, p.startsWith("@metaobjectsdev/") ? `^${cliVersion()}` : known[p]);
159
+ const typesPkg = `@types/${p.replace(/^@/, "").replace("/", "__")}`;
160
+ const typesRange = manifest.devDependencies?.[typesPkg];
161
+ if (typesRange !== undefined)
162
+ dev.set(typesPkg, typesRange);
163
+ }
164
+ return { runtime, dev };
165
+ }
166
+ function listTs(dir) {
167
+ const out = [];
168
+ for (const entry of readdirSync(dir)) {
169
+ const abs = join(dir, entry);
170
+ if (statSync(abs).isDirectory())
171
+ out.push(...listTs(abs));
172
+ else if (entry.endsWith(".ts"))
173
+ out.push(abs);
174
+ }
175
+ return out;
176
+ }
177
+ /**
178
+ * Every file under `<cwd>/codegen/runtime/`, compared to the installed package's source.
179
+ * Empty when the project owns no adapter copy. `not-in-package` is a file the package does
180
+ * not ship — your own addition, or one upstream deleted or renamed.
181
+ */
182
+ export async function runtimeCopyStatus(cwd) {
183
+ const root = join(cwd, OWNED_RUNTIME_DIR);
184
+ if (!existsSync(root))
185
+ return [];
186
+ let srcRoot;
187
+ try {
188
+ srcRoot = resolveRuntimePackage().srcRoot;
189
+ }
190
+ catch {
191
+ return [];
192
+ }
193
+ const rows = [];
194
+ for (const abs of listTs(root).sort()) {
195
+ const rel = toPosix(relative(root, abs));
196
+ const path = `${OWNED_RUNTIME_DIR}/${rel}`;
197
+ const upstream = join(srcRoot, rel);
198
+ if (!existsSync(upstream)) {
199
+ rows.push({ path, verdict: "not-in-package", localOnly: 0, referenceOnly: 0 });
200
+ continue;
201
+ }
202
+ const cmp = await compareOwnedCopy(readFileSync(abs, "utf8"), readFileSync(upstream, "utf8"));
203
+ rows.push({ path, verdict: cmp.verdict, localOnly: cmp.localOnly, referenceOnly: cmp.referenceOnly });
204
+ }
205
+ return rows;
206
+ }
207
+ /** The command that shows exactly how one owned file differs from the installed package's. */
208
+ export function runtimeDiffCommand(path) {
209
+ const rel = path.startsWith(`${OWNED_RUNTIME_DIR}/`) ? path.slice(OWNED_RUNTIME_DIR.length + 1) : path;
210
+ return `diff -u node_modules/${HTTP_RUNTIME_PACKAGE}/src/${rel} ${OWNED_RUNTIME_DIR}/${rel}`;
211
+ }
212
+ //# sourceMappingURL=runtime-eject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-eject.js","sourceRoot":"","sources":["../../../src/lib/runtime-eject.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/cli",
3
- "version": "1.0.9-rc.3",
3
+ "version": "1.0.9-rc.5",
4
4
  "description": "CLI for MetaObjects: scaffold, codegen, migrate, and drift-detection commands.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -51,15 +51,15 @@
51
51
  "@biomejs/js-api": "^0.7.0",
52
52
  "@biomejs/wasm-nodejs": "^1.9.4",
53
53
  "@libsql/kysely-libsql": "^0.4.0",
54
- "@metaobjectsdev/codegen-ts": "1.0.9-rc.3",
55
- "@metaobjectsdev/codegen-ts-react": "1.0.9-rc.3",
56
- "@metaobjectsdev/codegen-ts-tanstack": "1.0.9-rc.3",
57
- "@metaobjectsdev/docs-site": "1.0.9-rc.3",
58
- "@metaobjectsdev/metadata": "1.0.9-rc.3",
59
- "@metaobjectsdev/migrate-ts": "1.0.9-rc.3",
60
- "@metaobjectsdev/render": "1.0.9-rc.3",
61
- "@metaobjectsdev/runtime-ts": "1.0.9-rc.3",
62
- "@metaobjectsdev/sdk": "1.0.9-rc.3",
54
+ "@metaobjectsdev/codegen-ts": "1.0.9-rc.5",
55
+ "@metaobjectsdev/codegen-ts-react": "1.0.9-rc.5",
56
+ "@metaobjectsdev/codegen-ts-tanstack": "1.0.9-rc.5",
57
+ "@metaobjectsdev/docs-site": "1.0.9-rc.5",
58
+ "@metaobjectsdev/metadata": "1.0.9-rc.5",
59
+ "@metaobjectsdev/migrate-ts": "1.0.9-rc.5",
60
+ "@metaobjectsdev/render": "1.0.9-rc.5",
61
+ "@metaobjectsdev/runtime-ts": "1.0.9-rc.5",
62
+ "@metaobjectsdev/sdk": "1.0.9-rc.5",
63
63
  "@toon-format/toon": "^2.3.0",
64
64
  "jiti": "^2.4.0"
65
65
  },
@@ -25,7 +25,12 @@ import { log } from "../lib/log.js";
25
25
  import { declaredDependencyNames, readPackageManifest } from "../lib/package-manifest.js";
26
26
  import { compareOwnedCopy, type OwnedComparison } from "../lib/owned-copy.js";
27
27
  import { composeCatalog, packageOf } from "../lib/catalog.js";
28
- import { installSetFor, type InstallSet } from "../lib/install-set.js";
28
+ import { installSetFor, type InstallSet, type OwnedRuntimeInstall } from "../lib/install-set.js";
29
+ import {
30
+ ejectRuntime, ejectsRuntime, runtimeCopyStatus, runtimeDiffCommand, runtimeInstall,
31
+ type RuntimeEjectResult, type RuntimeFileResult,
32
+ } from "../lib/runtime-eject.js";
33
+ import { HTTP_RUNTIME_PACKAGE, OWNED_RUNTIME_DIR } from "@metaobjectsdev/codegen-ts";
29
34
  import { emitStructured, type OutputFormat } from "../lib/format.js";
30
35
  import {
31
36
  ejectLibrary, ejectableLibraryNames, isLibraryName, libraryStaleness,
@@ -159,6 +164,12 @@ export interface EjectResult {
159
164
  * cheapest thing that makes that condition observable.
160
165
  */
161
166
  comparison?: OwnedComparison | undefined;
167
+ /**
168
+ * ADR-0034 Amendment 3 (2026-09-24): the HTTP-adapter source this generator's OUTPUT
169
+ * imports, copied into `codegen/runtime/` so the adopter owns that too. Absent for a
170
+ * generator whose output imports no adapter.
171
+ */
172
+ runtime?: RuntimeEjectResult | undefined;
162
173
  }
163
174
 
164
175
  /** The `@metaobjectsdev/*` packages an ejected template imports, read from the file
@@ -241,8 +252,15 @@ export async function ejectGenerator(opts: EjectOptions): Promise<EjectResult> {
241
252
  const comparison =
242
253
  existing === undefined ? undefined : await compareOwnedCopy(existing, templateSource);
243
254
 
255
+ // The adapter source the OUTPUT imports is part of what is handed over — copied whether
256
+ // or not the generator file itself was already here, and under the same never-clobber
257
+ // rule, so re-running eject fills in a missing copy without touching a kept one.
258
+ const runtime = ejectsRuntime(opts.name)
259
+ ? await ejectRuntime(opts.cwd, [opts.name], opts.force === true)
260
+ : undefined;
261
+
244
262
  if (!opts.force && existing !== undefined) {
245
- return { ...common, status: "preserved", comparison };
263
+ return { ...common, status: "preserved", comparison, runtime };
246
264
  }
247
265
 
248
266
  await mkdir(join(opts.cwd, OWNED_GENERATORS_DIR), { recursive: true });
@@ -251,9 +269,45 @@ export async function ejectGenerator(opts: EjectOptions): Promise<EjectResult> {
251
269
  ...common,
252
270
  status: existing === undefined ? "created" : "replaced",
253
271
  comparison,
272
+ runtime,
254
273
  };
255
274
  }
256
275
 
276
+ /**
277
+ * The owned HTTP-adapter copy under `codegen/runtime/`, file by file, against the installed
278
+ * `@metaobjectsdev/runtime-ts` it was copied from. Shared by `meta eject --list` and
279
+ * `meta gen --list`. Empty when the project owns no adapter copy.
280
+ *
281
+ * `identical` says an upgrade can be taken by re-copying (`meta eject <name> --force`);
282
+ * `DIFFERS` says the copy is yours in a way the package is not — your fix, or upstream's —
283
+ * and names the diff that tells which.
284
+ */
285
+ export async function runtimeCopyLines(cwd: string): Promise<string[]> {
286
+ const rows = await runtimeCopyStatus(cwd);
287
+ if (rows.length === 0) return [];
288
+ const lines: string[] = [
289
+ `HTTP adapter source this project owns (${OWNED_RUNTIME_DIR}/), against the installed ${HTTP_RUNTIME_PACKAGE}:`,
290
+ ];
291
+ const differing: string[] = [];
292
+ for (const r of rows) {
293
+ if (r.verdict === "identical") lines.push(` ${r.path} [identical to the package]`);
294
+ else if (r.verdict === "reformatted") lines.push(` ${r.path} [same content as the package, your formatting]`);
295
+ else if (r.verdict === "not-in-package") lines.push(` ${r.path} [not in the package — yours, or removed upstream]`);
296
+ else {
297
+ differing.push(r.path);
298
+ lines.push(
299
+ ` ${r.path} [DIFFERS: ${r.referenceOnly} line(s) only in the package, ${r.localOnly} line(s) only in yours]`,
300
+ );
301
+ }
302
+ }
303
+ if (differing.length > 0) {
304
+ lines.push(" See the difference (upstream fix, or your own?) before taking an upgrade:");
305
+ for (const p of differing) lines.push(` ${runtimeDiffCommand(p)}`);
306
+ }
307
+ lines.push("");
308
+ return lines;
309
+ }
310
+
257
311
  /**
258
312
  * `--list` doubles as the staleness report for what this project ALREADY owns.
259
313
  *
@@ -315,6 +369,7 @@ async function listOutput(cwd: string): Promise<string> {
315
369
  );
316
370
  lines.push("");
317
371
  }
372
+ lines.push(...(await runtimeCopyLines(cwd)));
318
373
  // FR-043 — the shipped libraries, and how far this project's copies have drifted.
319
374
  lines.push("Shipped libraries (copy one into your own sources and own it):");
320
375
  for (const name of ejectableLibraryNames()) lines.push(` ${name}`);
@@ -369,6 +424,8 @@ interface EjectedRow {
369
424
  /** The two edits an adopter makes in `metaobjects.config.ts`. */
370
425
  wire: { import: string; entry: string };
371
426
  requires: readonly string[];
427
+ /** The adapter source copied beside it (ADR-0034 Amendment 3). Absent when none. */
428
+ runtime?: RuntimeFileResult[];
372
429
  }
373
430
 
374
431
  interface EjectPayload {
@@ -518,6 +575,33 @@ function reportOne(result: EjectResult, name: string): void {
518
575
  "AND the entry.",
519
576
  );
520
577
  for (const line of result.dependencyNotes) log.info(line);
578
+ if (result.runtime !== undefined && result.runtime.files.length > 0) reportRuntime(result.runtime.files);
579
+ }
580
+
581
+ /** What the adapter copy handed over, and how the emitted code reaches it. */
582
+ function reportRuntime(files: readonly RuntimeFileResult[]): void {
583
+ const written = files.filter((f) => f.status !== "preserved");
584
+ // A kept file that already matches the package is not news (ejecting `entity routes`
585
+ // copies the shared allowlist file once, then finds it) — only a kept file that differs is.
586
+ const kept = files.filter((f) => f.status === "preserved" && f.comparison?.verdict !== "identical");
587
+ log.info(
588
+ `The code this generator emits imports its HTTP adapter from ${OWNED_RUNTIME_DIR}/, not from ` +
589
+ `${HTTP_RUNTIME_PACKAGE}: the adapter, filter parser, error envelopes and pagination are yours too.`,
590
+ );
591
+ if (written.length > 0) {
592
+ log.info(` Copied ${written.length} file(s) into ${OWNED_RUNTIME_DIR}/ (verbatim from the installed package).`);
593
+ }
594
+ const keptDiffering = kept.filter((f) => f.comparison?.verdict === "differs");
595
+ if (kept.length > 0) {
596
+ log.info(
597
+ ` ${kept.length} file(s) were already there and were left untouched` +
598
+ (keptDiffering.length > 0 ? `, ${keptDiffering.length} of them differing from the package` : "") +
599
+ " (--force replaces them). `meta eject --list` compares each one.",
600
+ );
601
+ }
602
+ log.info(
603
+ ` To import the package instead, pass \`runtimeImport: "${HTTP_RUNTIME_PACKAGE}"\` to the generator.`,
604
+ );
521
605
  }
522
606
 
523
607
  export async function ejectCommand(
@@ -575,6 +659,9 @@ export async function ejectCommand(
575
659
  const rows: EjectedRow[] = [];
576
660
  const libraries: LibraryEjectResult[] = [];
577
661
  const templatePackages = new Set<string>();
662
+ // ADR-0034 Amendment 3: the generators whose adapter source was copied, and what it imports.
663
+ const runtimeGenerators = new Set<string>();
664
+ const runtimePackages = new Set<string>();
578
665
  try {
579
666
  for (const name of flags.names) {
580
667
  if (isLibraryName(name)) {
@@ -585,12 +672,17 @@ export async function ejectCommand(
585
672
  }
586
673
  const result = await ejectGenerator({ cwd, name, force: flags.force });
587
674
  for (const p of result.templatePackages) templatePackages.add(p);
675
+ if (result.runtime !== undefined) {
676
+ runtimeGenerators.add(name);
677
+ for (const p of result.runtime.packages) runtimePackages.add(p);
678
+ }
588
679
  rows.push({
589
680
  name,
590
681
  path: result.path,
591
682
  status: result.status,
592
683
  wire: { import: result.importLine, entry: `${result.exportName}()` },
593
684
  requires: catalog[name]?.requires ?? [],
685
+ ...(result.runtime !== undefined ? { runtime: result.runtime.files } : {}),
594
686
  });
595
687
  if (fmt === "text") reportOne(result, name);
596
688
  }
@@ -610,7 +702,15 @@ export async function ejectCommand(
610
702
  .filter((n) => !isLibraryName(n))
611
703
  .map((n) => catalog[n])
612
704
  .filter((e) => e !== undefined);
613
- const install = installSetFor(entries, [...templatePackages]);
705
+ // The copied adapter source imports its own dependencies (qs, the framework, the core
706
+ // `@metaobjectsdev/metadata`), and the generated code no longer imports the runtime
707
+ // package — so the install line trades one for the other.
708
+ let ownedRuntime: OwnedRuntimeInstall | undefined;
709
+ if (runtimeGenerators.size > 0) {
710
+ const { runtime, dev } = runtimeInstall([...runtimePackages]);
711
+ ownedRuntime = { generators: runtimeGenerators, runtime, dev };
712
+ }
713
+ const install = installSetFor(entries, [...templatePackages], ownedRuntime);
614
714
  const configKeys = [...new Set(entries.flatMap((e) => e.configKeys ?? []))].sort();
615
715
 
616
716
  if (fmt === "text") {
@@ -428,7 +428,9 @@ async function listCatalogCommand(
428
428
  // codegen config it was moved out of.
429
429
  libraries: genCollection.libraries,
430
430
  },
431
- probe: { metadata, scope: genCollection.inScope },
431
+ // The collection's own files, exactly as `meta gen` supplies them below —
432
+ // `shared-model` cannot be counted without them.
433
+ probe: { metadata, scope: genCollection.inScope, sourceFiles: genCollection.ownFiles },
432
434
  };
433
435
  } else {
434
436
  // No probe: still report `wired` / `owned` when a project happens to be here, since
@@ -0,0 +1,47 @@
1
+ // The dependency-free half of `meta generator` — its flags and help text — so the CLI entry
2
+ // point can print `meta generator --help` without loading the codegen packages the command
3
+ // itself needs.
4
+
5
+ /** Where owned generators live — the directory `meta init` scaffolds and `meta eject` fills. */
6
+ export const OWNED_GENERATORS_DIR = "codegen/generators";
7
+ export const CONFIG_FILE = "metaobjects.config.ts";
8
+
9
+ export const GENERATOR_SCOPES = ["entity", "package", "model"] as const;
10
+ export type GeneratorScope = (typeof GENERATOR_SCOPES)[number];
11
+
12
+ export const GENERATOR_OPTIONS = {
13
+ "scope": { type: "string", default: "entity" },
14
+ "force": { type: "boolean", default: false },
15
+ "no-wire": { type: "boolean", default: false },
16
+ } as const;
17
+
18
+ export const GENERATOR_HELP = `meta generator — write a generator of your own
19
+
20
+ USAGE:
21
+ meta generator new <name> [--scope entity|package|model] [--force] [--no-wire]
22
+
23
+ Writes ${OWNED_GENERATORS_DIR}/<name>.ts — a small, commented, WORKING generator — and
24
+ adds its import and entry to ${CONFIG_FILE}, so \`meta gen\` runs it and
25
+ \`meta verify --codegen\` gates its output straight away. Then edit the emit to produce
26
+ what you need: OpenAPI, JSON Schema, a client, DTOs, docs — anything the model describes.
27
+
28
+ This is the primary way to get an output MetaObjects does not ship. Reach for
29
+ \`meta eject <name>\` instead only when a reference generator already emits something
30
+ close to what you want (\`meta gen --list\` is the catalog).
31
+
32
+ <name> is kebab-case (json-schema, openapi, service-layer). It names the file, the
33
+ generator's \`name\` in diagnostics, and the exported factory (jsonSchemaGenerator).
34
+
35
+ FLAGS:
36
+ --scope <scope> What the generator walks, one file per unit (default: entity):
37
+ entity one file per object in the model
38
+ package one file per metadata package
39
+ model one file for the whole model
40
+ --force Overwrite an existing ${OWNED_GENERATORS_DIR}/<name>.ts
41
+ --no-wire Write the file only; print the import and entry to add yourself
42
+ --help, -h Print this help
43
+
44
+ Every port's shape, and JSON Schema + OpenAPI 3.1 examples to copy: the guide
45
+ docs/recipes/write-your-own-generator.md in the MetaObjects repository
46
+ (github.com/metaobjectsdev), and the metaobjects-codegen skill \`meta init\` installs.
47
+ `;