@metaobjectsdev/metadata 0.6.0-rc.1 → 0.7.0-rc.1
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/README.md +54 -3
- package/dist/attr-schema-validate.js +7 -7
- package/dist/attr-schema-validate.js.map +1 -1
- package/dist/core/export-json.d.ts +6 -7
- package/dist/core/export-json.d.ts.map +1 -1
- package/dist/core/export-json.js +15 -17
- package/dist/core/export-json.js.map +1 -1
- package/dist/core/index.d.ts +4 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/parser-yaml.d.ts.map +1 -1
- package/dist/core/parser-yaml.js +13 -4
- package/dist/core/parser-yaml.js.map +1 -1
- package/dist/errors.d.ts +28 -8
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +31 -5
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/json-path.d.ts +8 -0
- package/dist/json-path.d.ts.map +1 -0
- package/dist/json-path.js +39 -0
- package/dist/json-path.js.map +1 -0
- package/dist/loader/meta-data-loader.d.ts +47 -6
- package/dist/loader/meta-data-loader.d.ts.map +1 -1
- package/dist/loader/meta-data-loader.js +106 -8
- package/dist/loader/meta-data-loader.js.map +1 -1
- package/dist/loader/meta-data-source.d.ts +6 -2
- package/dist/loader/meta-data-source.d.ts.map +1 -1
- package/dist/loader/meta-data-source.js +10 -6
- package/dist/loader/meta-data-source.js.map +1 -1
- package/dist/loader/shortcuts.d.ts +9 -0
- package/dist/loader/shortcuts.d.ts.map +1 -0
- package/dist/loader/shortcuts.js +19 -0
- package/dist/loader/shortcuts.js.map +1 -0
- package/dist/loader/sources/directory-source.d.ts +15 -0
- package/dist/loader/sources/directory-source.d.ts.map +1 -0
- package/dist/loader/sources/directory-source.js +80 -0
- package/dist/loader/sources/directory-source.js.map +1 -0
- package/dist/loader/sources/file-source.d.ts +12 -0
- package/dist/loader/sources/file-source.d.ts.map +1 -0
- package/dist/loader/sources/file-source.js +46 -0
- package/dist/loader/sources/file-source.js.map +1 -0
- package/dist/loader/sources/index.d.ts +5 -0
- package/dist/loader/sources/index.d.ts.map +1 -0
- package/dist/loader/sources/index.js +5 -0
- package/dist/loader/sources/index.js.map +1 -0
- package/dist/loader/sources/uri-source.d.ts +9 -0
- package/dist/loader/sources/uri-source.d.ts.map +1 -0
- package/dist/loader/sources/uri-source.js +42 -0
- package/dist/loader/sources/uri-source.js.map +1 -0
- package/dist/loader/validation-passes.d.ts.map +1 -1
- package/dist/loader/validation-passes.js +27 -27
- package/dist/loader/validation-passes.js.map +1 -1
- package/dist/naming.d.ts +15 -2
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +20 -6
- package/dist/naming.js.map +1 -1
- package/dist/parser-core.d.ts +2 -4
- package/dist/parser-core.d.ts.map +1 -1
- package/dist/parser-core.js +111 -42
- package/dist/parser-core.js.map +1 -1
- package/dist/parser-json.d.ts.map +1 -1
- package/dist/parser-json.js +10 -2
- package/dist/parser-json.js.map +1 -1
- package/dist/persistence/source/validate-source-roles.js +2 -2
- package/dist/persistence/source/validate-source-roles.js.map +1 -1
- package/dist/semantic-diff.d.ts +5 -0
- package/dist/semantic-diff.d.ts.map +1 -0
- package/dist/semantic-diff.js +49 -0
- package/dist/semantic-diff.js.map +1 -0
- package/dist/shared/meta-data.d.ts +10 -0
- package/dist/shared/meta-data.d.ts.map +1 -1
- package/dist/shared/meta-data.js +23 -0
- package/dist/shared/meta-data.js.map +1 -1
- package/dist/source.d.ts +68 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +13 -0
- package/dist/source.js.map +1 -0
- package/dist/subtype-rules.js +1 -1
- package/dist/subtype-rules.js.map +1 -1
- package/dist/super-resolve.d.ts +2 -0
- package/dist/super-resolve.d.ts.map +1 -1
- package/dist/super-resolve.js +1 -1
- package/dist/super-resolve.js.map +1 -1
- package/package.json +1 -1
- package/src/attr-schema-validate.ts +7 -7
- package/src/core/export-json.ts +15 -18
- package/src/core/index.ts +8 -2
- package/src/core/parser-yaml.ts +15 -4
- package/src/errors.ts +42 -8
- package/src/index.ts +28 -3
- package/src/json-path.ts +46 -0
- package/src/loader/meta-data-loader.ts +148 -10
- package/src/loader/meta-data-source.ts +10 -6
- package/src/loader/shortcuts.ts +31 -0
- package/src/loader/sources/directory-source.ts +90 -0
- package/src/{core → loader/sources}/file-source.ts +3 -3
- package/src/loader/sources/index.ts +6 -0
- package/src/loader/sources/uri-source.ts +44 -0
- package/src/loader/validation-passes.ts +28 -24
- package/src/naming.ts +39 -7
- package/src/parser-core.ts +113 -43
- package/src/parser-json.ts +11 -2
- package/src/persistence/source/validate-source-roles.ts +2 -2
- package/src/semantic-diff.ts +48 -0
- package/src/shared/meta-data.ts +28 -0
- package/src/source.ts +61 -0
- package/src/subtype-rules.ts +1 -1
- package/src/super-resolve.ts +3 -1
- package/src/core/file-meta-data-loader.ts +0 -89
package/src/source.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// server/typescript/packages/metadata/src/source.ts
|
|
2
|
+
//
|
|
3
|
+
// FR5a — Loader error envelope + source-on-node (ADR-0009).
|
|
4
|
+
//
|
|
5
|
+
// Cross-port-aligned types: every metaobjects port emits the same envelope
|
|
6
|
+
// shape so a tool consuming errors from multiple language ports can compare
|
|
7
|
+
// them byte-identically.
|
|
8
|
+
|
|
9
|
+
/** Discriminated union over the provenance variants a metadata node or error
|
|
10
|
+
* can carry. See ADR-0009 §Decision for the canonical shape. */
|
|
11
|
+
export type ErrorSource =
|
|
12
|
+
| { format: "json"; files: [string]; jsonPath: string }
|
|
13
|
+
| { format: "yaml"; files: [string]; jsonPath: string;
|
|
14
|
+
yamlPosition?: { line: number; col: number } }
|
|
15
|
+
| { format: "merged"; files: string[]; jsonPath: string;
|
|
16
|
+
contributors: Contributor[] }
|
|
17
|
+
| { format: "resolved"; files: string[]; jsonPath?: string;
|
|
18
|
+
referrer?: string; target?: string }
|
|
19
|
+
| { format: "database"; dbLocation: { table: string; id: string };
|
|
20
|
+
jsonPath?: string }
|
|
21
|
+
| { format: "code"; caller?: string };
|
|
22
|
+
|
|
23
|
+
export interface Contributor {
|
|
24
|
+
file: string;
|
|
25
|
+
role: "overlay-base" | "overlay-extension" | "extends-base" | "extends-extension";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface NodeContext {
|
|
29
|
+
type?: string;
|
|
30
|
+
subtype?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
fqn?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Envelope shape every loader error conforms to. */
|
|
36
|
+
export interface LoaderError {
|
|
37
|
+
// REQUIRED — conformance-enforced.
|
|
38
|
+
code: string;
|
|
39
|
+
message: string;
|
|
40
|
+
source: ErrorSource;
|
|
41
|
+
// RECOMMENDED — optional per ADR-0009 §What ports are NOT required to do.
|
|
42
|
+
suggestions?: string[];
|
|
43
|
+
fixture?: string;
|
|
44
|
+
node?: NodeContext;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Warning envelope — same shape as LoaderError but a `WARN_*` code. */
|
|
48
|
+
export interface LoaderWarning {
|
|
49
|
+
code: string;
|
|
50
|
+
message: string;
|
|
51
|
+
source: ErrorSource;
|
|
52
|
+
suggestions?: string[];
|
|
53
|
+
fixture?: string;
|
|
54
|
+
node?: NodeContext;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Canonical synthetic envelope for programmatic / test-constructed nodes.
|
|
58
|
+
* `caller` is an optional human label (e.g. "QueriesTest.makePost"). */
|
|
59
|
+
export function codeSource(caller?: string): ErrorSource {
|
|
60
|
+
return caller ? { format: "code", caller } : { format: "code" };
|
|
61
|
+
}
|
package/src/subtype-rules.ts
CHANGED
|
@@ -35,7 +35,7 @@ function walk(model: MetaData, errors: ParseError[], warnings: string[]): void {
|
|
|
35
35
|
new ParseError(
|
|
36
36
|
`value object '${model.fqn()}' must not have a primary identity ` +
|
|
37
37
|
`(use subType: "entity" for records with identity)`,
|
|
38
|
-
{ code: "ERR_SUBTYPE_RULE_VIOLATION" },
|
|
38
|
+
{ code: "ERR_SUBTYPE_RULE_VIOLATION", source: model.source },
|
|
39
39
|
),
|
|
40
40
|
);
|
|
41
41
|
} else if (
|
package/src/super-resolve.ts
CHANGED
|
@@ -100,6 +100,8 @@ export interface DeferredSuperFailure {
|
|
|
100
100
|
nodeFqn: string;
|
|
101
101
|
/** The raw super ref string that didn't resolve. */
|
|
102
102
|
ref: string;
|
|
103
|
+
/** ADR-0009 provenance envelope of the referencing node (FR5a). */
|
|
104
|
+
source: import("./source.js").ErrorSource;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
/**
|
|
@@ -128,7 +130,7 @@ export function resolveDeferredSupers(root: MetaData): DeferredSuperFailure[] {
|
|
|
128
130
|
// Frozen — ignore; the loader should resolve before freeze.
|
|
129
131
|
}
|
|
130
132
|
} else {
|
|
131
|
-
failures.push({ nodeFqn: node.fqn(), ref: node.superRef });
|
|
133
|
+
failures.push({ nodeFqn: node.fqn(), ref: node.superRef, source: node.source });
|
|
132
134
|
}
|
|
133
135
|
});
|
|
134
136
|
return failures;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// FileMetaDataLoader — discovers file-backed MetaDataSources and runs the
|
|
2
|
-
// MetaDataLoader pipeline over them. A UrlMetaDataLoader will slot in the
|
|
3
|
-
// same way later.
|
|
4
|
-
|
|
5
|
-
import type { Stats } from "node:fs";
|
|
6
|
-
import { readdir, stat } from "node:fs/promises";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { MetaDataLoader, type LoadResult } from "../loader/meta-data-loader.js";
|
|
9
|
-
import { FileSource } from "./file-source.js";
|
|
10
|
-
import { parseYaml } from "./parser-yaml.js";
|
|
11
|
-
import type { ParseOptions, ParseResult } from "../parser-core.js";
|
|
12
|
-
import type { MetaDataSource } from "../loader/meta-data-source.js";
|
|
13
|
-
|
|
14
|
-
/** Minimal glob matcher supporting `*` (any chars except `/`) and `**` (any chars). */
|
|
15
|
-
function matchSimpleGlob(pattern: string, value: string): boolean {
|
|
16
|
-
const regexStr = pattern
|
|
17
|
-
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
18
|
-
.replace(/\*\*/g, "::DOUBLESTAR::")
|
|
19
|
-
.replace(/\*/g, "[^/]*")
|
|
20
|
-
.replace(/::DOUBLESTAR::/g, ".*");
|
|
21
|
-
return new RegExp(`^${regexStr}$`).test(value);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class FileMetaDataLoader extends MetaDataLoader {
|
|
25
|
-
/** Adds YAML parsing on top of the base loader's JSON-only `parseSource`. */
|
|
26
|
-
protected override parseSource(
|
|
27
|
-
content: string,
|
|
28
|
-
source: MetaDataSource,
|
|
29
|
-
parseOpts: ParseOptions,
|
|
30
|
-
): ParseResult {
|
|
31
|
-
if (source.format === "yaml") {
|
|
32
|
-
return parseYaml(content, parseOpts);
|
|
33
|
-
}
|
|
34
|
-
return super.parseSource(content, source, parseOpts);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Load every `.json` / `.yaml` / `.yml` file in a directory (non-recursive),
|
|
39
|
-
* in deterministic ordinal filename order.
|
|
40
|
-
* @param opts.exclude glob patterns (relative to dir) to skip — `*` / `**`.
|
|
41
|
-
*/
|
|
42
|
-
async loadDirectory(dir: string, opts?: { exclude?: string[] }): Promise<LoadResult> {
|
|
43
|
-
let entries: string[];
|
|
44
|
-
try {
|
|
45
|
-
// Sorted for deterministic multi-file load order: the overlay merge is
|
|
46
|
-
// order-sensitive (last-writer-wins on attr conflicts), so the scan must
|
|
47
|
-
// not depend on filesystem readdir order. Ordinal filename sort is
|
|
48
|
-
// reproducible across the Java/Python/C# ports.
|
|
49
|
-
entries = (await readdir(dir)).sort();
|
|
50
|
-
} catch (err) {
|
|
51
|
-
// Surface the I/O failure as a collected error via the empty-source path.
|
|
52
|
-
const emptyResult = await this.load([]);
|
|
53
|
-
return {
|
|
54
|
-
...emptyResult,
|
|
55
|
-
errors: [
|
|
56
|
-
new Error(`loadDirectory: cannot read ${dir}: ${(err as Error).message}`),
|
|
57
|
-
...emptyResult.errors,
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const excludes = opts?.exclude ?? [];
|
|
63
|
-
const paths: string[] = [];
|
|
64
|
-
for (const entry of entries) {
|
|
65
|
-
const lower = entry.toLowerCase();
|
|
66
|
-
if (!lower.endsWith(".json") && !lower.endsWith(".yaml") && !lower.endsWith(".yml")) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const filePath = join(dir, entry);
|
|
70
|
-
let statResult: Stats;
|
|
71
|
-
try {
|
|
72
|
-
statResult = await stat(filePath);
|
|
73
|
-
} catch {
|
|
74
|
-
// Entry vanished between readdir and stat (TOCTOU) or is not accessible.
|
|
75
|
-
// Skip it rather than breaking the no-throw contract of loadDirectory.
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (!statResult.isFile()) continue;
|
|
79
|
-
if (excludes.some((p) => matchSimpleGlob(p, entry))) continue;
|
|
80
|
-
paths.push(filePath);
|
|
81
|
-
}
|
|
82
|
-
return this.loadFiles(paths);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Load an explicit list of file paths, in order. */
|
|
86
|
-
async loadFiles(paths: string[]): Promise<LoadResult> {
|
|
87
|
-
return this.load(paths.map((p) => new FileSource(p)));
|
|
88
|
-
}
|
|
89
|
-
}
|