@metaobjectsdev/cli 1.0.0-rc.4 → 1.0.0-rc.6
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/src/commands/docs.d.ts.map +1 -1
- package/dist/src/commands/docs.js +41 -9
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/commands/gen.d.ts +8 -0
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +17 -4
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +181 -35
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/migrate.d.ts +6 -0
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +11 -5
- package/dist/src/commands/migrate.js.map +1 -1
- package/dist/src/commands/prompt-snapshot.d.ts.map +1 -1
- package/dist/src/commands/prompt-snapshot.js +2 -1
- package/dist/src/commands/prompt-snapshot.js.map +1 -1
- package/dist/src/commands/types.d.ts.map +1 -1
- package/dist/src/commands/types.js +12 -3
- package/dist/src/commands/types.js.map +1 -1
- package/dist/src/commands/upgrade.d.ts.map +1 -1
- package/dist/src/commands/upgrade.js +38 -4
- package/dist/src/commands/upgrade.js.map +1 -1
- package/dist/src/commands/verify.d.ts.map +1 -1
- package/dist/src/commands/verify.js +108 -30
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +55 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/advisory.d.ts +6 -0
- package/dist/src/lib/advisory.d.ts.map +1 -1
- package/dist/src/lib/advisory.js +13 -0
- package/dist/src/lib/advisory.js.map +1 -1
- package/dist/src/lib/anti-patterns.d.ts.map +1 -1
- package/dist/src/lib/anti-patterns.js +2 -4
- package/dist/src/lib/anti-patterns.js.map +1 -1
- package/dist/src/lib/args.d.ts +251 -12
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +119 -92
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/base-url-advisory.d.ts +37 -0
- package/dist/src/lib/base-url-advisory.d.ts.map +1 -0
- package/dist/src/lib/base-url-advisory.js +162 -0
- package/dist/src/lib/base-url-advisory.js.map +1 -0
- package/dist/src/lib/codegen-drift.d.ts +8 -0
- package/dist/src/lib/codegen-drift.d.ts.map +1 -1
- package/dist/src/lib/codegen-drift.js +43 -10
- package/dist/src/lib/codegen-drift.js.map +1 -1
- package/dist/src/lib/detect-stack.d.ts +17 -0
- package/dist/src/lib/detect-stack.d.ts.map +1 -1
- package/dist/src/lib/detect-stack.js +64 -2
- package/dist/src/lib/detect-stack.js.map +1 -1
- package/dist/src/lib/docs-drift.d.ts +12 -1
- package/dist/src/lib/docs-drift.d.ts.map +1 -1
- package/dist/src/lib/docs-drift.js +85 -16
- package/dist/src/lib/docs-drift.js.map +1 -1
- package/dist/src/lib/git-ignore.d.ts +59 -0
- package/dist/src/lib/git-ignore.d.ts.map +1 -0
- package/dist/src/lib/git-ignore.js +120 -0
- package/dist/src/lib/git-ignore.js.map +1 -0
- package/dist/src/lib/ignored-scaffold-check.d.ts +12 -0
- package/dist/src/lib/ignored-scaffold-check.d.ts.map +1 -0
- package/dist/src/lib/ignored-scaffold-check.js +75 -0
- package/dist/src/lib/ignored-scaffold-check.js.map +1 -0
- package/dist/src/lib/load-error.d.ts +46 -0
- package/dist/src/lib/load-error.d.ts.map +1 -0
- package/dist/src/lib/load-error.js +51 -0
- package/dist/src/lib/load-error.js.map +1 -0
- package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +65 -1
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -1
- package/dist/src/lib/manifest-ignored-check.js +23 -36
- package/dist/src/lib/manifest-ignored-check.js.map +1 -1
- package/dist/src/lib/output.d.ts +22 -1
- package/dist/src/lib/output.d.ts.map +1 -1
- package/dist/src/lib/output.js +39 -12
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/rel-posix.d.ts +5 -0
- package/dist/src/lib/rel-posix.d.ts.map +1 -0
- package/dist/src/lib/rel-posix.js +23 -0
- package/dist/src/lib/rel-posix.js.map +1 -0
- package/dist/src/lib/replay-remedy.d.ts +2 -0
- package/dist/src/lib/replay-remedy.d.ts.map +1 -0
- package/dist/src/lib/replay-remedy.js +69 -0
- package/dist/src/lib/replay-remedy.js.map +1 -0
- package/dist/src/lib/requirement-lint.d.ts.map +1 -1
- package/dist/src/lib/requirement-lint.js +25 -5
- package/dist/src/lib/requirement-lint.js.map +1 -1
- package/package.json +10 -10
- package/src/commands/docs.ts +51 -12
- package/src/commands/gen.ts +19 -4
- package/src/commands/init.ts +188 -39
- package/src/commands/migrate.ts +13 -5
- package/src/commands/prompt-snapshot.ts +2 -1
- package/src/commands/types.ts +12 -3
- package/src/commands/upgrade.ts +44 -4
- package/src/commands/verify.ts +120 -32
- package/src/index.ts +55 -8
- package/src/lib/advisory.ts +15 -0
- package/src/lib/anti-patterns.ts +2 -5
- package/src/lib/args.ts +154 -109
- package/src/lib/base-url-advisory.ts +173 -0
- package/src/lib/codegen-drift.ts +52 -9
- package/src/lib/detect-stack.ts +73 -2
- package/src/lib/docs-drift.ts +107 -17
- package/src/lib/git-ignore.ts +159 -0
- package/src/lib/ignored-scaffold-check.ts +78 -0
- package/src/lib/load-error.ts +89 -0
- package/src/lib/load-metaobjects-config.ts +58 -1
- package/src/lib/manifest-ignored-check.ts +25 -33
- package/src/lib/output.ts +64 -11
- package/src/lib/rel-posix.ts +25 -0
- package/src/lib/replay-remedy.ts +78 -0
- package/src/lib/requirement-lint.ts +26 -5
package/src/commands/init.ts
CHANGED
|
@@ -8,13 +8,14 @@ import {
|
|
|
8
8
|
assemble, resolveAgentContextRoot, planScaffold,
|
|
9
9
|
AGENT_CONTEXT_MANIFEST_PATH, type Manifest, type Stack,
|
|
10
10
|
} from "@metaobjectsdev/sdk/agent-context";
|
|
11
|
-
import { resolveStack } from "../lib/detect-stack.js";
|
|
11
|
+
import { assertKnownStackValues, resolveStack } from "../lib/detect-stack.js";
|
|
12
|
+
import { reportIgnoredScaffold } from "../lib/ignored-scaffold-check.js";
|
|
12
13
|
import { parseInitArgs } from "../lib/args.js";
|
|
13
14
|
import { log } from "../lib/log.js";
|
|
14
15
|
import { cliVersion } from "../lib/version.js";
|
|
15
16
|
import { declaredDependencyNames, type PackageManifest } from "../lib/package-manifest.js";
|
|
16
17
|
import { findWranglerConfig, parseWranglerConfig } from "@metaobjectsdev/migrate-ts";
|
|
17
|
-
import { readReferenceTemplate, type ReferenceGeneratorName } from "@metaobjectsdev/codegen-ts";
|
|
18
|
+
import { DEFAULT_DOCS_DIR, readReferenceTemplate, type ReferenceGeneratorName } from "@metaobjectsdev/codegen-ts";
|
|
18
19
|
|
|
19
20
|
// ADR-0034 scaffold-and-own — `meta init` copies the codegen reference templates into
|
|
20
21
|
// the consumer's repo so they OWN them; metaobjects.config.ts imports them locally.
|
|
@@ -138,9 +139,17 @@ export default defineConfig({
|
|
|
138
139
|
barrel(),
|
|
139
140
|
],
|
|
140
141
|
docs: {
|
|
141
|
-
outDir: "
|
|
142
|
+
outDir: "${DEFAULT_DOCS_DIR}", // every surface lands here (run: meta docs).
|
|
143
|
+
// A SUB-directory on purpose: these pages are regenerated
|
|
144
|
+
// and overwritten, and docs/ itself is usually yours.
|
|
142
145
|
layout: "flat", // or "package" for multi-package models
|
|
143
|
-
surfaces
|
|
146
|
+
// surfaces defaults to ["model", "api", "requirements", "agent"] — all four.
|
|
147
|
+
// Deliberately NOT narrowed here: the requirements and agent surfaces emit ZERO
|
|
148
|
+
// files for a project that has nothing for them to describe, and the always-on
|
|
149
|
+
// agent context points at the agent/ pages by name. Scaffolding a narrower list
|
|
150
|
+
// turned that pointer into a pointer at nothing. Narrow it yourself if you want
|
|
151
|
+
// fewer docs — but note it says what your docs ARE, not which of them you commit
|
|
152
|
+
// (a page you do not commit is exempt from verify --docs once it is git-ignored).
|
|
144
153
|
},
|
|
145
154
|
});
|
|
146
155
|
`;
|
|
@@ -474,6 +483,57 @@ async function writeOwnedGenerators(opts: InitOptions, result: InitResult): Prom
|
|
|
474
483
|
await writeFile(abs, readReferenceTemplate(name), "utf8");
|
|
475
484
|
result.created.push(rel);
|
|
476
485
|
}
|
|
486
|
+
await writeCodegenTsconfig(opts, result);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** The owned codegen tier's own tsconfig — see writeCodegenTsconfig. */
|
|
490
|
+
const CODEGEN_TSCONFIG_REL = "tsconfig.codegen.json";
|
|
491
|
+
|
|
492
|
+
const CODEGEN_TSCONFIG_BODY = `{
|
|
493
|
+
// The owned codegen tier typechecks itself.
|
|
494
|
+
//
|
|
495
|
+
// \`meta gen\` loads metaobjects.config.ts and ./codegen/** through jiti, which
|
|
496
|
+
// TRANSPILES WITHOUT TYPECHECKING. So a generator here can import a symbol the
|
|
497
|
+
// engine no longer exports, or call a method that does not exist, and every gate
|
|
498
|
+
// stays green until the import is evaluated — which for a generator you have not
|
|
499
|
+
// wired into \`generators: [...]\` may be never.
|
|
500
|
+
//
|
|
501
|
+
// A project's app tsconfig usually covers src/ and tests/ and NOT this tier, so
|
|
502
|
+
// without this file nothing compiles the code your build depends on. Run it with:
|
|
503
|
+
// npx tsc -p tsconfig.codegen.json --noEmit
|
|
504
|
+
"compilerOptions": {
|
|
505
|
+
"target": "ES2022",
|
|
506
|
+
"module": "nodenext",
|
|
507
|
+
"moduleResolution": "nodenext",
|
|
508
|
+
"strict": true,
|
|
509
|
+
"noEmit": true,
|
|
510
|
+
"skipLibCheck": true
|
|
511
|
+
},
|
|
512
|
+
"include": ["metaobjects.config.ts", "codegen/**/*.ts"]
|
|
513
|
+
}
|
|
514
|
+
`;
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Scaffold a tsconfig that covers the tier `meta init` just handed the adopter.
|
|
518
|
+
*
|
|
519
|
+
* Reported from an estate that found TWO real defects the moment one existed: a
|
|
520
|
+
* generator importing `CODEGEN_ATTR_EMIT_ROUTES` (retired with the `@emit*` family,
|
|
521
|
+
* and invisible because that generator was not in `generators: [...]`), and a wired
|
|
522
|
+
* generator carrying five type errors including `ownFields()` on a node that has no
|
|
523
|
+
* such method. Both found on the first run.
|
|
524
|
+
*
|
|
525
|
+
* Written only when absent, like every other scaffolded file — an adopter who has
|
|
526
|
+
* their own arrangement keeps it, and it is reported as preserved rather than
|
|
527
|
+
* silently skipped.
|
|
528
|
+
*/
|
|
529
|
+
async function writeCodegenTsconfig(opts: InitOptions, result: InitResult): Promise<void> {
|
|
530
|
+
const abs = join(opts.cwd, CODEGEN_TSCONFIG_REL);
|
|
531
|
+
if (await fileExists(abs)) {
|
|
532
|
+
result.preserved.push(CODEGEN_TSCONFIG_REL);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
await writeFile(abs, CODEGEN_TSCONFIG_BODY, "utf8");
|
|
536
|
+
result.created.push(CODEGEN_TSCONFIG_REL);
|
|
477
537
|
}
|
|
478
538
|
|
|
479
539
|
/**
|
|
@@ -543,6 +603,16 @@ async function writeConfigFile(opts: InitOptions, result: InitResult, agentDir:
|
|
|
543
603
|
}
|
|
544
604
|
|
|
545
605
|
export async function init(opts: InitOptions): Promise<InitResult> {
|
|
606
|
+
// Refused HERE, in the shared function, before anything is written — NOT at one CLI
|
|
607
|
+
// entry point. It was at `initCommand`'s arg parse only, and `meta agent-docs` calls
|
|
608
|
+
// this directly: `meta init --docs-only --server klingon` exited 2 with the message
|
|
609
|
+
// while `meta agent-docs --server klingon` exited 0, reported "11 files", and recorded
|
|
610
|
+
// `"servers": []`. That is the worse of the two doors to miss — `agent-docs` is the
|
|
611
|
+
// canonical redirect target for every language port, so the ports used the unguarded
|
|
612
|
+
// one. A non-empty override array suppresses both the prior manifest's stack AND
|
|
613
|
+
// detection, so a dropped value does not degrade, it inverts three statements in the
|
|
614
|
+
// generated context.
|
|
615
|
+
assertKnownStackValues({ servers: opts.servers ?? [], clients: opts.clients ?? [] });
|
|
546
616
|
const result: InitResult = { created: [], preserved: [], removed: [], warnings: [] };
|
|
547
617
|
const agentDir = join(opts.cwd, DEFAULT_METAOBJECTS_DIR);
|
|
548
618
|
const metaobjectsDir = join(opts.cwd, DEFAULT_METADATA_DIR);
|
|
@@ -607,6 +677,7 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
607
677
|
);
|
|
608
678
|
result.created.push(".metaobjects/AGENTS.md", ".metaobjects/CLAUDE.md", ".claude/skills/metaobjects-*", AGENT_CONTEXT_MANIFEST_PATH);
|
|
609
679
|
for (const name of SCAFFOLDED_GENERATOR_NAMES) result.created.push(`${OWNED_GENERATORS_DIR}/${name}.ts`);
|
|
680
|
+
result.created.push(CODEGEN_TSCONFIG_REL);
|
|
610
681
|
result.created.push("metaobjects.config.ts", DB_STUB_REL_PATH, ".gitignore");
|
|
611
682
|
return result;
|
|
612
683
|
}
|
|
@@ -713,7 +784,7 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
713
784
|
result.preserved.push(".gitignore");
|
|
714
785
|
}
|
|
715
786
|
|
|
716
|
-
await
|
|
787
|
+
await prepareManifestForScaffold(opts.cwd, result);
|
|
717
788
|
|
|
718
789
|
return result;
|
|
719
790
|
}
|
|
@@ -734,8 +805,13 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
734
805
|
* So set it — but never silently take a real CommonJS project with it. A project
|
|
735
806
|
* that has actual CJS sources gets a loud, specific warning instead of an edit,
|
|
736
807
|
* because changing a module system out from under working code is not ours to do.
|
|
808
|
+
*
|
|
809
|
+
* IT ALSO DECLARES WHAT THE SCAFFOLD AND THE GENERATED OUTPUT IMPORT, and the name it used
|
|
810
|
+
* to carry (`ensureEsmPackageType`) is why that went wrong: a function named for one job
|
|
811
|
+
* accreted a second, and the second silently inherited the first's early returns. The
|
|
812
|
+
* declarations are unconditional on a readable manifest — see the comment at that point.
|
|
737
813
|
*/
|
|
738
|
-
async function
|
|
814
|
+
async function prepareManifestForScaffold(cwd: string, result: InitResult): Promise<void> {
|
|
739
815
|
const pkgPath = join(cwd, "package.json");
|
|
740
816
|
if (!(await fileExists(pkgPath))) {
|
|
741
817
|
// No package.json at all: say what is needed rather than inventing a manifest
|
|
@@ -760,39 +836,59 @@ async function ensureEsmPackageType(cwd: string, result: InitResult): Promise<vo
|
|
|
760
836
|
return;
|
|
761
837
|
}
|
|
762
838
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
839
|
+
// TWO INDEPENDENT JOBS FROM HERE, and they must not share an exit.
|
|
840
|
+
//
|
|
841
|
+
// The module system is one; declaring what the scaffold and the generated output import
|
|
842
|
+
// is the other, and it is true of every project with a readable manifest — including the
|
|
843
|
+
// one that already had `"type": "module"` and needs no module-system edit at all. Those
|
|
844
|
+
// two were folded together, below this function's early returns, and the consequence was
|
|
845
|
+
// that the adopter who had set their project up CORRECTLY got no declarations and no
|
|
846
|
+
// warning: `meta gen` worked (the CLI aliases those specifiers for itself) while `tsc`
|
|
847
|
+
// reported TS2307 on all five files `meta init` had just written. Under a strict
|
|
848
|
+
// installer that is every one of them; npm's hoisting hid roughly half.
|
|
849
|
+
//
|
|
850
|
+
// `moduleSystemNote` is computed first and applied last so the two jobs share ONE write
|
|
851
|
+
// of the manifest, rather than one job's early return deciding the other's fate.
|
|
852
|
+
let moduleSystemNote: string | undefined;
|
|
853
|
+
|
|
854
|
+
if (pkg.type !== "module") {
|
|
855
|
+
if (await hasCommonJsSources(cwd)) {
|
|
856
|
+
// `"type": "module"` is NOT set — changing a module system out from under working
|
|
857
|
+
// CJS code is not ours to do. The dependency declarations below still apply: they
|
|
858
|
+
// are about what the generated code imports, not about how it is loaded.
|
|
859
|
+
result.warnings.push(
|
|
860
|
+
'this project has CommonJS sources, so `"type": "module"` was NOT set for you — ' +
|
|
861
|
+
"but the scaffolded generators and all generated code are ESM and will not " +
|
|
862
|
+
"compile without it. Either migrate the project to ESM, or keep the generated " +
|
|
863
|
+
"code in a sub-directory with its own package.json declaring `\"type\": \"module\"`.",
|
|
864
|
+
);
|
|
865
|
+
} else {
|
|
866
|
+
const declaredType = pkg.type; // read BEFORE the mutation below overwrites it
|
|
867
|
+
pkg.type = "module";
|
|
868
|
+
// Past tense, deliberately: this reports an edit already made. The imperative
|
|
869
|
+
// ("set `\"type\": \"module\"`") read as a TODO on the one line a newcomer sees
|
|
870
|
+
// last, so a scaffold that had just done the right thing looked like it had failed.
|
|
871
|
+
//
|
|
872
|
+
// And it must not claim the manifest was SILENT on the point: `npm init -y` writes
|
|
873
|
+
// `"type": "commonjs"` explicitly (npm 11.x), which is the dominant first-touch path,
|
|
874
|
+
// so "declared no module system" was false exactly where it is read most. Report what
|
|
875
|
+
// was actually there.
|
|
876
|
+
const previous = typeof declaredType === "string" ? declaredType : undefined;
|
|
877
|
+
moduleSystemNote =
|
|
878
|
+
`package.json ${previous === undefined ? "declared no module system" : `declared "type": "${previous}"`} — ` +
|
|
879
|
+
'set `"type": "module"` for you, because MetaObjects scaffolds and generates ESM, ' +
|
|
880
|
+
"which a CommonJS project cannot compile.";
|
|
881
|
+
}
|
|
773
882
|
}
|
|
774
883
|
|
|
775
|
-
const declaredType = pkg.type; // read BEFORE the mutation below overwrites it
|
|
776
|
-
pkg.type = "module";
|
|
777
884
|
const added = addScaffoldDevDependencies(pkg);
|
|
778
885
|
const addedRuntime = addScaffoldRuntimeDependencies(pkg);
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
//
|
|
786
|
-
// And it must not claim the manifest was SILENT on the point: `npm init -y` writes
|
|
787
|
-
// `"type": "commonjs"` explicitly (npm 11.x), which is the dominant first-touch path,
|
|
788
|
-
// so "declared no module system" was false exactly where it is read most. Report what
|
|
789
|
-
// was actually there.
|
|
790
|
-
const previous = typeof declaredType === "string" ? declaredType : undefined;
|
|
791
|
-
result.warnings.push(
|
|
792
|
-
`package.json ${previous === undefined ? "declared no module system" : `declared "type": "${previous}"`} — ` +
|
|
793
|
-
'set `"type": "module"` for you, because MetaObjects scaffolds and generates ESM, ' +
|
|
794
|
-
"which a CommonJS project cannot compile.",
|
|
795
|
-
);
|
|
886
|
+
if (moduleSystemNote !== undefined || added.length > 0 || addedRuntime.length > 0) {
|
|
887
|
+
// Preserve the file's existing indentation rather than reformatting someone's manifest.
|
|
888
|
+
const indent = /\n(\s+)"/.exec(raw)?.[1] ?? " ";
|
|
889
|
+
await writeFile(pkgPath, `${JSON.stringify(pkg, null, indent)}\n`, "utf8");
|
|
890
|
+
}
|
|
891
|
+
if (moduleSystemNote !== undefined) result.warnings.push(moduleSystemNote);
|
|
796
892
|
if (added.length > 0) {
|
|
797
893
|
result.warnings.push(
|
|
798
894
|
`added ${added.join(" + ")} to devDependencies — the scaffolded ` +
|
|
@@ -807,6 +903,22 @@ async function ensureEsmPackageType(cwd: string, result: InitResult): Promise<vo
|
|
|
807
903
|
"installed. Run your package manager's install before `meta gen`.",
|
|
808
904
|
);
|
|
809
905
|
}
|
|
906
|
+
// Never degrade in silence. The first version of this returned `{}` on any failure to
|
|
907
|
+
// read runtime-ts's peer ranges and skipped the three packages that come from them —
|
|
908
|
+
// which is exactly what happened against the published package, and the run looked
|
|
909
|
+
// successful while leaving the project with the six TS2307s this code exists to
|
|
910
|
+
// prevent. A fallback that cannot be observed is indistinguishable from a bug.
|
|
911
|
+
const undeclarable = SCAFFOLD_OUTPUT_PEERS.filter(
|
|
912
|
+
(name) => !declaredDependencyNames(pkg as PackageManifest).has(name),
|
|
913
|
+
);
|
|
914
|
+
if (undeclarable.length > 0) {
|
|
915
|
+
result.warnings.push(
|
|
916
|
+
`could NOT declare ${undeclarable.join(" + ")} — the version ranges are read from ` +
|
|
917
|
+
"@metaobjectsdev/runtime-ts's own peerDependencies and that manifest could not be " +
|
|
918
|
+
"read. Generated code imports them, so add them to dependencies by hand or " +
|
|
919
|
+
"`npx tsc` will report TS2307 on files `meta gen` writes.",
|
|
920
|
+
);
|
|
921
|
+
}
|
|
810
922
|
}
|
|
811
923
|
|
|
812
924
|
/**
|
|
@@ -904,13 +1016,39 @@ function scaffoldRuntimeDependencies(): Record<string, string> {
|
|
|
904
1016
|
return wanted;
|
|
905
1017
|
}
|
|
906
1018
|
|
|
907
|
-
/**
|
|
1019
|
+
/**
|
|
1020
|
+
* `@metaobjectsdev/runtime-ts`'s declared peer ranges, or `{}` if they cannot be read.
|
|
1021
|
+
*
|
|
1022
|
+
* Resolves the package's ENTRY and walks up to the nearest package.json, rather than
|
|
1023
|
+
* asking for `"@metaobjectsdev/runtime-ts/package.json"` directly. That direct form is
|
|
1024
|
+
* the obvious one and it is WRONG: a package's `exports` map gates every subpath, and
|
|
1025
|
+
* runtime-ts's exports are `.` / `./drivers` / `./fastify` / `./drizzle-fastify` /
|
|
1026
|
+
* `./hono` — no `./package.json`. Under real Node it throws
|
|
1027
|
+
* ERR_PACKAGE_PATH_NOT_EXPORTED. It appeared to work here only because the workspace
|
|
1028
|
+
* runs this under bun against a symlinked source tree; against the published package it
|
|
1029
|
+
* failed on the first try, silently, and `meta init` declared one package instead of
|
|
1030
|
+
* four. Resolving the entry is never gated — `.` is the one subpath every package
|
|
1031
|
+
* exports — so this form works under npm, pnpm, bun and a linked checkout alike.
|
|
1032
|
+
*/
|
|
908
1033
|
function runtimeTsPeerRanges(): Record<string, string> {
|
|
909
1034
|
try {
|
|
910
1035
|
const req = createRequire(import.meta.url);
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1036
|
+
let dir = dirname(req.resolve("@metaobjectsdev/runtime-ts"));
|
|
1037
|
+
// The entry sits under dist/; the manifest is at the package root above it.
|
|
1038
|
+
for (let hops = 0; hops < 8; hops++) {
|
|
1039
|
+
const candidate = join(dir, "package.json");
|
|
1040
|
+
if (existsSyncWrap(candidate)) {
|
|
1041
|
+
const manifest = JSON.parse(readFileSyncWrap(candidate, "utf8")) as PackageManifest & { name?: string };
|
|
1042
|
+
// Guard against stopping at a nested manifest that is not the package itself.
|
|
1043
|
+
if (manifest.name === "@metaobjectsdev/runtime-ts") {
|
|
1044
|
+
return (manifest.peerDependencies ?? {}) as Record<string, string>;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
const up = dirname(dir);
|
|
1048
|
+
if (up === dir) break;
|
|
1049
|
+
dir = up;
|
|
1050
|
+
}
|
|
1051
|
+
return {};
|
|
914
1052
|
} catch {
|
|
915
1053
|
return {};
|
|
916
1054
|
}
|
|
@@ -1010,6 +1148,9 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
|
|
|
1010
1148
|
let flags;
|
|
1011
1149
|
try {
|
|
1012
1150
|
flags = parseInitArgs(args);
|
|
1151
|
+
// Also here, so a bad value is a USAGE error (exit 2) rather than the exit 1 an
|
|
1152
|
+
// init failure gets. `init()` refuses it too, which is what covers every other door.
|
|
1153
|
+
assertKnownStackValues({ servers: flags.servers ?? [], clients: flags.clients ?? [] });
|
|
1013
1154
|
} catch (err) {
|
|
1014
1155
|
log.error((err as Error).message);
|
|
1015
1156
|
return 2;
|
|
@@ -1038,6 +1179,14 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
|
|
|
1038
1179
|
}
|
|
1039
1180
|
|
|
1040
1181
|
if (!flags.quiet) {
|
|
1182
|
+
// A count is not a guarantee that the files reached the repository. Many projects
|
|
1183
|
+
// git-ignore `.claude/` under a comment about credentials — a common and defensible
|
|
1184
|
+
// convention — and eight of the eleven files land there. Ignored, they exist only on
|
|
1185
|
+
// the machine that ran this: not committed, absent from CI and from a fresh clone,
|
|
1186
|
+
// invisible to a teammate, while `.agent-context.json` (which IS committed) tracks
|
|
1187
|
+
// them. That silently voids the whole downstream agent-context design, and the answer
|
|
1188
|
+
// was available from the outputs this command had just written.
|
|
1189
|
+
reportIgnoredScaffold(cwd, result.created);
|
|
1041
1190
|
if (flags.docsOnly) {
|
|
1042
1191
|
log.info(`Scaffolded the MetaObjects agent context (${result.created.length} files): .metaobjects/AGENTS.md + .claude/skills/metaobjects-*.`);
|
|
1043
1192
|
for (const w of result.warnings) log.info(` ${w}`);
|
package/src/commands/migrate.ts
CHANGED
|
@@ -58,8 +58,16 @@ import {
|
|
|
58
58
|
} from "../lib/wrangler.js";
|
|
59
59
|
import { buildProjectionViews } from "@metaobjectsdev/codegen-ts";
|
|
60
60
|
import { tokensToAllowOptions, describeChange } from "../lib/allow.js";
|
|
61
|
+
import { reportLoadError } from "../lib/load-error.js";
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Print a load failure with everything the loader's ADR-0009 envelope carried — the stable
|
|
65
|
+
* code, the file, the json path, and the loader's own next steps — instead of the bare
|
|
66
|
+
* `err.message` five commands used to print. See `lib/load-error.ts`.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
export const MIGRATE_HELP_TEXT = `meta migrate — diff metadata vs live DB; emit migration SQL files
|
|
63
71
|
|
|
64
72
|
USAGE:
|
|
65
73
|
meta migrate [baseline|apply-pending] [flags]
|
|
@@ -628,7 +636,7 @@ export async function migrateCommand(
|
|
|
628
636
|
...postgresLoadOptions,
|
|
629
637
|
});
|
|
630
638
|
} catch (err) {
|
|
631
|
-
log
|
|
639
|
+
reportLoadError(log, "failed to load metadata", err);
|
|
632
640
|
return 2;
|
|
633
641
|
}
|
|
634
642
|
|
|
@@ -1056,7 +1064,7 @@ export async function runBaseline(
|
|
|
1056
1064
|
...baselineLoadOptions,
|
|
1057
1065
|
});
|
|
1058
1066
|
} catch (err) {
|
|
1059
|
-
log
|
|
1067
|
+
reportLoadError(log, "migrate baseline: failed to load metadata", err);
|
|
1060
1068
|
return 2;
|
|
1061
1069
|
}
|
|
1062
1070
|
const baselineViews = buildProjectionViews(metadata, { dialect: config.dialect, columnNamingStrategy: baselineStrategy });
|
|
@@ -1195,7 +1203,7 @@ export async function runOfflineGenerate(
|
|
|
1195
1203
|
...offlineLoadOptions,
|
|
1196
1204
|
});
|
|
1197
1205
|
} catch (err) {
|
|
1198
|
-
log
|
|
1206
|
+
reportLoadError(log, "migrate: failed to load metadata", err);
|
|
1199
1207
|
return 2;
|
|
1200
1208
|
}
|
|
1201
1209
|
|
|
@@ -1460,7 +1468,7 @@ async function runD1Migrate(
|
|
|
1460
1468
|
...d1LoadOptions,
|
|
1461
1469
|
});
|
|
1462
1470
|
} catch (err) {
|
|
1463
|
-
log
|
|
1471
|
+
reportLoadError(log, "migrate: failed to load metadata", err);
|
|
1464
1472
|
return 2;
|
|
1465
1473
|
}
|
|
1466
1474
|
|
|
@@ -14,6 +14,7 @@ import { parsePromptSnapshotArgs } from "../lib/args.js";
|
|
|
14
14
|
import { log } from "../lib/log.js";
|
|
15
15
|
import { FileProvider } from "../lib/file-provider.js";
|
|
16
16
|
import { snapshotPaths, unifiedDiff } from "../lib/snapshot.js";
|
|
17
|
+
import { reportLoadError } from "../lib/load-error.js";
|
|
17
18
|
import { loadMemoryOptionsFrom, loadMetaobjectsConfig, resolveGenConfigDir } from "../lib/load-metaobjects-config.js";
|
|
18
19
|
import { loadMemory, resolveCollection } from "@metaobjectsdev/sdk";
|
|
19
20
|
import { TYPE_TEMPLATE, TEMPLATE_ATTR_TEXT_REF, TEMPLATE_ATTR_FORMAT } from "@metaobjectsdev/metadata";
|
|
@@ -78,7 +79,7 @@ export async function promptSnapshotCommand(args: string[], cwd: string): Promis
|
|
|
78
79
|
...configLoadOptions,
|
|
79
80
|
});
|
|
80
81
|
} catch (err) {
|
|
81
|
-
log
|
|
82
|
+
reportLoadError(log, "failed to load metadata", err);
|
|
82
83
|
return 1;
|
|
83
84
|
}
|
|
84
85
|
|
package/src/commands/types.ts
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
// The `--json` flag this once advertised in its own --help, twice, is not coming back: the
|
|
21
21
|
// CLI rejects a bare `--json` before a command ever sees its args (index.ts, `formatAlias`),
|
|
22
22
|
// on purpose — one global spelling for all three formats.
|
|
23
|
-
import { composeRegistry,
|
|
24
|
-
import {
|
|
23
|
+
import { composeRegistry, buildVocabularyCatalog } from "@metaobjectsdev/metadata";
|
|
24
|
+
import { defaultLoadMemoryProviders } from "@metaobjectsdev/sdk";
|
|
25
25
|
import { log } from "../lib/log.js";
|
|
26
26
|
import { emitStructured, type OutputFormat } from "../lib/format.js";
|
|
27
27
|
|
|
@@ -226,7 +226,16 @@ export async function typesCommand(args: string[], fmt: OutputFormat = "text"):
|
|
|
226
226
|
// @sortable, @dbColumnType), `view.textarea` with none (no @rows), and the eight
|
|
227
227
|
// documentation commonAttrs — @title among them — were absent entirely. This is the same
|
|
228
228
|
// provider set the loader composes, so what this prints is what the loader accepts.
|
|
229
|
-
|
|
229
|
+
// `defaultLoadMemoryProviders` — THE composition, not a second spelling of it. This read
|
|
230
|
+
// `[...coreProviders]`, which is what that constant happens to equal today, so the claim
|
|
231
|
+
// above ("the same provider set the loader composes") was true by coincidence and the gate
|
|
232
|
+
// that pins the default composition against the canonical manifest had no jurisdiction
|
|
233
|
+
// here: add or drop a provider in the default set and `meta types` would silently print a
|
|
234
|
+
// vocabulary the loader does not accept, with nothing failing. `forgeTypesProvider` was
|
|
235
|
+
// once in this list, and printing it as registered vocabulary was part of how a TS-only
|
|
236
|
+
// registration read as cross-port: `meta types forge --all` listed 20 attrs no other port
|
|
237
|
+
// has. One composition, one door.
|
|
238
|
+
const registry = composeRegistry([...defaultLoadMemoryProviders], { validate: true });
|
|
230
239
|
const catalog = buildVocabularyCatalog(registry);
|
|
231
240
|
|
|
232
241
|
// Flatten the catalog into searchable entries.
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -21,7 +21,13 @@
|
|
|
21
21
|
import { readFile, writeFile } from "node:fs/promises";
|
|
22
22
|
import { extname, relative } from "node:path";
|
|
23
23
|
import { resolveCollection } from "@metaobjectsdev/sdk";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
rewriteDocument,
|
|
26
|
+
composeRegistry,
|
|
27
|
+
coreProviders,
|
|
28
|
+
SUBTYPE_BASE,
|
|
29
|
+
retirementSuggestions,
|
|
30
|
+
} from "@metaobjectsdev/metadata";
|
|
25
31
|
import { log } from "../lib/log.js";
|
|
26
32
|
|
|
27
33
|
/** YAML authoring (ADR-0006). Rewritten by the `yaml`-backed arm, loaded on demand below. */
|
|
@@ -96,7 +102,29 @@ export async function upgradeCommand(args: string[], cwd: string): Promise<numbe
|
|
|
96
102
|
? (await import("@metaobjectsdev/metadata/vocabulary-rewrite-yaml")).rewriteYamlDocument
|
|
97
103
|
: undefined;
|
|
98
104
|
|
|
99
|
-
|
|
105
|
+
// Which types have an ABSTRACT ANCHOR at `<type>.base`, derived from the core registry
|
|
106
|
+
// rather than listed here: `base` is an anchor exactly when the type registers some OTHER
|
|
107
|
+
// subtype for it to anchor. The rewriter is registry-free on purpose, so the caller — this
|
|
108
|
+
// command, which can build a registry — supplies the fact.
|
|
109
|
+
//
|
|
110
|
+
// Without it, `meta upgrade` answered "nothing to rewrite" on an estate whose
|
|
111
|
+
// `{"object.base": …}` the loader refuses (ERR_ABSTRACT_SUBTYPE_AUTHORED) — the change
|
|
112
|
+
// §10A of the 1.0 guide LEADS WITH. The one command whose job is "what does the new
|
|
113
|
+
// version need me to change?" said "nothing", and the adopter then hit the load failure.
|
|
114
|
+
const registry = composeRegistry(coreProviders);
|
|
115
|
+
// `allTypes()` yields one TypeId per registered type.subType pair, so take the distinct
|
|
116
|
+
// TYPE names off it.
|
|
117
|
+
const abstractAnchorTypes = [...new Set(registry.allTypes().map((id) => id.type))]
|
|
118
|
+
.filter((t) => {
|
|
119
|
+
const subs = registry.allSubTypesOf(t);
|
|
120
|
+
return subs.includes(SUBTYPE_BASE) && subs.some((sub) => sub !== SUBTYPE_BASE);
|
|
121
|
+
})
|
|
122
|
+
.sort();
|
|
123
|
+
|
|
124
|
+
const opts = {
|
|
125
|
+
abstractAnchorTypes,
|
|
126
|
+
...(flags.maxVersion !== undefined ? { maxVersion: flags.maxVersion } : {}),
|
|
127
|
+
};
|
|
100
128
|
|
|
101
129
|
for (const file of files) {
|
|
102
130
|
const rel = relative(projectRoot, file);
|
|
@@ -120,10 +148,22 @@ export async function upgradeCommand(args: string[], cwd: string): Promise<numbe
|
|
|
120
148
|
log.info(`\n${rel}`);
|
|
121
149
|
for (const c of r.changes) log.info(` ${c.line}: @${c.from} → ${c.to}`);
|
|
122
150
|
for (const f of r.refusals) {
|
|
151
|
+
// The WHY and the replacement both print, and the guide is not an alternative to
|
|
152
|
+
// them. This used to read `Retired in <since>. See <guide>` — dropping `why`
|
|
153
|
+
// whenever a guide existed, which is almost always, and dropping `replacedBy`
|
|
154
|
+
// unconditionally. For the `@forge*` entries that removed the only actionable
|
|
155
|
+
// sentence there is ("opt the provider in explicitly"), and for an authored
|
|
156
|
+
// `<type>.base` it announced a retirement of something that was never authorable —
|
|
157
|
+
// telling the reader they used to have a working feature. Hence "no longer accepted
|
|
158
|
+
// as of", which is true of both, with the carefully-written `why` beside it.
|
|
123
159
|
log.warn(
|
|
124
|
-
` ${f.line}: ${f.subject}${f.value !== undefined ? `: ${f.value}` : ""} — needs a decision
|
|
125
|
-
`
|
|
160
|
+
` ${f.line}: ${f.subject}${f.value !== undefined ? `: ${f.value}` : ""} — needs a decision ` +
|
|
161
|
+
`(no longer accepted as of ${f.since}).`,
|
|
126
162
|
);
|
|
163
|
+
log.warn(` ${f.why}`);
|
|
164
|
+
// The same suggestion list every OTHER door prints for a retirement, rather than a
|
|
165
|
+
// second rendering of the same note that can drift from it.
|
|
166
|
+
for (const line of retirementSuggestions(f)) log.warn(` ${line}`);
|
|
127
167
|
}
|
|
128
168
|
|
|
129
169
|
totalChanges += r.changes.length;
|