@metaobjectsdev/sdk 0.23.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/agent-context/skills/metaobjects-audit/SKILL.md +20 -7
  2. package/agent-context/skills/metaobjects-audit/references/capability-checklist.md +64 -18
  3. package/agent-context/skills/metaobjects-audit/references/csharp.md +2 -1
  4. package/agent-context/skills/metaobjects-audit/references/java.md +3 -2
  5. package/agent-context/skills/metaobjects-audit/references/kotlin.md +2 -1
  6. package/agent-context/skills/metaobjects-audit/references/requirements.md +3 -4
  7. package/agent-context/skills/metaobjects-audit/references/typescript.md +1 -1
  8. package/agent-context/skills/metaobjects-authoring/SKILL.md +28 -3
  9. package/agent-context/skills/metaobjects-authoring/references/requirements.md +49 -23
  10. package/agent-context/skills/metaobjects-codegen/SKILL.md +4 -3
  11. package/agent-context/skills/metaobjects-codegen/references/csharp.md +2 -2
  12. package/agent-context/skills/metaobjects-codegen/references/java.md +3 -3
  13. package/agent-context/skills/metaobjects-codegen/references/kotlin.md +4 -4
  14. package/agent-context/skills/metaobjects-codegen/references/python.md +2 -2
  15. package/agent-context/skills/metaobjects-codegen/references/typescript.md +3 -3
  16. package/agent-context/skills/metaobjects-fit-assessment/SKILL.md +4 -4
  17. package/agent-context/skills/metaobjects-prompts/SKILL.md +53 -32
  18. package/agent-context/skills/metaobjects-prompts/references/csharp.md +26 -21
  19. package/agent-context/skills/metaobjects-prompts/references/java.md +24 -18
  20. package/agent-context/skills/metaobjects-prompts/references/kotlin.md +25 -20
  21. package/agent-context/skills/metaobjects-prompts/references/python.md +68 -26
  22. package/agent-context/skills/metaobjects-prompts/references/typescript.md +29 -23
  23. package/agent-context/skills/metaobjects-verify/references/migration.md +1 -1
  24. package/agent-context/skills/metaobjects-verify/references/requirements.md +36 -22
  25. package/agent-context/templates/always-on.md.mustache +3 -1
  26. package/dist/agent-docs/body.d.ts +1 -1
  27. package/dist/agent-docs/body.d.ts.map +1 -1
  28. package/dist/agent-docs/body.js +24 -2
  29. package/dist/agent-docs/body.js.map +1 -1
  30. package/dist/collection.d.ts +71 -0
  31. package/dist/collection.d.ts.map +1 -0
  32. package/dist/collection.js +121 -0
  33. package/dist/collection.js.map +1 -0
  34. package/dist/config.d.ts +51 -21
  35. package/dist/config.d.ts.map +1 -1
  36. package/dist/config.js +76 -10
  37. package/dist/config.js.map +1 -1
  38. package/dist/discovery.d.ts +43 -0
  39. package/dist/discovery.d.ts.map +1 -0
  40. package/dist/discovery.js +100 -0
  41. package/dist/discovery.js.map +1 -0
  42. package/dist/index.d.ts +10 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +17 -4
  45. package/dist/index.js.map +1 -1
  46. package/dist/memory.d.ts +42 -19
  47. package/dist/memory.d.ts.map +1 -1
  48. package/dist/memory.js +38 -95
  49. package/dist/memory.js.map +1 -1
  50. package/dist/metadata-files.d.ts +67 -0
  51. package/dist/metadata-files.d.ts.map +1 -0
  52. package/dist/metadata-files.js +155 -0
  53. package/dist/metadata-files.js.map +1 -0
  54. package/dist/records/any.d.ts +8 -8
  55. package/dist/records/decision.d.ts +2 -2
  56. package/dist/records/glossary.d.ts +4 -4
  57. package/dist/records/principle.d.ts +2 -2
  58. package/dist/scope.d.ts +16 -0
  59. package/dist/scope.d.ts.map +1 -0
  60. package/dist/scope.js +75 -0
  61. package/dist/scope.js.map +1 -0
  62. package/dist/sources.d.ts +86 -0
  63. package/dist/sources.d.ts.map +1 -0
  64. package/dist/sources.js +130 -0
  65. package/dist/sources.js.map +1 -0
  66. package/package.json +2 -2
  67. package/src/agent-docs/body.ts +24 -2
  68. package/src/collection.ts +183 -0
  69. package/src/config.ts +80 -12
  70. package/src/discovery.ts +110 -0
  71. package/src/index.ts +26 -9
  72. package/src/memory.ts +65 -99
  73. package/src/metadata-files.ts +158 -0
  74. package/src/scope.ts +95 -0
  75. package/src/sources.ts +160 -0
