@metaobjectsdev/metadata 0.5.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/LICENSE +189 -0
- package/README.md +29 -0
- package/dist/attr-class-map.d.ts +26 -0
- package/dist/attr-class-map.d.ts.map +1 -0
- package/dist/attr-class-map.js +44 -0
- package/dist/attr-class-map.js.map +1 -0
- package/dist/attr-schema-validate.d.ts +9 -0
- package/dist/attr-schema-validate.d.ts.map +1 -0
- package/dist/attr-schema-validate.js +100 -0
- package/dist/attr-schema-validate.js.map +1 -0
- package/dist/constants.d.ts +208 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +419 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/attr/attr-constants.d.ts +12 -0
- package/dist/core/attr/attr-constants.d.ts.map +1 -0
- package/dist/core/attr/attr-constants.js +27 -0
- package/dist/core/attr/attr-constants.js.map +1 -0
- package/dist/core/attr/meta-attr-filter.d.ts +10 -0
- package/dist/core/attr/meta-attr-filter.d.ts.map +1 -0
- package/dist/core/attr/meta-attr-filter.js +56 -0
- package/dist/core/attr/meta-attr-filter.js.map +1 -0
- package/dist/core/attr/meta-attr-properties.d.ts +9 -0
- package/dist/core/attr/meta-attr-properties.d.ts.map +1 -0
- package/dist/core/attr/meta-attr-properties.js +22 -0
- package/dist/core/attr/meta-attr-properties.js.map +1 -0
- package/dist/core/attr/meta-attr-stringarray.d.ts +9 -0
- package/dist/core/attr/meta-attr-stringarray.d.ts.map +1 -0
- package/dist/core/attr/meta-attr-stringarray.js +29 -0
- package/dist/core/attr/meta-attr-stringarray.js.map +1 -0
- package/dist/core/attr/meta-attr.d.ts +37 -0
- package/dist/core/attr/meta-attr.d.ts.map +1 -0
- package/dist/core/attr/meta-attr.js +97 -0
- package/dist/core/attr/meta-attr.js.map +1 -0
- package/dist/core/export-json.d.ts +29 -0
- package/dist/core/export-json.d.ts.map +1 -0
- package/dist/core/export-json.js +45 -0
- package/dist/core/export-json.js.map +1 -0
- package/dist/core/field/field-constants.d.ts +40 -0
- package/dist/core/field/field-constants.d.ts.map +1 -0
- package/dist/core/field/field-constants.js +66 -0
- package/dist/core/field/field-constants.js.map +1 -0
- package/dist/core/field/field-schema.d.ts +6 -0
- package/dist/core/field/field-schema.d.ts.map +1 -0
- package/dist/core/field/field-schema.js +92 -0
- package/dist/core/field/field-schema.js.map +1 -0
- package/dist/core/field/meta-field.d.ts +50 -0
- package/dist/core/field/meta-field.d.ts.map +1 -0
- package/dist/core/field/meta-field.js +121 -0
- package/dist/core/field/meta-field.js.map +1 -0
- package/dist/core/file-meta-data-loader.d.ts +18 -0
- package/dist/core/file-meta-data-loader.d.ts.map +1 -0
- package/dist/core/file-meta-data-loader.js +81 -0
- package/dist/core/file-meta-data-loader.js.map +1 -0
- package/dist/core/file-source.d.ts +12 -0
- package/dist/core/file-source.d.ts.map +1 -0
- package/dist/core/file-source.js +46 -0
- package/dist/core/file-source.js.map +1 -0
- package/dist/core/identity/identity-constants.d.ts +19 -0
- package/dist/core/identity/identity-constants.d.ts.map +1 -0
- package/dist/core/identity/identity-constants.js +35 -0
- package/dist/core/identity/identity-constants.js.map +1 -0
- package/dist/core/identity/identity-schema.d.ts +6 -0
- package/dist/core/identity/identity-schema.d.ts.map +1 -0
- package/dist/core/identity/identity-schema.js +55 -0
- package/dist/core/identity/identity-schema.js.map +1 -0
- package/dist/core/identity/meta-identity.d.ts +71 -0
- package/dist/core/identity/meta-identity.d.ts.map +1 -0
- package/dist/core/identity/meta-identity.js +129 -0
- package/dist/core/identity/meta-identity.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +11 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/object/meta-object.d.ts +39 -0
- package/dist/core/object/meta-object.d.ts.map +1 -0
- package/dist/core/object/meta-object.js +80 -0
- package/dist/core/object/meta-object.js.map +1 -0
- package/dist/core/object/object-constants.d.ts +5 -0
- package/dist/core/object/object-constants.d.ts.map +1 -0
- package/dist/core/object/object-constants.js +18 -0
- package/dist/core/object/object-constants.js.map +1 -0
- package/dist/core/object/object-schema.d.ts +4 -0
- package/dist/core/object/object-schema.d.ts.map +1 -0
- package/dist/core/object/object-schema.js +5 -0
- package/dist/core/object/object-schema.js.map +1 -0
- package/dist/core/parser-yaml.d.ts +3 -0
- package/dist/core/parser-yaml.d.ts.map +1 -0
- package/dist/core/parser-yaml.js +39 -0
- package/dist/core/parser-yaml.js.map +1 -0
- package/dist/core/query/query-constants.d.ts +20 -0
- package/dist/core/query/query-constants.d.ts.map +1 -0
- package/dist/core/query/query-constants.js +56 -0
- package/dist/core/query/query-constants.js.map +1 -0
- package/dist/core/relationship/find-reference.d.ts +22 -0
- package/dist/core/relationship/find-reference.d.ts.map +1 -0
- package/dist/core/relationship/find-reference.js +29 -0
- package/dist/core/relationship/find-reference.js.map +1 -0
- package/dist/core/relationship/meta-relationship.d.ts +11 -0
- package/dist/core/relationship/meta-relationship.d.ts.map +1 -0
- package/dist/core/relationship/meta-relationship.js +27 -0
- package/dist/core/relationship/meta-relationship.js.map +1 -0
- package/dist/core/relationship/relationship-constants.d.ts +14 -0
- package/dist/core/relationship/relationship-constants.d.ts.map +1 -0
- package/dist/core/relationship/relationship-constants.js +28 -0
- package/dist/core/relationship/relationship-constants.js.map +1 -0
- package/dist/core/relationship/relationship-schema.d.ts +4 -0
- package/dist/core/relationship/relationship-schema.d.ts.map +1 -0
- package/dist/core/relationship/relationship-schema.js +37 -0
- package/dist/core/relationship/relationship-schema.js.map +1 -0
- package/dist/core/validator/meta-validator.d.ts +29 -0
- package/dist/core/validator/meta-validator.d.ts.map +1 -0
- package/dist/core/validator/meta-validator.js +49 -0
- package/dist/core/validator/meta-validator.js.map +1 -0
- package/dist/core/validator/validator-constants.d.ts +11 -0
- package/dist/core/validator/validator-constants.d.ts.map +1 -0
- package/dist/core/validator/validator-constants.js +25 -0
- package/dist/core/validator/validator-constants.js.map +1 -0
- package/dist/core/validator/validator-schema.d.ts +4 -0
- package/dist/core/validator/validator-schema.d.ts.map +1 -0
- package/dist/core/validator/validator-schema.js +38 -0
- package/dist/core/validator/validator-schema.js.map +1 -0
- package/dist/core/yaml-desugar.d.ts +10 -0
- package/dist/core/yaml-desugar.d.ts.map +1 -0
- package/dist/core/yaml-desugar.js +99 -0
- package/dist/core/yaml-desugar.js.map +1 -0
- package/dist/core-attr-schemas.d.ts +22 -0
- package/dist/core-attr-schemas.d.ts.map +1 -0
- package/dist/core-attr-schemas.js +324 -0
- package/dist/core-attr-schemas.js.map +1 -0
- package/dist/core-types.d.ts +20 -0
- package/dist/core-types.d.ts.map +1 -0
- package/dist/core-types.js +225 -0
- package/dist/core-types.js.map +1 -0
- package/dist/data-converter.d.ts +17 -0
- package/dist/data-converter.d.ts.map +1 -0
- package/dist/data-converter.js +117 -0
- package/dist/data-converter.js.map +1 -0
- package/dist/data-type.d.ts +15 -0
- package/dist/data-type.d.ts.map +1 -0
- package/dist/data-type.js +25 -0
- package/dist/data-type.js.map +1 -0
- package/dist/db/db-attr-schemas.d.ts +8 -0
- package/dist/db/db-attr-schemas.d.ts.map +1 -0
- package/dist/db/db-attr-schemas.js +26 -0
- package/dist/db/db-attr-schemas.js.map +1 -0
- package/dist/db/db-provider.d.ts +3 -0
- package/dist/db/db-provider.d.ts.map +1 -0
- package/dist/db/db-provider.js +28 -0
- package/dist/db/db-provider.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/loader/meta-data-loader.d.ts +87 -0
- package/dist/loader/meta-data-loader.d.ts.map +1 -0
- package/dist/loader/meta-data-loader.js +232 -0
- package/dist/loader/meta-data-loader.js.map +1 -0
- package/dist/loader/meta-data-source.d.ts +23 -0
- package/dist/loader/meta-data-source.d.ts.map +1 -0
- package/dist/loader/meta-data-source.js +20 -0
- package/dist/loader/meta-data-source.js.map +1 -0
- package/dist/loader/validation-passes.d.ts +7 -0
- package/dist/loader/validation-passes.d.ts.map +1 -0
- package/dist/loader/validation-passes.js +244 -0
- package/dist/loader/validation-passes.js.map +1 -0
- package/dist/meta/find-reference.d.ts +22 -0
- package/dist/meta/find-reference.d.ts.map +1 -0
- package/dist/meta/find-reference.js +29 -0
- package/dist/meta/find-reference.js.map +1 -0
- package/dist/meta/meta-attr.d.ts +8 -0
- package/dist/meta/meta-attr.d.ts.map +1 -0
- package/dist/meta/meta-attr.js +17 -0
- package/dist/meta/meta-attr.js.map +1 -0
- package/dist/meta/meta-data.d.ts +107 -0
- package/dist/meta/meta-data.d.ts.map +1 -0
- package/dist/meta/meta-data.js +302 -0
- package/dist/meta/meta-data.js.map +1 -0
- package/dist/meta/meta-field.d.ts +48 -0
- package/dist/meta/meta-field.d.ts.map +1 -0
- package/dist/meta/meta-field.js +94 -0
- package/dist/meta/meta-field.js.map +1 -0
- package/dist/meta/meta-identity.d.ts +71 -0
- package/dist/meta/meta-identity.d.ts.map +1 -0
- package/dist/meta/meta-identity.js +129 -0
- package/dist/meta/meta-identity.js.map +1 -0
- package/dist/meta/meta-layout.d.ts +23 -0
- package/dist/meta/meta-layout.d.ts.map +1 -0
- package/dist/meta/meta-layout.js +45 -0
- package/dist/meta/meta-layout.js.map +1 -0
- package/dist/meta/meta-object.d.ts +40 -0
- package/dist/meta/meta-object.d.ts.map +1 -0
- package/dist/meta/meta-object.js +81 -0
- package/dist/meta/meta-object.js.map +1 -0
- package/dist/meta/meta-origin.d.ts +32 -0
- package/dist/meta/meta-origin.d.ts.map +1 -0
- package/dist/meta/meta-origin.js +55 -0
- package/dist/meta/meta-origin.js.map +1 -0
- package/dist/meta/meta-relationship.d.ts +11 -0
- package/dist/meta/meta-relationship.d.ts.map +1 -0
- package/dist/meta/meta-relationship.js +27 -0
- package/dist/meta/meta-relationship.js.map +1 -0
- package/dist/meta/meta-root.d.ts +12 -0
- package/dist/meta/meta-root.d.ts.map +1 -0
- package/dist/meta/meta-root.js +24 -0
- package/dist/meta/meta-root.js.map +1 -0
- package/dist/meta/meta-source.d.ts +18 -0
- package/dist/meta/meta-source.d.ts.map +1 -0
- package/dist/meta/meta-source.js +31 -0
- package/dist/meta/meta-source.js.map +1 -0
- package/dist/meta/meta-validator.d.ts +29 -0
- package/dist/meta/meta-validator.d.ts.map +1 -0
- package/dist/meta/meta-validator.js +49 -0
- package/dist/meta/meta-validator.js.map +1 -0
- package/dist/meta/meta-view.d.ts +4 -0
- package/dist/meta/meta-view.d.ts.map +1 -0
- package/dist/meta/meta-view.js +8 -0
- package/dist/meta/meta-view.js.map +1 -0
- package/dist/naming.d.ts +27 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +72 -0
- package/dist/naming.js.map +1 -0
- package/dist/object-serializer.d.ts +10 -0
- package/dist/object-serializer.d.ts.map +1 -0
- package/dist/object-serializer.js +128 -0
- package/dist/object-serializer.js.map +1 -0
- package/dist/overlay.d.ts +2 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +6 -0
- package/dist/overlay.js.map +1 -0
- package/dist/parser-core.d.ts +47 -0
- package/dist/parser-core.d.ts.map +1 -0
- package/dist/parser-core.js +516 -0
- package/dist/parser-core.js.map +1 -0
- package/dist/parser-json.d.ts +4 -0
- package/dist/parser-json.d.ts.map +1 -0
- package/dist/parser-json.js +19 -0
- package/dist/parser-json.js.map +1 -0
- package/dist/persistence/db/db-attr-schemas.d.ts +8 -0
- package/dist/persistence/db/db-attr-schemas.d.ts.map +1 -0
- package/dist/persistence/db/db-attr-schemas.js +28 -0
- package/dist/persistence/db/db-attr-schemas.js.map +1 -0
- package/dist/persistence/db/db-constants.d.ts +5 -0
- package/dist/persistence/db/db-constants.d.ts.map +1 -0
- package/dist/persistence/db/db-constants.js +6 -0
- package/dist/persistence/db/db-constants.js.map +1 -0
- package/dist/persistence/db/db-provider.d.ts +3 -0
- package/dist/persistence/db/db-provider.d.ts.map +1 -0
- package/dist/persistence/db/db-provider.js +29 -0
- package/dist/persistence/db/db-provider.js.map +1 -0
- package/dist/persistence/db/db-schema.d.ts +8 -0
- package/dist/persistence/db/db-schema.d.ts.map +1 -0
- package/dist/persistence/db/db-schema.js +27 -0
- package/dist/persistence/db/db-schema.js.map +1 -0
- package/dist/persistence/origin/meta-origin.d.ts +32 -0
- package/dist/persistence/origin/meta-origin.d.ts.map +1 -0
- package/dist/persistence/origin/meta-origin.js +55 -0
- package/dist/persistence/origin/meta-origin.js.map +1 -0
- package/dist/persistence/origin/origin-constants.d.ts +12 -0
- package/dist/persistence/origin/origin-constants.d.ts.map +1 -0
- package/dist/persistence/origin/origin-constants.js +27 -0
- package/dist/persistence/origin/origin-constants.js.map +1 -0
- package/dist/persistence/origin/origin-schema.d.ts +4 -0
- package/dist/persistence/origin/origin-schema.d.ts.map +1 -0
- package/dist/persistence/origin/origin-schema.js +49 -0
- package/dist/persistence/origin/origin-schema.js.map +1 -0
- package/dist/persistence/source/meta-source.d.ts +18 -0
- package/dist/persistence/source/meta-source.d.ts.map +1 -0
- package/dist/persistence/source/meta-source.js +31 -0
- package/dist/persistence/source/meta-source.js.map +1 -0
- package/dist/persistence/source/source-constants.d.ts +16 -0
- package/dist/persistence/source/source-constants.d.ts.map +1 -0
- package/dist/persistence/source/source-constants.js +28 -0
- package/dist/persistence/source/source-constants.js.map +1 -0
- package/dist/presentation/layout/layout-constants.d.ts +10 -0
- package/dist/presentation/layout/layout-constants.d.ts.map +1 -0
- package/dist/presentation/layout/layout-constants.js +21 -0
- package/dist/presentation/layout/layout-constants.js.map +1 -0
- package/dist/presentation/layout/layout-schema.d.ts +4 -0
- package/dist/presentation/layout/layout-schema.d.ts.map +1 -0
- package/dist/presentation/layout/layout-schema.js +46 -0
- package/dist/presentation/layout/layout-schema.js.map +1 -0
- package/dist/presentation/layout/meta-layout.d.ts +23 -0
- package/dist/presentation/layout/meta-layout.d.ts.map +1 -0
- package/dist/presentation/layout/meta-layout.js +47 -0
- package/dist/presentation/layout/meta-layout.js.map +1 -0
- package/dist/presentation/view/meta-view.d.ts +4 -0
- package/dist/presentation/view/meta-view.d.ts.map +1 -0
- package/dist/presentation/view/meta-view.js +8 -0
- package/dist/presentation/view/meta-view.js.map +1 -0
- package/dist/presentation/view/view-constants.d.ts +20 -0
- package/dist/presentation/view/view-constants.d.ts.map +1 -0
- package/dist/presentation/view/view-constants.js +47 -0
- package/dist/presentation/view/view-constants.js.map +1 -0
- package/dist/presentation/view/view-schema.d.ts +4 -0
- package/dist/presentation/view/view-schema.d.ts.map +1 -0
- package/dist/presentation/view/view-schema.js +15 -0
- package/dist/presentation/view/view-schema.js.map +1 -0
- package/dist/provider.d.ts +20 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +58 -0
- package/dist/provider.js.map +1 -0
- package/dist/registry.d.ts +89 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +107 -0
- package/dist/registry.js.map +1 -0
- package/dist/serializer-json.d.ts +16 -0
- package/dist/serializer-json.d.ts.map +1 -0
- package/dist/serializer-json.js +154 -0
- package/dist/serializer-json.js.map +1 -0
- package/dist/shared/base-types.d.ts +23 -0
- package/dist/shared/base-types.d.ts.map +1 -0
- package/dist/shared/base-types.js +43 -0
- package/dist/shared/base-types.js.map +1 -0
- package/dist/shared/meta-data.d.ts +123 -0
- package/dist/shared/meta-data.d.ts.map +1 -0
- package/dist/shared/meta-data.js +365 -0
- package/dist/shared/meta-data.js.map +1 -0
- package/dist/shared/meta-root.d.ts +12 -0
- package/dist/shared/meta-root.d.ts.map +1 -0
- package/dist/shared/meta-root.js +24 -0
- package/dist/shared/meta-root.js.map +1 -0
- package/dist/shared/structural.d.ts +20 -0
- package/dist/shared/structural.d.ts.map +1 -0
- package/dist/shared/structural.js +49 -0
- package/dist/shared/structural.js.map +1 -0
- package/dist/subtype-rules.d.ts +8 -0
- package/dist/subtype-rules.d.ts.map +1 -0
- package/dist/subtype-rules.js +34 -0
- package/dist/subtype-rules.js.map +1 -0
- package/dist/super-resolve.d.ts +34 -0
- package/dist/super-resolve.d.ts.map +1 -0
- package/dist/super-resolve.js +124 -0
- package/dist/super-resolve.js.map +1 -0
- package/package.json +50 -0
- package/src/attr-class-map.ts +64 -0
- package/src/attr-schema-validate.ts +134 -0
- package/src/core/attr/attr-constants.ts +31 -0
- package/src/core/attr/meta-attr-filter.ts +67 -0
- package/src/core/attr/meta-attr-properties.ts +26 -0
- package/src/core/attr/meta-attr-stringarray.ts +31 -0
- package/src/core/attr/meta-attr.ts +125 -0
- package/src/core/export-json.ts +66 -0
- package/src/core/field/field-constants.ts +79 -0
- package/src/core/field/field-schema.ts +121 -0
- package/src/core/field/meta-field.ts +179 -0
- package/src/core/file-meta-data-loader.ts +89 -0
- package/src/core/file-source.ts +52 -0
- package/src/core/identity/identity-constants.ts +44 -0
- package/src/core/identity/identity-schema.ts +80 -0
- package/src/core/identity/meta-identity.ts +148 -0
- package/src/core/index.ts +12 -0
- package/src/core/object/meta-object.ts +151 -0
- package/src/core/object/object-constants.ts +21 -0
- package/src/core/object/object-schema.ts +7 -0
- package/src/core/parser-yaml.ts +54 -0
- package/src/core/query/query-constants.ts +66 -0
- package/src/core/relationship/find-reference.ts +44 -0
- package/src/core/relationship/meta-relationship.ts +36 -0
- package/src/core/relationship/relationship-constants.ts +38 -0
- package/src/core/relationship/relationship-schema.ts +49 -0
- package/src/core/validator/meta-validator.ts +62 -0
- package/src/core/validator/validator-constants.ts +31 -0
- package/src/core/validator/validator-schema.ts +50 -0
- package/src/core/yaml-desugar.ts +145 -0
- package/src/core-types.ts +329 -0
- package/src/data-converter.ts +125 -0
- package/src/data-type.ts +33 -0
- package/src/errors.ts +68 -0
- package/src/index.ts +165 -0
- package/src/loader/meta-data-loader.ts +307 -0
- package/src/loader/meta-data-source.ts +35 -0
- package/src/loader/validation-passes.ts +370 -0
- package/src/naming.ts +86 -0
- package/src/object-serializer.ts +153 -0
- package/src/overlay.ts +5 -0
- package/src/parser-core.ts +815 -0
- package/src/parser-json.ts +28 -0
- package/src/persistence/db/db-constants.ts +6 -0
- package/src/persistence/db/db-provider.ts +36 -0
- package/src/persistence/db/db-schema.ts +40 -0
- package/src/persistence/origin/meta-origin.ts +67 -0
- package/src/persistence/origin/origin-constants.ts +35 -0
- package/src/persistence/origin/origin-schema.ts +66 -0
- package/src/persistence/source/meta-source.ts +38 -0
- package/src/persistence/source/source-constants.ts +35 -0
- package/src/presentation/layout/layout-constants.ts +27 -0
- package/src/presentation/layout/layout-schema.ts +62 -0
- package/src/presentation/layout/meta-layout.ts +61 -0
- package/src/presentation/view/meta-view.ts +8 -0
- package/src/presentation/view/view-constants.ts +53 -0
- package/src/presentation/view/view-schema.ts +21 -0
- package/src/provider.ts +85 -0
- package/src/registry.ts +190 -0
- package/src/serializer-json.ts +210 -0
- package/src/shared/base-types.ts +52 -0
- package/src/shared/meta-data.ts +443 -0
- package/src/shared/meta-root.ts +33 -0
- package/src/shared/structural.ts +62 -0
- package/src/subtype-rules.ts +56 -0
- package/src/super-resolve.ts +147 -0
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
// Shared canonical tree-builder — redesigned (post-v0.3) format.
|
|
2
|
+
//
|
|
3
|
+
// buildTree() turns a canonical-shaped JS object into a typed MetaData tree.
|
|
4
|
+
// parser-json.ts and parser-yaml.ts both funnel their parsed/desugared input
|
|
5
|
+
// through here so JSON and YAML stay isomorphic ("one structure, two
|
|
6
|
+
// renderings").
|
|
7
|
+
//
|
|
8
|
+
// Node encoding: every node is a single-key map { "<type>.<subType>": <body> }.
|
|
9
|
+
// The wrapper key FUSES type and subType into one dotted token (object.entity,
|
|
10
|
+
// field.long, metadata.root). The body is a map of reserved structural keys
|
|
11
|
+
// (name/package/extends/abstract/overlay/isArray/children), `@`-prefixed
|
|
12
|
+
// attributes, and the children list.
|
|
13
|
+
//
|
|
14
|
+
// Operator vocabulary (canonical, no aliases):
|
|
15
|
+
// - extends: string ref to a supertype (immediate resolution against
|
|
16
|
+
// the accumulating root, or deferred for cross-file refs)
|
|
17
|
+
// - overlay:true re-opens an existing same-(type,name) child; errors if missing
|
|
18
|
+
//
|
|
19
|
+
// Default (no operator): silently reuse existing same-(type,name) child, or
|
|
20
|
+
// create new.
|
|
21
|
+
//
|
|
22
|
+
// Context package for super resolution:
|
|
23
|
+
// Each node's effective package is its own package if set, or inherited from
|
|
24
|
+
// the nearest ancestor with a package.
|
|
25
|
+
|
|
26
|
+
import { TypeId, TypeRegistry } from "./registry.js";
|
|
27
|
+
import type { MetaData } from "./shared/meta-data.js";
|
|
28
|
+
import { MetaRoot } from "./shared/meta-root.js";
|
|
29
|
+
import { MetaAttr } from "./core/attr/meta-attr.js";
|
|
30
|
+
import { inferAttrSubType } from "./serializer-json.js";
|
|
31
|
+
import { ParseError, type ErrorCode } from "./errors.js";
|
|
32
|
+
import { resolveSuperRef } from "./super-resolve.js";
|
|
33
|
+
import {
|
|
34
|
+
TYPE_ATTR,
|
|
35
|
+
TYPE_FIELD,
|
|
36
|
+
TYPE_OBJECT,
|
|
37
|
+
TYPE_VALIDATOR,
|
|
38
|
+
SUBTYPE_BASE,
|
|
39
|
+
} from "./shared/base-types.js";
|
|
40
|
+
import {
|
|
41
|
+
RESERVED_KEYS,
|
|
42
|
+
RESERVED_KEY_NAME,
|
|
43
|
+
RESERVED_KEY_PACKAGE,
|
|
44
|
+
RESERVED_KEY_EXTENDS,
|
|
45
|
+
RESERVED_KEY_ABSTRACT,
|
|
46
|
+
RESERVED_KEY_OVERLAY,
|
|
47
|
+
RESERVED_KEY_IS_ARRAY,
|
|
48
|
+
RESERVED_KEY_CHILDREN,
|
|
49
|
+
RESERVED_KEY_VALUE,
|
|
50
|
+
JSON_KEY_SCHEMA,
|
|
51
|
+
ATTR_PREFIX,
|
|
52
|
+
TYPE_SUBTYPE_SEPARATOR,
|
|
53
|
+
PACKAGE_SEPARATOR,
|
|
54
|
+
} from "./shared/structural.js";
|
|
55
|
+
import { ATTR_SUBTYPE_PROPERTIES } from "./core/attr/attr-constants.js";
|
|
56
|
+
import type { AttrValue } from "./shared/meta-data.js";
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Public API
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
export interface ParseOptions {
|
|
63
|
+
registry: TypeRegistry; // required — types must be registered
|
|
64
|
+
strict?: boolean; // throw on parsing problems? default false
|
|
65
|
+
sourceName?: string; // for error messages, e.g., "fishstore.json"
|
|
66
|
+
/**
|
|
67
|
+
* Loader's accumulating root. If provided, parse merges nodes into it.
|
|
68
|
+
* If undefined, creates a new root from the JSON's root node.
|
|
69
|
+
*
|
|
70
|
+
* When provided, the returned `root` is this same instance (possibly mutated).
|
|
71
|
+
* When undefined, a fresh root is created from the JSON.
|
|
72
|
+
*/
|
|
73
|
+
intoRoot?: MetaRoot;
|
|
74
|
+
/**
|
|
75
|
+
* If true, super refs that don't resolve at parse time are NOT a parse error;
|
|
76
|
+
* the model retains its raw `superRef` and a second pass (via
|
|
77
|
+
* resolveDeferredSupers in super-resolve.ts) is expected after all input
|
|
78
|
+
* files are parsed. Used by Loader.load/loadJsonStrings to support cross-file
|
|
79
|
+
* super resolution where one file may declare a super target that lives in
|
|
80
|
+
* another file parsed later.
|
|
81
|
+
*/
|
|
82
|
+
deferSuperResolution?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ParseResult {
|
|
86
|
+
root: MetaRoot;
|
|
87
|
+
warnings: string[];
|
|
88
|
+
errors: ParseError[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Internal helper — build ParseError opts, omitting undefined fields
|
|
93
|
+
// (required by exactOptionalPropertyTypes: true in the project tsconfig)
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
export function errOpts(
|
|
97
|
+
source: string | undefined,
|
|
98
|
+
path?: string,
|
|
99
|
+
): { source?: string; path?: string } {
|
|
100
|
+
const opts: { source?: string; path?: string } = {};
|
|
101
|
+
if (source !== undefined) opts.source = source;
|
|
102
|
+
if (path !== undefined) opts.path = path;
|
|
103
|
+
return opts;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Internal helper — report a problem: throw in strict mode, push warning otherwise
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
function reportProblem(
|
|
111
|
+
msg: string,
|
|
112
|
+
strict: boolean,
|
|
113
|
+
warnings: string[],
|
|
114
|
+
source: string | undefined,
|
|
115
|
+
path: string,
|
|
116
|
+
code?: ErrorCode,
|
|
117
|
+
): void {
|
|
118
|
+
if (strict) {
|
|
119
|
+
const opts = errOpts(source, path);
|
|
120
|
+
if (code !== undefined) throw new ParseError(msg, { ...opts, code });
|
|
121
|
+
else throw new ParseError(msg, opts);
|
|
122
|
+
}
|
|
123
|
+
warnings.push(msg);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Internal helper — split a fused wrapper key into (type, subType).
|
|
128
|
+
//
|
|
129
|
+
// Canonical JSON always writes the full `type.subType`. An omitted subType
|
|
130
|
+
// (a bare `type` key, no `.`) resolves to the type's registry default — the
|
|
131
|
+
// first registered subtype, falling back to SUBTYPE_BASE.
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
interface SplitKey {
|
|
135
|
+
type: string;
|
|
136
|
+
subType: string;
|
|
137
|
+
/** true when the subType was fused into the key; false when defaulted. */
|
|
138
|
+
explicit: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function defaultSubTypeFor(type: string, registry: TypeRegistry): string {
|
|
142
|
+
const subs = registry.allSubTypesOf(type);
|
|
143
|
+
const candidate = subs.length > 0 ? subs[0]! : SUBTYPE_BASE;
|
|
144
|
+
if (!registry.has(type, candidate) && registry.has(type, SUBTYPE_BASE)) {
|
|
145
|
+
return SUBTYPE_BASE;
|
|
146
|
+
}
|
|
147
|
+
return candidate;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function splitTypeKey(key: string, registry: TypeRegistry): SplitKey {
|
|
151
|
+
const dotIdx = key.indexOf(TYPE_SUBTYPE_SEPARATOR);
|
|
152
|
+
if (dotIdx < 0) {
|
|
153
|
+
// Bare type, no fused subType — resolve via the registry default.
|
|
154
|
+
return { type: key, subType: defaultSubTypeFor(key, registry), explicit: false };
|
|
155
|
+
}
|
|
156
|
+
const type = key.slice(0, dotIdx);
|
|
157
|
+
const subType = key.slice(dotIdx + TYPE_SUBTYPE_SEPARATOR.length);
|
|
158
|
+
// Malformed keys (e.g. "object.entity.extra", ".entity", "object.") are not
|
|
159
|
+
// validated here — they fall through to the downstream registry.has() check
|
|
160
|
+
// which reports them as an unknown type or subtype error.
|
|
161
|
+
return { type, subType, explicit: true };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Helper: expand relative package paths
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Expands a package path relative to a base package.
|
|
170
|
+
* Java semantics:
|
|
171
|
+
* - Absolute path (::foo::bar) → prepended with base: "acme" + "::foo" → "acme::foo::bar"
|
|
172
|
+
* - Relative parent (..) → handled in super resolution, not here
|
|
173
|
+
* - No leading :: → used as-is
|
|
174
|
+
*/
|
|
175
|
+
function expandPackageForPath(basePkg: string, pkgPath: string): string {
|
|
176
|
+
if (basePkg.trim() === "" || !pkgPath.startsWith(PACKAGE_SEPARATOR)) {
|
|
177
|
+
return pkgPath;
|
|
178
|
+
}
|
|
179
|
+
return basePkg + pkgPath;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Main entry point
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
// Module-level flag consumed by parseNodeFresh during super resolution.
|
|
187
|
+
// Safe because buildTree is fully synchronous — no reentrancy risk within
|
|
188
|
+
// a single parse call. Set at buildTree entry, read deep in the call tree.
|
|
189
|
+
let _deferSuperResolution = false;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* buildTree — the shared registry-driven tree-builder.
|
|
193
|
+
*
|
|
194
|
+
* Turns an already-parsed *canonical-shaped* JS object (from JSON.parse, or
|
|
195
|
+
* from the YAML parser's desugar) into a typed MetaData tree. Both parseJson
|
|
196
|
+
* and parseYaml funnel through here, guaranteeing one structure / two
|
|
197
|
+
* renderings stay isomorphic.
|
|
198
|
+
*
|
|
199
|
+
* Throws ParseError for top-level structural problems (matching the historical
|
|
200
|
+
* parseJson behavior); collects content-level problems into the result.
|
|
201
|
+
*/
|
|
202
|
+
export function buildTree(parsed: unknown, opts: ParseOptions): ParseResult {
|
|
203
|
+
const warnings: string[] = [];
|
|
204
|
+
const errors: ParseError[] = [];
|
|
205
|
+
const strict = opts.strict ?? false;
|
|
206
|
+
const source = opts.sourceName;
|
|
207
|
+
_deferSuperResolution = opts.deferSuperResolution === true;
|
|
208
|
+
|
|
209
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
210
|
+
throw new ParseError("Top-level metadata must be an object", { ...errOpts(source), code: "ERR_TOP_LEVEL_NOT_OBJECT" });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const topLevel = parsed as Record<string, unknown>;
|
|
214
|
+
|
|
215
|
+
// --- Find the wrapper key (skip $schema) ---
|
|
216
|
+
const wrapperKeys = Object.keys(topLevel).filter((k) => k !== JSON_KEY_SCHEMA);
|
|
217
|
+
|
|
218
|
+
if (wrapperKeys.length === 0) {
|
|
219
|
+
throw new ParseError("Top-level metadata object has no type wrapper key", { ...errOpts(source), code: "ERR_TOP_LEVEL_NOT_OBJECT" });
|
|
220
|
+
}
|
|
221
|
+
if (wrapperKeys.length > 1) {
|
|
222
|
+
throw new ParseError(
|
|
223
|
+
`Top-level metadata object must have exactly one wrapper key (found: ${wrapperKeys.join(", ")})`,
|
|
224
|
+
{ ...errOpts(source), code: "ERR_TOP_LEVEL_NOT_OBJECT" },
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const rootKey = wrapperKeys[0]!;
|
|
229
|
+
const rootData = topLevel[rootKey];
|
|
230
|
+
|
|
231
|
+
if (typeof rootData !== "object" || rootData === null || Array.isArray(rootData)) {
|
|
232
|
+
throw new ParseError(
|
|
233
|
+
`Top-level wrapper "${rootKey}" must contain an object`,
|
|
234
|
+
{ ...errOpts(source, rootKey), code: "ERR_TOP_LEVEL_NOT_OBJECT" },
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const rootDataObj = rootData as Record<string, unknown>;
|
|
239
|
+
const { type: rootType, subType: rootSubType } = splitTypeKey(rootKey, opts.registry);
|
|
240
|
+
|
|
241
|
+
// Check root type is registered (always throw — can't skip the root)
|
|
242
|
+
if (!opts.registry.has(rootType, rootSubType)) {
|
|
243
|
+
const rootTypeCode = opts.registry.allSubTypesOf(rootType).length > 0
|
|
244
|
+
? "ERR_UNKNOWN_SUBTYPE" as const
|
|
245
|
+
: "ERR_UNKNOWN_TYPE" as const;
|
|
246
|
+
throw new ParseError(
|
|
247
|
+
`Unknown root type "${rootType}.${rootSubType}" — not registered`,
|
|
248
|
+
{ ...errOpts(source, rootKey), code: rootTypeCode },
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (opts.intoRoot !== undefined) {
|
|
253
|
+
// --- Merge mode: parse root's attrs/children into the existing root ---
|
|
254
|
+
// The JSON root's own package/super/reserved-keys are not re-applied to the
|
|
255
|
+
// existing root. BUT: children from the NEW JSON should inherit from the
|
|
256
|
+
// NEW root's package, not the existing root's.
|
|
257
|
+
const newRootPkg = rootDataObj[RESERVED_KEY_PACKAGE];
|
|
258
|
+
const contextPkg = (typeof newRootPkg === "string" ? newRootPkg : opts.intoRoot.package) ?? "";
|
|
259
|
+
parseNodeInto(
|
|
260
|
+
rootDataObj,
|
|
261
|
+
opts.intoRoot,
|
|
262
|
+
opts.intoRoot, // accumulating root for super resolution
|
|
263
|
+
contextPkg,
|
|
264
|
+
opts.registry,
|
|
265
|
+
warnings,
|
|
266
|
+
errors,
|
|
267
|
+
strict,
|
|
268
|
+
source,
|
|
269
|
+
rootKey,
|
|
270
|
+
);
|
|
271
|
+
return { root: opts.intoRoot, warnings, errors };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// --- Fresh root mode: create a new root from the JSON ---
|
|
275
|
+
// The cast is safe within the core provider: `metadata.root` is the only
|
|
276
|
+
// registered `metadata` subtype, and its factory unconditionally produces a
|
|
277
|
+
// MetaRoot (see core-types.ts). A registry that registered a second
|
|
278
|
+
// `metadata.*` subtype backed by a non-MetaRoot factory would break this
|
|
279
|
+
// cast — a known limitation of the `TypeDefinition.factory: => MetaData`
|
|
280
|
+
// signature. `parseNodeFresh` is a general node parser, so MetaData is its
|
|
281
|
+
// correct return type; this top-level callsite is where the doc-root invariant holds.
|
|
282
|
+
const root = parseNodeFresh(
|
|
283
|
+
rootType,
|
|
284
|
+
rootSubType,
|
|
285
|
+
rootDataObj,
|
|
286
|
+
undefined, // no accumulating root yet — built as we go
|
|
287
|
+
"", // no inherited context pkg yet for the root itself
|
|
288
|
+
opts.registry,
|
|
289
|
+
warnings,
|
|
290
|
+
errors,
|
|
291
|
+
strict,
|
|
292
|
+
source,
|
|
293
|
+
rootKey,
|
|
294
|
+
) as MetaRoot;
|
|
295
|
+
return { root, warnings, errors };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// parseNodeFresh — creates a NEW node for this metadata node.
|
|
300
|
+
//
|
|
301
|
+
// `accumRoot` is the root of the accumulating tree (for super resolution).
|
|
302
|
+
// When parsing the root node itself, accumRoot is undefined on first call;
|
|
303
|
+
// the newly created root is passed as accumRoot for its children.
|
|
304
|
+
//
|
|
305
|
+
// `inheritedContextPkg` is the effective package from the nearest ancestor
|
|
306
|
+
// that had an explicit package. Used when this node has no own package.
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
function parseNodeFresh(
|
|
310
|
+
type: string,
|
|
311
|
+
subType: string,
|
|
312
|
+
nodeData: Record<string, unknown>,
|
|
313
|
+
accumRoot: MetaData | undefined,
|
|
314
|
+
inheritedContextPkg: string,
|
|
315
|
+
registry: TypeRegistry,
|
|
316
|
+
warnings: string[],
|
|
317
|
+
errors: ParseError[],
|
|
318
|
+
strict: boolean,
|
|
319
|
+
source: string | undefined,
|
|
320
|
+
path: string,
|
|
321
|
+
parentType?: string, // optional: type of the parent node (for inheritance rules)
|
|
322
|
+
parent?: MetaData, // optional: the parent model itself (for checking fqn)
|
|
323
|
+
): MetaData {
|
|
324
|
+
// --- Look up type in registry ---
|
|
325
|
+
if (!registry.has(type, subType)) {
|
|
326
|
+
if (registry.has(type, SUBTYPE_BASE)) {
|
|
327
|
+
subType = SUBTYPE_BASE;
|
|
328
|
+
} else {
|
|
329
|
+
const msg = `Unknown type "${type}.${subType}" — not registered`;
|
|
330
|
+
errors.push(new ParseError(msg, { ...errOpts(source, path), code: "ERR_UNKNOWN_TYPE" }));
|
|
331
|
+
const rawName = nodeData[RESERVED_KEY_NAME];
|
|
332
|
+
const name = typeof rawName === "string" ? rawName : "";
|
|
333
|
+
return new MetaRoot(new TypeId(type, subType), name);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// --- Determine name ---
|
|
338
|
+
const rawName = nodeData[RESERVED_KEY_NAME];
|
|
339
|
+
const name = typeof rawName === "string" ? rawName : "";
|
|
340
|
+
|
|
341
|
+
// --- Create the model ---
|
|
342
|
+
const def = registry.find(type, subType)!;
|
|
343
|
+
const model = def.factory(def.typeId, name);
|
|
344
|
+
|
|
345
|
+
// --- Apply reserved keys (package, extends, abstract, isArray) ---
|
|
346
|
+
applyReservedKeys(model, nodeData, strict, source, path, warnings, inheritedContextPkg);
|
|
347
|
+
|
|
348
|
+
// --- Inherit package from context if not explicitly set ---
|
|
349
|
+
// Java rule (BaseMetaDataParser.shouldInheritPackageFromParent):
|
|
350
|
+
// - Fields within objects: no package (simple names)
|
|
351
|
+
// - Fields NOT within objects: inherit parent's package (for abstract fields at root level)
|
|
352
|
+
// - Validators within fields: inherit the field's package ONLY if field's FQN contains "::"
|
|
353
|
+
// - Objects and other types: don't inherit
|
|
354
|
+
if (model.package === undefined && inheritedContextPkg !== "") {
|
|
355
|
+
let shouldInherit = false;
|
|
356
|
+
|
|
357
|
+
if (type === TYPE_FIELD && parentType !== TYPE_OBJECT) {
|
|
358
|
+
shouldInherit = true;
|
|
359
|
+
} else if (type === TYPE_VALIDATOR && parentType === TYPE_FIELD && parent !== undefined) {
|
|
360
|
+
shouldInherit = parent.fqn().includes(PACKAGE_SEPARATOR);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (shouldInherit) {
|
|
364
|
+
model.setPackage(inheritedContextPkg);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// --- Determine the effective context package for super resolution ---
|
|
369
|
+
const effectivePkg = model.package ?? inheritedContextPkg;
|
|
370
|
+
|
|
371
|
+
// --- Resolve super IMMEDIATELY against the accumulating root ---
|
|
372
|
+
// (Skipped when deferSuperResolution is true — the loader resolves after
|
|
373
|
+
// all input files have been parsed, so cross-file super refs work.)
|
|
374
|
+
if (model.superRef !== undefined && accumRoot !== undefined && !_deferSuperResolution) {
|
|
375
|
+
const superModel = resolveSuperRef(model.superRef, effectivePkg, accumRoot);
|
|
376
|
+
if (superModel !== undefined) {
|
|
377
|
+
model.setSuperResolved(superModel);
|
|
378
|
+
} else {
|
|
379
|
+
throw new ParseError(
|
|
380
|
+
`the SuperClass '${model.superRef}' does not exist in file '${source ?? "<unknown>"}'`,
|
|
381
|
+
{ ...errOpts(source, path), code: "ERR_UNRESOLVED_SUPER" },
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
} else if (model.superRef !== undefined && accumRoot === undefined) {
|
|
385
|
+
// Root node has a super ref — not resolvable against itself.
|
|
386
|
+
reportProblem(
|
|
387
|
+
`super on root node ('${model.superRef}') is not supported and will be ignored`,
|
|
388
|
+
strict,
|
|
389
|
+
warnings,
|
|
390
|
+
source,
|
|
391
|
+
path,
|
|
392
|
+
"ERR_UNRESOLVED_SUPER",
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// --- Process inline attributes and other keys ---
|
|
397
|
+
applyInlineAttrsAndUnknownKeys(model, nodeData, strict, source, path, warnings, registry);
|
|
398
|
+
|
|
399
|
+
// --- Process children ---
|
|
400
|
+
// For the root node we use itself as the accumRoot for its children.
|
|
401
|
+
const childAccumRoot = accumRoot ?? model;
|
|
402
|
+
const childInheritedContextPkg = model.package ?? inheritedContextPkg;
|
|
403
|
+
processChildren(model, nodeData, childAccumRoot, childInheritedContextPkg, registry, warnings, errors, strict, source, path);
|
|
404
|
+
|
|
405
|
+
return model;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
409
|
+
// parseNodeInto — merges a JSON node's attrs/children into an EXISTING model.
|
|
410
|
+
//
|
|
411
|
+
// Used for the intoRoot mode (top-level merge), overlay: true nodes, and the
|
|
412
|
+
// default same-name reuse path. Does NOT re-apply reserved keys (subType,
|
|
413
|
+
// extends, package) — those belong to the original model's identity.
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
|
|
416
|
+
function parseNodeInto(
|
|
417
|
+
nodeData: Record<string, unknown>,
|
|
418
|
+
target: MetaData,
|
|
419
|
+
accumRoot: MetaData,
|
|
420
|
+
inheritedContextPkg: string,
|
|
421
|
+
registry: TypeRegistry,
|
|
422
|
+
warnings: string[],
|
|
423
|
+
errors: ParseError[],
|
|
424
|
+
strict: boolean,
|
|
425
|
+
source: string | undefined,
|
|
426
|
+
path: string,
|
|
427
|
+
): void {
|
|
428
|
+
// Apply inline attrs (not reserved keys — those stay on the existing model)
|
|
429
|
+
applyInlineAttrsAndUnknownKeys(target, nodeData, strict, source, path, warnings, registry);
|
|
430
|
+
|
|
431
|
+
// The effective package for children: use inheritedContextPkg (from the new
|
|
432
|
+
// JSON's root) — not target.package, because target is the existing model
|
|
433
|
+
// being merged into, and new children inherit from the NEW context.
|
|
434
|
+
const effectivePkg = inheritedContextPkg;
|
|
435
|
+
|
|
436
|
+
processChildren(target, nodeData, accumRoot, effectivePkg, registry, warnings, errors, strict, source, path);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
// createOrFindMetaData — per-node merge logic.
|
|
441
|
+
//
|
|
442
|
+
// overlay: true → find-or-throw (throw if no existing same-(type,name) child)
|
|
443
|
+
// default → find-or-create (silently reuse if found)
|
|
444
|
+
// ---------------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
function createOrFindMetaData(
|
|
447
|
+
type: string,
|
|
448
|
+
subType: string,
|
|
449
|
+
nodeData: Record<string, unknown>,
|
|
450
|
+
parent: MetaData,
|
|
451
|
+
accumRoot: MetaData,
|
|
452
|
+
inheritedContextPkg: string,
|
|
453
|
+
registry: TypeRegistry,
|
|
454
|
+
warnings: string[],
|
|
455
|
+
errors: ParseError[],
|
|
456
|
+
strict: boolean,
|
|
457
|
+
source: string | undefined,
|
|
458
|
+
path: string,
|
|
459
|
+
): MetaData | undefined {
|
|
460
|
+
// Only `overlay: true` re-opens an existing node. Anything else falls through
|
|
461
|
+
// to the default reuse-or-create path.
|
|
462
|
+
const isOverlayNode = nodeData[RESERVED_KEY_OVERLAY] === true;
|
|
463
|
+
|
|
464
|
+
// Determine name (needed for the lookup). Lookup key is (type, name).
|
|
465
|
+
const rawName = nodeData[RESERVED_KEY_NAME];
|
|
466
|
+
const name = typeof rawName === "string" ? rawName : "";
|
|
467
|
+
|
|
468
|
+
// Look up an existing child with (type, name). Skip unnamed nodes — they
|
|
469
|
+
// are always distinct (e.g. inline validators, anonymous attrs).
|
|
470
|
+
const existing = name !== "" ? parent.ownChildByTypeAndName(type, name) : undefined;
|
|
471
|
+
|
|
472
|
+
if (isOverlayNode) {
|
|
473
|
+
if (existing === undefined) {
|
|
474
|
+
throw new ParseError(
|
|
475
|
+
`Overlay operation requested for [${type}:${name}] but no existing metadata found to merge into`,
|
|
476
|
+
{ ...errOpts(source, path), code: "ERR_OVERLAY_NO_TARGET" },
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
existing.setIsMerge(true);
|
|
480
|
+
parseNodeInto(nodeData, existing, accumRoot, inheritedContextPkg, registry, warnings, errors, strict, source, path);
|
|
481
|
+
return existing;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Default: no operator → silently reuse existing or create new.
|
|
485
|
+
if (existing !== undefined) {
|
|
486
|
+
parseNodeInto(nodeData, existing, accumRoot, inheritedContextPkg, registry, warnings, errors, strict, source, path);
|
|
487
|
+
return existing;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Not found (or unnamed) → create new
|
|
491
|
+
return parseNodeFresh(type, subType, nodeData, accumRoot, inheritedContextPkg, registry, warnings, errors, strict, source, path, parent.type, parent);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// ---------------------------------------------------------------------------
|
|
495
|
+
// applyReservedKeys — apply package, extends, abstract, isArray to a model.
|
|
496
|
+
//
|
|
497
|
+
// Called only when CREATING a new model (not when merging into existing).
|
|
498
|
+
// ---------------------------------------------------------------------------
|
|
499
|
+
|
|
500
|
+
function applyReservedKeys(
|
|
501
|
+
model: MetaData,
|
|
502
|
+
nodeData: Record<string, unknown>,
|
|
503
|
+
strict: boolean,
|
|
504
|
+
source: string | undefined,
|
|
505
|
+
path: string,
|
|
506
|
+
warnings: string[],
|
|
507
|
+
contextPkg?: string,
|
|
508
|
+
): void {
|
|
509
|
+
// package
|
|
510
|
+
const rawPkg = nodeData[RESERVED_KEY_PACKAGE];
|
|
511
|
+
if (rawPkg !== undefined) {
|
|
512
|
+
if (typeof rawPkg !== "string") {
|
|
513
|
+
reportProblem(`"${RESERVED_KEY_PACKAGE}" must be a string at ${path}`, strict, warnings, source, path, "ERR_BAD_ATTR_VALUE");
|
|
514
|
+
} else {
|
|
515
|
+
const expandedPkg = contextPkg !== undefined ? expandPackageForPath(contextPkg, rawPkg) : rawPkg;
|
|
516
|
+
model.setPackage(expandedPkg);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// extends — store the raw supertype ref; resolution happens after this call.
|
|
521
|
+
const rawExtends = nodeData[RESERVED_KEY_EXTENDS];
|
|
522
|
+
if (rawExtends !== undefined) {
|
|
523
|
+
if (typeof rawExtends !== "string") {
|
|
524
|
+
reportProblem(`"${RESERVED_KEY_EXTENDS}" must be a string at ${path}`, strict, warnings, source, path, "ERR_UNRESOLVED_SUPER");
|
|
525
|
+
} else {
|
|
526
|
+
model.setSuper(rawExtends);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// abstract — structural key (true → abstract node)
|
|
531
|
+
const rawAbstract = nodeData[RESERVED_KEY_ABSTRACT];
|
|
532
|
+
if (rawAbstract !== undefined) {
|
|
533
|
+
if (typeof rawAbstract !== "boolean") {
|
|
534
|
+
reportProblem(`"${RESERVED_KEY_ABSTRACT}" must be a boolean at ${path}`, strict, warnings, source, path, "ERR_BAD_ATTR_VALUE");
|
|
535
|
+
} else {
|
|
536
|
+
model.setIsAbstract(rawAbstract);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// isArray — structural key (true → array node)
|
|
541
|
+
const rawIsArray = nodeData[RESERVED_KEY_IS_ARRAY];
|
|
542
|
+
if (rawIsArray !== undefined) {
|
|
543
|
+
if (typeof rawIsArray !== "boolean") {
|
|
544
|
+
reportProblem(`"${RESERVED_KEY_IS_ARRAY}" must be a boolean at ${path}`, strict, warnings, source, path, "ERR_BAD_ATTR_VALUE");
|
|
545
|
+
} else {
|
|
546
|
+
model.setIsArray(rawIsArray);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
// applyInlineAttrsAndUnknownKeys — apply @-prefixed attrs and warn about unknowns
|
|
553
|
+
//
|
|
554
|
+
// Called for both fresh creates AND merge-into-existing paths.
|
|
555
|
+
// Does NOT process reserved structural keys.
|
|
556
|
+
// ---------------------------------------------------------------------------
|
|
557
|
+
|
|
558
|
+
function applyInlineAttrsAndUnknownKeys(
|
|
559
|
+
model: MetaData,
|
|
560
|
+
nodeData: Record<string, unknown>,
|
|
561
|
+
strict: boolean,
|
|
562
|
+
source: string | undefined,
|
|
563
|
+
path: string,
|
|
564
|
+
warnings: string[],
|
|
565
|
+
registry: TypeRegistry,
|
|
566
|
+
): void {
|
|
567
|
+
for (const key of Object.keys(nodeData)) {
|
|
568
|
+
// Skip all reserved structural keys (already handled or intentionally ignored)
|
|
569
|
+
if (RESERVED_KEYS.has(key)) continue;
|
|
570
|
+
|
|
571
|
+
if (!key.startsWith(ATTR_PREFIX)) {
|
|
572
|
+
const displayName =
|
|
573
|
+
model.name !== "" ? `${model.type}.${model.subType} '${model.name}'` : `${model.type}.${model.subType}`;
|
|
574
|
+
reportProblem(
|
|
575
|
+
`Unknown key '${key}' on ${displayName} at ${path} (must be reserved or ${ATTR_PREFIX}-prefixed)`,
|
|
576
|
+
strict, warnings, source, path, "ERR_UNKNOWN_ATTR",
|
|
577
|
+
);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Inline attribute (@-prefixed) — materialize into a MetaAttr instance.
|
|
582
|
+
const attrName = key.slice(ATTR_PREFIX.length);
|
|
583
|
+
const rawVal = nodeData[key];
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
const attr = materializeAttr(model, attrName, rawVal, registry);
|
|
587
|
+
model.setMetaAttr(attr);
|
|
588
|
+
} catch (err) {
|
|
589
|
+
reportProblem(
|
|
590
|
+
`Failed to convert attribute "${ATTR_PREFIX}${attrName}" at ${path}: ${(err as Error).message}`,
|
|
591
|
+
strict, warnings, source, path, "ERR_BAD_ATTR_VALUE",
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ---------------------------------------------------------------------------
|
|
598
|
+
// materializeAttr — build a single attr into the right MetaAttr subclass:
|
|
599
|
+
// declared subtype from the owner's AttrSchema (if any), else inferred from the
|
|
600
|
+
// value shape. The instance coerces + desugars its own value.
|
|
601
|
+
// ---------------------------------------------------------------------------
|
|
602
|
+
|
|
603
|
+
function materializeAttr(
|
|
604
|
+
owner: MetaData,
|
|
605
|
+
attrName: string,
|
|
606
|
+
rawVal: unknown,
|
|
607
|
+
registry: TypeRegistry,
|
|
608
|
+
): MetaAttr {
|
|
609
|
+
const attrSpec = registry.attrsOf(owner.type, owner.subType).find((s) => s.name === attrName);
|
|
610
|
+
let subType: string;
|
|
611
|
+
if (attrSpec !== undefined && attrSpec.valueType !== undefined) {
|
|
612
|
+
subType = attrSpec.valueType;
|
|
613
|
+
} else {
|
|
614
|
+
// Undeclared or declared-but-untyped (@default): preserve the author's shape.
|
|
615
|
+
subType = inferUndeclaredAttrSubType(rawVal);
|
|
616
|
+
}
|
|
617
|
+
const def = registry.find(TYPE_ATTR, subType);
|
|
618
|
+
const node = (def !== undefined
|
|
619
|
+
? def.factory(def.typeId, attrName)
|
|
620
|
+
: new MetaAttr(new TypeId(TYPE_ATTR, subType), attrName)) as MetaAttr;
|
|
621
|
+
const coerced = node.coerce(rawVal);
|
|
622
|
+
const desugared = node.desugar(coerced);
|
|
623
|
+
node.setAttr(RESERVED_KEY_VALUE, desugared);
|
|
624
|
+
return node;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Undeclared attr → pick the subtype from the value's runtime shape, preserving
|
|
628
|
+
// type (a numeric string stays string). Wraps inferAttrSubType (scalar/array
|
|
629
|
+
// rule, incl. the int/long/double range split) with the object + null-reject
|
|
630
|
+
// branches that predate object attrs: a plain object → properties; null /
|
|
631
|
+
// undefined are not valid undeclared attr values.
|
|
632
|
+
function inferUndeclaredAttrSubType(raw: unknown): string {
|
|
633
|
+
if (raw === null || raw === undefined) {
|
|
634
|
+
throw new Error(`${raw === null ? "null" : "undefined"} is not a valid attr value`);
|
|
635
|
+
}
|
|
636
|
+
if (typeof raw === "object" && !Array.isArray(raw)) return ATTR_SUBTYPE_PROPERTIES;
|
|
637
|
+
return inferAttrSubType(raw as AttrValue);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// ---------------------------------------------------------------------------
|
|
641
|
+
// processChildren — parse the "children" array of a node
|
|
642
|
+
// ---------------------------------------------------------------------------
|
|
643
|
+
|
|
644
|
+
function processChildren(
|
|
645
|
+
parent: MetaData,
|
|
646
|
+
nodeData: Record<string, unknown>,
|
|
647
|
+
accumRoot: MetaData,
|
|
648
|
+
inheritedContextPkg: string,
|
|
649
|
+
registry: TypeRegistry,
|
|
650
|
+
warnings: string[],
|
|
651
|
+
errors: ParseError[],
|
|
652
|
+
strict: boolean,
|
|
653
|
+
source: string | undefined,
|
|
654
|
+
path: string,
|
|
655
|
+
): void {
|
|
656
|
+
const rawChildren = nodeData[RESERVED_KEY_CHILDREN];
|
|
657
|
+
if (rawChildren === undefined) return;
|
|
658
|
+
|
|
659
|
+
if (!Array.isArray(rawChildren)) {
|
|
660
|
+
reportProblem(`"${RESERVED_KEY_CHILDREN}" must be an array at ${path}`, strict, warnings, source, path, "ERR_TOP_LEVEL_NOT_OBJECT");
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
for (let i = 0; i < rawChildren.length; i++) {
|
|
665
|
+
const childEntry = rawChildren[i];
|
|
666
|
+
const childPath = `${path}.${RESERVED_KEY_CHILDREN}[${i}]`;
|
|
667
|
+
|
|
668
|
+
if (typeof childEntry !== "object" || childEntry === null || Array.isArray(childEntry)) {
|
|
669
|
+
reportProblem(`Child at ${childPath} must be an object`, strict, warnings, source, childPath, "ERR_TOP_LEVEL_NOT_OBJECT");
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const childRecord = childEntry as Record<string, unknown>;
|
|
674
|
+
const childKeys = Object.keys(childRecord);
|
|
675
|
+
|
|
676
|
+
if (childKeys.length !== 1) {
|
|
677
|
+
const msg =
|
|
678
|
+
childKeys.length === 0
|
|
679
|
+
? `Child at ${childPath} has no type wrapper key`
|
|
680
|
+
: `Child at ${childPath} has multiple keys (${childKeys.join(", ")}) — each child must have exactly one wrapper key`;
|
|
681
|
+
reportProblem(msg, strict, warnings, source, childPath, "ERR_TOP_LEVEL_NOT_OBJECT");
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const childKey = childKeys[0]!;
|
|
686
|
+
const childData = childRecord[childKey];
|
|
687
|
+
const childNodePath = `${childPath}.${childKey}`;
|
|
688
|
+
|
|
689
|
+
if (typeof childData !== "object" || childData === null || Array.isArray(childData)) {
|
|
690
|
+
reportProblem(
|
|
691
|
+
`Child wrapper "${childKey}" at ${childNodePath} must contain an object`,
|
|
692
|
+
strict, warnings, source, childNodePath, "ERR_TOP_LEVEL_NOT_OBJECT",
|
|
693
|
+
);
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const childDataObj = childData as Record<string, unknown>;
|
|
698
|
+
const { type: childType, subType: childSubTypeRaw, explicit } = splitTypeKey(childKey, registry);
|
|
699
|
+
|
|
700
|
+
// --- Check if this child type is registered ---
|
|
701
|
+
// An EXPLICIT unknown subType (fused into the key) is an error — never
|
|
702
|
+
// silently downgraded to base. An OMITTED subType that resolves to an
|
|
703
|
+
// unregistered default falls back to base.
|
|
704
|
+
let childSubType = childSubTypeRaw;
|
|
705
|
+
if (!registry.has(childType, childSubType)) {
|
|
706
|
+
if (!explicit && registry.has(childType, SUBTYPE_BASE)) {
|
|
707
|
+
childSubType = SUBTYPE_BASE;
|
|
708
|
+
} else {
|
|
709
|
+
const childTypeCode = explicit && registry.allSubTypesOf(childType).length > 0
|
|
710
|
+
? "ERR_UNKNOWN_SUBTYPE" as const
|
|
711
|
+
: "ERR_UNKNOWN_TYPE" as const;
|
|
712
|
+
errors.push(
|
|
713
|
+
new ParseError(
|
|
714
|
+
`Unknown type "${childType}.${childSubType}" — not registered`,
|
|
715
|
+
{ ...errOpts(source, childNodePath), code: childTypeCode },
|
|
716
|
+
),
|
|
717
|
+
);
|
|
718
|
+
continue; // skip this child
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// --- Special handling for "attr" child nodes ---
|
|
723
|
+
if (childType === TYPE_ATTR) {
|
|
724
|
+
parseAttrChild(parent, childType, childSubType, childDataObj, registry, warnings, strict, source, childNodePath);
|
|
725
|
+
} else {
|
|
726
|
+
// Use createOrFindMetaData to handle overlay/default per-node logic
|
|
727
|
+
const childModel = createOrFindMetaData(
|
|
728
|
+
childType,
|
|
729
|
+
childSubType,
|
|
730
|
+
childDataObj,
|
|
731
|
+
parent,
|
|
732
|
+
accumRoot,
|
|
733
|
+
inheritedContextPkg,
|
|
734
|
+
registry,
|
|
735
|
+
warnings,
|
|
736
|
+
errors,
|
|
737
|
+
strict,
|
|
738
|
+
source,
|
|
739
|
+
childNodePath,
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
if (childModel !== undefined && !parent.ownChildren().includes(childModel)) {
|
|
743
|
+
parent.addChild(childModel);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// ---------------------------------------------------------------------------
|
|
750
|
+
// Attr child node — materialize into a MetaAttr instance (NOT a child).
|
|
751
|
+
// ---------------------------------------------------------------------------
|
|
752
|
+
//
|
|
753
|
+
// A typed attr is encoded as { "attr.<subType>": { "name": ..., "value": ... } }
|
|
754
|
+
// inside a node's children array. The subType is fused into the wrapper key.
|
|
755
|
+
//
|
|
756
|
+
// The instance coerces + desugars toward its OWN subtype (the wrapper key's
|
|
757
|
+
// subType, e.g. attr.stringarray) — StringArrayAttr.coerce wraps the bare
|
|
758
|
+
// string, FilterAttr.desugar normalizes. The attr is attached via setMetaAttr,
|
|
759
|
+
// never addChild: attrs are no longer structural children (D2/D5).
|
|
760
|
+
|
|
761
|
+
function parseAttrChild(
|
|
762
|
+
parent: MetaData,
|
|
763
|
+
attrType: string,
|
|
764
|
+
attrSubType: string,
|
|
765
|
+
attrData: Record<string, unknown>,
|
|
766
|
+
registry: TypeRegistry,
|
|
767
|
+
warnings: string[],
|
|
768
|
+
strict: boolean,
|
|
769
|
+
source: string | undefined,
|
|
770
|
+
path: string,
|
|
771
|
+
): void {
|
|
772
|
+
const attrName = attrData[RESERVED_KEY_NAME];
|
|
773
|
+
const attrValue = attrData[RESERVED_KEY_VALUE];
|
|
774
|
+
|
|
775
|
+
if (typeof attrName !== "string" || attrName === "") {
|
|
776
|
+
reportProblem(
|
|
777
|
+
`attr child at ${path} requires a non-empty "${RESERVED_KEY_NAME}" string`,
|
|
778
|
+
strict, warnings, source, path, "ERR_MISSING_REQUIRED_ATTR",
|
|
779
|
+
);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (attrValue === undefined) {
|
|
784
|
+
reportProblem(
|
|
785
|
+
`attr child "${attrName}" at ${path} is missing "${RESERVED_KEY_VALUE}"`,
|
|
786
|
+
strict, warnings, source, path, "ERR_MISSING_REQUIRED_ATTR",
|
|
787
|
+
);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Resolve the attr node's own subtype (fall back to base if unregistered).
|
|
792
|
+
const resolvedSubType =
|
|
793
|
+
registry.has(attrType, attrSubType) || !registry.has(attrType, SUBTYPE_BASE)
|
|
794
|
+
? attrSubType
|
|
795
|
+
: SUBTYPE_BASE;
|
|
796
|
+
const attrDef = registry.find(attrType, resolvedSubType);
|
|
797
|
+
|
|
798
|
+
const node = (attrDef !== undefined
|
|
799
|
+
? attrDef.factory(attrDef.typeId, attrName)
|
|
800
|
+
: new MetaAttr(new TypeId(attrType, resolvedSubType), attrName)) as MetaAttr;
|
|
801
|
+
|
|
802
|
+
try {
|
|
803
|
+
const coerced = node.coerce(attrValue);
|
|
804
|
+
const desugared = node.desugar(coerced);
|
|
805
|
+
node.setAttr(RESERVED_KEY_VALUE, desugared);
|
|
806
|
+
} catch (err) {
|
|
807
|
+
reportProblem(
|
|
808
|
+
`Failed to convert attr child "${attrName}" value at ${path}: ${(err as Error).message}`,
|
|
809
|
+
strict, warnings, source, path, "ERR_BAD_ATTR_VALUE",
|
|
810
|
+
);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
parent.setMetaAttr(node);
|
|
815
|
+
}
|