@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,370 @@
|
|
|
1
|
+
// Stateless validation passes for the MetaDataLoader pipeline.
|
|
2
|
+
//
|
|
3
|
+
// Each function takes a fully-merged MetaData root and returns errors or
|
|
4
|
+
// warnings. No loader state is read or written — these are pure functions.
|
|
5
|
+
//
|
|
6
|
+
// Exported: validateDataGridSortFields, validateFilterableHasIndex,
|
|
7
|
+
// validateOriginPaths (called by MetaDataLoader.load() in order).
|
|
8
|
+
// Private: _findObject, _findField, _findRelationship,
|
|
9
|
+
// _validateFromPath, _validateViaPath (helpers, not exported).
|
|
10
|
+
|
|
11
|
+
import type { MetaData } from "../shared/meta-data.js";
|
|
12
|
+
import { ParseError } from "../errors.js";
|
|
13
|
+
import {
|
|
14
|
+
TYPE_OBJECT,
|
|
15
|
+
TYPE_FIELD,
|
|
16
|
+
TYPE_LAYOUT,
|
|
17
|
+
TYPE_IDENTITY,
|
|
18
|
+
TYPE_ORIGIN,
|
|
19
|
+
TYPE_RELATIONSHIP,
|
|
20
|
+
} from "../shared/base-types.js";
|
|
21
|
+
import {
|
|
22
|
+
LAYOUT_SUBTYPE_DATA_GRID,
|
|
23
|
+
LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD,
|
|
24
|
+
LAYOUT_DATA_GRID_ATTR_FILTER,
|
|
25
|
+
} from "../presentation/layout/layout-constants.js";
|
|
26
|
+
import {
|
|
27
|
+
FIELD_ATTR_FILTERABLE,
|
|
28
|
+
} from "../core/field/field-constants.js";
|
|
29
|
+
import { FIELD_ATTR_DB_INDEXED } from "../persistence/db/db-constants.js";
|
|
30
|
+
import { IDENTITY_ATTR_FIELDS } from "../core/identity/identity-constants.js";
|
|
31
|
+
import {
|
|
32
|
+
ORIGIN_SUBTYPE_PASSTHROUGH,
|
|
33
|
+
ORIGIN_SUBTYPE_AGGREGATE,
|
|
34
|
+
ORIGIN_PASSTHROUGH_ATTR_FROM,
|
|
35
|
+
ORIGIN_PASSTHROUGH_ATTR_VIA,
|
|
36
|
+
ORIGIN_AGGREGATE_ATTR_OF,
|
|
37
|
+
ORIGIN_AGGREGATE_ATTR_VIA,
|
|
38
|
+
} from "../persistence/origin/origin-constants.js";
|
|
39
|
+
import { RELATIONSHIP_ATTR_OBJECT_REF } from "../core/relationship/relationship-constants.js";
|
|
40
|
+
import {
|
|
41
|
+
FILTER_COMPOSE_OR,
|
|
42
|
+
FILTER_COMPOSE_AND,
|
|
43
|
+
opsForSubType,
|
|
44
|
+
} from "../core/query/query-constants.js";
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Layout dataGrid @defaultSortField validation
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
export function validateDataGridSortFields(root: MetaData): ParseError[] {
|
|
51
|
+
const errors: ParseError[] = [];
|
|
52
|
+
for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
|
|
53
|
+
// Use children() so inherited fields (via extends:/super:) are
|
|
54
|
+
// visible when validating @defaultSortField references.
|
|
55
|
+
const effective = obj.children();
|
|
56
|
+
const fieldNames = new Set(
|
|
57
|
+
effective.filter((c) => c.type === TYPE_FIELD).map((f) => f.name),
|
|
58
|
+
);
|
|
59
|
+
for (const layout of effective.filter((c) => c.type === TYPE_LAYOUT && c.subType === LAYOUT_SUBTYPE_DATA_GRID)) {
|
|
60
|
+
const sortField = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD);
|
|
61
|
+
if (typeof sortField === "string" && !fieldNames.has(sortField)) {
|
|
62
|
+
errors.push(
|
|
63
|
+
new ParseError(
|
|
64
|
+
`dataGrid layout "${layout.name}" on entity "${obj.name}" has @defaultSortField "${sortField}" ` +
|
|
65
|
+
`but no such field exists on "${obj.name}". Available fields: ${[...fieldNames].join(", ")}`,
|
|
66
|
+
{ code: "ERR_BAD_DEFAULT_SORT_FIELD" },
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return errors;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// @filterable without index validation
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
export function validateFilterableHasIndex(root: MetaData): string[] {
|
|
80
|
+
const warnings: string[] = [];
|
|
81
|
+
for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
|
|
82
|
+
// Use children() so inherited fields and identities (via extends:/super:)
|
|
83
|
+
// are included when checking filterable-without-index.
|
|
84
|
+
const effective = obj.children();
|
|
85
|
+
// Build the set of field names that are part of any identity on this object.
|
|
86
|
+
const indexedFieldNames = new Set<string>();
|
|
87
|
+
for (const identity of effective.filter((c) => c.type === TYPE_IDENTITY)) {
|
|
88
|
+
const fields = identity.ownAttr(IDENTITY_ATTR_FIELDS);
|
|
89
|
+
if (typeof fields === "string") {
|
|
90
|
+
for (const name of fields.split(",")) indexedFieldNames.add(name.trim());
|
|
91
|
+
} else if (Array.isArray(fields)) {
|
|
92
|
+
for (const name of fields) if (typeof name === "string") indexedFieldNames.add(name);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const field of effective.filter((c) => c.type === TYPE_FIELD)) {
|
|
97
|
+
const filterable = field.ownAttr(FIELD_ATTR_FILTERABLE);
|
|
98
|
+
if (filterable !== true) continue;
|
|
99
|
+
if (field.ownAttr(FIELD_ATTR_DB_INDEXED) === true) continue;
|
|
100
|
+
if (indexedFieldNames.has(field.name)) continue;
|
|
101
|
+
warnings.push(
|
|
102
|
+
`[filterable-without-index] field "${obj.name}.${field.name}" has @filterable: true but is not ` +
|
|
103
|
+
`part of any identity. Filtering on this field will sequential-scan. Add @db.indexed: true ` +
|
|
104
|
+
`to the field (when supported), or remove @filterable: true.`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return warnings;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Origin path validation
|
|
113
|
+
//
|
|
114
|
+
// Walks every projection's fields, finds `origin` (TYPE_ORIGIN) children,
|
|
115
|
+
// and validates:
|
|
116
|
+
// - passthrough.@from resolves to an existing entity + field
|
|
117
|
+
// - aggregate.@of resolves to an existing entity + field
|
|
118
|
+
// - .@via paths resolve through real relationships, hopping entity-by-entity
|
|
119
|
+
// using each relationship's @objectRef
|
|
120
|
+
//
|
|
121
|
+
// Note: @agg vocabulary is validated by validateAttrSchema (A3 pass) via
|
|
122
|
+
// allowedValues on the origin.aggregate @agg attr schema — not here.
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
function _findObject(root: MetaData, name: string): MetaData | undefined {
|
|
126
|
+
return root.ownChildren().find((c) => c.type === TYPE_OBJECT && c.name === name);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function _findField(obj: MetaData, name: string): MetaData | undefined {
|
|
130
|
+
// Use children() so inherited fields (via extends:/super:) are included.
|
|
131
|
+
return obj.children().find((c) => c.type === TYPE_FIELD && c.name === name);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function _findRelationship(obj: MetaData, name: string): MetaData | undefined {
|
|
135
|
+
// Use children() so inherited relationships (via extends:/super:) are included.
|
|
136
|
+
return obj.children().find((c) => c.type === TYPE_RELATIONSHIP && c.name === name);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function _validateFromPath(
|
|
140
|
+
fromAttr: string,
|
|
141
|
+
root: MetaData,
|
|
142
|
+
projectionName: string,
|
|
143
|
+
fieldName: string,
|
|
144
|
+
errors: ParseError[],
|
|
145
|
+
label: string = "origin.passthrough.@from",
|
|
146
|
+
): void {
|
|
147
|
+
const dotIdx = fromAttr.indexOf(".");
|
|
148
|
+
if (dotIdx < 1 || dotIdx === fromAttr.length - 1) {
|
|
149
|
+
errors.push(
|
|
150
|
+
new ParseError(
|
|
151
|
+
`${label} "${fromAttr}" on ${projectionName}.${fieldName}: must be of form "Entity.field".`,
|
|
152
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const entityName = fromAttr.slice(0, dotIdx);
|
|
158
|
+
const targetFieldName = fromAttr.slice(dotIdx + 1);
|
|
159
|
+
const sourceObj = _findObject(root, entityName);
|
|
160
|
+
if (!sourceObj) {
|
|
161
|
+
errors.push(
|
|
162
|
+
new ParseError(
|
|
163
|
+
`${label} "${fromAttr}" on ${projectionName}.${fieldName}: no such entity "${entityName}".`,
|
|
164
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const sourceField = _findField(sourceObj, targetFieldName);
|
|
170
|
+
if (!sourceField) {
|
|
171
|
+
errors.push(
|
|
172
|
+
new ParseError(
|
|
173
|
+
`${label} "${fromAttr}" on ${projectionName}.${fieldName}: no such field "${targetFieldName}" on ${entityName}.`,
|
|
174
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function _validateViaPath(
|
|
181
|
+
viaAttr: string,
|
|
182
|
+
root: MetaData,
|
|
183
|
+
projectionName: string,
|
|
184
|
+
fieldName: string,
|
|
185
|
+
errors: ParseError[],
|
|
186
|
+
): void {
|
|
187
|
+
const segments = viaAttr.split(".");
|
|
188
|
+
if (segments.length < 2) {
|
|
189
|
+
errors.push(
|
|
190
|
+
new ParseError(
|
|
191
|
+
`origin.@via "${viaAttr}" on ${projectionName}.${fieldName}: must be of form "Entity.relationship[.relationship...]".`,
|
|
192
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
193
|
+
),
|
|
194
|
+
);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const [entityName, ...relSegments] = segments as [string, ...string[]];
|
|
198
|
+
let currentObj = _findObject(root, entityName);
|
|
199
|
+
if (!currentObj) {
|
|
200
|
+
errors.push(
|
|
201
|
+
new ParseError(
|
|
202
|
+
`origin.@via "${viaAttr}" on ${projectionName}.${fieldName}: no such entity "${entityName}".`,
|
|
203
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
for (const relName of relSegments) {
|
|
209
|
+
const rel = _findRelationship(currentObj, relName);
|
|
210
|
+
if (!rel) {
|
|
211
|
+
errors.push(
|
|
212
|
+
new ParseError(
|
|
213
|
+
`origin.@via "${viaAttr}" on ${projectionName}.${fieldName}: no such relationship "${relName}" on ${currentObj.name}.`,
|
|
214
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const refTarget = rel.ownAttr(RELATIONSHIP_ATTR_OBJECT_REF);
|
|
220
|
+
if (typeof refTarget !== "string" || refTarget === "") {
|
|
221
|
+
errors.push(
|
|
222
|
+
new ParseError(
|
|
223
|
+
`origin.@via "${viaAttr}" on ${projectionName}.${fieldName}: relationship "${relName}" on ${currentObj.name} is missing @objectRef.`,
|
|
224
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
225
|
+
),
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const nextObj = _findObject(root, refTarget);
|
|
230
|
+
if (!nextObj) {
|
|
231
|
+
errors.push(
|
|
232
|
+
new ParseError(
|
|
233
|
+
`origin.@via "${viaAttr}" on ${projectionName}.${fieldName}: relationship "${relName}" points to non-existent entity "${refTarget}".`,
|
|
234
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
currentObj = nextObj;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function validateOriginPaths(root: MetaData): ParseError[] {
|
|
244
|
+
const errors: ParseError[] = [];
|
|
245
|
+
for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
|
|
246
|
+
for (const field of obj.ownChildren().filter((c) => c.type === TYPE_FIELD)) {
|
|
247
|
+
for (const origin of field.ownChildren().filter((c) => c.type === TYPE_ORIGIN)) {
|
|
248
|
+
if (origin.subType === ORIGIN_SUBTYPE_PASSTHROUGH) {
|
|
249
|
+
const from = origin.ownAttr(ORIGIN_PASSTHROUGH_ATTR_FROM);
|
|
250
|
+
if (typeof from !== "string" || from === "") {
|
|
251
|
+
errors.push(
|
|
252
|
+
new ParseError(
|
|
253
|
+
`origin.passthrough on ${obj.name}.${field.name}: missing @from.`,
|
|
254
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
255
|
+
),
|
|
256
|
+
);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
_validateFromPath(from, root, obj.name, field.name, errors);
|
|
260
|
+
const via = origin.ownAttr(ORIGIN_PASSTHROUGH_ATTR_VIA);
|
|
261
|
+
if (typeof via === "string" && via !== "") {
|
|
262
|
+
_validateViaPath(via, root, obj.name, field.name, errors);
|
|
263
|
+
}
|
|
264
|
+
} else if (origin.subType === ORIGIN_SUBTYPE_AGGREGATE) {
|
|
265
|
+
const of_ = origin.ownAttr(ORIGIN_AGGREGATE_ATTR_OF);
|
|
266
|
+
if (typeof of_ !== "string" || of_ === "") {
|
|
267
|
+
errors.push(
|
|
268
|
+
new ParseError(
|
|
269
|
+
`origin.aggregate on ${obj.name}.${field.name}: missing @of.`,
|
|
270
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
271
|
+
),
|
|
272
|
+
);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
_validateFromPath(of_, root, obj.name, field.name, errors, "origin.aggregate.@of");
|
|
276
|
+
const via = origin.ownAttr(ORIGIN_AGGREGATE_ATTR_VIA);
|
|
277
|
+
if (typeof via !== "string" || via === "") {
|
|
278
|
+
errors.push(
|
|
279
|
+
new ParseError(
|
|
280
|
+
`origin.aggregate on ${obj.name}.${field.name}: missing @via (aggregates require a relationship path).`,
|
|
281
|
+
{ code: "ERR_INVALID_ORIGIN" },
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
_validateViaPath(via, root, obj.name, field.name, errors);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return errors;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
// Layout dataGrid @filter value validation
|
|
296
|
+
//
|
|
297
|
+
// Runs after extends: resolution (so inherited @filterable fields are visible)
|
|
298
|
+
// and after parse-time desugaring (so every clause is canonical { op: value }).
|
|
299
|
+
// Builds the allowlist from @filterable fields using OPS_BY_SUBTYPE, then checks
|
|
300
|
+
// every filtered field is filterable and every op is allowed for its subtype.
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
export function validateDataGridFilterValues(root: MetaData): ParseError[] {
|
|
304
|
+
const errors: ParseError[] = [];
|
|
305
|
+
for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
|
|
306
|
+
const effective = obj.children();
|
|
307
|
+
const allow = new Map<string, readonly string[]>();
|
|
308
|
+
for (const f of effective.filter((c) => c.type === TYPE_FIELD)) {
|
|
309
|
+
if (f.ownAttr(FIELD_ATTR_FILTERABLE) === true) {
|
|
310
|
+
allow.set(f.name, opsForSubType(f.subType));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
for (const layout of effective.filter(
|
|
314
|
+
(c) => c.type === TYPE_LAYOUT && c.subType === LAYOUT_SUBTYPE_DATA_GRID,
|
|
315
|
+
)) {
|
|
316
|
+
const filter = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_FILTER);
|
|
317
|
+
// Type errors (e.g. legacy string form) are reported by validateAttrSchema.
|
|
318
|
+
if (typeof filter !== "object" || filter === null || Array.isArray(filter)) continue;
|
|
319
|
+
checkFilterClauses(filter as Record<string, unknown>, allow, obj.name, layout.name, errors);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return errors;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function checkFilterClauses(
|
|
326
|
+
filter: Record<string, unknown>,
|
|
327
|
+
allow: Map<string, readonly string[]>,
|
|
328
|
+
entityName: string,
|
|
329
|
+
layoutName: string,
|
|
330
|
+
errors: ParseError[],
|
|
331
|
+
): void {
|
|
332
|
+
for (const [key, clause] of Object.entries(filter)) {
|
|
333
|
+
if (key === FILTER_COMPOSE_OR || key === FILTER_COMPOSE_AND) {
|
|
334
|
+
if (Array.isArray(clause)) {
|
|
335
|
+
for (const sub of clause) {
|
|
336
|
+
if (typeof sub === "object" && sub !== null && !Array.isArray(sub)) {
|
|
337
|
+
checkFilterClauses(sub as Record<string, unknown>, allow, entityName, layoutName, errors);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const allowedOps = allow.get(key);
|
|
344
|
+
if (allowedOps === undefined) {
|
|
345
|
+
errors.push(
|
|
346
|
+
new ParseError(
|
|
347
|
+
`dataGrid layout "${layoutName}" on entity "${entityName}" has @filter over ` +
|
|
348
|
+
`non-filterable field "${key}". Filterable fields: ${[...allow.keys()].join(", ") || "(none)"}`,
|
|
349
|
+
{ code: "ERR_BAD_ATTR_FILTER" },
|
|
350
|
+
),
|
|
351
|
+
);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
// After parse-time desugaring (FilterAttr.desugar), every non-composition field clause
|
|
355
|
+
// is canonical { op: value } — a bare scalar should not reach here; the object guard is defensive.
|
|
356
|
+
if (typeof clause === "object" && clause !== null && !Array.isArray(clause)) {
|
|
357
|
+
for (const op of Object.keys(clause)) {
|
|
358
|
+
if (!allowedOps.includes(op)) {
|
|
359
|
+
errors.push(
|
|
360
|
+
new ParseError(
|
|
361
|
+
`dataGrid layout "${layoutName}" on entity "${entityName}" @filter uses disallowed ` +
|
|
362
|
+
`op "${key}.${op}". Allowed ops for "${key}": ${allowedOps.join(", ")}`,
|
|
363
|
+
{ code: "ERR_BAD_ATTR_FILTER" },
|
|
364
|
+
),
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
package/src/naming.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { MetaData } from "./shared/meta-data.js";
|
|
2
|
+
import { TYPE_FIELD, TYPE_SOURCE } from "./shared/base-types.js";
|
|
3
|
+
import { PACKAGE_SEPARATOR } from "./shared/structural.js";
|
|
4
|
+
import { FIELD_ATTR_DB_COLUMN } from "./persistence/db/db-constants.js";
|
|
5
|
+
import {
|
|
6
|
+
SOURCE_SUBTYPE_DB_TABLE,
|
|
7
|
+
SOURCE_SUBTYPE_DB_VIEW,
|
|
8
|
+
SOURCE_DB_TABLE_ATTR_NAME,
|
|
9
|
+
SOURCE_ATTR_SCHEMA,
|
|
10
|
+
} from "./persistence/source/source-constants.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Strip the package prefix from a metadata-qualified name
|
|
14
|
+
* (e.g. "pkg::Name" → "Name"). Returns the input unchanged if no
|
|
15
|
+
* package separator is present. Single canonical helper consumed by
|
|
16
|
+
* both find-reference (cross-entity lookup) and codegen-ts (FQN
|
|
17
|
+
* normalization in generated code).
|
|
18
|
+
*/
|
|
19
|
+
export function stripPackage(name: string | undefined): string {
|
|
20
|
+
if (!name) return "";
|
|
21
|
+
const idx = name.lastIndexOf(PACKAGE_SEPARATOR);
|
|
22
|
+
return idx === -1 ? name : name.slice(idx + PACKAGE_SEPARATOR.length);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function toSnakeCase(s: string): string {
|
|
26
|
+
return s
|
|
27
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2")
|
|
28
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
29
|
+
.toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function pluralize(s: string): string {
|
|
33
|
+
if (/(s|x|z|ch|sh)$/i.test(s)) return s + "es";
|
|
34
|
+
if (/[^aeiou]y$/i.test(s)) return s.slice(0, -1) + "ies";
|
|
35
|
+
return s + "s";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveTableName(entity: MetaData): string {
|
|
39
|
+
const source = entity.ownChildren().find(
|
|
40
|
+
(c) => c.type === TYPE_SOURCE && c.subType === SOURCE_SUBTYPE_DB_TABLE,
|
|
41
|
+
);
|
|
42
|
+
const name = source?.ownAttr(SOURCE_DB_TABLE_ATTR_NAME);
|
|
43
|
+
if (typeof name === "string" && name !== "") return name;
|
|
44
|
+
return pluralize(toSnakeCase(entity.name));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function resolveColumnName(field: MetaData): string {
|
|
48
|
+
const attr = field.ownAttr(FIELD_ATTR_DB_COLUMN);
|
|
49
|
+
if (typeof attr === "string") return attr;
|
|
50
|
+
return toSnakeCase(field.name);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns the DB schema declared on an entity's source[dbTable] or source[dbView] child,
|
|
55
|
+
* or undefined if no @schema attr is set or no source child exists. Callers decide what
|
|
56
|
+
* "undefined" means for their dialect — Postgres treats it as the default public schema,
|
|
57
|
+
* SQLite treats it as the only allowed value (no schema concept).
|
|
58
|
+
*/
|
|
59
|
+
export function resolveTableSchema(entity: MetaData): string | undefined {
|
|
60
|
+
const source = entity.ownChildren().find(
|
|
61
|
+
(c) => c.type === TYPE_SOURCE
|
|
62
|
+
&& (c.subType === SOURCE_SUBTYPE_DB_TABLE || c.subType === SOURCE_SUBTYPE_DB_VIEW),
|
|
63
|
+
);
|
|
64
|
+
if (!source) return undefined;
|
|
65
|
+
const schema = source.ownAttr(SOURCE_ATTR_SCHEMA);
|
|
66
|
+
if (typeof schema === "string" && schema !== "") return schema;
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Per-entity {jsName ↔ dbColumn} map. Built once per entity to avoid re-walking children on every row. */
|
|
71
|
+
export interface EntityNameMap {
|
|
72
|
+
jsToDb: Map<string, string>;
|
|
73
|
+
dbToJs: Map<string, string>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function buildNameMap(entity: MetaData): EntityNameMap {
|
|
77
|
+
const jsToDb = new Map<string, string>();
|
|
78
|
+
const dbToJs = new Map<string, string>();
|
|
79
|
+
for (const child of entity.ownChildren()) {
|
|
80
|
+
if (child.type !== TYPE_FIELD) continue;
|
|
81
|
+
const dbCol = resolveColumnName(child);
|
|
82
|
+
jsToDb.set(child.name, dbCol);
|
|
83
|
+
dbToJs.set(dbCol, child.name);
|
|
84
|
+
}
|
|
85
|
+
return { jsToDb, dbToJs };
|
|
86
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Metadata-driven object serializer.
|
|
2
|
+
//
|
|
3
|
+
// objectToJson / jsonToObject convert application-object instances <-> JSON
|
|
4
|
+
// driven by a MetaObject's field metadata — no per-entity code. Dispatch is on
|
|
5
|
+
// each field's DataType. Plain objects in, plain objects out (the caller does
|
|
6
|
+
// JSON.parse / JSON.stringify). Best-effort and total: neither function throws
|
|
7
|
+
// for malformed instance data, and there is no validation (settled in the
|
|
8
|
+
// spec — Java's object reader does not validate either).
|
|
9
|
+
|
|
10
|
+
import type { MetaData } from "./shared/meta-data.js";
|
|
11
|
+
import type { MetaObject } from "./core/object/meta-object.js";
|
|
12
|
+
import type { MetaField } from "./core/field/meta-field.js";
|
|
13
|
+
import {
|
|
14
|
+
DATA_TYPE_BOOLEAN,
|
|
15
|
+
DATA_TYPE_INT,
|
|
16
|
+
DATA_TYPE_LONG,
|
|
17
|
+
DATA_TYPE_DOUBLE,
|
|
18
|
+
DATA_TYPE_STRING,
|
|
19
|
+
DATA_TYPE_DATE,
|
|
20
|
+
DATA_TYPE_OBJECT,
|
|
21
|
+
} from "./data-type.js";
|
|
22
|
+
import { TYPE_OBJECT } from "./shared/base-types.js";
|
|
23
|
+
|
|
24
|
+
const TYPE_DISCRIMINATOR = "@type";
|
|
25
|
+
|
|
26
|
+
export interface ObjectSerializeOptions {
|
|
27
|
+
/** Emit the `@type` discriminator property. Default true. */
|
|
28
|
+
emitType?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Serialize an application-object instance to wire JSON, driven by `mo`. */
|
|
32
|
+
export function objectToJson(
|
|
33
|
+
mo: MetaObject,
|
|
34
|
+
instance: Record<string, unknown>,
|
|
35
|
+
opts?: ObjectSerializeOptions,
|
|
36
|
+
): Record<string, unknown> {
|
|
37
|
+
const out: Record<string, unknown> = {};
|
|
38
|
+
if (opts?.emitType ?? true) {
|
|
39
|
+
out[TYPE_DISCRIMINATOR] = mo.name;
|
|
40
|
+
}
|
|
41
|
+
for (const field of mo.fields()) {
|
|
42
|
+
const raw = instance[field.name];
|
|
43
|
+
if (raw === undefined) continue; // absent field → omit
|
|
44
|
+
out[field.name] = field.isArray
|
|
45
|
+
? mapArray(raw, (el) => toJsonValue(field, el, mo))
|
|
46
|
+
: toJsonValue(field, raw, mo);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Parse wire JSON into an application-object instance, driven by `mo`. */
|
|
52
|
+
export function jsonToObject(
|
|
53
|
+
mo: MetaObject,
|
|
54
|
+
json: Record<string, unknown>,
|
|
55
|
+
): Record<string, unknown> {
|
|
56
|
+
const out: Record<string, unknown> = {};
|
|
57
|
+
for (const field of mo.fields()) {
|
|
58
|
+
const raw = json[field.name];
|
|
59
|
+
if (raw === undefined) continue;
|
|
60
|
+
out[field.name] = field.isArray
|
|
61
|
+
? mapArray(raw, (el) => fromJsonValue(field, el, mo))
|
|
62
|
+
: fromJsonValue(field, raw, mo);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --- write-side conversion -------------------------------------------------
|
|
68
|
+
|
|
69
|
+
function toJsonValue(field: MetaField, raw: unknown, mo: MetaObject): unknown {
|
|
70
|
+
switch (field.dataType) {
|
|
71
|
+
case DATA_TYPE_DATE:
|
|
72
|
+
return toIsoString(raw);
|
|
73
|
+
case DATA_TYPE_OBJECT: {
|
|
74
|
+
const target = resolveObjectRef(field, mo);
|
|
75
|
+
if (target === undefined || !isPlainObject(raw)) return raw;
|
|
76
|
+
// Nested objects always carry their own @type (a generic reader needs
|
|
77
|
+
// it); `emitType: false` suppresses only the top-level discriminator.
|
|
78
|
+
return objectToJson(target, raw);
|
|
79
|
+
}
|
|
80
|
+
case DATA_TYPE_BOOLEAN:
|
|
81
|
+
case DATA_TYPE_INT:
|
|
82
|
+
case DATA_TYPE_LONG:
|
|
83
|
+
case DATA_TYPE_DOUBLE:
|
|
84
|
+
case DATA_TYPE_STRING:
|
|
85
|
+
default:
|
|
86
|
+
return raw; // scalars: already JSON-native, pass through
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// --- read-side conversion --------------------------------------------------
|
|
91
|
+
|
|
92
|
+
function fromJsonValue(field: MetaField, raw: unknown, mo: MetaObject): unknown {
|
|
93
|
+
switch (field.dataType) {
|
|
94
|
+
case DATA_TYPE_BOOLEAN:
|
|
95
|
+
return coerceBoolean(raw);
|
|
96
|
+
case DATA_TYPE_INT:
|
|
97
|
+
case DATA_TYPE_LONG:
|
|
98
|
+
case DATA_TYPE_DOUBLE:
|
|
99
|
+
return coerceNumeric(raw);
|
|
100
|
+
case DATA_TYPE_OBJECT: {
|
|
101
|
+
const target = resolveObjectRef(field, mo);
|
|
102
|
+
if (target === undefined || !isPlainObject(raw)) return raw;
|
|
103
|
+
return jsonToObject(target, raw);
|
|
104
|
+
}
|
|
105
|
+
case DATA_TYPE_STRING:
|
|
106
|
+
case DATA_TYPE_DATE: // kept as the ISO string; the UI builds its own Date
|
|
107
|
+
default:
|
|
108
|
+
return raw;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --- helpers ---------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
function mapArray(raw: unknown, convert: (el: unknown) => unknown): unknown {
|
|
115
|
+
return Array.isArray(raw) ? raw.map(convert) : raw;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
119
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function toIsoString(raw: unknown): unknown {
|
|
123
|
+
if (raw instanceof Date) return raw.toISOString();
|
|
124
|
+
if (typeof raw === "number") return new Date(raw).toISOString();
|
|
125
|
+
return raw; // a string passes through; anything else best-effort
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function coerceBoolean(raw: unknown): unknown {
|
|
129
|
+
if (typeof raw === "boolean") return raw;
|
|
130
|
+
if (raw === "true") return true;
|
|
131
|
+
if (raw === "false") return false;
|
|
132
|
+
return raw;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function coerceNumeric(raw: unknown): unknown {
|
|
136
|
+
if (typeof raw === "number") return raw;
|
|
137
|
+
if (typeof raw === "string" && raw.trim() !== "" && Number.isFinite(Number(raw))) {
|
|
138
|
+
return Number(raw);
|
|
139
|
+
}
|
|
140
|
+
return raw;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Resolve an object-typed field's `@objectRef` to its target MetaObject by
|
|
144
|
+
* walking to the tree root and looking up the named object child. Returns
|
|
145
|
+
* undefined when there is no ref or it does not resolve (caller passes the
|
|
146
|
+
* raw value through — best-effort). */
|
|
147
|
+
function resolveObjectRef(field: MetaField, mo: MetaObject): MetaObject | undefined {
|
|
148
|
+
const ref = field.objectRef;
|
|
149
|
+
if (ref === undefined) return undefined;
|
|
150
|
+
const found: MetaData | undefined = mo.root().ownChildByTypeAndName(TYPE_OBJECT, ref);
|
|
151
|
+
if (found === undefined) return undefined;
|
|
152
|
+
return found as MetaObject;
|
|
153
|
+
}
|