@metaobjectsdev/metadata 0.24.5 → 1.0.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/dist/attr-schema-validate.js +15 -3
- package/dist/attr-schema-validate.js.map +1 -1
- package/dist/core/attr/attr-definition.embedded.js +1 -1
- package/dist/core/attr/attr-definition.embedded.js.map +1 -1
- package/dist/core/field/field-definition.embedded.js +1 -1
- package/dist/core/field/field-definition.embedded.js.map +1 -1
- package/dist/core/object/meta-object.d.ts +2 -0
- package/dist/core/object/meta-object.d.ts.map +1 -1
- package/dist/core/object/meta-object.js +20 -0
- package/dist/core/object/meta-object.js.map +1 -1
- package/dist/core/object/object-definition.embedded.js +1 -1
- package/dist/core/object/object-definition.embedded.js.map +1 -1
- package/dist/core/relationship/relationship-definition.embedded.js +1 -1
- package/dist/core/relationship/relationship-definition.embedded.js.map +1 -1
- package/dist/core/validator/validator-definition.embedded.js +1 -1
- package/dist/core/validator/validator-definition.embedded.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/naming.d.ts +107 -0
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +155 -14
- package/dist/naming.js.map +1 -1
- package/dist/parser-core.d.ts.map +1 -1
- package/dist/parser-core.js +130 -11
- package/dist/parser-core.js.map +1 -1
- package/dist/persistence/origin/origin-definition.embedded.js +1 -1
- package/dist/persistence/origin/origin-definition.embedded.js.map +1 -1
- package/dist/persistence/source/source-definition.embedded.js +1 -1
- package/dist/persistence/source/source-definition.embedded.js.map +1 -1
- package/dist/presentation/layout/layout-definition.embedded.js +1 -1
- package/dist/presentation/layout/layout-definition.embedded.js.map +1 -1
- package/dist/presentation/view/view-definition.embedded.js +1 -1
- package/dist/presentation/view/view-definition.embedded.js.map +1 -1
- package/dist/registry-coverage.d.ts +0 -6
- package/dist/registry-coverage.d.ts.map +1 -1
- package/dist/registry-coverage.js +25 -1
- package/dist/registry-coverage.js.map +1 -1
- package/dist/registry-manifest.d.ts +13 -6
- package/dist/registry-manifest.d.ts.map +1 -1
- package/dist/registry-manifest.js +6 -5
- package/dist/registry-manifest.js.map +1 -1
- package/dist/retired-vocabulary.d.ts +23 -0
- package/dist/retired-vocabulary.d.ts.map +1 -1
- package/dist/retired-vocabulary.js +99 -1
- package/dist/retired-vocabulary.js.map +1 -1
- package/dist/shared/meta-data.d.ts.map +1 -1
- package/dist/shared/meta-data.js +23 -1
- package/dist/shared/meta-data.js.map +1 -1
- package/dist/template/template-definition.embedded.js +1 -1
- package/dist/template/template-definition.embedded.js.map +1 -1
- package/dist/vocabulary-catalog.d.ts +49 -0
- package/dist/vocabulary-catalog.d.ts.map +1 -0
- package/dist/vocabulary-catalog.js +80 -0
- package/dist/vocabulary-catalog.js.map +1 -0
- package/package.json +1 -1
- package/src/attr-schema-validate.ts +15 -3
- package/src/core/attr/attr-definition.embedded.ts +1 -1
- package/src/core/field/field-definition.embedded.ts +1 -1
- package/src/core/object/meta-object.ts +22 -0
- package/src/core/object/object-definition.embedded.ts +1 -1
- package/src/core/relationship/relationship-definition.embedded.ts +1 -1
- package/src/core/validator/validator-definition.embedded.ts +1 -1
- package/src/errors.ts +11 -1
- package/src/index.ts +8 -1
- package/src/naming.ts +164 -18
- package/src/parser-core.ts +142 -12
- package/src/persistence/origin/origin-definition.embedded.ts +1 -1
- package/src/persistence/source/source-definition.embedded.ts +1 -1
- package/src/presentation/layout/layout-definition.embedded.ts +1 -1
- package/src/presentation/view/view-definition.embedded.ts +1 -1
- package/src/registry-coverage.ts +25 -3
- package/src/registry-manifest.ts +9 -8
- package/src/retired-vocabulary.ts +107 -1
- package/src/shared/meta-data.ts +23 -3
- package/src/template/template-definition.embedded.ts +1 -1
- package/src/vocabulary-catalog.ts +133 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// server/typescript/packages/metadata/src/retired-vocabulary.ts
|
|
2
2
|
//
|
|
3
|
-
// Vocabulary this project USED to register
|
|
3
|
+
// Vocabulary this project USED to register — or used to READ and document without ever
|
|
4
|
+
// registering — and what to do instead.
|
|
4
5
|
//
|
|
5
6
|
// WHY THIS EXISTS (#337). Under ADR-0023 the registry is strict and sealed, so retired
|
|
6
7
|
// vocabulary fails the load — correctly. But the message it failed with said the attribute
|
|
@@ -45,6 +46,15 @@ export interface RetirementNote {
|
|
|
45
46
|
readonly replacedBy?: string;
|
|
46
47
|
/** Repo-relative migration guide. */
|
|
47
48
|
readonly migration?: string;
|
|
49
|
+
/**
|
|
50
|
+
* True when `meta upgrade --apply` can make this edit itself.
|
|
51
|
+
*
|
|
52
|
+
* Carried on the NOTE, not just the entry, because the diagnostic is the only place an
|
|
53
|
+
* adopter meets a retirement — and the difference between "run one command" and "this is
|
|
54
|
+
* a judgement call" is the single most useful thing to tell them at that moment. Absent
|
|
55
|
+
* ⇒ the human decides (see `VocabularyRewrite`).
|
|
56
|
+
*/
|
|
57
|
+
readonly automated?: boolean;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
/**
|
|
@@ -97,6 +107,7 @@ export interface RetiredEntry extends RetirementNote {
|
|
|
97
107
|
|
|
98
108
|
const REQUIREMENT_MIGRATION = "docs/features/migrations/verified-by-retirement.md";
|
|
99
109
|
const RETIRED_STATUS_MIGRATION = "docs/features/migrations/retired-status-restore.md";
|
|
110
|
+
const EMIT_ATTR_MIGRATION = "docs/features/migrations/emit-attrs-to-generator-config.md";
|
|
100
111
|
|
|
101
112
|
export const RETIRED_VOCABULARY: readonly RetiredEntry[] = [
|
|
102
113
|
// ── 0.24.0: `@violation` is renamed `@counterexample` ──
|
|
@@ -244,8 +255,80 @@ export const RETIRED_VOCABULARY: readonly RetiredEntry[] = [
|
|
|
244
255
|
// caught this exists to prevent.
|
|
245
256
|
rewrite: { kind: "dropAttr" },
|
|
246
257
|
},
|
|
258
|
+
// ── FR-040: the @emit* family leaves the model (0.25.0) ──
|
|
259
|
+
//
|
|
260
|
+
// Five attributes — @emitRoutes, @emitTanstack, @emitForm, @emitGrid, @emitAngular — were
|
|
261
|
+
// READ by the TypeScript generators as per-entity kill switches, and documented as THE way
|
|
262
|
+
// to suppress an artifact, but they were never REGISTERED by any provider. So they worked
|
|
263
|
+
// under `meta gen`, which loads open, and failed `meta verify`, which loads strict: a
|
|
264
|
+
// mechanism we documented broke the drift gate we documented beside it.
|
|
265
|
+
//
|
|
266
|
+
// They are retired rather than registered. codegen-ts's own constants file already called
|
|
267
|
+
// them "NOT metamodel vocabulary — they tune codegen, not the model" and then read them off
|
|
268
|
+
// metadata anyway; that contradiction is the defect, and ADR-0023 §2 names the class.
|
|
269
|
+
// Registering them instead would move `metamodelVersion` and oblige four other ports to
|
|
270
|
+
// carry a TypeScript-only generator flag none of them will ever read.
|
|
271
|
+
//
|
|
272
|
+
// WHAT REPLACED THEM IS CONFIGURATION, NOT VOCABULARY, so `replacedBy` names a mechanism
|
|
273
|
+
// rather than an attribute — the only entries here that do. The rule is one sentence:
|
|
274
|
+
// decide per generator what you consume, wire only the generators whose output you import,
|
|
275
|
+
// and narrow one with its own `filter`.
|
|
276
|
+
//
|
|
277
|
+
// `dropAttr` is safe for all five: after this release nothing reads them, so removing one
|
|
278
|
+
// cannot change what is emitted. What it DOES change is what `meta gen` emitted BEFORE —
|
|
279
|
+
// an adopter who suppressed an artifact this way now gets that artifact — which is why the
|
|
280
|
+
// run also warns by name, rather than letting the file appear with no explanation.
|
|
281
|
+
{
|
|
282
|
+
type: "object", subType: "*", attr: "emitRoutes",
|
|
283
|
+
since: "0.25.0",
|
|
284
|
+
why: "it was never registered vocabulary, so the opt-out we documented passed `meta gen` " +
|
|
285
|
+
"and failed `meta verify`",
|
|
286
|
+
replacedBy: "the routes generator's own `filter` — routesFile({ filter: (e) => … })",
|
|
287
|
+
migration: EMIT_ATTR_MIGRATION,
|
|
288
|
+
rewrite: { kind: "dropAttr" },
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
type: "object", subType: "*", attr: "emitTanstack",
|
|
292
|
+
since: "0.25.0",
|
|
293
|
+
why: "it was never registered vocabulary, so the opt-out we documented passed `meta gen` " +
|
|
294
|
+
"and failed `meta verify`",
|
|
295
|
+
replacedBy: "the `filter` option on tanstackQuery() / tanstackGrid() / tanstackGridHook()",
|
|
296
|
+
migration: EMIT_ATTR_MIGRATION,
|
|
297
|
+
rewrite: { kind: "dropAttr" },
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
type: "object", subType: "*", attr: "emitForm",
|
|
301
|
+
since: "0.25.0",
|
|
302
|
+
why: "it was never registered vocabulary, and its own doc comment described it backwards " +
|
|
303
|
+
"— as opt-IN via `true`, while the code implemented opt-OUT via `false`",
|
|
304
|
+
replacedBy: "formFile({ filter: (e) => … })",
|
|
305
|
+
migration: EMIT_ATTR_MIGRATION,
|
|
306
|
+
rewrite: { kind: "dropAttr" },
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
type: "object", subType: "*", attr: "emitAngular",
|
|
310
|
+
since: "0.25.0",
|
|
311
|
+
why: "it was never registered vocabulary, and unlike its four siblings it was not even a " +
|
|
312
|
+
"named constant — just a bare string literal in four generators",
|
|
313
|
+
replacedBy: "the `filter` option on the Angular generators",
|
|
314
|
+
migration: EMIT_ATTR_MIGRATION,
|
|
315
|
+
rewrite: { kind: "dropAttr" },
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
// The one that is opt-IN rather than opt-out, so `filter` cannot replace it: a filter is
|
|
319
|
+
// ANDed with the built-in gates and can only ever narrow. It became a generator OPTION,
|
|
320
|
+
// which must be passed to BOTH grid generators — they emit a matched pair of files.
|
|
321
|
+
type: "object", subType: "*", attr: "emitGrid",
|
|
322
|
+
since: "0.25.0",
|
|
323
|
+
why: "it was never registered vocabulary, and being an opt-IN it could not be expressed " +
|
|
324
|
+
"by a generator filter, which only ever narrows",
|
|
325
|
+
replacedBy: "the `tphSubtypeGrids` option on BOTH tanstackGrid() and tanstackGridHook()",
|
|
326
|
+
migration: EMIT_ATTR_MIGRATION,
|
|
327
|
+
rewrite: { kind: "dropAttr" },
|
|
328
|
+
},
|
|
247
329
|
];
|
|
248
330
|
|
|
331
|
+
|
|
249
332
|
/**
|
|
250
333
|
* True when `entry` governs `typeKey` (`"<type>.<subType>"`).
|
|
251
334
|
*
|
|
@@ -267,6 +350,7 @@ export function note(entry: RetiredEntry): RetirementNote {
|
|
|
267
350
|
why: entry.why,
|
|
268
351
|
...(entry.replacedBy !== undefined ? { replacedBy: entry.replacedBy } : {}),
|
|
269
352
|
...(entry.migration !== undefined ? { migration: entry.migration } : {}),
|
|
353
|
+
...(entry.rewrite !== undefined ? { automated: true } : {}),
|
|
270
354
|
};
|
|
271
355
|
}
|
|
272
356
|
|
|
@@ -321,3 +405,25 @@ export function retirementHint(n: RetirementNote): string {
|
|
|
321
405
|
if (n.migration !== undefined) parts.push(`Migration: ${n.migration}`);
|
|
322
406
|
return `${parts.join(". ")}.`;
|
|
323
407
|
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* The ADR-0009 `suggestions[]` for a retirement: what to DO, as distinct from
|
|
411
|
+
* `retirementHint`, which says what happened.
|
|
412
|
+
*
|
|
413
|
+
* WHY THIS EXISTS. A caller that knows only "the load failed with ERR_UNKNOWN_ATTR" gives
|
|
414
|
+
* the generic three exits — register the attr, stash it in an `attr.properties` bag, or
|
|
415
|
+
* re-run with `--lax`. For a TYPO that is exactly right. For a RETIREMENT the middle one is
|
|
416
|
+
* actively harmful: the properties bag is exempt from the strict-attr check, so it loads,
|
|
417
|
+
* and the value then sits there reaching nothing. The adopter gets a green `meta verify`
|
|
418
|
+
* over metadata that no longer means what they wrote — a loud, correct failure converted
|
|
419
|
+
* into a quiet, wrong pass. So a retirement supplies its own exits and the caller prints
|
|
420
|
+
* those instead of guessing.
|
|
421
|
+
*/
|
|
422
|
+
export function retirementSuggestions(n: RetirementNote): string[] {
|
|
423
|
+
const out: string[] = [];
|
|
424
|
+
if (n.automated === true) out.push("Run `meta upgrade --apply` to make this edit for you.");
|
|
425
|
+
if (n.replacedBy !== undefined) out.push(`Use ${n.replacedBy} instead.`);
|
|
426
|
+
if (n.migration !== undefined) out.push(`Migration guide: ${n.migration}`);
|
|
427
|
+
// Never the `attr.properties` bag: it would load, and mean nothing. See above.
|
|
428
|
+
return out;
|
|
429
|
+
}
|
package/src/shared/meta-data.ts
CHANGED
|
@@ -497,14 +497,34 @@ export abstract class MetaData {
|
|
|
497
497
|
// Track which of our own children matched (overrode) a super child position.
|
|
498
498
|
const appendQueue: MetaData[] = [];
|
|
499
499
|
|
|
500
|
+
// Indices already taken by an own child in THIS pass. Without it, an own child
|
|
501
|
+
// written into `result[idx]` stays visible to the next own sibling's scan, so a
|
|
502
|
+
// second own child sharing the same (type, name) matches its own sibling and
|
|
503
|
+
// overwrites it. `extends` decides what a child overrides; a sibling is not a super.
|
|
504
|
+
// The append queue already closed the other branch — a non-shadowing own child is
|
|
505
|
+
// deferred so it cannot be matched later — and this closes the branch it left open.
|
|
506
|
+
//
|
|
507
|
+
// The shape is a write-through entity (two unnamed `source.rdb` children, primary +
|
|
508
|
+
// replica) whose base ALSO declares a source: the entity's own primary was silently
|
|
509
|
+
// dropped, and `primaryRdbSource` reads `children()`.
|
|
510
|
+
// A flag array, not a Set: `result` does not grow until after this loop, so the length
|
|
511
|
+
// is known, and this is the memoized effective-children path walked for every node.
|
|
512
|
+
// One small allocation, index access instead of hashing, and no per-child closure.
|
|
513
|
+
const claimed = new Uint8Array(result.length);
|
|
500
514
|
for (const ownChild of this._children) {
|
|
501
515
|
// Find the index in result that has the same (type, name).
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
516
|
+
let idx = -1;
|
|
517
|
+
for (let i = 0; i < result.length; i++) {
|
|
518
|
+
const sc = result[i] as MetaData;
|
|
519
|
+
if (claimed[i] === 0 && sc.type === ownChild.type && sc.name === ownChild.name) {
|
|
520
|
+
idx = i;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
505
524
|
if (idx !== -1) {
|
|
506
525
|
// Replace the super child with our own (in-place override).
|
|
507
526
|
result[idx] = ownChild;
|
|
527
|
+
claimed[idx] = 1;
|
|
508
528
|
} else {
|
|
509
529
|
// No matching super child — will be appended at the end.
|
|
510
530
|
appendQueue.push(ownChild);
|
|
@@ -12,7 +12,7 @@ export const TEMPLATE_DEFINITION: ProviderDefinition = {
|
|
|
12
12
|
{
|
|
13
13
|
"type": "template",
|
|
14
14
|
"subType": "base",
|
|
15
|
-
"description": "Abstract base template — the shared root subtype for the fourth pillar (FR-004, ADR-0011). A template is a typed payload bound to either a rendered text artifact (prompt/output) or a tool-call envelope. The base carries no attrs of its own; concrete subtypes add their reference + governance attrs.",
|
|
15
|
+
"description": "Abstract base template — the shared root subtype for the fourth pillar (FR-004, ADR-0011). A template is a typed payload bound to either a rendered text artifact (prompt/output) or a tool-call envelope. The base carries no attrs of its own; concrete subtypes add their reference + governance attrs. Not authored directly: a `template.base` node fails to load (ERR_ABSTRACT_SUBTYPE_AUTHORED) — this subtype is a registry anchor concrete subtypes inherit from, never a node in a document.",
|
|
16
16
|
"rules": "A single MetaTemplate node class backs every template subtype (no subType→class dispatch); the subtype selects which reserved-attr set applies. @format (a closed-enum ATTRIBUTE, never a subtype) drives the render engine's escaper/whitespace behavior, so a new format costs one escaper + one enum value rather than a new subtype + cross-language port."
|
|
17
17
|
},
|
|
18
18
|
{
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// #357 — "what can I author HERE?", which is NOT the question the registry manifest answers.
|
|
2
|
+
//
|
|
3
|
+
// `buildRegistryManifest` answers "what must all five ports byte-match?" and deliberately
|
|
4
|
+
// carves rows OUT of its answer: the `metadata.base` inheritance anchor, and the 13
|
|
5
|
+
// TS-web-presentation `view.*` controls, which stay REGISTERED in TypeScript (the loader
|
|
6
|
+
// must accept an authored `view.dropdown`) but are deregistered in C# and Python and
|
|
7
|
+
// excluded from the shared canonical everywhere. That carve-out is correct and documented
|
|
8
|
+
// (fixtures/registry-conformance/README.md, B-2).
|
|
9
|
+
//
|
|
10
|
+
// `meta types` — the vocabulary search the generated `AGENTS.md` and the
|
|
11
|
+
// `metaobjects-authoring` skill both make STEP 1 of the authoring procedure — was built on
|
|
12
|
+
// that same function, so the answer it gave an author was the cross-port contract with
|
|
13
|
+
// this port's own vocabulary removed: 2 of the 15 registered `view.*` subtypes, with the
|
|
14
|
+
// other 13 reported exactly as a genuine typo is. Following the documented procedure, an
|
|
15
|
+
// author correctly concluded `view.text` does not exist.
|
|
16
|
+
//
|
|
17
|
+
// One function cannot answer both questions, so there are two. This one enumerates every
|
|
18
|
+
// (type, subType) the registry accepts and MARKS the rows the cross-port manifest omits,
|
|
19
|
+
// which is the honest way to surface what the carve-out means: not "missing" but
|
|
20
|
+
// "TypeScript-only".
|
|
21
|
+
|
|
22
|
+
import type { TypeDefinition, TypeRegistry } from "./registry.js";
|
|
23
|
+
import {
|
|
24
|
+
type ManifestAttr,
|
|
25
|
+
type ManifestChild,
|
|
26
|
+
classifyPerTypeAttr,
|
|
27
|
+
INCLUDED,
|
|
28
|
+
compareStrings,
|
|
29
|
+
sortedAttrs,
|
|
30
|
+
sortedChildren,
|
|
31
|
+
METAMODEL_VERSION,
|
|
32
|
+
} from "./registry-manifest.js";
|
|
33
|
+
import { classifyTypeSubType } from "./registry-manifest-exclusions.js";
|
|
34
|
+
import { SUBTYPE_BASE } from "./shared/base-types.js";
|
|
35
|
+
|
|
36
|
+
/** One registered (type, subType), as an AUTHOR sees it. */
|
|
37
|
+
export interface VocabularyType {
|
|
38
|
+
type: string;
|
|
39
|
+
subType: string;
|
|
40
|
+
description: string;
|
|
41
|
+
rules?: string;
|
|
42
|
+
example?: string;
|
|
43
|
+
whenToUse?: string;
|
|
44
|
+
attrs: ManifestAttr[];
|
|
45
|
+
children: ManifestChild[];
|
|
46
|
+
parents?: string[];
|
|
47
|
+
/**
|
|
48
|
+
* False when the cross-port manifest carves this row out — vocabulary THIS port
|
|
49
|
+
* accepts that the shared five-port contract does not carry. An author can use it
|
|
50
|
+
* here; a sibling port may not have it.
|
|
51
|
+
*/
|
|
52
|
+
crossPort: boolean;
|
|
53
|
+
/** Why it is not cross-port (the carve-out's declared category). Absent when it is. */
|
|
54
|
+
portPrivateReason?: string;
|
|
55
|
+
/**
|
|
56
|
+
* True for a type's shared ROOT subtype (`<type>.base`). Attrs and child rules
|
|
57
|
+
* registered there apply to every subtype of the type (see constraint-merge.ts), so a
|
|
58
|
+
* `base` row is a summary of the whole family rather than a control an author picks.
|
|
59
|
+
* Deliberately NOT called "abstract": nothing in the registry records abstractness, and
|
|
60
|
+
* some `base` rows (`attr.base`) are the authorable polymorphic form.
|
|
61
|
+
*/
|
|
62
|
+
sharedRoot: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Every (type, subType) the registry accepts, plus what every node accepts. */
|
|
66
|
+
export interface VocabularyCatalog {
|
|
67
|
+
metamodelVersion: string;
|
|
68
|
+
types: VocabularyType[];
|
|
69
|
+
/** Attrs accepted on EVERY node, whatever its type (`@title`, `@description`, …). */
|
|
70
|
+
commonAttrs: ManifestAttr[];
|
|
71
|
+
defaultSubTypes: Record<string, string>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function toVocabularyType(def: TypeDefinition): VocabularyType {
|
|
75
|
+
const { type, subType } = def.typeId;
|
|
76
|
+
const reason = classifyTypeSubType(type, subType);
|
|
77
|
+
const out: VocabularyType = {
|
|
78
|
+
type,
|
|
79
|
+
subType,
|
|
80
|
+
description: def.description,
|
|
81
|
+
...(def.rules !== undefined ? { rules: def.rules } : {}),
|
|
82
|
+
...(def.example !== undefined ? { example: def.example } : {}),
|
|
83
|
+
...(def.whenToUse !== undefined ? { whenToUse: def.whenToUse } : {}),
|
|
84
|
+
// Same attr filter the manifest applies — the carved-out per-type attr names are
|
|
85
|
+
// bare structural keywords (`isArray`, `extends`) that `@`-prefix to ERR_RESERVED_ATTR,
|
|
86
|
+
// OO-port native bindings, and the `description` commonAttr re-registered per type.
|
|
87
|
+
// Listing any of them here would teach metadata the loader rejects. The manifest's
|
|
88
|
+
// FR-024 requiredness override is deliberately NOT applied: that freezes the
|
|
89
|
+
// CROSS-PORT value during a reference-first rollout, and this answer is about what
|
|
90
|
+
// THIS registry requires of an author right now.
|
|
91
|
+
attrs: sortedAttrs(def.attributes.filter((a) => classifyPerTypeAttr(a.name) === INCLUDED)),
|
|
92
|
+
children: sortedChildren(def.childRules),
|
|
93
|
+
crossPort: reason === undefined,
|
|
94
|
+
...(reason !== undefined ? { portPrivateReason: reason } : {}),
|
|
95
|
+
sharedRoot: subType === SUBTYPE_BASE,
|
|
96
|
+
};
|
|
97
|
+
if (def.parents !== undefined && def.parents.length > 0) {
|
|
98
|
+
out.parents = [...def.parents].sort(compareStrings);
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Build the authoring-facing vocabulary catalog from an assembled registry.
|
|
105
|
+
*
|
|
106
|
+
* The registry must be COMPOSED (`composeRegistry(coreProviders)`), not merely
|
|
107
|
+
* `registerCoreTypes`'d: the db, ui-web and documentation providers each register attrs
|
|
108
|
+
* onto types the core provider declares, so a partially-composed registry reports a type
|
|
109
|
+
* that exists with most of its attributes missing — `field.string` with 6 attrs instead of
|
|
110
|
+
* 16, and no commonAttrs at all.
|
|
111
|
+
*/
|
|
112
|
+
export function buildVocabularyCatalog(registry: TypeRegistry): VocabularyCatalog {
|
|
113
|
+
const types = registry
|
|
114
|
+
.allTypes()
|
|
115
|
+
.map((typeId) => toVocabularyType(registry.find(typeId.type, typeId.subType) as TypeDefinition))
|
|
116
|
+
.sort((a, b) =>
|
|
117
|
+
compareStrings(`${a.type}.${a.subType}`, `${b.type}.${b.subType}`),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const typeNames = Array.from(new Set(types.map((t) => t.type))).sort(compareStrings);
|
|
121
|
+
const defaultSubTypes: Record<string, string> = {};
|
|
122
|
+
for (const typeName of typeNames) {
|
|
123
|
+
const defaultSub = registry.defaultSubTypeOf(typeName);
|
|
124
|
+
if (defaultSub !== undefined) defaultSubTypes[typeName] = defaultSub;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
metamodelVersion: METAMODEL_VERSION,
|
|
129
|
+
types,
|
|
130
|
+
commonAttrs: sortedAttrs(registry.getCommonAttrs()),
|
|
131
|
+
defaultSubTypes,
|
|
132
|
+
};
|
|
133
|
+
}
|