@@ -127,6 +127,7 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
127
127
  scope: z.ZodUnion<[z.ZodLiteral<"global">, z.ZodArray<z.ZodString, "many">]>;
128
128
  }, "strip", z.ZodTypeAny, {
129
129
  type: "decision";
130
+ scope: string[] | "global";
130
131
  schema_version: 1;
131
132
  id: string;
132
133
  title: string;
@@ -141,7 +142,6 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
141
142
  }[];
142
143
  rationale: string;
143
144
  alternatives_considered: string[];
144
- scope: string[] | "global";
145
145
  superseded_by?: string | undefined;
146
146
  evidence?: {
147
147
  commits?: string[] | undefined;
@@ -150,6 +150,7 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
150
150
  } | undefined;
151
151
  }, {
152
152
  type: "decision";
153
+ scope: string[] | "global";
153
154
  schema_version: 1;
154
155
  id: string;
155
156
  title: string;
@@ -159,7 +160,6 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
159
160
  last_validated_against_commit: string;
160
161
  rationale: string;
161
162
  alternatives_considered: string[];
162
- scope: string[] | "global";
163
163
  superseded_by?: string | undefined;
164
164
  evidence?: {
165
165
  commits?: string[] | undefined;
@@ -216,6 +216,7 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
216
216
  enforcement: z.ZodDefault<z.ZodEnum<["advisory", "block"]>>;
217
217
  }, "strip", z.ZodTypeAny, {
218
218
  type: "principle";
219
+ scope: string[];
219
220
  schema_version: 1;
220
221
  id: string;
221
222
  title: string;
@@ -230,7 +231,6 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
230
231
  }[];
231
232
  examples: string[];
232
233
  rationale: string;
233
- scope: string[];
234
234
  statement: string;
235
235
  counter_examples: string[];
236
236
  enforcement: "advisory" | "block";
@@ -242,6 +242,7 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
242
242
  } | undefined;
243
243
  }, {
244
244
  type: "principle";
245
+ scope: string[];
245
246
  schema_version: 1;
246
247
  id: string;
247
248
  title: string;
@@ -251,7 +252,6 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
251
252
  last_validated_against_commit: string;
252
253
  examples: string[];
253
254
  rationale: string;
254
- scope: string[];
255
255
  statement: string;
256
256
  counter_examples: string[];
257
257
  superseded_by?: string | undefined;
@@ -310,11 +310,11 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
310
310
  entity: z.ZodOptional<z.ZodString>;
311
311
  files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
312
312
  }, "strip", z.ZodTypeAny, {
313
- entity?: string | undefined;
314
313
  files?: string[] | undefined;
315
- }, {
316
314
  entity?: string | undefined;
315
+ }, {
317
316
  files?: string[] | undefined;
317
+ entity?: string | undefined;
318
318
  }>;
319
319
  see_also: z.ZodArray<z.ZodString, "many">;
320
320
  }, "strip", z.ZodTypeAny, {
@@ -335,8 +335,8 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
335
335
  synonyms: string[];
336
336
  definition: string;
337
337
  code_anchors: {
338
- entity?: string | undefined;
339
338
  files?: string[] | undefined;
339
+ entity?: string | undefined;
340
340
  };
341
341
  see_also: string[];
342
342
  superseded_by?: string | undefined;
@@ -358,8 +358,8 @@ export declare const AnyRecord: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
358
358
  synonyms: string[];
359
359
  definition: string;
360
360
  code_anchors: {
361
- entity?: string | undefined;
362
361
  files?: string[] | undefined;
362
+ entity?: string | undefined;
363
363
  };
364
364
  see_also: string[];
365
365
  superseded_by?: string | undefined;
@@ -41,6 +41,7 @@ export declare const DecisionRecord: z.ZodObject<{
41
41
  scope: z.ZodUnion<[z.ZodLiteral<"global">, z.ZodArray<z.ZodString, "many">]>;
42
42
  }, "strip", z.ZodTypeAny, {
43
43
  type: "decision";
44
+ scope: string[] | "global";
44
45
  schema_version: 1;
45
46
  id: string;
46
47
  title: string;
@@ -55,7 +56,6 @@ export declare const DecisionRecord: z.ZodObject<{
55
56
  }[];
56
57
  rationale: string;
57
58
  alternatives_considered: string[];
58
- scope: string[] | "global";
59
59
  superseded_by?: string | undefined;
60
60
  evidence?: {
61
61
  commits?: string[] | undefined;
@@ -64,6 +64,7 @@ export declare const DecisionRecord: z.ZodObject<{
64
64
  } | undefined;
65
65
  }, {
66
66
  type: "decision";
67
+ scope: string[] | "global";
67
68
  schema_version: 1;
68
69
  id: string;
69
70
  title: string;
@@ -73,7 +74,6 @@ export declare const DecisionRecord: z.ZodObject<{
73
74
  last_validated_against_commit: string;
74
75
  rationale: string;
75
76
  alternatives_considered: string[];
76
- scope: string[] | "global";
77
77
  superseded_by?: string | undefined;
78
78
  evidence?: {
79
79
  commits?: string[] | undefined;
@@ -43,11 +43,11 @@ export declare const GlossaryRecord: z.ZodObject<{
43
43
  entity: z.ZodOptional<z.ZodString>;
44
44
  files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
45
45
  }, "strip", z.ZodTypeAny, {
46
- entity?: string | undefined;
47
46
  files?: string[] | undefined;
48
- }, {
49
47
  entity?: string | undefined;
48
+ }, {
50
49
  files?: string[] | undefined;
50
+ entity?: string | undefined;
51
51
  }>;
52
52
  see_also: z.ZodArray<z.ZodString, "many">;
53
53
  }, "strip", z.ZodTypeAny, {
@@ -68,8 +68,8 @@ export declare const GlossaryRecord: z.ZodObject<{
68
68
  synonyms: string[];
69
69
  definition: string;
70
70
  code_anchors: {
71
- entity?: string | undefined;
72
71
  files?: string[] | undefined;
72
+ entity?: string | undefined;
73
73
  };
74
74
  see_also: string[];
75
75
  superseded_by?: string | undefined;
@@ -91,8 +91,8 @@ export declare const GlossaryRecord: z.ZodObject<{
91
91
  synonyms: string[];
92
92
  definition: string;
93
93
  code_anchors: {
94
- entity?: string | undefined;
95
94
  files?: string[] | undefined;
95
+ entity?: string | undefined;
96
96
  };
97
97
  see_also: string[];
98
98
  superseded_by?: string | undefined;
@@ -44,6 +44,7 @@ export declare const PrincipleRecord: z.ZodObject<{
44
44
  enforcement: z.ZodDefault<z.ZodEnum<["advisory", "block"]>>;
45
45
  }, "strip", z.ZodTypeAny, {
46
46
  type: "principle";
47
+ scope: string[];
47
48
  schema_version: 1;
48
49
  id: string;
49
50
  title: string;
@@ -58,7 +59,6 @@ export declare const PrincipleRecord: z.ZodObject<{
58
59
  }[];
59
60
  examples: string[];
60
61
  rationale: string;
61
- scope: string[];
62
62
  statement: string;
63
63
  counter_examples: string[];
64
64
  enforcement: "advisory" | "block";
@@ -70,6 +70,7 @@ export declare const PrincipleRecord: z.ZodObject<{
70
70
  } | undefined;
71
71
  }, {
72
72
  type: "principle";
73
+ scope: string[];
73
74
  schema_version: 1;
74
75
  id: string;
75
76
  title: string;
@@ -79,7 +80,6 @@ export declare const PrincipleRecord: z.ZodObject<{
79
80
  last_validated_against_commit: string;
80
81
  examples: string[];
81
82
  rationale: string;
82
- scope: string[];
83
83
  statement: string;
84
84
  counter_examples: string[];
85
85
  superseded_by?: string | undefined;
@@ -0,0 +1,16 @@
1
+ /** A consumer-side output filter over fully-qualified node names. */
2
+ export interface Scope {
3
+ /** Absent or empty means "everything". */
4
+ readonly include?: readonly string[];
5
+ /** Applied after `include`. */
6
+ readonly exclude?: readonly string[];
7
+ }
8
+ export interface CompiledScope {
9
+ readonly include: readonly RegExp[];
10
+ readonly exclude: readonly RegExp[];
11
+ }
12
+ export declare function compilePattern(pattern: string): RegExp;
13
+ export declare function compileScope(scope: Scope): CompiledScope;
14
+ /** True when `fqn` is inside the scope. An empty `include` means everything. */
15
+ export declare function matchesScope(fqn: string, compiled: CompiledScope): boolean;
16
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAkBA,qEAAqE;AACrE,MAAM,WAAW,KAAK;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAuCD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAYtD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,aAAa,CAKxD;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAI1E"}
package/dist/scope.js ADDED
@@ -0,0 +1,75 @@
1
+ // server/typescript/packages/sdk/src/scope.ts
2
+ //
3
+ // Phase-1 metadata-source-resolution — the scope pattern engine.
4
+ //
5
+ // A pure, no-I/O module deciding whether a fully-qualified node name falls
6
+ // inside a consumer's declared `include`/`exclude` scope. Source resolution
7
+ // and discovery (later phase-1 tasks) build on this; a cross-language
8
+ // conformance corpus pins its semantics, so exact pattern behavior matters.
9
+ // NOTE: PACKAGE_SEPARATOR is NOT re-exported from the browser-safe
10
+ // `@metaobjectsdev/metadata/constants` barrel (that barrel only re-exports
11
+ // the per-concern `*-constants.ts` modules; `PACKAGE_SEPARATOR` lives in
12
+ // `shared/structural.ts`, exported from the package root). This package
13
+ // (`@metaobjectsdev/sdk`) is server-side, not a `client/web/**` browser
14
+ // package, so importing metamodel values from the root — the same thing
15
+ // `memory.ts` and `forge-types.ts` in this package already do — is correct.
16
+ import { PACKAGE_SEPARATOR, ParseError, codeSource } from "@metaobjectsdev/metadata";
17
+ /** One package segment: any run of characters containing no separator char. */
18
+ const SEGMENT = "[^:]+";
19
+ /** One or more segments, separator-joined — the `**` expansion. */
20
+ const SEGMENTS = `${SEGMENT}(?:${PACKAGE_SEPARATOR}${SEGMENT})*`;
21
+ function escapeLiteral(text) {
22
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23
+ }
24
+ /** Compile one segment. `**` spans segments; `*` never crosses a separator. */
25
+ function compileSegment(segment, pattern) {
26
+ if (segment.length === 0) {
27
+ throw new ParseError(`empty segment in scope pattern "${pattern}"`, {
28
+ code: "ERR_SCOPE_PATTERN_INVALID",
29
+ source: codeSource("compileSegment"),
30
+ });
31
+ }
32
+ // A segment surviving the split on the two-character PACKAGE_SEPARATOR
33
+ // ("::") can still contain a lone ":" when the pattern has an odd colon
34
+ // run — e.g. "acme:::Order".split("::") => ["acme", ":Order"]. SEGMENT
35
+ // ([^:]+) already excludes ":" from a well-formed segment, so a leftover
36
+ // ":" here means the separator was malformed, not that ":" is meant
37
+ // literally. Left unchecked, escapeLiteral treats it as a literal
38
+ // character and compiles a regex requiring three colons in a row — which
39
+ // no legal "::"-joined fully-qualified name can ever contain, so the
40
+ // pattern silently matches nothing instead of failing loud.
41
+ if (segment.includes(":")) {
42
+ throw new ParseError(`scope pattern "${pattern}" has a malformed separator (an odd run of ":") — segments are joined by "::", never a single ":"`, { code: "ERR_SCOPE_PATTERN_INVALID", source: codeSource("compileSegment") });
43
+ }
44
+ if (segment === "**")
45
+ return `(?:${SEGMENTS})`;
46
+ // `*` inside a segment matches any characters except the separator char.
47
+ return segment.split("*").map(escapeLiteral).join("[^:]*");
48
+ }
49
+ export function compilePattern(pattern) {
50
+ if (pattern.length === 0) {
51
+ throw new ParseError(`scope pattern must not be empty`, {
52
+ code: "ERR_SCOPE_PATTERN_INVALID",
53
+ source: codeSource("compilePattern"),
54
+ });
55
+ }
56
+ const body = pattern
57
+ .split(PACKAGE_SEPARATOR)
58
+ .map((segment) => compileSegment(segment, pattern))
59
+ .join(PACKAGE_SEPARATOR);
60
+ return new RegExp(`^${body}$`);
61
+ }
62
+ export function compileScope(scope) {
63
+ return {
64
+ include: (scope.include ?? []).map(compilePattern),
65
+ exclude: (scope.exclude ?? []).map(compilePattern),
66
+ };
67
+ }
68
+ /** True when `fqn` is inside the scope. An empty `include` means everything. */
69
+ export function matchesScope(fqn, compiled) {
70
+ const included = compiled.include.length === 0 || compiled.include.some((re) => re.test(fqn));
71
+ if (!included)
72
+ return false;
73
+ return !compiled.exclude.some((re) => re.test(fqn));
74
+ }
75
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /** Tagged union of source kinds. `resource` and `package` are declared now so
2
+ * the config shape is stable across phases; only `path` resolves in phase 1 —
3
+ * `resource`/`package` throw `ERR_SOURCE_KIND_UNSUPPORTED`. */
4
+ export type SourceSpec = {
5
+ readonly path: string;
6
+ } | {
7
+ readonly resource: string;
8
+ } | {
9
+ readonly package: string;
10
+ };
11
+ export interface ResolvedSource {
12
+ /** Absolute path of one metadata file. */
13
+ readonly file: string;
14
+ /** The spec that contributed it — provenance for diagnostics. */
15
+ readonly spec: SourceSpec;
16
+ }
17
+ /** Used when `sources` is absent or empty in `.metaobjects/config.json`. A
18
+ * DEFAULT, never a requirement — a project that declares `sources` explicitly
19
+ * need not include the default directory at all. Built from
20
+ * `DEFAULT_METADATA_DIR` (`metadata-files.ts`'s single definition) rather than
21
+ * restating that name here: a second independent encoding of the same
22
+ * default would let `resolveCollection`'s "does the default dir exist"
23
+ * check (`collection.ts`) desync from what `resolveSources` actually
24
+ * resolves the moment the default ever changed — silently reproducing the
25
+ * "two code paths disagree about where metadata lives" class of bug this
26
+ * whole mechanism exists to eliminate. */
27
+ export declare const DEFAULT_SOURCES: readonly SourceSpec[];
28
+ /**
29
+ * The declared source SET in CANONICAL order — kind-validated, then sorted by
30
+ * spec CONTENT rather than by declaration order.
31
+ *
32
+ * This is the ONE place declaration order is discarded, and the module's "pure
33
+ * function of the SET" invariant rests on it: the emitted file order, the spec
34
+ * attributed to a file two specs both reach, and which of several unresolvable
35
+ * paths reports its `ERR_SOURCE_UNRESOLVED` first are all decided here.
36
+ * Validation runs across the WHOLE list before any sorting or filesystem I/O —
37
+ * interleaved with resolution, which error code came back would depend on
38
+ * declaration order, contradicting that same invariant.
39
+ *
40
+ * Exported because `resolveCollection` derives `sourceRoots` from the declared
41
+ * specs and must use this identical ordering; a second sort would be a second
42
+ * definition of "canonical".
43
+ */
44
+ export declare function orderedPathSpecs(specs: readonly SourceSpec[]): {
45
+ readonly path: string;
46
+ }[];
47
+ /**
48
+ * Where a declared `path` source lives on disk: absolute as written, otherwise
49
+ * relative to the DECLARING config's directory — never to ambient
50
+ * `process.cwd()`.
51
+ *
52
+ * One definition, because this expression *is* the rule for where a declared
53
+ * source lives, which is the single piece of knowledge this module exists to
54
+ * own. A caller that needs a source's root directory (rather than its files)
55
+ * calls this rather than restating it.
56
+ */
57
+ export declare function resolveSpecPath(configDir: string, spec: {
58
+ readonly path: string;
59
+ }): string;
60
+ /**
61
+ * Resolve a declared source SET to a canonically-ordered list of metadata files.
62
+ *
63
+ * The full result — each entry's `.file` AND its `.spec` — is a pure function
64
+ * of the SET of `specs`: permuting `specs` cannot change the output. One thing
65
+ * makes that hold: the specs are processed in CONTENT order
66
+ * (`JSON.stringify(spec)`, ascending) rather than declared order, so both the
67
+ * emitted file order and the spec attributed to a file overlapping two specs
68
+ * are decided by content alone. Declared order carries no information anywhere
69
+ * in this function.
70
+ *
71
+ * Within one directory spec the file order is `listMetadataFiles`'s — files at
72
+ * a level, then that level's subdirectories, depth-first. That is deliberately
73
+ * NOT a flat sort of absolute paths: see the file header, and
74
+ * `test/source-order.test.ts`.
75
+ *
76
+ * Only `path` specs resolve in phase 1: a directory is walked recursively, a
77
+ * file is taken as-is. An unresolvable `path` throws `ERR_SOURCE_UNRESOLVED`
78
+ * rather than silently contributing nothing; `resource`/`package` specs throw
79
+ * `ERR_SOURCE_KIND_UNSUPPORTED`.
80
+ *
81
+ * @param configDir absolute directory of the declaring config (the parent of
82
+ * `.metaobjects/`) — relative `path` specs resolve against it, never against
83
+ * ambient `process.cwd()`.
84
+ */
85
+ export declare function resolveSources(configDir: string, specs: readonly SourceSpec[]): Promise<ResolvedSource[]>;
86
+ //# sourceMappingURL=sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../src/sources.ts"],"names":[],"mappings":"AA2BA;;gEAEgE;AAChE,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAED;;;;;;;;;2CAS2C;AAC3C,eAAO,MAAM,eAAe,EAAE,SAAS,UAAU,EAAqC,CAAC;AAgBvF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAK1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE1F;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,UAAU,EAAE,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC,CA0B3B"}
@@ -0,0 +1,130 @@
1
+ // server/typescript/packages/sdk/src/sources.ts
2
+ //
3
+ // Phase-1 metadata-source-resolution — source spec resolution.
4
+ //
5
+ // Turns a declared source SET (`.metaobjects/config.json`'s `sources`) into a
6
+ // canonically-ordered, de-duplicated list of metadata file paths. The FULL
7
+ // result — including which spec each entry attributes to — is a pure
8
+ // function of the source SET, never of declaration order: permuting `specs`
9
+ // cannot change the output, even when two specs overlap on the same file.
10
+ // `test/order-independence.test.ts` pins that (the design's linchpin), so the
11
+ // canonical spec ordering below is load-bearing.
12
+ //
13
+ // Canonical is NOT the same as "flat-sorted". Within one directory spec the
14
+ // order is `listMetadataFiles`'s (metadata-files.ts) — files at a level, then
15
+ // that level's subdirectories, depth-first — because that is the order production
16
+ // has always handed the loader, and declaration order survives into generated
17
+ // output (the barrel's export list, the shared `enums.ts`, `meta docs` page
18
+ // order, `meta export`'s sibling order). A flat sort of absolute paths
19
+ // silently reorders any project with a subdirectory whose name sorts before a
20
+ // sibling file. Across specs, order is decided by spec CONTENT, which is what
21
+ // keeps the whole result permutation-invariant. `test/source-order.test.ts`
22
+ // pins both halves.
23
+ import { stat } from "node:fs/promises";
24
+ import { isAbsolute, resolve } from "node:path";
25
+ import { ParseError, codeSource } from "@metaobjectsdev/metadata";
26
+ import { DEFAULT_METADATA_DIR, listMetadataFiles } from "./metadata-files.js";
27
+ /** Used when `sources` is absent or empty in `.metaobjects/config.json`. A
28
+ * DEFAULT, never a requirement — a project that declares `sources` explicitly
29
+ * need not include the default directory at all. Built from
30
+ * `DEFAULT_METADATA_DIR` (`metadata-files.ts`'s single definition) rather than
31
+ * restating that name here: a second independent encoding of the same
32
+ * default would let `resolveCollection`'s "does the default dir exist"
33
+ * check (`collection.ts`) desync from what `resolveSources` actually
34
+ * resolves the moment the default ever changed — silently reproducing the
35
+ * "two code paths disagree about where metadata lives" class of bug this
36
+ * whole mechanism exists to eliminate. */
37
+ export const DEFAULT_SOURCES = [{ path: DEFAULT_METADATA_DIR }];
38
+ /** Narrows `spec` to its `path` arm, throwing `ERR_SOURCE_KIND_UNSUPPORTED`
39
+ * for `resource`/`package` — phase 1 resolves `path` only. Returns rather than
40
+ * asserting so one call both validates and narrows: an `asserts` signature has
41
+ * to be re-invoked wherever TypeScript's control-flow analysis cannot carry the
42
+ * narrowing, which is a language workaround masquerading as a second check. */
43
+ function toPathSpec(spec) {
44
+ if ("path" in spec)
45
+ return spec;
46
+ const kind = "resource" in spec ? "resource" : "package";
47
+ throw new ParseError(`source kind "${kind}" is not supported by this toolchain yet; use a "path" source`, { code: "ERR_SOURCE_KIND_UNSUPPORTED", source: codeSource("resolveSources") });
48
+ }
49
+ /**
50
+ * The declared source SET in CANONICAL order — kind-validated, then sorted by
51
+ * spec CONTENT rather than by declaration order.
52
+ *
53
+ * This is the ONE place declaration order is discarded, and the module's "pure
54
+ * function of the SET" invariant rests on it: the emitted file order, the spec
55
+ * attributed to a file two specs both reach, and which of several unresolvable
56
+ * paths reports its `ERR_SOURCE_UNRESOLVED` first are all decided here.
57
+ * Validation runs across the WHOLE list before any sorting or filesystem I/O —
58
+ * interleaved with resolution, which error code came back would depend on
59
+ * declaration order, contradicting that same invariant.
60
+ *
61
+ * Exported because `resolveCollection` derives `sourceRoots` from the declared
62
+ * specs and must use this identical ordering; a second sort would be a second
63
+ * definition of "canonical".
64
+ */
65
+ export function orderedPathSpecs(specs) {
66
+ return specs.map(toPathSpec).sort((a, b) => {
67
+ const [ja, jb] = [JSON.stringify(a), JSON.stringify(b)];
68
+ return ja < jb ? -1 : ja > jb ? 1 : 0;
69
+ });
70
+ }
71
+ /**
72
+ * Where a declared `path` source lives on disk: absolute as written, otherwise
73
+ * relative to the DECLARING config's directory — never to ambient
74
+ * `process.cwd()`.
75
+ *
76
+ * One definition, because this expression *is* the rule for where a declared
77
+ * source lives, which is the single piece of knowledge this module exists to
78
+ * own. A caller that needs a source's root directory (rather than its files)
79
+ * calls this rather than restating it.
80
+ */
81
+ export function resolveSpecPath(configDir, spec) {
82
+ return isAbsolute(spec.path) ? spec.path : resolve(configDir, spec.path);
83
+ }
84
+ /**
85
+ * Resolve a declared source SET to a canonically-ordered list of metadata files.
86
+ *
87
+ * The full result — each entry's `.file` AND its `.spec` — is a pure function
88
+ * of the SET of `specs`: permuting `specs` cannot change the output. One thing
89
+ * makes that hold: the specs are processed in CONTENT order
90
+ * (`JSON.stringify(spec)`, ascending) rather than declared order, so both the
91
+ * emitted file order and the spec attributed to a file overlapping two specs
92
+ * are decided by content alone. Declared order carries no information anywhere
93
+ * in this function.
94
+ *
95
+ * Within one directory spec the file order is `listMetadataFiles`'s — files at
96
+ * a level, then that level's subdirectories, depth-first. That is deliberately
97
+ * NOT a flat sort of absolute paths: see the file header, and
98
+ * `test/source-order.test.ts`.
99
+ *
100
+ * Only `path` specs resolve in phase 1: a directory is walked recursively, a
101
+ * file is taken as-is. An unresolvable `path` throws `ERR_SOURCE_UNRESOLVED`
102
+ * rather than silently contributing nothing; `resource`/`package` specs throw
103
+ * `ERR_SOURCE_KIND_UNSUPPORTED`.
104
+ *
105
+ * @param configDir absolute directory of the declaring config (the parent of
106
+ * `.metaobjects/`) — relative `path` specs resolve against it, never against
107
+ * ambient `process.cwd()`.
108
+ */
109
+ export async function resolveSources(configDir, specs) {
110
+ // Kind-validated and content-ordered in one pass — see `orderedPathSpecs`.
111
+ const ordered = orderedPathSpecs(specs);
112
+ // Insertion order IS output order — a Map preserves it, so the per-spec walk
113
+ // order above survives to the caller. First contributor wins a shared file,
114
+ // which is content-determined because `ordered` is.
115
+ const byFile = new Map();
116
+ for (const spec of ordered) {
117
+ const target = resolveSpecPath(configDir, spec);
118
+ const stats = await stat(target).catch(() => undefined);
119
+ if (stats === undefined) {
120
+ throw new ParseError(`source path "${spec.path}" does not exist (resolved to ${target}, relative to ${configDir})`, { code: "ERR_SOURCE_UNRESOLVED", source: codeSource("resolveSources") });
121
+ }
122
+ const found = stats.isDirectory() ? await listMetadataFiles(target) : [target];
123
+ for (const file of found) {
124
+ if (!byFile.has(file))
125
+ byFile.set(file, spec);
126
+ }
127
+ }
128
+ return [...byFile].map(([file, spec]) => ({ file, spec }));
129
+ }
130
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../src/sources.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/sdk",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "Workspace helpers and agent-docs utilities for MetaObjects projects.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -56,7 +56,7 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@metaobjectsdev/metadata": "0.23.1",
59
+ "@metaobjectsdev/metadata": "0.24.0",
60
60
  "zod": "^3.23.0"
61
61
  },
62
62
  "devDependencies": {
@@ -222,7 +222,7 @@ export default defineConfig({
222
222
 
223
223
  Filters live on the generator entry: \`routesFile({ filter: e => e.name !== "AuditLog" })\`
224
224
 
225
- \`.metaobjects/config.json\` is unchanged — it still holds static project state (schema_version, pending_in_git, confidence_thresholds). Generator wiring belongs in \`metaobjects.config.ts\` so TypeScript can type-check the imports.
225
+ \`.metaobjects/config.json\` holds static project state (schema_version, pending_in_git, confidence_thresholds) **and \`sources\` — the set of places your metadata comes from** (see "Where metadata comes from" below). Generator wiring belongs in \`metaobjects.config.ts\` so TypeScript can type-check the imports.
226
226
 
227
227
  ## Generated hooks + grids (TanStack)
228
228
 
@@ -503,13 +503,35 @@ metaobjects/
503
503
  └── _pending/<pkg>.json proposed packages awaiting review
504
504
 
505
505
  .metaobjects/
506
- ├── config.json static project state
506
+ ├── config.json static project state + \`sources\`
507
507
  ├── migrations/ written by meta migrate
508
508
  └── .gen-state/ codegen merge base (gitignored)
509
509
 
510
510
  metaobjects.config.ts generator wiring (committed)
511
511
  \`\`\`
512
512
 
513
+ ### Where metadata comes from — the \`sources\` set
514
+
515
+ **\`metaobjects/\` is the DEFAULT VALUE of \`sources\`, not a requirement.** When \`sources\` is absent or empty in \`.metaobjects/config.json\`, it takes that default — the \`metaobjects/\` directory beside the \`.metaobjects/\` folder holding the config. \`meta init\` scaffolds \`"sources": []\`, so a project that does nothing takes the default.
516
+
517
+ Do NOT assume that directory exists. A project may declare \`sources\` and put its metadata anywhere — a sibling module, a shared model repository, a single file — and need not have a directory of that name at all:
518
+
519
+ \`\`\`json
520
+ {
521
+ "schema_version": 1,
522
+ "sources": [
523
+ { "path": "../model/src/main/resources/metadata" },
524
+ { "path": "metaobjects" }
525
+ ]
526
+ }
527
+ \`\`\`
528
+
529
+ \`meta gen\`, \`meta migrate\`, \`meta verify\`, \`meta docs\` and \`meta export\` all read exactly that set, so pointing \`sources\` elsewhere moves every command together. A \`path\` is read **in place and never installed** or copied.
530
+
531
+ \`sources\` is read by **all four CLI surfaces** — the Node \`meta\` CLI, \`dotnet meta\` (C#), \`metaobjects\` (Python) and \`metaobjects:generate\` (Java and Kotlin, via Maven). Each resolves the same files from the same declaration.
532
+
533
+ **\`sources\` is a set, not an ordered list** — resolution is order-independent, so declaration order never changes what loads. \`config.json\` also rejects unknown top-level keys, so a misspelled key is an error rather than a silently ignored one.
534
+
513
535
  ## Worked example
514
536
 
515
537
  \`\`\`json