@prisma/orm-family-sql 0.16.0-dev.36
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 +201 -0
- package/README.md +28 -0
- package/dist/ast-BstHjAoG.mjs +325 -0
- package/dist/ast-BstHjAoG.mjs.map +1 -0
- package/dist/ast-DSuzmsWn.d.mts +287 -0
- package/dist/ast-DSuzmsWn.d.mts.map +1 -0
- package/dist/authoring-type-constructors-Blqoa2Ua-DyCmD6mp.mjs +316 -0
- package/dist/authoring-type-constructors-Blqoa2Ua-DyCmD6mp.mjs.map +1 -0
- package/dist/build-contract-DBCz_YSs-DO0wMHai.mjs +778 -0
- package/dist/build-contract-DBCz_YSs-DO0wMHai.mjs.map +1 -0
- package/dist/builder.d.mts +3 -0
- package/dist/builder.mjs +1 -0
- package/dist/builder__runtime.d.mts +42 -0
- package/dist/builder__runtime.d.mts.map +1 -0
- package/dist/builder__runtime.mjs +955 -0
- package/dist/builder__runtime.mjs.map +1 -0
- package/dist/builder__types.d.mts +3 -0
- package/dist/builder__types.mjs +1 -0
- package/dist/canonicalization-hooks-CYnW_sGn.d.mts +10 -0
- package/dist/canonicalization-hooks-CYnW_sGn.d.mts.map +1 -0
- package/dist/canonicalization-hooks-DfIc_lOO.mjs +87 -0
- package/dist/canonicalization-hooks-DfIc_lOO.mjs.map +1 -0
- package/dist/codec-descriptor-registry-BJVJRftS.mjs +96 -0
- package/dist/codec-descriptor-registry-BJVJRftS.mjs.map +1 -0
- package/dist/codec-types-MOaceoPi-C5eL2sao.d.mts +116 -0
- package/dist/codec-types-MOaceoPi-C5eL2sao.d.mts.map +1 -0
- package/dist/config-types-47qB33rW.d.mts +21 -0
- package/dist/config-types-47qB33rW.d.mts.map +1 -0
- package/dist/config-types-N7PJc6VN.mjs +65 -0
- package/dist/config-types-N7PJc6VN.mjs.map +1 -0
- package/dist/contract-builder-MvJJrQ-5.d.mts +1306 -0
- package/dist/contract-builder-MvJJrQ-5.d.mts.map +1 -0
- package/dist/contract-builder-YjMQcvZ1.mjs +1615 -0
- package/dist/contract-builder-YjMQcvZ1.mjs.map +1 -0
- package/dist/contract-emitter.d.mts +25 -0
- package/dist/contract-emitter.d.mts.map +1 -0
- package/dist/contract-emitter.mjs +2 -0
- package/dist/contract-errors-BCN6RVI5-5IHBF_lg.mjs +9 -0
- package/dist/contract-errors-BCN6RVI5-5IHBF_lg.mjs.map +1 -0
- package/dist/contract-psl.d.mts +65 -0
- package/dist/contract-psl.d.mts.map +1 -0
- package/dist/contract-psl.mjs +2 -0
- package/dist/contract-psl__provider.d.mts +23 -0
- package/dist/contract-psl__provider.d.mts.map +1 -0
- package/dist/contract-psl__provider.mjs +105 -0
- package/dist/contract-psl__provider.mjs.map +1 -0
- package/dist/contract-ts.d.mts +3 -0
- package/dist/contract-ts.mjs +4 -0
- package/dist/contract-ts__config-types.d.mts +2 -0
- package/dist/contract-ts__config-types.mjs +2 -0
- package/dist/contract-ts__contract-builder.d.mts +2 -0
- package/dist/contract-ts__contract-builder.mjs +3 -0
- package/dist/contract-view-BMR5IWk4.mjs +39 -0
- package/dist/contract-view-BMR5IWk4.mjs.map +1 -0
- package/dist/contract-view-BbWQp6r4.d.mts +61 -0
- package/dist/contract-view-BbWQp6r4.d.mts.map +1 -0
- package/dist/contract.d.mts +26 -0
- package/dist/contract.mjs +17 -0
- package/dist/contract__canonicalization-hooks.d.mts +2 -0
- package/dist/contract__canonicalization-hooks.mjs +2 -0
- package/dist/contract__contract-view.d.mts +2 -0
- package/dist/contract__contract-view.mjs +2 -0
- package/dist/contract__entity-handle-lowering-hook.d.mts +2 -0
- package/dist/contract__entity-handle-lowering-hook.mjs +2 -0
- package/dist/contract__entity-kinds.d.mts +2 -0
- package/dist/contract__entity-kinds.mjs +2 -0
- package/dist/contract__factories.d.mts +2 -0
- package/dist/contract__factories.mjs +2 -0
- package/dist/contract__foreign-key-materialization.d.mts +2 -0
- package/dist/contract__foreign-key-materialization.mjs +2 -0
- package/dist/contract__index-naming.d.mts +2 -0
- package/dist/contract__index-naming.mjs +2 -0
- package/dist/contract__index-type-validation.d.mts +2 -0
- package/dist/contract__index-type-validation.mjs +2 -0
- package/dist/contract__index-types.d.mts +3 -0
- package/dist/contract__index-types.mjs +2 -0
- package/dist/contract__pack-types.d.mts +2 -0
- package/dist/contract__pack-types.mjs +1 -0
- package/dist/contract__referential-action-sql.d.mts +2 -0
- package/dist/contract__referential-action-sql.mjs +2 -0
- package/dist/contract__resolve-storage-table.d.mts +2 -0
- package/dist/contract__resolve-storage-table.mjs +2 -0
- package/dist/contract__types.d.mts +11 -0
- package/dist/contract__types.mjs +4 -0
- package/dist/contract__validators.d.mts +2 -0
- package/dist/contract__validators.mjs +3 -0
- package/dist/contract__value-set-derivation-hook.d.mts +2 -0
- package/dist/contract__value-set-derivation-hook.mjs +2 -0
- package/dist/control-B7iS4jv4.mjs +1326 -0
- package/dist/control-B7iS4jv4.mjs.map +1 -0
- package/dist/control-BHmsZudH.d.mts +948 -0
- package/dist/control-BHmsZudH.d.mts.map +1 -0
- package/dist/control-adapter-4y1O8Ps3.d.mts +1 -0
- package/dist/control-adapter-C99J7aEF-C-17lFY8.d.mts +191 -0
- package/dist/control-adapter-C99J7aEF-C-17lFY8.d.mts.map +1 -0
- package/dist/db-DhMsmBS--DKENDi3l.d.mts +365 -0
- package/dist/db-DhMsmBS--DKENDi3l.d.mts.map +1 -0
- package/dist/ddl-types-BMp2bWVS-D1waZJ4n.mjs +153 -0
- package/dist/ddl-types-BMp2bWVS-D1waZJ4n.mjs.map +1 -0
- package/dist/ddl-types-rrMsf77U-DGctYf-T.d.mts +149 -0
- package/dist/ddl-types-rrMsf77U-DGctYf-T.d.mts.map +1 -0
- package/dist/diff-B16bL_IR.mjs +13 -0
- package/dist/diff-B16bL_IR.mjs.map +1 -0
- package/dist/diff-DhlzHOMD.d.mts +104 -0
- package/dist/diff-DhlzHOMD.d.mts.map +1 -0
- package/dist/dist-C_q1_QR9.mjs +9 -0
- package/dist/dist-C_q1_QR9.mjs.map +1 -0
- package/dist/dist-DrScXbhq.mjs +414 -0
- package/dist/dist-DrScXbhq.mjs.map +1 -0
- package/dist/dist-x6jvtkKM.mjs +68 -0
- package/dist/dist-x6jvtkKM.mjs.map +1 -0
- package/dist/entity-handle-lowering-hook-CIvRKBfa.d.mts +92 -0
- package/dist/entity-handle-lowering-hook-CIvRKBfa.d.mts.map +1 -0
- package/dist/entity-handle-lowering-hook-DwLIKNiS.mjs +11 -0
- package/dist/entity-handle-lowering-hook-DwLIKNiS.mjs.map +1 -0
- package/dist/entity-kinds-DKxwKolf-CcsY-iYV.mjs +136 -0
- package/dist/entity-kinds-DKxwKolf-CcsY-iYV.mjs.map +1 -0
- package/dist/entity-kinds-Dh6VH4uK.d.mts +29 -0
- package/dist/entity-kinds-Dh6VH4uK.d.mts.map +1 -0
- package/dist/errors-B5g0xWro-mWH2C07F.mjs +9 -0
- package/dist/errors-B5g0xWro-mWH2C07F.mjs.map +1 -0
- package/dist/errors.d.mts +63 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +2 -0
- package/dist/expression-DIf8F4Mc.d.mts +167 -0
- package/dist/expression-DIf8F4Mc.d.mts.map +1 -0
- package/dist/expression-RUO52bJB.mjs +109 -0
- package/dist/expression-RUO52bJB.mjs.map +1 -0
- package/dist/factories-B7tA4y9D.d.mts +39 -0
- package/dist/factories-B7tA4y9D.d.mts.map +1 -0
- package/dist/factories-CDG9zBxC.mjs +77 -0
- package/dist/factories-CDG9zBxC.mjs.map +1 -0
- package/dist/family.d.mts +13 -0
- package/dist/family.mjs +12 -0
- package/dist/family__control-adapter.d.mts +3 -0
- package/dist/family__control-adapter.mjs +1 -0
- package/dist/family__control.d.mts +4 -0
- package/dist/family__control.mjs +4 -0
- package/dist/family__diff.d.mts +3 -0
- package/dist/family__diff.mjs +3 -0
- package/dist/family__ir.d.mts +2 -0
- package/dist/family__ir.mjs +3 -0
- package/dist/family__migration.d.mts +2 -0
- package/dist/family__migration.mjs +2 -0
- package/dist/family__pack.d.mts +249 -0
- package/dist/family__pack.d.mts.map +1 -0
- package/dist/family__pack.mjs +18 -0
- package/dist/family__pack.mjs.map +1 -0
- package/dist/family__psl-infer.d.mts +2 -0
- package/dist/family__psl-infer.mjs +2 -0
- package/dist/family__runtime.d.mts +3 -0
- package/dist/family__runtime.mjs +3 -0
- package/dist/family__verify.d.mts +2 -0
- package/dist/family__verify.mjs +2 -0
- package/dist/foreign-key-BSy9u4O4-BnsOe5Nf.d.mts +85 -0
- package/dist/foreign-key-BSy9u4O4-BnsOe5Nf.d.mts.map +1 -0
- package/dist/foreign-key-materialization-Ca-wRbmj.d.mts +65 -0
- package/dist/foreign-key-materialization-Ca-wRbmj.d.mts.map +1 -0
- package/dist/foreign-key-materialization-HI7BIQrG.mjs +72 -0
- package/dist/foreign-key-materialization-HI7BIQrG.mjs.map +1 -0
- package/dist/index-Bb9U36N4.d.mts +511 -0
- package/dist/index-Bb9U36N4.d.mts.map +1 -0
- package/dist/index-BgaTJRxj.d.mts +25 -0
- package/dist/index-BgaTJRxj.d.mts.map +1 -0
- package/dist/index-naming-BZWoh45T.d.mts +62 -0
- package/dist/index-naming-BZWoh45T.d.mts.map +1 -0
- package/dist/index-naming-BoGBcMmI-DiJAr66H.mjs +108 -0
- package/dist/index-naming-BoGBcMmI-DiJAr66H.mjs.map +1 -0
- package/dist/index-type-validation-COH4d78M.mjs +17 -0
- package/dist/index-type-validation-COH4d78M.mjs.map +1 -0
- package/dist/index-type-validation-DN0L2mqm.d.mts +10 -0
- package/dist/index-type-validation-DN0L2mqm.d.mts.map +1 -0
- package/dist/index-types-CNhHgKHi.d.mts +1 -0
- package/dist/index-types-DJQ_xwaE.mjs +40 -0
- package/dist/index-types-DJQ_xwaE.mjs.map +1 -0
- package/dist/index-types-DPVhsW0v-D-BikM4h.d.mts +31 -0
- package/dist/index-types-DPVhsW0v-D-BikM4h.d.mts.map +1 -0
- package/dist/interpreter-5WgpL-ax-_nqHLUTr.mjs +3526 -0
- package/dist/interpreter-5WgpL-ax-_nqHLUTr.mjs.map +1 -0
- package/dist/ir-B3bKMnGQ.d.mts +123 -0
- package/dist/ir-B3bKMnGQ.d.mts.map +1 -0
- package/dist/ir-I-xciGUH.mjs +36 -0
- package/dist/ir-I-xciGUH.mjs.map +1 -0
- package/dist/lane-query-builder.d.mts +236 -0
- package/dist/lane-query-builder.d.mts.map +1 -0
- package/dist/lane-query-builder.mjs +67 -0
- package/dist/lane-query-builder.mjs.map +1 -0
- package/dist/middleware-B4XbF4d4-tfwBl6mX.d.mts +119 -0
- package/dist/middleware-B4XbF4d4-tfwBl6mX.d.mts.map +1 -0
- package/dist/middleware-ByRsXxxq.d.mts +1 -0
- package/dist/middleware-CMr4CHNz-DHIUdxlm.mjs +80 -0
- package/dist/middleware-CMr4CHNz-DHIUdxlm.mjs.map +1 -0
- package/dist/migration-CK4oePIo.mjs +44 -0
- package/dist/migration-CK4oePIo.mjs.map +1 -0
- package/dist/migration-CW0ZJLxP.d.mts +44 -0
- package/dist/migration-CW0ZJLxP.d.mts.map +1 -0
- package/dist/naming-BwqqVcZy-BgTObIQw.mjs +177 -0
- package/dist/naming-BwqqVcZy-BgTObIQw.mjs.map +1 -0
- package/dist/naming-jLgdauLi-C84G_UHi.d.mts +133 -0
- package/dist/naming-jLgdauLi-C84G_UHi.d.mts.map +1 -0
- package/dist/operations.d.mts +2 -0
- package/dist/operations.mjs +2 -0
- package/dist/orm-client.d.mts +1316 -0
- package/dist/orm-client.d.mts.map +1 -0
- package/dist/orm-client.mjs +4544 -0
- package/dist/orm-client.mjs.map +1 -0
- package/dist/pack-types-BcLTpwbv.d.mts +14 -0
- package/dist/pack-types-BcLTpwbv.d.mts.map +1 -0
- package/dist/plan-BTD8xFfa-Dd_K-A1R.d.mts +45 -0
- package/dist/plan-BTD8xFfa-Dd_K-A1R.d.mts.map +1 -0
- package/dist/plan-CkpcgUaM.mjs +34 -0
- package/dist/plan-CkpcgUaM.mjs.map +1 -0
- package/dist/plan-Cm4jaoNM.d.mts +2 -0
- package/dist/psl-infer-6je61Z6J.d.mts +119 -0
- package/dist/psl-infer-6je61Z6J.d.mts.map +1 -0
- package/dist/psl-infer-DGftl-HW.mjs +362 -0
- package/dist/psl-infer-DGftl-HW.mjs.map +1 -0
- package/dist/query-lane-context-Bz8XJZ45.d.mts +1 -0
- package/dist/query-lane-context-DtdVMqoz-C2Tj5QHA.d.mts +94 -0
- package/dist/query-lane-context-DtdVMqoz-C2Tj5QHA.d.mts.map +1 -0
- package/dist/referential-action-sql-Csd6dA8G.d.mts +12 -0
- package/dist/referential-action-sql-Csd6dA8G.d.mts.map +1 -0
- package/dist/referential-action-sql-DSqkrySN.mjs +17 -0
- package/dist/referential-action-sql-DSqkrySN.mjs.map +1 -0
- package/dist/relational-core.d.mts +13 -0
- package/dist/relational-core.mjs +8 -0
- package/dist/relational-core__ast.d.mts +5 -0
- package/dist/relational-core__ast.mjs +5 -0
- package/dist/relational-core__codec-descriptor-registry.d.mts +39 -0
- package/dist/relational-core__codec-descriptor-registry.d.mts.map +1 -0
- package/dist/relational-core__codec-descriptor-registry.mjs +2 -0
- package/dist/relational-core__contract-free.d.mts +220 -0
- package/dist/relational-core__contract-free.d.mts.map +1 -0
- package/dist/relational-core__contract-free.mjs +393 -0
- package/dist/relational-core__contract-free.mjs.map +1 -0
- package/dist/relational-core__expression.d.mts +2 -0
- package/dist/relational-core__expression.mjs +2 -0
- package/dist/relational-core__middleware.d.mts +3 -0
- package/dist/relational-core__middleware.mjs +2 -0
- package/dist/relational-core__plan.d.mts +4 -0
- package/dist/relational-core__plan.mjs +2 -0
- package/dist/relational-core__query-lane-context.d.mts +3 -0
- package/dist/relational-core__query-lane-context.mjs +1 -0
- package/dist/relational-core__types.d.mts +3 -0
- package/dist/relational-core__types.mjs +1 -0
- package/dist/resolve-storage-table-DUwyRJmP.d.mts +21 -0
- package/dist/resolve-storage-table-DUwyRJmP.d.mts.map +1 -0
- package/dist/resolve-storage-table-DZsfnzeW.mjs +46 -0
- package/dist/resolve-storage-table-DZsfnzeW.mjs.map +1 -0
- package/dist/runtime-DNewRuec.d.mts +46 -0
- package/dist/runtime-DNewRuec.d.mts.map +1 -0
- package/dist/runtime-tIHpGCcG.mjs +55 -0
- package/dist/runtime-tIHpGCcG.mjs.map +1 -0
- package/dist/runtime.d.mts +4 -0
- package/dist/runtime.mjs +1606 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/schema-differ-DpG_gnur-g5_iks8G.d.mts +47 -0
- package/dist/schema-differ-DpG_gnur-g5_iks8G.d.mts.map +1 -0
- package/dist/schema-ir.d.mts +2 -0
- package/dist/schema-ir.mjs +2 -0
- package/dist/schema-ir__naming.d.mts +2 -0
- package/dist/schema-ir__naming.mjs +2 -0
- package/dist/schema-ir__types.d.mts +3 -0
- package/dist/schema-ir__types.mjs +2 -0
- package/dist/schema-verify-vtFZbmGq-BNfTr6fU.mjs +222 -0
- package/dist/schema-verify-vtFZbmGq-BNfTr6fU.mjs.map +1 -0
- package/dist/serialized-index-B96AwqtU-DcqcB4q_.d.mts +26 -0
- package/dist/serialized-index-B96AwqtU-DcqcB4q_.d.mts.map +1 -0
- package/dist/serialized-index-Dp5_EjXD-DMsw8jf3.mjs +64 -0
- package/dist/serialized-index-Dp5_EjXD-DMsw8jf3.mjs.map +1 -0
- package/dist/sql-contract-serializer-2oBWuxTe-B9hzBBv1.mjs +171 -0
- package/dist/sql-contract-serializer-2oBWuxTe-B9hzBBv1.mjs.map +1 -0
- package/dist/sql-execution-plan-CJmsCP7n-DoEpNn7r.d.mts +32 -0
- package/dist/sql-execution-plan-CJmsCP7n-DoEpNn7r.d.mts.map +1 -0
- package/dist/sql-index-DsI8HAeK-BJsn-jvD.d.mts +72 -0
- package/dist/sql-index-DsI8HAeK-BJsn-jvD.d.mts.map +1 -0
- package/dist/sql-node-kEz6YFZo-v2F7delx.d.mts +42 -0
- package/dist/sql-node-kEz6YFZo-v2F7delx.d.mts.map +1 -0
- package/dist/sql-storage-B0QwiTg6-B8DVctJm.d.mts +171 -0
- package/dist/sql-storage-B0QwiTg6-B8DVctJm.d.mts.map +1 -0
- package/dist/storage-table-BBaAmanc-BwR37LMA.d.mts +130 -0
- package/dist/storage-table-BBaAmanc-BwR37LMA.d.mts.map +1 -0
- package/dist/storage-table-_LWDmPfv-DFhU2dML.mjs +277 -0
- package/dist/storage-table-_LWDmPfv-DFhU2dML.mjs.map +1 -0
- package/dist/storage-value-set-BGlj5Uus-CvZD--GG.d.mts +41 -0
- package/dist/storage-value-set-BGlj5Uus-CvZD--GG.d.mts.map +1 -0
- package/dist/timestamp-now-generator-DRXygu32-DN9Qb1uI.mjs +173 -0
- package/dist/timestamp-now-generator-DRXygu32-DN9Qb1uI.mjs.map +1 -0
- package/dist/types-BchwQpmO.d.mts +16 -0
- package/dist/types-BchwQpmO.d.mts.map +1 -0
- package/dist/types-BgkZru_1-H7yfikSQ.mjs +1444 -0
- package/dist/types-BgkZru_1-H7yfikSQ.mjs.map +1 -0
- package/dist/types-CMMQS6aG.mjs +111 -0
- package/dist/types-CMMQS6aG.mjs.map +1 -0
- package/dist/types-CuDrWc5v-ISVXBmBs.d.mts +134 -0
- package/dist/types-CuDrWc5v-ISVXBmBs.d.mts.map +1 -0
- package/dist/types-CvbBw1Jo-3KQdErJY.d.mts +688 -0
- package/dist/types-CvbBw1Jo-3KQdErJY.d.mts.map +1 -0
- package/dist/types-DIBA80mc-wf5wLmON.d.mts +723 -0
- package/dist/types-DIBA80mc-wf5wLmON.d.mts.map +1 -0
- package/dist/types-DMxc5KZa.d.mts +1 -0
- package/dist/types-Dl20lxky-DC_jdA0T.d.mts +247 -0
- package/dist/types-Dl20lxky-DC_jdA0T.d.mts.map +1 -0
- package/dist/types-DmhUVq4u-BpqqjPJx.mjs +719 -0
- package/dist/types-DmhUVq4u-BpqqjPJx.mjs.map +1 -0
- package/dist/types-IqZMLrFC-DtdJNICr.d.mts +426 -0
- package/dist/types-IqZMLrFC-DtdJNICr.d.mts.map +1 -0
- package/dist/types-TvBZ2Ne9.d.mts +1 -0
- package/dist/unique-constraint-jQWfH7dg-zLWtFp99.d.mts +32 -0
- package/dist/unique-constraint-jQWfH7dg-zLWtFp99.d.mts.map +1 -0
- package/dist/util-DQQgv2j1-D6INZaRG.mjs +34 -0
- package/dist/util-DQQgv2j1-D6INZaRG.mjs.map +1 -0
- package/dist/validators-C4PItWnf.mjs +0 -0
- package/dist/validators-C4PItWnf.mjs.map +1 -0
- package/dist/validators-CxeHhHrf.d.mts +206 -0
- package/dist/validators-CxeHhHrf.d.mts.map +1 -0
- package/dist/value-set-derivation-hook-BhgGVtw0.d.mts +35 -0
- package/dist/value-set-derivation-hook-BhgGVtw0.d.mts.map +1 -0
- package/dist/value-set-derivation-hook-Di9JZ0X7.mjs +22 -0
- package/dist/value-set-derivation-hook-Di9JZ0X7.mjs.map +1 -0
- package/dist/verify-E6Dd2vYp-gYwnlhLm.mjs +86 -0
- package/dist/verify-E6Dd2vYp-gYwnlhLm.mjs.map +1 -0
- package/dist/verify-zDRnw0Rn.d.mts +26 -0
- package/dist/verify-zDRnw0Rn.d.mts.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,1615 @@
|
|
|
1
|
+
import { t as providesEntityHandleLowering } from "./entity-handle-lowering-hook-DwLIKNiS.mjs";
|
|
2
|
+
import { u as toStorageTypeInstance } from "./types-CMMQS6aG.mjs";
|
|
3
|
+
import { n as contractError, t as buildSqlContractFromDefinition } from "./build-contract-DBCz_YSs-DO0wMHai.mjs";
|
|
4
|
+
import { InternalError } from "@prisma/orm-framework/utils/internal-error";
|
|
5
|
+
import { isColumnDefault } from "@prisma/orm-framework/contract/types";
|
|
6
|
+
import { assertNoCrossRegistryCollisions, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, mergeAuthoringNamespaces, validateAuthoringHelperArguments } from "@prisma/orm-framework/components/authoring";
|
|
7
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
8
|
+
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
9
|
+
import { bindEnumType, createEntityHelpersFromNamespace, enumType, isEnumTypeHandle, member } from "@prisma/orm-framework/contract-authoring";
|
|
10
|
+
//#region ../../../2-sql/2-authoring/contract-ts/dist/contract-builder.mjs
|
|
11
|
+
function isNamedConstraintOptionsLike(value) {
|
|
12
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
13
|
+
if (Object.keys(value).some((key) => key !== "name")) return false;
|
|
14
|
+
const name = value.name;
|
|
15
|
+
return name === void 0 || typeof name === "string";
|
|
16
|
+
}
|
|
17
|
+
const blockedSegments = /* @__PURE__ */ new Set([
|
|
18
|
+
"__proto__",
|
|
19
|
+
"constructor",
|
|
20
|
+
"prototype"
|
|
21
|
+
]);
|
|
22
|
+
function assertSafeHelperKey(key, path) {
|
|
23
|
+
if (blockedSegments.has(key)) throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Invalid authoring helper "${[...path, key].join(".")}". Helper path segments must not use "${key}".`, { meta: {
|
|
24
|
+
helperPath: [...path, key].join("."),
|
|
25
|
+
segment: key
|
|
26
|
+
} });
|
|
27
|
+
}
|
|
28
|
+
function createTypeHelpersFromNamespace(namespace, path = []) {
|
|
29
|
+
const helpers = {};
|
|
30
|
+
for (const [key, value] of Object.entries(namespace)) {
|
|
31
|
+
assertSafeHelperKey(key, path);
|
|
32
|
+
const currentPath = [...path, key];
|
|
33
|
+
if (isAuthoringTypeConstructorDescriptor(value)) {
|
|
34
|
+
const helperPath = currentPath.join(".");
|
|
35
|
+
helpers[key] = (...args) => {
|
|
36
|
+
validateAuthoringHelperArguments(helperPath, value.args, args);
|
|
37
|
+
const triple = instantiateAuthoringTypeConstructor(value, args);
|
|
38
|
+
return toStorageTypeInstance({
|
|
39
|
+
codecId: triple.codecId,
|
|
40
|
+
nativeType: triple.nativeType,
|
|
41
|
+
typeParams: triple.typeParams ?? {}
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
helpers[key] = createTypeHelpersFromNamespace(value, currentPath);
|
|
47
|
+
}
|
|
48
|
+
return helpers;
|
|
49
|
+
}
|
|
50
|
+
function createFieldPresetHelper(options) {
|
|
51
|
+
return (...rawArgs) => {
|
|
52
|
+
const acceptsNamedConstraintOptions = options.descriptor.output.id === true || options.descriptor.output.unique === true;
|
|
53
|
+
const declaredArguments = options.descriptor.args ?? [];
|
|
54
|
+
if (acceptsNamedConstraintOptions && rawArgs.length > declaredArguments.length + 1) throw contractError("CONTRACT.ARGUMENT_INVALID", `${options.helperPath} expects at most ${declaredArguments.length + 1} argument(s), received ${rawArgs.length}`, { meta: {
|
|
55
|
+
helperPath: options.helperPath,
|
|
56
|
+
expected: declaredArguments.length + 1,
|
|
57
|
+
received: rawArgs.length
|
|
58
|
+
} });
|
|
59
|
+
let args = rawArgs;
|
|
60
|
+
let namedConstraintOptions;
|
|
61
|
+
if (acceptsNamedConstraintOptions && rawArgs.length === declaredArguments.length + 1) {
|
|
62
|
+
const maybeNamedConstraintOptions = rawArgs.at(-1);
|
|
63
|
+
if (!isNamedConstraintOptionsLike(maybeNamedConstraintOptions)) throw contractError("CONTRACT.ARGUMENT_INVALID", `${options.helperPath} accepts an optional trailing { name?: string } constraint options object`, { meta: { helperPath: options.helperPath } });
|
|
64
|
+
namedConstraintOptions = maybeNamedConstraintOptions;
|
|
65
|
+
args = rawArgs.slice(0, -1);
|
|
66
|
+
}
|
|
67
|
+
validateAuthoringHelperArguments(options.helperPath, options.descriptor.args, args);
|
|
68
|
+
return options.build({
|
|
69
|
+
args,
|
|
70
|
+
...namedConstraintOptions ? { namedConstraintOptions } : {}
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function createFieldHelpersFromNamespace(namespace, createLeafHelper, path = []) {
|
|
75
|
+
const helpers = {};
|
|
76
|
+
for (const [key, value] of Object.entries(namespace)) {
|
|
77
|
+
assertSafeHelperKey(key, path);
|
|
78
|
+
const currentPath = [...path, key];
|
|
79
|
+
if (isAuthoringFieldPresetDescriptor(value)) {
|
|
80
|
+
helpers[key] = createLeafHelper({
|
|
81
|
+
helperPath: currentPath.join("."),
|
|
82
|
+
descriptor: value
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
helpers[key] = createFieldHelpersFromNamespace(value, createLeafHelper, currentPath);
|
|
87
|
+
}
|
|
88
|
+
return helpers;
|
|
89
|
+
}
|
|
90
|
+
function toColumnDefault(value) {
|
|
91
|
+
if (isColumnDefault(value)) return value;
|
|
92
|
+
return {
|
|
93
|
+
kind: "literal",
|
|
94
|
+
value
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var ScalarFieldBuilder = class ScalarFieldBuilder {
|
|
98
|
+
state;
|
|
99
|
+
constructor(state) {
|
|
100
|
+
this.state = state;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Returns the physical column name when `.column(name)` was called, or
|
|
104
|
+
* `undefined` when the field uses the default (logical field name) mapping.
|
|
105
|
+
* Used by cross-space FK lowering to stamp the physical column name onto
|
|
106
|
+
* `TargetFieldRef.columnName` so FK target columns are resolved correctly.
|
|
107
|
+
*/
|
|
108
|
+
get physicalColumnName() {
|
|
109
|
+
return this.state.columnName;
|
|
110
|
+
}
|
|
111
|
+
optional() {
|
|
112
|
+
return new ScalarFieldBuilder(blindCast({
|
|
113
|
+
...this.state,
|
|
114
|
+
nullable: true
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
column(name) {
|
|
118
|
+
return new ScalarFieldBuilder(blindCast({
|
|
119
|
+
...this.state,
|
|
120
|
+
columnName: name
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
many() {
|
|
124
|
+
return new ScalarFieldBuilder(blindCast({
|
|
125
|
+
...this.state,
|
|
126
|
+
many: true
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
default(value) {
|
|
130
|
+
return new ScalarFieldBuilder({
|
|
131
|
+
...this.state,
|
|
132
|
+
default: toColumnDefault(value)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
defaultSql(expression) {
|
|
136
|
+
return new ScalarFieldBuilder({
|
|
137
|
+
...this.state,
|
|
138
|
+
default: {
|
|
139
|
+
kind: "function",
|
|
140
|
+
expression
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
id(options) {
|
|
145
|
+
return new ScalarFieldBuilder(blindCast({
|
|
146
|
+
...this.state,
|
|
147
|
+
id: options?.name ? { name: options.name } : {}
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
unique(options) {
|
|
151
|
+
return new ScalarFieldBuilder(blindCast({
|
|
152
|
+
...this.state,
|
|
153
|
+
unique: options?.name ? { name: options.name } : {}
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
sql(spec) {
|
|
157
|
+
const idSpec = "id" in spec ? spec.id : void 0;
|
|
158
|
+
const uniqueSpec = "unique" in spec ? spec.unique : void 0;
|
|
159
|
+
if (idSpec && !this.state.id) throw contractError("CONTRACT.ARGUMENT_INVALID", "field.sql({ id }) requires an existing inline .id(...) declaration.");
|
|
160
|
+
if (uniqueSpec && !this.state.unique) throw contractError("CONTRACT.ARGUMENT_INVALID", "field.sql({ unique }) requires an existing inline .unique(...) declaration.");
|
|
161
|
+
return new ScalarFieldBuilder(blindCast({
|
|
162
|
+
...this.state,
|
|
163
|
+
...spec.column ? { columnName: spec.column } : {},
|
|
164
|
+
...idSpec ? { id: { name: idSpec.name } } : {},
|
|
165
|
+
...uniqueSpec ? { unique: { name: uniqueSpec.name } } : {}
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
build() {
|
|
169
|
+
return this.state;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
var EnumScalarFieldBuilder = class EnumScalarFieldBuilder extends ScalarFieldBuilder {
|
|
173
|
+
#handle;
|
|
174
|
+
constructor(state, handle) {
|
|
175
|
+
super(state);
|
|
176
|
+
this.#handle = handle;
|
|
177
|
+
}
|
|
178
|
+
default(value) {
|
|
179
|
+
return blindCast(new EnumScalarFieldBuilder({
|
|
180
|
+
...this.build(),
|
|
181
|
+
default: {
|
|
182
|
+
kind: "literal",
|
|
183
|
+
value
|
|
184
|
+
}
|
|
185
|
+
}, this.#handle));
|
|
186
|
+
}
|
|
187
|
+
defaultSql(_expression) {
|
|
188
|
+
throw contractError("CONTRACT.DEFAULT_INVALID", "defaultSql is not available on an enum field; use .default(members.X) instead", { meta: { reason: "defaultSql-on-enum-field" } });
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
function columnField(descriptor) {
|
|
192
|
+
return new ScalarFieldBuilder({
|
|
193
|
+
kind: "scalar",
|
|
194
|
+
descriptor,
|
|
195
|
+
nullable: false
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function generatedField(spec) {
|
|
199
|
+
return new ScalarFieldBuilder({
|
|
200
|
+
kind: "scalar",
|
|
201
|
+
descriptor: {
|
|
202
|
+
...spec.type,
|
|
203
|
+
...spec.typeParams ? { typeParams: spec.typeParams } : {}
|
|
204
|
+
},
|
|
205
|
+
nullable: false,
|
|
206
|
+
executionDefaults: { onCreate: spec.generated }
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function namedTypeField(typeRef) {
|
|
210
|
+
if (isEnumTypeHandle(typeRef)) return new EnumScalarFieldBuilder(blindCast({
|
|
211
|
+
kind: "scalar",
|
|
212
|
+
typeRef,
|
|
213
|
+
nullable: false
|
|
214
|
+
}), typeRef);
|
|
215
|
+
return new ScalarFieldBuilder({
|
|
216
|
+
kind: "scalar",
|
|
217
|
+
typeRef,
|
|
218
|
+
nullable: false
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function buildFieldPreset(descriptor, args, namedConstraintOptions) {
|
|
222
|
+
const preset = instantiateAuthoringFieldPreset(descriptor, args);
|
|
223
|
+
return new ScalarFieldBuilder({
|
|
224
|
+
kind: "scalar",
|
|
225
|
+
descriptor: preset.descriptor,
|
|
226
|
+
nullable: preset.nullable,
|
|
227
|
+
...ifDefined("default", preset.default),
|
|
228
|
+
...ifDefined("executionDefaults", preset.executionDefaults),
|
|
229
|
+
...preset.id ? { id: namedConstraintOptions?.name ? { name: namedConstraintOptions.name } : {} } : {},
|
|
230
|
+
...preset.unique ? { unique: namedConstraintOptions?.name ? { name: namedConstraintOptions.name } : {} } : {}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
var RelationBuilder = class RelationBuilder {
|
|
234
|
+
state;
|
|
235
|
+
constructor(state) {
|
|
236
|
+
this.state = state;
|
|
237
|
+
}
|
|
238
|
+
sql(spec) {
|
|
239
|
+
if (this.state.kind !== "belongsTo") throw contractError("CONTRACT.RELATION_INVALID", "relation.sql(...) is only supported for belongsTo relations.", { meta: { relationKind: this.state.kind } });
|
|
240
|
+
return new RelationBuilder({
|
|
241
|
+
...this.state,
|
|
242
|
+
sql: spec
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
build() {
|
|
246
|
+
return this.state;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
function normalizeFieldRefInput(input) {
|
|
250
|
+
return (Array.isArray(input) ? input : [input]).map((ref) => ref.fieldName);
|
|
251
|
+
}
|
|
252
|
+
function normalizeTargetFieldRefInput(input) {
|
|
253
|
+
const refs = Array.isArray(input) ? input : [input];
|
|
254
|
+
const [first] = refs;
|
|
255
|
+
if (!first) throw contractError("CONTRACT.FOREIGN_KEY_INVALID", "Expected at least one target ref", { meta: { reason: "empty-target-refs" } });
|
|
256
|
+
if (refs.some((ref) => ref.modelName !== first.modelName)) throw contractError("CONTRACT.FOREIGN_KEY_INVALID", "All target refs in a foreign key must point to the same model", { meta: {
|
|
257
|
+
mismatch: "modelName",
|
|
258
|
+
models: refs.map((ref) => ref.modelName)
|
|
259
|
+
} });
|
|
260
|
+
if (refs.some((ref) => ref.spaceId !== first.spaceId)) throw contractError("CONTRACT.FOREIGN_KEY_INVALID", `All target refs in a compound foreign key must share the same spaceId (found mismatch: "${first.spaceId ?? "<local>"}" vs "${refs.find((r) => r.spaceId !== first.spaceId)?.spaceId ?? "<local>"}")`, { meta: {
|
|
261
|
+
mismatch: "spaceId",
|
|
262
|
+
first: first.spaceId,
|
|
263
|
+
second: refs.find((r) => r.spaceId !== first.spaceId)?.spaceId
|
|
264
|
+
} });
|
|
265
|
+
if (refs.some((ref) => ref.namespaceId !== first.namespaceId)) throw contractError("CONTRACT.FOREIGN_KEY_INVALID", "All target refs in a compound foreign key must share the same namespaceId (found mismatch)", { meta: { mismatch: "namespaceId" } });
|
|
266
|
+
if (refs.some((ref) => ref.tableName !== first.tableName)) throw contractError("CONTRACT.FOREIGN_KEY_INVALID", "All target refs in a compound foreign key must share the same tableName (found mismatch)", { meta: { mismatch: "tableName" } });
|
|
267
|
+
return {
|
|
268
|
+
modelName: first.modelName,
|
|
269
|
+
fieldNames: refs.map((ref) => ref.columnName ?? ref.fieldName),
|
|
270
|
+
source: refs.some((ref) => ref.source === "string") ? "string" : "token",
|
|
271
|
+
spaceId: first.spaceId,
|
|
272
|
+
namespaceId: first.namespaceId,
|
|
273
|
+
tableName: first.tableName
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function createConstraintsDsl() {
|
|
277
|
+
function ref(modelName, fieldName) {
|
|
278
|
+
return {
|
|
279
|
+
kind: "targetFieldRef",
|
|
280
|
+
source: "string",
|
|
281
|
+
modelName,
|
|
282
|
+
fieldName
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function id(fieldOrFields, options) {
|
|
286
|
+
return {
|
|
287
|
+
kind: "id",
|
|
288
|
+
fields: normalizeFieldRefInput(fieldOrFields),
|
|
289
|
+
...options?.name ? { name: options.name } : {}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function unique(fieldOrFields, options) {
|
|
293
|
+
return {
|
|
294
|
+
kind: "unique",
|
|
295
|
+
fields: normalizeFieldRefInput(fieldOrFields),
|
|
296
|
+
...options?.name ? { name: options.name } : {}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function index(fieldsOrOptions, options) {
|
|
300
|
+
const isExpressionForm = !Array.isArray(fieldsOrOptions) && typeof fieldsOrOptions === "object" && "expression" in fieldsOrOptions;
|
|
301
|
+
const opts = isExpressionForm ? fieldsOrOptions : options;
|
|
302
|
+
const carried = {
|
|
303
|
+
kind: "index",
|
|
304
|
+
...opts?.name !== void 0 ? { name: opts.name } : {},
|
|
305
|
+
...opts?.map !== void 0 ? { map: opts.map } : {},
|
|
306
|
+
...opts?.where !== void 0 ? { where: opts.where } : {},
|
|
307
|
+
...opts?.unique !== void 0 ? { unique: opts.unique } : {},
|
|
308
|
+
...opts?.type !== void 0 ? { type: opts.type } : {},
|
|
309
|
+
...opts?.options !== void 0 ? { options: blindCast(opts.options) } : {}
|
|
310
|
+
};
|
|
311
|
+
return isExpressionForm ? {
|
|
312
|
+
...carried,
|
|
313
|
+
expression: fieldsOrOptions.expression
|
|
314
|
+
} : {
|
|
315
|
+
...carried,
|
|
316
|
+
fields: normalizeFieldRefInput(fieldsOrOptions)
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function foreignKey(fieldOrFields, target, options) {
|
|
320
|
+
const normalizedTarget = normalizeTargetFieldRefInput(target);
|
|
321
|
+
return {
|
|
322
|
+
kind: "fk",
|
|
323
|
+
fields: normalizeFieldRefInput(fieldOrFields),
|
|
324
|
+
targetModel: normalizedTarget.modelName,
|
|
325
|
+
targetFields: normalizedTarget.fieldNames,
|
|
326
|
+
targetSource: normalizedTarget.source,
|
|
327
|
+
...normalizedTarget.spaceId !== void 0 ? { targetSpaceId: normalizedTarget.spaceId } : {},
|
|
328
|
+
...normalizedTarget.namespaceId !== void 0 ? { targetNamespaceId: normalizedTarget.namespaceId } : {},
|
|
329
|
+
...normalizedTarget.tableName !== void 0 ? { targetTableName: normalizedTarget.tableName } : {},
|
|
330
|
+
...options?.name ? { name: options.name } : {},
|
|
331
|
+
...options?.onDelete ? { onDelete: options.onDelete } : {},
|
|
332
|
+
...options?.onUpdate ? { onUpdate: options.onUpdate } : {},
|
|
333
|
+
...options?.constraint !== void 0 ? { constraint: options.constraint } : {},
|
|
334
|
+
...options?.index !== void 0 ? { index: options.index } : {}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
ref,
|
|
339
|
+
id,
|
|
340
|
+
unique,
|
|
341
|
+
index,
|
|
342
|
+
foreignKey
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function createFieldRefs(fields) {
|
|
346
|
+
const refs = {};
|
|
347
|
+
for (const fieldName of Object.keys(fields)) refs[fieldName] = {
|
|
348
|
+
kind: "columnRef",
|
|
349
|
+
fieldName
|
|
350
|
+
};
|
|
351
|
+
return refs;
|
|
352
|
+
}
|
|
353
|
+
function createModelTokenRefs(modelName, fields, crossSpaceCoordinate) {
|
|
354
|
+
const refs = {};
|
|
355
|
+
for (const [fieldName, fieldBuilder] of Object.entries(fields)) {
|
|
356
|
+
const physicalColumn = crossSpaceCoordinate !== void 0 ? fieldBuilder.physicalColumnName : void 0;
|
|
357
|
+
refs[fieldName] = {
|
|
358
|
+
kind: "targetFieldRef",
|
|
359
|
+
source: "token",
|
|
360
|
+
modelName,
|
|
361
|
+
fieldName,
|
|
362
|
+
...crossSpaceCoordinate !== void 0 ? {
|
|
363
|
+
spaceId: crossSpaceCoordinate.spaceId,
|
|
364
|
+
...crossSpaceCoordinate.namespaceId !== void 0 ? { namespaceId: crossSpaceCoordinate.namespaceId } : {},
|
|
365
|
+
...crossSpaceCoordinate.tableName !== void 0 ? { tableName: crossSpaceCoordinate.tableName } : {},
|
|
366
|
+
...physicalColumn !== void 0 ? { columnName: physicalColumn } : {}
|
|
367
|
+
} : {}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return refs;
|
|
371
|
+
}
|
|
372
|
+
function buildStageSpec(stageInput, context) {
|
|
373
|
+
if (typeof stageInput === "function") return stageInput(context);
|
|
374
|
+
return stageInput;
|
|
375
|
+
}
|
|
376
|
+
function createAttributeConstraintsDsl() {
|
|
377
|
+
const constraints = createConstraintsDsl();
|
|
378
|
+
return {
|
|
379
|
+
id: constraints.id,
|
|
380
|
+
unique: constraints.unique
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function createSqlConstraintsDsl() {
|
|
384
|
+
const constraints = createConstraintsDsl();
|
|
385
|
+
return {
|
|
386
|
+
index: constraints.index,
|
|
387
|
+
foreignKey: constraints.foreignKey,
|
|
388
|
+
ref: constraints.ref
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function createColumnRefs(fields) {
|
|
392
|
+
return createFieldRefs(fields);
|
|
393
|
+
}
|
|
394
|
+
function findDuplicateRelationName(existingRelations, nextRelations) {
|
|
395
|
+
return Object.keys(nextRelations).find((relationName) => Object.hasOwn(existingRelations, relationName));
|
|
396
|
+
}
|
|
397
|
+
var ContractModelBuilder = class ContractModelBuilder {
|
|
398
|
+
stageOne;
|
|
399
|
+
attributesFactory;
|
|
400
|
+
sqlFactory;
|
|
401
|
+
spaceId;
|
|
402
|
+
tableName;
|
|
403
|
+
refs;
|
|
404
|
+
constructor(stageOne, attributesFactory, sqlFactory, spaceId, tableName) {
|
|
405
|
+
this.stageOne = stageOne;
|
|
406
|
+
this.attributesFactory = attributesFactory;
|
|
407
|
+
this.sqlFactory = sqlFactory;
|
|
408
|
+
this.spaceId = spaceId;
|
|
409
|
+
this.tableName = tableName;
|
|
410
|
+
const crossSpaceCoordinate = spaceId !== void 0 ? {
|
|
411
|
+
spaceId,
|
|
412
|
+
...stageOne.namespace !== void 0 ? { namespaceId: stageOne.namespace } : {},
|
|
413
|
+
...tableName !== void 0 ? { tableName } : {}
|
|
414
|
+
} : void 0;
|
|
415
|
+
this.refs = blindCast(stageOne.modelName ? createModelTokenRefs(stageOne.modelName, stageOne.fields, crossSpaceCoordinate) : void 0);
|
|
416
|
+
}
|
|
417
|
+
ref(fieldName) {
|
|
418
|
+
const modelName = this.stageOne.modelName;
|
|
419
|
+
if (!modelName) throw contractError("CONTRACT.MODEL_TOKEN_INVALID", "Model tokens require model(\"ModelName\", ...) before calling .ref(...)");
|
|
420
|
+
return {
|
|
421
|
+
kind: "targetFieldRef",
|
|
422
|
+
source: "token",
|
|
423
|
+
modelName,
|
|
424
|
+
fieldName
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
relations(relations) {
|
|
428
|
+
const duplicateRelationName = findDuplicateRelationName(this.stageOne.relations, relations);
|
|
429
|
+
if (duplicateRelationName) throw contractError("CONTRACT.NAME_DUPLICATE", `Model "${this.stageOne.modelName ?? "<anonymous>"}" already defines relation "${duplicateRelationName}".`, { meta: {
|
|
430
|
+
kind: "relation",
|
|
431
|
+
name: duplicateRelationName,
|
|
432
|
+
modelName: this.stageOne.modelName
|
|
433
|
+
} });
|
|
434
|
+
return new ContractModelBuilder({
|
|
435
|
+
...this.stageOne,
|
|
436
|
+
relations: {
|
|
437
|
+
...this.stageOne.relations,
|
|
438
|
+
...relations
|
|
439
|
+
}
|
|
440
|
+
}, this.attributesFactory, this.sqlFactory, this.spaceId, this.tableName);
|
|
441
|
+
}
|
|
442
|
+
attributes(specOrFactory) {
|
|
443
|
+
return new ContractModelBuilder(this.stageOne, specOrFactory, this.sqlFactory, this.spaceId, this.tableName);
|
|
444
|
+
}
|
|
445
|
+
sql(specOrFactory) {
|
|
446
|
+
const nextTableName = typeof specOrFactory !== "function" ? specOrFactory.table : this.tableName;
|
|
447
|
+
return blindCast(new ContractModelBuilder(this.stageOne, this.attributesFactory, specOrFactory, this.spaceId, nextTableName));
|
|
448
|
+
}
|
|
449
|
+
buildAttributesSpec() {
|
|
450
|
+
if (!this.attributesFactory) return;
|
|
451
|
+
return buildStageSpec(this.attributesFactory, {
|
|
452
|
+
fields: createFieldRefs(this.stageOne.fields),
|
|
453
|
+
constraints: createAttributeConstraintsDsl()
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
buildSqlSpec() {
|
|
457
|
+
if (!this.sqlFactory) return;
|
|
458
|
+
return buildStageSpec(this.sqlFactory, {
|
|
459
|
+
cols: createColumnRefs(this.stageOne.fields),
|
|
460
|
+
constraints: createSqlConstraintsDsl()
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
function isLazyRelationModelName(value) {
|
|
465
|
+
return typeof value === "object" && value !== null && "kind" in value && value.kind === "lazyRelationModelName" && "resolve" in value && typeof value.resolve === "function";
|
|
466
|
+
}
|
|
467
|
+
function resolveNamedModelTokenName(token) {
|
|
468
|
+
const modelName = token.stageOne.modelName;
|
|
469
|
+
if (!modelName) throw contractError("CONTRACT.MODEL_TOKEN_INVALID", "Relation targets require named model tokens. Use model(\"ModelName\", ...) before passing a token to rel.*(...).");
|
|
470
|
+
return modelName;
|
|
471
|
+
}
|
|
472
|
+
function normalizeRelationModelSource(target) {
|
|
473
|
+
if (typeof target === "string") return {
|
|
474
|
+
kind: "relationModelName",
|
|
475
|
+
source: "string",
|
|
476
|
+
modelName: target
|
|
477
|
+
};
|
|
478
|
+
if (typeof target === "function") return {
|
|
479
|
+
kind: "lazyRelationModelName",
|
|
480
|
+
source: "lazyToken",
|
|
481
|
+
resolve: () => resolveNamedModelTokenName(target())
|
|
482
|
+
};
|
|
483
|
+
return {
|
|
484
|
+
kind: "relationModelName",
|
|
485
|
+
source: "token",
|
|
486
|
+
modelName: resolveNamedModelTokenName(target)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function model(modelNameOrInput, maybeInput) {
|
|
490
|
+
const input = typeof modelNameOrInput === "string" ? maybeInput : modelNameOrInput;
|
|
491
|
+
if (!input) throw contractError("CONTRACT.ARGUMENT_INVALID", "model(\"ModelName\", ...) requires a model definition.");
|
|
492
|
+
return new ContractModelBuilder({
|
|
493
|
+
...typeof modelNameOrInput === "string" ? { modelName: modelNameOrInput } : {},
|
|
494
|
+
...input.namespace !== void 0 ? { namespace: input.namespace } : {},
|
|
495
|
+
fields: input.fields,
|
|
496
|
+
relations: input.relations ?? {}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Factory for building a standalone branded extension model handle.
|
|
501
|
+
*
|
|
502
|
+
* Use this instead of `new ContractModelBuilder(…)` when constructing handles
|
|
503
|
+
* for models that live in a foreign contract space (e.g. a Supabase extension
|
|
504
|
+
* model referenced by a user's contract). The `spaceId` brands the returned
|
|
505
|
+
* handle so `refs.<field>.spaceId` carries the foreign space identifier.
|
|
506
|
+
*
|
|
507
|
+
* @param name - The domain model name as declared in the foreign contract
|
|
508
|
+
* (e.g. `'AuthUser'`, not a bare table alias like `'User'`).
|
|
509
|
+
* @param input.namespace - The namespace within the foreign space (e.g. `'auth'`).
|
|
510
|
+
* @param input.fields - Field definitions (use `field.column(…)`).
|
|
511
|
+
* @param input.table - The physical table name in the foreign schema.
|
|
512
|
+
* @param spaceId - The extension space identifier (e.g. `'supabase'`).
|
|
513
|
+
*/
|
|
514
|
+
function extensionModel(name, input, spaceId) {
|
|
515
|
+
return new ContractModelBuilder({
|
|
516
|
+
modelName: name,
|
|
517
|
+
namespace: input.namespace,
|
|
518
|
+
fields: input.fields,
|
|
519
|
+
relations: {}
|
|
520
|
+
}, void 0, void 0, spaceId, input.table);
|
|
521
|
+
}
|
|
522
|
+
function isCrossSpaceHandle(value) {
|
|
523
|
+
if (typeof value !== "object" || value === null) return false;
|
|
524
|
+
const rec = blindCast(value);
|
|
525
|
+
return typeof rec["spaceId"] === "string" && typeof rec["stageOne"] === "object" && rec["stageOne"] !== null;
|
|
526
|
+
}
|
|
527
|
+
function belongsTo(toModel, options) {
|
|
528
|
+
const resolvedModel = typeof toModel === "function" ? toModel() : toModel;
|
|
529
|
+
const crossSpaceCoordinate = isCrossSpaceHandle(resolvedModel) ? {
|
|
530
|
+
spaceId: resolvedModel.spaceId,
|
|
531
|
+
...resolvedModel.tableName !== void 0 ? { tableName: resolvedModel.tableName } : {},
|
|
532
|
+
...resolvedModel.stageOne.namespace !== void 0 ? { namespaceId: resolvedModel.stageOne.namespace } : {}
|
|
533
|
+
} : void 0;
|
|
534
|
+
return new RelationBuilder({
|
|
535
|
+
kind: "belongsTo",
|
|
536
|
+
toModel: normalizeRelationModelSource(toModel),
|
|
537
|
+
from: options.from,
|
|
538
|
+
to: options.to,
|
|
539
|
+
...crossSpaceCoordinate !== void 0 ? crossSpaceCoordinate : {}
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
function hasMany(toModel, options) {
|
|
543
|
+
return new RelationBuilder({
|
|
544
|
+
kind: "hasMany",
|
|
545
|
+
toModel: normalizeRelationModelSource(toModel),
|
|
546
|
+
by: options.by
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function hasOne(toModel, options) {
|
|
550
|
+
return new RelationBuilder({
|
|
551
|
+
kind: "hasOne",
|
|
552
|
+
toModel: normalizeRelationModelSource(toModel),
|
|
553
|
+
by: options.by
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
function manyToMany(toModel, options) {
|
|
557
|
+
return new RelationBuilder({
|
|
558
|
+
kind: "manyToMany",
|
|
559
|
+
toModel: normalizeRelationModelSource(toModel),
|
|
560
|
+
through: normalizeRelationModelSource(options.through),
|
|
561
|
+
from: options.from,
|
|
562
|
+
to: options.to
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
const rel = {
|
|
566
|
+
belongsTo,
|
|
567
|
+
hasMany,
|
|
568
|
+
hasOne,
|
|
569
|
+
manyToMany
|
|
570
|
+
};
|
|
571
|
+
const field = {
|
|
572
|
+
column: columnField,
|
|
573
|
+
generated: generatedField,
|
|
574
|
+
namedType: namedTypeField
|
|
575
|
+
};
|
|
576
|
+
function isContractInput(value) {
|
|
577
|
+
if (typeof value !== "object" || value === null || !("target" in value) || !("family" in value)) return false;
|
|
578
|
+
const target = value.target;
|
|
579
|
+
const family = value.family;
|
|
580
|
+
return typeof target === "object" && target !== null && "kind" in target && target.kind === "target" && typeof family === "object" && family !== null && "kind" in family && family.kind === "family";
|
|
581
|
+
}
|
|
582
|
+
function isRelationFieldArray(value) {
|
|
583
|
+
return Array.isArray(value);
|
|
584
|
+
}
|
|
585
|
+
function normalizeRelationFieldNames(value) {
|
|
586
|
+
if (isRelationFieldArray(value)) return value;
|
|
587
|
+
return [value];
|
|
588
|
+
}
|
|
589
|
+
function resolveRelationModelName(value) {
|
|
590
|
+
if (isLazyRelationModelName(value)) return value.resolve();
|
|
591
|
+
return value.modelName;
|
|
592
|
+
}
|
|
593
|
+
function applyNaming(name, strategy) {
|
|
594
|
+
if (!strategy || strategy === "identity") return name;
|
|
595
|
+
let result = "";
|
|
596
|
+
for (let index = 0; index < name.length; index += 1) {
|
|
597
|
+
const char = name[index];
|
|
598
|
+
if (!char) continue;
|
|
599
|
+
const lower = char.toLowerCase();
|
|
600
|
+
if (char !== lower && index > 0) {
|
|
601
|
+
const prev = name[index - 1];
|
|
602
|
+
const next = name[index + 1];
|
|
603
|
+
const prevIsLower = !!prev && prev === prev.toLowerCase();
|
|
604
|
+
const nextIsLower = !!next && next === next.toLowerCase();
|
|
605
|
+
if (prevIsLower || nextIsLower) result += "_";
|
|
606
|
+
}
|
|
607
|
+
result += lower;
|
|
608
|
+
}
|
|
609
|
+
return result;
|
|
610
|
+
}
|
|
611
|
+
function extractTypeNamespace(pack) {
|
|
612
|
+
return pack.authoring?.type ?? {};
|
|
613
|
+
}
|
|
614
|
+
function extractFieldNamespace(pack) {
|
|
615
|
+
return pack.authoring?.field ?? {};
|
|
616
|
+
}
|
|
617
|
+
function extractEntitiesNamespace(pack) {
|
|
618
|
+
return pack.authoring?.entityTypes ?? {};
|
|
619
|
+
}
|
|
620
|
+
function composeTypeNamespace(components) {
|
|
621
|
+
const merged = {};
|
|
622
|
+
for (const component of components) {
|
|
623
|
+
const ns = extractTypeNamespace(component);
|
|
624
|
+
if (Object.keys(ns).length > 0) mergeAuthoringNamespaces(merged, ns, [], "typeConstructor", "type");
|
|
625
|
+
}
|
|
626
|
+
return merged;
|
|
627
|
+
}
|
|
628
|
+
function composeFieldNamespace(components) {
|
|
629
|
+
const merged = {};
|
|
630
|
+
for (const component of components) {
|
|
631
|
+
const ns = extractFieldNamespace(component);
|
|
632
|
+
if (Object.keys(ns).length > 0) mergeAuthoringNamespaces(merged, ns, [], "fieldPreset", "field");
|
|
633
|
+
}
|
|
634
|
+
return merged;
|
|
635
|
+
}
|
|
636
|
+
function composeEntityNamespace(components) {
|
|
637
|
+
const merged = {};
|
|
638
|
+
for (const component of components) {
|
|
639
|
+
const ns = extractEntitiesNamespace(component);
|
|
640
|
+
if (Object.keys(ns).length > 0) mergeAuthoringNamespaces(merged, ns, [], "entity", "entity");
|
|
641
|
+
}
|
|
642
|
+
return merged;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Reserved top-level keys on the composed helpers surface — the
|
|
646
|
+
* built-in `model` / `rel` helpers, the `field` / `type` namespace
|
|
647
|
+
* objects. Pack-contributed entity types are flattened to the same
|
|
648
|
+
* top-level shape (e.g. `helpers.enum({...})`), so a pack cannot
|
|
649
|
+
* contribute an entity type whose name collides with one of these
|
|
650
|
+
* reserved keys without silently overwriting at runtime. Detect the
|
|
651
|
+
* collision at compose time and fail loudly with a guidance message.
|
|
652
|
+
*/
|
|
653
|
+
const RESERVED_HELPER_KEYS = [
|
|
654
|
+
"field",
|
|
655
|
+
"model",
|
|
656
|
+
"rel",
|
|
657
|
+
"type"
|
|
658
|
+
];
|
|
659
|
+
function assertNoBuiltInEntityCollisions(namespace) {
|
|
660
|
+
const collisions = Object.keys(namespace).filter((name) => RESERVED_HELPER_KEYS.includes(name));
|
|
661
|
+
if (collisions.length > 0) throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Pack-contributed entity type(s) ${collisions.map((c) => `"${c}"`).join(", ")} collide with the reserved built-in helper key(s) on the composed helpers surface. Reserved keys: ${RESERVED_HELPER_KEYS.map((k) => `"${k}"`).join(", ")}.`, { meta: {
|
|
662
|
+
collisions,
|
|
663
|
+
reservedKeys: RESERVED_HELPER_KEYS
|
|
664
|
+
} });
|
|
665
|
+
}
|
|
666
|
+
function createComposedFieldHelpers(fieldNamespace) {
|
|
667
|
+
const helperNamespace = createFieldHelpersFromNamespace(fieldNamespace, ({ helperPath, descriptor }) => createFieldPresetHelper({
|
|
668
|
+
helperPath,
|
|
669
|
+
descriptor,
|
|
670
|
+
build: ({ args, namedConstraintOptions }) => buildFieldPreset(descriptor, args, namedConstraintOptions)
|
|
671
|
+
}));
|
|
672
|
+
const coreFieldHelpers = {
|
|
673
|
+
column: field.column,
|
|
674
|
+
generated: field.generated,
|
|
675
|
+
namedType: field.namedType
|
|
676
|
+
};
|
|
677
|
+
const coreHelperNames = new Set(Object.keys(coreFieldHelpers));
|
|
678
|
+
for (const helperName of Object.keys(helperNamespace)) if (coreHelperNames.has(helperName)) throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Duplicate authoring field helper "${helperName}". Core field helpers reserve that name.`, { meta: { helperName } });
|
|
679
|
+
return {
|
|
680
|
+
...coreFieldHelpers,
|
|
681
|
+
...helperNamespace
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function createComposedAuthoringHelpers(options) {
|
|
685
|
+
const extensionValues = Object.values(options.extensions ?? {});
|
|
686
|
+
const components = [
|
|
687
|
+
options.family,
|
|
688
|
+
options.target,
|
|
689
|
+
...extensionValues
|
|
690
|
+
];
|
|
691
|
+
const typeNamespace = composeTypeNamespace(components);
|
|
692
|
+
const fieldNamespace = composeFieldNamespace(components);
|
|
693
|
+
const entityNamespace = composeEntityNamespace(components);
|
|
694
|
+
assertNoCrossRegistryCollisions(typeNamespace, fieldNamespace, entityNamespace);
|
|
695
|
+
assertNoBuiltInEntityCollisions(entityNamespace);
|
|
696
|
+
return {
|
|
697
|
+
...createEntityHelpersFromNamespace(entityNamespace, { ctx: {
|
|
698
|
+
family: options.family.familyId,
|
|
699
|
+
target: options.target.targetId
|
|
700
|
+
} }),
|
|
701
|
+
field: createComposedFieldHelpers(fieldNamespace),
|
|
702
|
+
model,
|
|
703
|
+
rel,
|
|
704
|
+
type: createTypeHelpersFromNamespace(typeNamespace)
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
function hasNamedModelToken(models, modelName) {
|
|
708
|
+
return models[modelName]?.stageOne.modelName === modelName;
|
|
709
|
+
}
|
|
710
|
+
function formatFieldSelection(fieldNames) {
|
|
711
|
+
if (fieldNames.length === 1) return `'${fieldNames[0]}'`;
|
|
712
|
+
return `[${fieldNames.map((fieldName) => `'${fieldName}'`).join(", ")}]`;
|
|
713
|
+
}
|
|
714
|
+
function formatTokenFieldSelection(modelName, fieldNames) {
|
|
715
|
+
if (fieldNames.length === 1) return `${modelName}.refs.${fieldNames[0]}`;
|
|
716
|
+
return `[${fieldNames.map((fieldName) => `${modelName}.refs.${fieldName}`).join(", ")}]`;
|
|
717
|
+
}
|
|
718
|
+
function formatConstraintsRefCall(modelName, fieldNames) {
|
|
719
|
+
if (fieldNames.length === 1) return `constraints.ref('${modelName}', '${fieldNames[0]}')`;
|
|
720
|
+
return `[${fieldNames.map((fieldName) => `constraints.ref('${modelName}', '${fieldName}')`).join(", ")}]`;
|
|
721
|
+
}
|
|
722
|
+
function formatRelationModelDisplay(relationModel) {
|
|
723
|
+
if (relationModel.kind === "lazyRelationModelName") return `() => ${relationModel.resolve()}`;
|
|
724
|
+
return relationModel.source === "string" ? `'${relationModel.modelName}'` : relationModel.modelName;
|
|
725
|
+
}
|
|
726
|
+
function formatRelationCall(relation, targetModelDisplay) {
|
|
727
|
+
if (relation.kind === "belongsTo") return `rel.belongsTo(${targetModelDisplay}, { from: ${formatFieldSelection(normalizeRelationFieldNames(relation.from))}, to: ${formatFieldSelection(normalizeRelationFieldNames(relation.to))} })`;
|
|
728
|
+
if (relation.kind === "hasMany" || relation.kind === "hasOne") {
|
|
729
|
+
const by = formatFieldSelection(normalizeRelationFieldNames(relation.by));
|
|
730
|
+
return `rel.${relation.kind}(${targetModelDisplay}, { by: ${by} })`;
|
|
731
|
+
}
|
|
732
|
+
return `rel.manyToMany(${targetModelDisplay}, { through: ${formatRelationModelDisplay(relation.through)}, from: ${formatFieldSelection(normalizeRelationFieldNames(relation.from))}, to: ${formatFieldSelection(normalizeRelationFieldNames(relation.to))} })`;
|
|
733
|
+
}
|
|
734
|
+
function formatManyToManyCallWithThrough(relation, throughDisplay) {
|
|
735
|
+
return `rel.manyToMany(${formatRelationModelDisplay(relation.toModel)}, { through: ${throughDisplay}, from: ${formatFieldSelection(normalizeRelationFieldNames(relation.from))}, to: ${formatFieldSelection(normalizeRelationFieldNames(relation.to))} })`;
|
|
736
|
+
}
|
|
737
|
+
const WARNING_BATCH_THRESHOLD = 5;
|
|
738
|
+
function flushWarnings(warnings) {
|
|
739
|
+
if (warnings.length === 0) return;
|
|
740
|
+
if (warnings.length <= WARNING_BATCH_THRESHOLD) {
|
|
741
|
+
for (const message of warnings) process.emitWarning(message, { code: "PN_CONTRACT_TYPED_FALLBACK_AVAILABLE" });
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
process.emitWarning(`${warnings.length} contract references use string fallbacks where typed alternatives are available. Use named model tokens and typed storage type refs for autocomplete and type safety.
|
|
745
|
+
` + warnings.map((w) => ` - ${w}`).join("\n"), { code: "PN_CONTRACT_TYPED_FALLBACK_AVAILABLE" });
|
|
746
|
+
}
|
|
747
|
+
function formatFallbackWarning(location, current, suggested) {
|
|
748
|
+
return `Contract ${location} uses ${current}. Use ${suggested} when the named model token is available in the same contract to keep typed relation targets and model refs.`;
|
|
749
|
+
}
|
|
750
|
+
function emitTypedNamedTypeFallbackWarnings(models, storageTypes) {
|
|
751
|
+
const warnings = [];
|
|
752
|
+
const warnedFields = /* @__PURE__ */ new Set();
|
|
753
|
+
for (const [modelName, modelDefinition] of Object.entries(models)) for (const [fieldName, fieldBuilder] of Object.entries(modelDefinition.stageOne.fields)) {
|
|
754
|
+
const fieldState = fieldBuilder.build();
|
|
755
|
+
if (typeof fieldState.typeRef !== "string" || !(fieldState.typeRef in storageTypes)) continue;
|
|
756
|
+
const warningKey = `${modelName}.${fieldName}`;
|
|
757
|
+
if (warnedFields.has(warningKey)) continue;
|
|
758
|
+
warnedFields.add(warningKey);
|
|
759
|
+
warnings.push(`Contract field "${modelName}.${fieldName}" uses field.namedType('${fieldState.typeRef}'). Use field.namedType(types.${fieldState.typeRef}) when the storage type is declared in the same contract to keep autocomplete and typed local refs.`);
|
|
760
|
+
}
|
|
761
|
+
flushWarnings(warnings);
|
|
762
|
+
}
|
|
763
|
+
function emitTypedCrossModelFallbackWarnings(collection) {
|
|
764
|
+
const warnings = [];
|
|
765
|
+
const warnedKeys = /* @__PURE__ */ new Set();
|
|
766
|
+
for (const spec of collection.modelSpecs.values()) {
|
|
767
|
+
for (const [relationName, relationBuilder] of Object.entries(spec.relations)) {
|
|
768
|
+
const relation = relationBuilder.build();
|
|
769
|
+
if (relation.toModel.kind === "relationModelName" && relation.toModel.source === "string" && hasNamedModelToken(collection.models, relation.toModel.modelName)) {
|
|
770
|
+
const warningKey = `${spec.modelName}.${relationName}.toModel`;
|
|
771
|
+
if (!warnedKeys.has(warningKey)) {
|
|
772
|
+
warnedKeys.add(warningKey);
|
|
773
|
+
const current = formatRelationCall(relation, `'${relation.toModel.modelName}'`);
|
|
774
|
+
const suggested = formatRelationCall(relation, relation.toModel.modelName);
|
|
775
|
+
warnings.push(formatFallbackWarning(`relation "${spec.modelName}.${relationName}"`, current, suggested));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (relation.kind === "manyToMany" && relation.through.kind === "relationModelName" && relation.through.source === "string" && hasNamedModelToken(collection.models, relation.through.modelName)) {
|
|
779
|
+
const warningKey = `${spec.modelName}.${relationName}.through`;
|
|
780
|
+
if (!warnedKeys.has(warningKey)) {
|
|
781
|
+
warnedKeys.add(warningKey);
|
|
782
|
+
const current = formatManyToManyCallWithThrough(relation, `'${relation.through.modelName}'`);
|
|
783
|
+
const suggested = formatManyToManyCallWithThrough(relation, relation.through.modelName);
|
|
784
|
+
warnings.push(formatFallbackWarning(`relation "${spec.modelName}.${relationName}"`, current, suggested));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
for (const [foreignKeyIndex, foreignKey] of (spec.sqlSpec?.foreignKeys ?? []).entries()) {
|
|
789
|
+
if (foreignKey.targetSource !== "string" || !hasNamedModelToken(collection.models, foreignKey.targetModel)) continue;
|
|
790
|
+
const warningKey = `${spec.modelName}.sql.foreignKeys.${foreignKeyIndex}`;
|
|
791
|
+
if (warnedKeys.has(warningKey)) continue;
|
|
792
|
+
warnedKeys.add(warningKey);
|
|
793
|
+
const current = formatConstraintsRefCall(foreignKey.targetModel, foreignKey.targetFields);
|
|
794
|
+
const suggested = formatTokenFieldSelection(foreignKey.targetModel, foreignKey.targetFields);
|
|
795
|
+
warnings.push(formatFallbackWarning(`model "${spec.modelName}"`, `${current} in .sql(...)`, suggested));
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
flushWarnings(warnings);
|
|
799
|
+
}
|
|
800
|
+
function buildStorageTypeReverseLookup(storageTypes) {
|
|
801
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
802
|
+
for (const [key, instance] of Object.entries(storageTypes)) lookup.set(instance, key);
|
|
803
|
+
return lookup;
|
|
804
|
+
}
|
|
805
|
+
function resolveFieldDescriptor(modelName, fieldName, fieldState, storageTypes, storageTypeReverseLookup) {
|
|
806
|
+
if ("descriptor" in fieldState && fieldState.descriptor) return fieldState.descriptor;
|
|
807
|
+
if ("typeRef" in fieldState && fieldState.typeRef) {
|
|
808
|
+
if (isEnumTypeHandle(fieldState.typeRef)) return {
|
|
809
|
+
codecId: fieldState.typeRef.codecId,
|
|
810
|
+
nativeType: fieldState.typeRef.nativeType
|
|
811
|
+
};
|
|
812
|
+
const typeRef = typeof fieldState.typeRef === "string" ? fieldState.typeRef : storageTypeReverseLookup.get(fieldState.typeRef);
|
|
813
|
+
if (!typeRef) throw contractError("CONTRACT.TYPE_UNKNOWN", `Field "${modelName}.${fieldName}" references a storage type instance that is not present in definition.types`, { meta: {
|
|
814
|
+
modelName,
|
|
815
|
+
fieldName,
|
|
816
|
+
reason: "instance-not-in-definition-types"
|
|
817
|
+
} });
|
|
818
|
+
const referencedType = storageTypes[typeRef];
|
|
819
|
+
if (!referencedType) throw contractError("CONTRACT.TYPE_UNKNOWN", `Field "${modelName}.${fieldName}" references unknown storage type "${typeRef}"`, { meta: {
|
|
820
|
+
modelName,
|
|
821
|
+
fieldName,
|
|
822
|
+
typeRef
|
|
823
|
+
} });
|
|
824
|
+
return {
|
|
825
|
+
codecId: referencedType.codecId,
|
|
826
|
+
nativeType: referencedType.nativeType,
|
|
827
|
+
typeRef
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
throw contractError("CONTRACT.TYPE_UNKNOWN", `Field "${modelName}.${fieldName}" does not resolve to a storage descriptor`, { meta: {
|
|
831
|
+
modelName,
|
|
832
|
+
fieldName,
|
|
833
|
+
reason: "unresolved-storage-descriptor"
|
|
834
|
+
} });
|
|
835
|
+
}
|
|
836
|
+
function mapFieldNamesToColumnNames(modelName, fieldNames, fieldToColumn) {
|
|
837
|
+
return fieldNames.map((fieldName) => {
|
|
838
|
+
const columnName = fieldToColumn[fieldName];
|
|
839
|
+
if (!columnName) throw contractError("CONTRACT.FIELD_UNKNOWN", `Unknown field "${modelName}.${fieldName}" in contract definition`, { meta: {
|
|
840
|
+
modelName,
|
|
841
|
+
fieldName
|
|
842
|
+
} });
|
|
843
|
+
return columnName;
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
function assertRelationFieldArity(params) {
|
|
847
|
+
if (params.leftFields.length === params.rightFields.length) return;
|
|
848
|
+
throw contractError("CONTRACT.RELATION_INVALID", `Relation "${params.modelName}.${params.relationName}" maps ${params.leftFields.length} ${params.leftLabel} field(s) to ${params.rightFields.length} ${params.rightLabel} field(s).`, { meta: {
|
|
849
|
+
modelName: params.modelName,
|
|
850
|
+
relationName: params.relationName,
|
|
851
|
+
reason: "field-count-mismatch"
|
|
852
|
+
} });
|
|
853
|
+
}
|
|
854
|
+
function resolveInlineIdConstraint(spec) {
|
|
855
|
+
const inlineIdFields = [];
|
|
856
|
+
let idName;
|
|
857
|
+
for (const [fieldName, fieldBuilder] of Object.entries(spec.fieldBuilders)) {
|
|
858
|
+
const fieldState = fieldBuilder.build();
|
|
859
|
+
if (!fieldState.id) continue;
|
|
860
|
+
inlineIdFields.push(fieldName);
|
|
861
|
+
if (fieldState.id.name) idName = fieldState.id.name;
|
|
862
|
+
}
|
|
863
|
+
if (inlineIdFields.length === 0) return;
|
|
864
|
+
if (inlineIdFields.length > 1) throw contractError("CONTRACT.IDENTITY_INVALID", `Model "${spec.modelName}" marks multiple fields with .id(). Use .attributes(...) for compound identities.`, { meta: {
|
|
865
|
+
modelName: spec.modelName,
|
|
866
|
+
reason: "multiple-inline-ids",
|
|
867
|
+
fields: inlineIdFields
|
|
868
|
+
} });
|
|
869
|
+
const [inlineIdField] = inlineIdFields;
|
|
870
|
+
if (!inlineIdField) return;
|
|
871
|
+
return {
|
|
872
|
+
kind: "id",
|
|
873
|
+
fields: [inlineIdField],
|
|
874
|
+
...idName ? { name: idName } : {}
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
function collectInlineUniqueConstraints(spec) {
|
|
878
|
+
const constraints = [];
|
|
879
|
+
for (const [fieldName, fieldBuilder] of Object.entries(spec.fieldBuilders)) {
|
|
880
|
+
const fieldState = fieldBuilder.build();
|
|
881
|
+
if (!fieldState.unique) continue;
|
|
882
|
+
constraints.push({
|
|
883
|
+
kind: "unique",
|
|
884
|
+
fields: [fieldName],
|
|
885
|
+
...fieldState.unique.name ? { name: fieldState.unique.name } : {}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
return constraints;
|
|
889
|
+
}
|
|
890
|
+
function resolveModelIdConstraint(spec) {
|
|
891
|
+
const inlineId = resolveInlineIdConstraint(spec);
|
|
892
|
+
const attributeId = spec.attributesSpec?.id;
|
|
893
|
+
if (inlineId && attributeId) throw contractError("CONTRACT.IDENTITY_INVALID", `Model "${spec.modelName}" defines identity both inline and in .attributes(...). Pick one identity style.`, { meta: {
|
|
894
|
+
modelName: spec.modelName,
|
|
895
|
+
reason: "inline-and-attributes"
|
|
896
|
+
} });
|
|
897
|
+
const resolvedId = attributeId ?? inlineId;
|
|
898
|
+
if (resolvedId && resolvedId.fields.length === 0) throw contractError("CONTRACT.IDENTITY_INVALID", `Model "${spec.modelName}" defines an empty identity. Add at least one field.`, { meta: {
|
|
899
|
+
modelName: spec.modelName,
|
|
900
|
+
reason: "empty-identity"
|
|
901
|
+
} });
|
|
902
|
+
return resolvedId;
|
|
903
|
+
}
|
|
904
|
+
function resolveModelUniqueConstraints(spec) {
|
|
905
|
+
const attributeUniques = spec.attributesSpec?.uniques ?? [];
|
|
906
|
+
for (const unique of attributeUniques) if (unique.fields.length === 0) throw contractError("CONTRACT.CONSTRAINT_INVALID", `Model "${spec.modelName}" defines an empty unique constraint. Add at least one field.`, { meta: { modelName: spec.modelName } });
|
|
907
|
+
return [...collectInlineUniqueConstraints(spec), ...attributeUniques];
|
|
908
|
+
}
|
|
909
|
+
function resolveRelationForeignKeys(spec, allSpecs) {
|
|
910
|
+
const foreignKeys = [];
|
|
911
|
+
for (const [relationName, relationBuilder] of Object.entries(spec.relations)) {
|
|
912
|
+
const relation = relationBuilder.build();
|
|
913
|
+
if (relation.kind !== "belongsTo" || !relation.sql?.fk) continue;
|
|
914
|
+
const targetModelName = resolveRelationModelName(relation.toModel);
|
|
915
|
+
if (relation.spaceId !== void 0) {
|
|
916
|
+
const fields = normalizeRelationFieldNames(relation.from);
|
|
917
|
+
const targetFields = normalizeRelationFieldNames(relation.to);
|
|
918
|
+
assertRelationFieldArity({
|
|
919
|
+
modelName: spec.modelName,
|
|
920
|
+
relationName,
|
|
921
|
+
leftLabel: "source",
|
|
922
|
+
leftFields: fields,
|
|
923
|
+
rightLabel: "target",
|
|
924
|
+
rightFields: targetFields
|
|
925
|
+
});
|
|
926
|
+
foreignKeys.push({
|
|
927
|
+
kind: "fk",
|
|
928
|
+
fields,
|
|
929
|
+
targetModel: targetModelName,
|
|
930
|
+
targetFields,
|
|
931
|
+
targetSpaceId: relation.spaceId,
|
|
932
|
+
...relation.namespaceId !== void 0 ? { targetNamespaceId: relation.namespaceId } : {},
|
|
933
|
+
...relation.tableName !== void 0 ? { targetTableName: relation.tableName } : {},
|
|
934
|
+
...relation.sql.fk.name ? { name: relation.sql.fk.name } : {},
|
|
935
|
+
...relation.sql.fk.onDelete ? { onDelete: relation.sql.fk.onDelete } : {},
|
|
936
|
+
...relation.sql.fk.onUpdate ? { onUpdate: relation.sql.fk.onUpdate } : {},
|
|
937
|
+
...relation.sql.fk.constraint !== void 0 ? { constraint: relation.sql.fk.constraint } : {},
|
|
938
|
+
...relation.sql.fk.index !== void 0 ? { index: relation.sql.fk.index } : {}
|
|
939
|
+
});
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (!allSpecs.has(targetModelName)) throw contractError("CONTRACT.MODEL_UNKNOWN", `Relation "${spec.modelName}.${relationName}" references unknown model "${targetModelName}"`, { meta: {
|
|
943
|
+
sourceModel: spec.modelName,
|
|
944
|
+
relationName,
|
|
945
|
+
targetModel: targetModelName
|
|
946
|
+
} });
|
|
947
|
+
const fields = normalizeRelationFieldNames(relation.from);
|
|
948
|
+
const targetFields = normalizeRelationFieldNames(relation.to);
|
|
949
|
+
assertRelationFieldArity({
|
|
950
|
+
modelName: spec.modelName,
|
|
951
|
+
relationName,
|
|
952
|
+
leftLabel: "source",
|
|
953
|
+
leftFields: fields,
|
|
954
|
+
rightLabel: "target",
|
|
955
|
+
rightFields: targetFields
|
|
956
|
+
});
|
|
957
|
+
foreignKeys.push({
|
|
958
|
+
kind: "fk",
|
|
959
|
+
fields,
|
|
960
|
+
targetModel: targetModelName,
|
|
961
|
+
targetFields,
|
|
962
|
+
...relation.sql.fk.name ? { name: relation.sql.fk.name } : {},
|
|
963
|
+
...relation.sql.fk.onDelete ? { onDelete: relation.sql.fk.onDelete } : {},
|
|
964
|
+
...relation.sql.fk.onUpdate ? { onUpdate: relation.sql.fk.onUpdate } : {},
|
|
965
|
+
...relation.sql.fk.constraint !== void 0 ? { constraint: relation.sql.fk.constraint } : {},
|
|
966
|
+
...relation.sql.fk.index !== void 0 ? { index: relation.sql.fk.index } : {}
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
return foreignKeys;
|
|
970
|
+
}
|
|
971
|
+
function resolveRelationAnchorFields(spec) {
|
|
972
|
+
const idFields = spec.idConstraint?.fields;
|
|
973
|
+
if (idFields && idFields.length > 0) return idFields;
|
|
974
|
+
if ("id" in spec.fieldToColumn) return ["id"];
|
|
975
|
+
throw contractError("CONTRACT.IDENTITY_INVALID", `Model "${spec.modelName}" needs an explicit id or an "id" field to anchor non-owning relations`, { meta: {
|
|
976
|
+
modelName: spec.modelName,
|
|
977
|
+
reason: "missing-anchor-id"
|
|
978
|
+
} });
|
|
979
|
+
}
|
|
980
|
+
function lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs, extensions) {
|
|
981
|
+
const targetModelName = resolveRelationModelName(relation.toModel);
|
|
982
|
+
const fromFields = normalizeRelationFieldNames(relation.from);
|
|
983
|
+
const toFields = normalizeRelationFieldNames(relation.to);
|
|
984
|
+
assertRelationFieldArity({
|
|
985
|
+
modelName: currentSpec.modelName,
|
|
986
|
+
relationName,
|
|
987
|
+
leftLabel: "source",
|
|
988
|
+
leftFields: fromFields,
|
|
989
|
+
rightLabel: "target",
|
|
990
|
+
rightFields: toFields
|
|
991
|
+
});
|
|
992
|
+
if (relation.spaceId !== void 0) {
|
|
993
|
+
assertKnownExtensionPack(extensions, relation.spaceId, `Relation "${currentSpec.modelName}.${relationName}"`);
|
|
994
|
+
const targetTable = relation.tableName ?? targetModelName.toLowerCase();
|
|
995
|
+
const parentColumns = mapFieldNamesToColumnNames(currentSpec.modelName, fromFields, currentSpec.fieldToColumn);
|
|
996
|
+
return {
|
|
997
|
+
fieldName: relationName,
|
|
998
|
+
toModel: targetModelName,
|
|
999
|
+
toTable: targetTable,
|
|
1000
|
+
cardinality: "N:1",
|
|
1001
|
+
spaceId: relation.spaceId,
|
|
1002
|
+
...relation.namespaceId !== void 0 ? { namespaceId: relation.namespaceId } : {},
|
|
1003
|
+
on: {
|
|
1004
|
+
parentTable: currentSpec.tableName,
|
|
1005
|
+
parentColumns,
|
|
1006
|
+
childTable: targetTable,
|
|
1007
|
+
childColumns: toFields
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
const targetSpec = allSpecs.get(targetModelName);
|
|
1012
|
+
if (!targetSpec) throw contractError("CONTRACT.MODEL_UNKNOWN", `Relation "${currentSpec.modelName}.${relationName}" references unknown model "${targetModelName}"`, { meta: {
|
|
1013
|
+
sourceModel: currentSpec.modelName,
|
|
1014
|
+
relationName,
|
|
1015
|
+
targetModel: targetModelName
|
|
1016
|
+
} });
|
|
1017
|
+
return {
|
|
1018
|
+
fieldName: relationName,
|
|
1019
|
+
toModel: targetModelName,
|
|
1020
|
+
toTable: targetSpec.tableName,
|
|
1021
|
+
cardinality: "N:1",
|
|
1022
|
+
on: {
|
|
1023
|
+
parentTable: currentSpec.tableName,
|
|
1024
|
+
parentColumns: mapFieldNamesToColumnNames(currentSpec.modelName, fromFields, currentSpec.fieldToColumn),
|
|
1025
|
+
childTable: targetSpec.tableName,
|
|
1026
|
+
childColumns: mapFieldNamesToColumnNames(targetSpec.modelName, toFields, targetSpec.fieldToColumn)
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
function lowerHasOwnershipRelation(relationName, relation, currentSpec, allSpecs) {
|
|
1031
|
+
const targetModelName = resolveRelationModelName(relation.toModel);
|
|
1032
|
+
const targetSpec = allSpecs.get(targetModelName);
|
|
1033
|
+
if (!targetSpec) throw contractError("CONTRACT.MODEL_UNKNOWN", `Relation "${currentSpec.modelName}.${relationName}" references unknown model "${targetModelName}"`, { meta: {
|
|
1034
|
+
sourceModel: currentSpec.modelName,
|
|
1035
|
+
relationName,
|
|
1036
|
+
targetModel: targetModelName
|
|
1037
|
+
} });
|
|
1038
|
+
const parentFields = resolveRelationAnchorFields(currentSpec);
|
|
1039
|
+
const childFields = normalizeRelationFieldNames(relation.by);
|
|
1040
|
+
assertRelationFieldArity({
|
|
1041
|
+
modelName: currentSpec.modelName,
|
|
1042
|
+
relationName,
|
|
1043
|
+
leftLabel: "anchor",
|
|
1044
|
+
leftFields: parentFields,
|
|
1045
|
+
rightLabel: "child",
|
|
1046
|
+
rightFields: childFields
|
|
1047
|
+
});
|
|
1048
|
+
return {
|
|
1049
|
+
fieldName: relationName,
|
|
1050
|
+
toModel: targetModelName,
|
|
1051
|
+
toTable: targetSpec.tableName,
|
|
1052
|
+
cardinality: relation.kind === "hasMany" ? "1:N" : "1:1",
|
|
1053
|
+
on: {
|
|
1054
|
+
parentTable: currentSpec.tableName,
|
|
1055
|
+
parentColumns: mapFieldNamesToColumnNames(currentSpec.modelName, parentFields, currentSpec.fieldToColumn),
|
|
1056
|
+
childTable: targetSpec.tableName,
|
|
1057
|
+
childColumns: mapFieldNamesToColumnNames(targetSpec.modelName, childFields, targetSpec.fieldToColumn)
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function lowerManyToManyRelation(relationName, relation, currentSpec, allSpecs) {
|
|
1062
|
+
const targetModelName = resolveRelationModelName(relation.toModel);
|
|
1063
|
+
const targetSpec = allSpecs.get(targetModelName);
|
|
1064
|
+
if (!targetSpec) throw contractError("CONTRACT.MODEL_UNKNOWN", `Relation "${currentSpec.modelName}.${relationName}" references unknown model "${targetModelName}"`, { meta: {
|
|
1065
|
+
sourceModel: currentSpec.modelName,
|
|
1066
|
+
relationName,
|
|
1067
|
+
targetModel: targetModelName
|
|
1068
|
+
} });
|
|
1069
|
+
const throughModelName = resolveRelationModelName(relation.through);
|
|
1070
|
+
const throughSpec = allSpecs.get(throughModelName);
|
|
1071
|
+
if (!throughSpec) throw contractError("CONTRACT.MODEL_UNKNOWN", `Relation "${currentSpec.modelName}.${relationName}" references unknown through model "${throughModelName}"`, { meta: {
|
|
1072
|
+
sourceModel: currentSpec.modelName,
|
|
1073
|
+
relationName,
|
|
1074
|
+
targetModel: throughModelName
|
|
1075
|
+
} });
|
|
1076
|
+
const currentAnchorFields = resolveRelationAnchorFields(currentSpec);
|
|
1077
|
+
const targetAnchorFields = resolveRelationAnchorFields(targetSpec);
|
|
1078
|
+
const throughFromFields = normalizeRelationFieldNames(relation.from);
|
|
1079
|
+
const throughToFields = normalizeRelationFieldNames(relation.to);
|
|
1080
|
+
if (currentAnchorFields.length !== throughFromFields.length || targetAnchorFields.length !== throughToFields.length) throw contractError("CONTRACT.RELATION_INVALID", `Relation "${currentSpec.modelName}.${relationName}" has mismatched many-to-many field counts.`, { meta: {
|
|
1081
|
+
modelName: currentSpec.modelName,
|
|
1082
|
+
relationName,
|
|
1083
|
+
reason: "many-to-many-field-count-mismatch"
|
|
1084
|
+
} });
|
|
1085
|
+
return {
|
|
1086
|
+
fieldName: relationName,
|
|
1087
|
+
toModel: targetModelName,
|
|
1088
|
+
toTable: targetSpec.tableName,
|
|
1089
|
+
cardinality: "N:M",
|
|
1090
|
+
through: {
|
|
1091
|
+
table: throughSpec.tableName,
|
|
1092
|
+
...ifDefined("namespaceId", throughSpec.namespace),
|
|
1093
|
+
parentColumns: mapFieldNamesToColumnNames(throughSpec.modelName, throughFromFields, throughSpec.fieldToColumn),
|
|
1094
|
+
childColumns: mapFieldNamesToColumnNames(throughSpec.modelName, throughToFields, throughSpec.fieldToColumn)
|
|
1095
|
+
},
|
|
1096
|
+
on: {
|
|
1097
|
+
parentTable: currentSpec.tableName,
|
|
1098
|
+
parentColumns: mapFieldNamesToColumnNames(currentSpec.modelName, currentAnchorFields, currentSpec.fieldToColumn),
|
|
1099
|
+
childTable: throughSpec.tableName,
|
|
1100
|
+
childColumns: mapFieldNamesToColumnNames(throughSpec.modelName, throughFromFields, throughSpec.fieldToColumn)
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
function resolveRelationNode(relationName, relation, currentSpec, allSpecs, extensions) {
|
|
1105
|
+
if (relation.kind === "belongsTo") return lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs, extensions);
|
|
1106
|
+
if (relation.kind === "hasMany" || relation.kind === "hasOne") return lowerHasOwnershipRelation(relationName, relation, currentSpec, allSpecs);
|
|
1107
|
+
return lowerManyToManyRelation(relationName, relation, currentSpec, allSpecs);
|
|
1108
|
+
}
|
|
1109
|
+
function lowerLocalForeignKeyNode(spec, targetSpec, foreignKey) {
|
|
1110
|
+
return {
|
|
1111
|
+
columns: mapFieldNamesToColumnNames(spec.modelName, foreignKey.fields, spec.fieldToColumn),
|
|
1112
|
+
references: {
|
|
1113
|
+
model: targetSpec.modelName,
|
|
1114
|
+
table: targetSpec.tableName,
|
|
1115
|
+
columns: mapFieldNamesToColumnNames(targetSpec.modelName, foreignKey.targetFields, targetSpec.fieldToColumn)
|
|
1116
|
+
},
|
|
1117
|
+
...foreignKey.name ? { name: foreignKey.name } : {},
|
|
1118
|
+
...foreignKey.onDelete ? { onDelete: foreignKey.onDelete } : {},
|
|
1119
|
+
...foreignKey.onUpdate ? { onUpdate: foreignKey.onUpdate } : {},
|
|
1120
|
+
...foreignKey.constraint !== void 0 ? { constraint: foreignKey.constraint } : {},
|
|
1121
|
+
...foreignKey.index !== void 0 ? { index: foreignKey.index } : {}
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
function lowerCrossSpaceForeignKeyNode(spec, foreignKey) {
|
|
1125
|
+
return {
|
|
1126
|
+
columns: mapFieldNamesToColumnNames(spec.modelName, foreignKey.fields, spec.fieldToColumn),
|
|
1127
|
+
references: {
|
|
1128
|
+
model: foreignKey.targetModel,
|
|
1129
|
+
table: foreignKey.targetTableName ?? foreignKey.targetModel.toLowerCase(),
|
|
1130
|
+
columns: foreignKey.targetFields,
|
|
1131
|
+
...foreignKey.targetNamespaceId !== void 0 ? { namespaceId: foreignKey.targetNamespaceId } : {},
|
|
1132
|
+
spaceId: foreignKey.targetSpaceId
|
|
1133
|
+
},
|
|
1134
|
+
...foreignKey.name ? { name: foreignKey.name } : {},
|
|
1135
|
+
...foreignKey.onDelete ? { onDelete: foreignKey.onDelete } : {},
|
|
1136
|
+
...foreignKey.onUpdate ? { onUpdate: foreignKey.onUpdate } : {},
|
|
1137
|
+
...foreignKey.constraint !== void 0 ? { constraint: foreignKey.constraint } : {},
|
|
1138
|
+
...foreignKey.index !== void 0 ? { index: foreignKey.index } : {}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
function assertKnownExtensionPack(extensions, spaceId, context) {
|
|
1142
|
+
if (extensions !== void 0 && Object.hasOwn(extensions, spaceId)) return;
|
|
1143
|
+
throw contractError("CONTRACT.PACK_MISSING", `${context} references contract space "${spaceId}" but "${spaceId}" is not declared in extensions. Add the pack to extensions.`, { meta: {
|
|
1144
|
+
spaceId,
|
|
1145
|
+
context
|
|
1146
|
+
} });
|
|
1147
|
+
}
|
|
1148
|
+
function resolveForeignKeyNodes(spec, allSpecs, extensions) {
|
|
1149
|
+
const relationForeignKeys = resolveRelationForeignKeys(spec, allSpecs).map((foreignKey) => {
|
|
1150
|
+
if (foreignKey.targetSpaceId !== void 0) {
|
|
1151
|
+
assertKnownExtensionPack(extensions, foreignKey.targetSpaceId, `Relation-derived foreign key on "${spec.modelName}"`);
|
|
1152
|
+
return lowerCrossSpaceForeignKeyNode(spec, {
|
|
1153
|
+
...foreignKey,
|
|
1154
|
+
targetSpaceId: foreignKey.targetSpaceId
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
const targetSpec = allSpecs.get(foreignKey.targetModel);
|
|
1158
|
+
if (!targetSpec) throw contractError("CONTRACT.MODEL_UNKNOWN", `Foreign key on "${spec.modelName}" references unknown model "${foreignKey.targetModel}"`, { meta: {
|
|
1159
|
+
sourceModel: spec.modelName,
|
|
1160
|
+
targetModel: foreignKey.targetModel
|
|
1161
|
+
} });
|
|
1162
|
+
return lowerLocalForeignKeyNode(spec, targetSpec, foreignKey);
|
|
1163
|
+
});
|
|
1164
|
+
const sqlForeignKeys = (spec.sqlSpec?.foreignKeys ?? []).map((foreignKey) => {
|
|
1165
|
+
if (foreignKey.targetSpaceId !== void 0) {
|
|
1166
|
+
assertKnownExtensionPack(extensions, foreignKey.targetSpaceId, `Foreign key on "${spec.modelName}"`);
|
|
1167
|
+
return lowerCrossSpaceForeignKeyNode(spec, {
|
|
1168
|
+
...foreignKey,
|
|
1169
|
+
targetSpaceId: foreignKey.targetSpaceId
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
const targetSpec = allSpecs.get(foreignKey.targetModel);
|
|
1173
|
+
if (!targetSpec) throw contractError("CONTRACT.MODEL_UNKNOWN", `Foreign key on "${spec.modelName}" references unknown model "${foreignKey.targetModel}"`, { meta: {
|
|
1174
|
+
sourceModel: spec.modelName,
|
|
1175
|
+
targetModel: foreignKey.targetModel
|
|
1176
|
+
} });
|
|
1177
|
+
return lowerLocalForeignKeyNode(spec, targetSpec, foreignKey);
|
|
1178
|
+
});
|
|
1179
|
+
return [...relationForeignKeys, ...sqlForeignKeys];
|
|
1180
|
+
}
|
|
1181
|
+
function resolveModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup, extensions) {
|
|
1182
|
+
const fields = [];
|
|
1183
|
+
for (const [fieldName, fieldBuilder] of Object.entries(spec.fieldBuilders)) {
|
|
1184
|
+
const fieldState = fieldBuilder.build();
|
|
1185
|
+
const descriptor = resolveFieldDescriptor(spec.modelName, fieldName, fieldState, storageTypes, storageTypeReverseLookup);
|
|
1186
|
+
const columnName = spec.fieldToColumn[fieldName];
|
|
1187
|
+
if (!columnName) throw new InternalError(`Column name resolution failed for "${spec.modelName}.${fieldName}"`);
|
|
1188
|
+
const enumHandle = "typeRef" in fieldState && isEnumTypeHandle(fieldState.typeRef) ? fieldState.typeRef : void 0;
|
|
1189
|
+
fields.push({
|
|
1190
|
+
fieldName,
|
|
1191
|
+
columnName,
|
|
1192
|
+
descriptor,
|
|
1193
|
+
nullable: fieldState.nullable,
|
|
1194
|
+
...fieldState.many === true ? { many: true } : {},
|
|
1195
|
+
...fieldState.default ? { default: fieldState.default } : {},
|
|
1196
|
+
...fieldState.executionDefaults ? { executionDefaults: fieldState.executionDefaults } : {},
|
|
1197
|
+
...enumHandle !== void 0 ? { enumTypeHandle: enumHandle } : {}
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
const { idConstraint } = spec;
|
|
1201
|
+
const uniques = resolveModelUniqueConstraints(spec).map((unique) => ({
|
|
1202
|
+
columns: mapFieldNamesToColumnNames(spec.modelName, unique.fields, spec.fieldToColumn),
|
|
1203
|
+
...unique.name ? { name: unique.name } : {}
|
|
1204
|
+
}));
|
|
1205
|
+
const indexes = (spec.sqlSpec?.indexes ?? []).map((index) => {
|
|
1206
|
+
const method = blindCast({
|
|
1207
|
+
type: index.type,
|
|
1208
|
+
options: index.options
|
|
1209
|
+
});
|
|
1210
|
+
const carried = {
|
|
1211
|
+
where: index.where,
|
|
1212
|
+
unique: index.unique,
|
|
1213
|
+
name: index.name,
|
|
1214
|
+
map: index.map,
|
|
1215
|
+
...method
|
|
1216
|
+
};
|
|
1217
|
+
return index.expression !== void 0 ? {
|
|
1218
|
+
...carried,
|
|
1219
|
+
expression: index.expression
|
|
1220
|
+
} : {
|
|
1221
|
+
...carried,
|
|
1222
|
+
columns: mapFieldNamesToColumnNames(spec.modelName, index.fields ?? [], spec.fieldToColumn)
|
|
1223
|
+
};
|
|
1224
|
+
});
|
|
1225
|
+
const foreignKeys = resolveForeignKeyNodes(spec, allSpecs, extensions);
|
|
1226
|
+
const relations = Object.entries(spec.relations).map(([relationName, relationBuilder]) => resolveRelationNode(relationName, relationBuilder.build(), spec, allSpecs, extensions));
|
|
1227
|
+
return {
|
|
1228
|
+
modelName: spec.modelName,
|
|
1229
|
+
tableName: spec.tableName,
|
|
1230
|
+
...spec.namespace !== void 0 ? { namespaceId: spec.namespace } : {},
|
|
1231
|
+
fields,
|
|
1232
|
+
...idConstraint ? { id: {
|
|
1233
|
+
columns: mapFieldNamesToColumnNames(spec.modelName, idConstraint.fields, spec.fieldToColumn),
|
|
1234
|
+
...idConstraint.name ? { name: idConstraint.name } : {}
|
|
1235
|
+
} } : {},
|
|
1236
|
+
...uniques.length > 0 ? { uniques } : {},
|
|
1237
|
+
...indexes.length > 0 ? { indexes } : {},
|
|
1238
|
+
...foreignKeys.length > 0 ? { foreignKeys } : {},
|
|
1239
|
+
...relations.length > 0 ? { relations } : {},
|
|
1240
|
+
...ifDefined("control", spec.sqlSpec?.control)
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
function collectRuntimeModelSpecs(definition) {
|
|
1244
|
+
const storageTypes = { ...definition.types ?? {} };
|
|
1245
|
+
const models = { ...definition.models ?? {} };
|
|
1246
|
+
emitTypedNamedTypeFallbackWarnings(models, storageTypes);
|
|
1247
|
+
const modelSpecs = /* @__PURE__ */ new Map();
|
|
1248
|
+
const tableOwners = /* @__PURE__ */ new Map();
|
|
1249
|
+
for (const [modelName, modelDefinition] of Object.entries(models)) {
|
|
1250
|
+
const tokenModelName = modelDefinition.stageOne.modelName;
|
|
1251
|
+
if (tokenModelName && tokenModelName !== modelName) throw contractError("CONTRACT.MODEL_TOKEN_INVALID", `Model token "${tokenModelName}" must be assigned to models.${tokenModelName}. Received models.${modelName}.`, { meta: {
|
|
1252
|
+
tokenModelName,
|
|
1253
|
+
assignedKey: modelName
|
|
1254
|
+
} });
|
|
1255
|
+
const attributesSpec = modelDefinition.buildAttributesSpec();
|
|
1256
|
+
const sqlSpec = modelDefinition.buildSqlSpec();
|
|
1257
|
+
const tableName = sqlSpec?.table ?? applyNaming(modelName, definition.naming?.tables);
|
|
1258
|
+
const namespaceId = modelDefinition.stageOne.namespace ?? definition.target.defaultNamespaceId;
|
|
1259
|
+
const tableKey = JSON.stringify([namespaceId, tableName]);
|
|
1260
|
+
const existingModel = tableOwners.get(tableKey);
|
|
1261
|
+
if (existingModel) throw contractError("CONTRACT.NAME_DUPLICATE", `Models "${existingModel}" and "${modelName}" both map to table "${tableName}".`, { meta: {
|
|
1262
|
+
kind: "table",
|
|
1263
|
+
name: tableName,
|
|
1264
|
+
first: existingModel,
|
|
1265
|
+
second: modelName
|
|
1266
|
+
} });
|
|
1267
|
+
tableOwners.set(tableKey, modelName);
|
|
1268
|
+
const fieldToColumn = {};
|
|
1269
|
+
const columnOwners = /* @__PURE__ */ new Map();
|
|
1270
|
+
for (const [fieldName, fieldBuilder] of Object.entries(modelDefinition.stageOne.fields)) {
|
|
1271
|
+
const columnName = fieldBuilder.build().columnName ?? applyNaming(fieldName, definition.naming?.columns);
|
|
1272
|
+
const existingField = columnOwners.get(columnName);
|
|
1273
|
+
if (existingField) throw contractError("CONTRACT.NAME_DUPLICATE", `Model "${modelName}" maps both "${existingField}" and "${fieldName}" to column "${columnName}".`, { meta: {
|
|
1274
|
+
kind: "column",
|
|
1275
|
+
name: columnName,
|
|
1276
|
+
first: existingField,
|
|
1277
|
+
second: fieldName
|
|
1278
|
+
} });
|
|
1279
|
+
columnOwners.set(columnName, fieldName);
|
|
1280
|
+
fieldToColumn[fieldName] = columnName;
|
|
1281
|
+
}
|
|
1282
|
+
const fieldBuilders = modelDefinition.stageOne.fields;
|
|
1283
|
+
const idConstraint = resolveModelIdConstraint({
|
|
1284
|
+
modelName,
|
|
1285
|
+
fieldBuilders,
|
|
1286
|
+
attributesSpec
|
|
1287
|
+
});
|
|
1288
|
+
modelSpecs.set(modelName, {
|
|
1289
|
+
modelName,
|
|
1290
|
+
tableName,
|
|
1291
|
+
namespace: modelDefinition.stageOne.namespace,
|
|
1292
|
+
fieldBuilders,
|
|
1293
|
+
fieldToColumn,
|
|
1294
|
+
relations: modelDefinition.stageOne.relations,
|
|
1295
|
+
attributesSpec,
|
|
1296
|
+
sqlSpec,
|
|
1297
|
+
idConstraint
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
return {
|
|
1301
|
+
storageTypes,
|
|
1302
|
+
models,
|
|
1303
|
+
modelSpecs
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
function lowerModels(collection, extensions) {
|
|
1307
|
+
emitTypedCrossModelFallbackWarnings(collection);
|
|
1308
|
+
const storageTypeReverseLookup = buildStorageTypeReverseLookup(collection.storageTypes);
|
|
1309
|
+
return Array.from(collection.modelSpecs.values()).map((spec) => resolveModelNode(spec, collection.modelSpecs, collection.storageTypes, storageTypeReverseLookup, extensions));
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Kind-agnostic walk over the author-declared `entities` handle list:
|
|
1313
|
+
*
|
|
1314
|
+
* 1. Index the bound packs' `entityTypes` contributions by discriminator so
|
|
1315
|
+
* each handle's `entityKind` maps to the pack that registered it; a
|
|
1316
|
+
* handle whose kind no composed pack registers is an error naming the
|
|
1317
|
+
* kind.
|
|
1318
|
+
* 2. Resolve each handle's declared model refs (`handle.refs`, actual
|
|
1319
|
+
* model-handle objects) to storage table coordinates — identity against
|
|
1320
|
+
* the contract's `models` record first, then the handle's declared model
|
|
1321
|
+
* name against the build's model specs; never by re-deriving a table
|
|
1322
|
+
* name. A cross-space (extensionModel) handle resolves to its own
|
|
1323
|
+
* coordinate annotated with `spaceId`.
|
|
1324
|
+
* 3. Call each owning pack's batch lowering hook once with all of its
|
|
1325
|
+
* claimed handles, and fold the returned rows into the namespace-scoped
|
|
1326
|
+
* attachments (namespace → kind → key), rejecting two different entities
|
|
1327
|
+
* in one slot. The result becomes `ContractDefinition.attachedEntities`.
|
|
1328
|
+
*
|
|
1329
|
+
* No entity kind is named anywhere in this walk.
|
|
1330
|
+
*/
|
|
1331
|
+
function lowerPackEntityHandles(definition, modelSpecs) {
|
|
1332
|
+
const entities = definition.entities;
|
|
1333
|
+
if (entities === void 0 || entities.length === 0) return void 0;
|
|
1334
|
+
const components = [definition.target, ...Object.values(definition.extensions ?? {})];
|
|
1335
|
+
const owningComponent = /* @__PURE__ */ new Map();
|
|
1336
|
+
const walkEntityTypes = (namespace, component) => {
|
|
1337
|
+
for (const value of Object.values(namespace)) if (isAuthoringEntityTypeDescriptor(value)) owningComponent.set(value.discriminator, component);
|
|
1338
|
+
else walkEntityTypes(value, component);
|
|
1339
|
+
};
|
|
1340
|
+
for (const component of components) {
|
|
1341
|
+
const entityTypes = component.authoring?.entityTypes;
|
|
1342
|
+
if (entityTypes !== void 0) walkEntityTypes(entityTypes, component);
|
|
1343
|
+
}
|
|
1344
|
+
const defaultNamespaceId = definition.target.defaultNamespaceId;
|
|
1345
|
+
const modelNamesByIdentity = /* @__PURE__ */ new Map();
|
|
1346
|
+
for (const [modelName, modelBuilder] of Object.entries(definition.models ?? {})) modelNamesByIdentity.set(modelBuilder, modelName);
|
|
1347
|
+
const coordinateOf = (modelName) => {
|
|
1348
|
+
const spec = modelSpecs.get(modelName);
|
|
1349
|
+
if (spec === void 0) return void 0;
|
|
1350
|
+
return {
|
|
1351
|
+
kind: "resolved",
|
|
1352
|
+
namespaceId: spec.namespace ?? defaultNamespaceId,
|
|
1353
|
+
tableName: spec.tableName,
|
|
1354
|
+
modelName
|
|
1355
|
+
};
|
|
1356
|
+
};
|
|
1357
|
+
const declaredModelName = (value) => {
|
|
1358
|
+
if (typeof value !== "object" || value === null || !("stageOne" in value)) return void 0;
|
|
1359
|
+
const stageOne = value.stageOne;
|
|
1360
|
+
if (typeof stageOne !== "object" || stageOne === null || !("modelName" in stageOne)) return;
|
|
1361
|
+
return typeof stageOne.modelName === "string" ? stageOne.modelName : void 0;
|
|
1362
|
+
};
|
|
1363
|
+
const resolveRef = (value) => {
|
|
1364
|
+
const modelName = declaredModelName(value);
|
|
1365
|
+
if (isCrossSpaceHandle(value)) {
|
|
1366
|
+
const tableName = value.tableName;
|
|
1367
|
+
const namespaceId = value.stageOne.namespace;
|
|
1368
|
+
if (tableName !== void 0 && namespaceId !== void 0) return {
|
|
1369
|
+
kind: "cross-space",
|
|
1370
|
+
spaceId: value.spaceId,
|
|
1371
|
+
namespaceId,
|
|
1372
|
+
tableName,
|
|
1373
|
+
...ifDefined("modelName", modelName)
|
|
1374
|
+
};
|
|
1375
|
+
return {
|
|
1376
|
+
kind: "unresolved",
|
|
1377
|
+
...ifDefined("modelName", modelName)
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
const identityName = modelNamesByIdentity.get(value);
|
|
1381
|
+
return (identityName !== void 0 ? coordinateOf(identityName) : void 0) ?? (modelName !== void 0 ? coordinateOf(modelName) : void 0) ?? {
|
|
1382
|
+
kind: "unresolved",
|
|
1383
|
+
...ifDefined("modelName", modelName)
|
|
1384
|
+
};
|
|
1385
|
+
};
|
|
1386
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
1387
|
+
for (const handle of entities) {
|
|
1388
|
+
const component = owningComponent.get(handle.entityKind);
|
|
1389
|
+
if (component === void 0) throw contractError("CONTRACT.ENTITY_KIND_UNKNOWN", `defineContract: entities contains a handle with entityKind "${handle.entityKind}", which no composed pack registers. Compose a pack whose entityTypes contribution claims "${handle.entityKind}", or remove the handle.`, { meta: { entityKind: handle.entityKind } });
|
|
1390
|
+
const refs = {};
|
|
1391
|
+
for (const [refName, refValue] of Object.entries(handle.refs ?? {})) refs[refName] = resolveRef(refValue);
|
|
1392
|
+
const forComponent = claimed.get(component) ?? [];
|
|
1393
|
+
forComponent.push({
|
|
1394
|
+
handle,
|
|
1395
|
+
refs
|
|
1396
|
+
});
|
|
1397
|
+
claimed.set(component, forComponent);
|
|
1398
|
+
}
|
|
1399
|
+
const pack = {};
|
|
1400
|
+
for (const [component, handles] of claimed) {
|
|
1401
|
+
const authoring = component.authoring;
|
|
1402
|
+
if (!providesEntityHandleLowering(authoring)) {
|
|
1403
|
+
const kinds = [...new Set(handles.map((entry) => entry.handle.entityKind))].sort();
|
|
1404
|
+
throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `defineContract: entityKind(s) ${kinds.map((kind) => `"${kind}"`).join(", ")} are registered by a pack that does not implement entity-handle lowering (no lowerEntityHandles on its authoring contributions).`, { meta: {
|
|
1405
|
+
entityKinds: kinds,
|
|
1406
|
+
reason: "missing-lowerEntityHandles"
|
|
1407
|
+
} });
|
|
1408
|
+
}
|
|
1409
|
+
for (const row of authoring.lowerEntityHandles({
|
|
1410
|
+
handles,
|
|
1411
|
+
defaultNamespaceId
|
|
1412
|
+
})) {
|
|
1413
|
+
const forNamespace = pack[row.namespaceId] ?? {};
|
|
1414
|
+
pack[row.namespaceId] = forNamespace;
|
|
1415
|
+
const forKind = forNamespace[row.entityKind] ?? {};
|
|
1416
|
+
forNamespace[row.entityKind] = forKind;
|
|
1417
|
+
const existing = forKind[row.key];
|
|
1418
|
+
if (existing !== void 0 && existing !== row.entity) throw contractError("CONTRACT.NAME_DUPLICATE", `defineContract: two different "${row.entityKind}" entities named "${row.key}" in namespace "${row.namespaceId}" — pack-entity names must be unique per namespace.`, { meta: {
|
|
1419
|
+
kind: row.entityKind,
|
|
1420
|
+
name: row.key,
|
|
1421
|
+
namespaceId: row.namespaceId
|
|
1422
|
+
} });
|
|
1423
|
+
forKind[row.key] = row.entity;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return pack;
|
|
1427
|
+
}
|
|
1428
|
+
function buildContractDefinition(definition) {
|
|
1429
|
+
const collection = collectRuntimeModelSpecs(definition);
|
|
1430
|
+
const models = lowerModels(collection, definition.extensions);
|
|
1431
|
+
const attachedEntities = lowerPackEntityHandles(definition, collection.modelSpecs);
|
|
1432
|
+
return {
|
|
1433
|
+
target: definition.target,
|
|
1434
|
+
...ifDefined("defaultControlPolicy", definition.defaultControlPolicy),
|
|
1435
|
+
...definition.extensions ? { extensions: definition.extensions } : {},
|
|
1436
|
+
...definition.storageHash ? { storageHash: definition.storageHash } : {},
|
|
1437
|
+
...definition.foreignKeyDefaults ? { foreignKeyDefaults: definition.foreignKeyDefaults } : {},
|
|
1438
|
+
...Object.keys(collection.storageTypes).length > 0 ? { storageTypes: collection.storageTypes } : {},
|
|
1439
|
+
...definition.namespaces ? { namespaces: definition.namespaces } : {},
|
|
1440
|
+
warnings: void 0,
|
|
1441
|
+
createNamespace: definition.createNamespace,
|
|
1442
|
+
...definition.enums && Object.keys(definition.enums).length > 0 ? { enums: definition.enums } : {},
|
|
1443
|
+
...attachedEntities && Object.keys(attachedEntities).length > 0 ? { attachedEntities } : {},
|
|
1444
|
+
models
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
function validateTargetPackRef(family, target) {
|
|
1448
|
+
if (family.familyId !== "sql") throw contractError("CONTRACT.PACK_FAMILY_MISMATCH", `defineContract only accepts SQL family packs. Received family "${family.familyId}".`, { meta: {
|
|
1449
|
+
packId: family.id,
|
|
1450
|
+
packFamilyId: family.familyId,
|
|
1451
|
+
contractFamilyId: "sql"
|
|
1452
|
+
} });
|
|
1453
|
+
if (target.familyId !== family.familyId) throw contractError("CONTRACT.PACK_FAMILY_MISMATCH", `target pack "${target.id}" targets family "${target.familyId}" but contract family is "${family.familyId}".`, { meta: {
|
|
1454
|
+
packId: target.id,
|
|
1455
|
+
packFamilyId: target.familyId,
|
|
1456
|
+
contractFamilyId: family.familyId
|
|
1457
|
+
} });
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Per-target reserved namespace names enforced by `defineContract` for
|
|
1461
|
+
* SQL family contracts. Two categories:
|
|
1462
|
+
*
|
|
1463
|
+
* 1. **IR sentinels** (`__unbound__`, `__unspecified__`) — reserved on
|
|
1464
|
+
* every SQL target. The double-underscore decoration marks them as
|
|
1465
|
+
* framework-reserved coordinates; user code must not declare them
|
|
1466
|
+
* explicitly.
|
|
1467
|
+
* 2. **Target-specific PSL keywords** — Postgres reserves the bare
|
|
1468
|
+
* `unbound` identifier for the late-binding opt-in
|
|
1469
|
+
* (`namespace unbound { … }`) so the TS surface must reject it from
|
|
1470
|
+
* `defineContract({ namespaces })` lists. SQLite has no schema
|
|
1471
|
+
* concept and rejects every non-empty namespaces list outright;
|
|
1472
|
+
* callers should declare `namespaces: []` or omit the field.
|
|
1473
|
+
*/
|
|
1474
|
+
function validateNamespaceDeclarations(target, namespaces) {
|
|
1475
|
+
if (!namespaces) return;
|
|
1476
|
+
if (target.targetId === "sqlite" && namespaces.length > 0) throw contractError("CONTRACT.NAMESPACE_UNSUPPORTED", `defineContract: SQLite contracts cannot declare namespaces (SQLite has no schema concept; emitted DDL is always unqualified). Received namespaces: [${namespaces.map((name) => `"${name}"`).join(", ")}].`, { meta: {
|
|
1477
|
+
namespaces,
|
|
1478
|
+
targetId: target.targetId
|
|
1479
|
+
} });
|
|
1480
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1481
|
+
for (const namespace of namespaces) {
|
|
1482
|
+
if (namespace.length === 0) throw contractError("CONTRACT.NAMESPACE_INVALID", "defineContract: namespace names cannot be empty.", { meta: {
|
|
1483
|
+
namespace,
|
|
1484
|
+
reason: "empty"
|
|
1485
|
+
} });
|
|
1486
|
+
if (namespace.trim().length === 0) throw contractError("CONTRACT.NAMESPACE_INVALID", `defineContract: namespace name "${namespace}" cannot be whitespace-only.`, { meta: {
|
|
1487
|
+
namespace,
|
|
1488
|
+
reason: "whitespace-only"
|
|
1489
|
+
} });
|
|
1490
|
+
if (namespace === "__unbound__" || namespace === "__unspecified__") throw contractError("CONTRACT.NAMESPACE_INVALID", `defineContract: namespace name "${namespace}" is a reserved IR sentinel and cannot appear in the declared namespaces list.`, { meta: {
|
|
1491
|
+
namespace,
|
|
1492
|
+
reason: "reserved-ir-sentinel"
|
|
1493
|
+
} });
|
|
1494
|
+
if (target.targetId === "postgres" && namespace === "unbound") throw contractError("CONTRACT.NAMESPACE_INVALID", `defineContract: namespace name "unbound" is reserved by Postgres for the late-binding opt-in (use \`namespace unbound { … }\` in PSL instead of declaring it as a regular schema).`, { meta: {
|
|
1495
|
+
namespace,
|
|
1496
|
+
reason: "reserved-by-postgres"
|
|
1497
|
+
} });
|
|
1498
|
+
if (seen.has(namespace)) throw contractError("CONTRACT.NAME_DUPLICATE", `defineContract: namespaces list contains duplicate entry "${namespace}".`, { meta: {
|
|
1499
|
+
kind: "namespace",
|
|
1500
|
+
name: namespace
|
|
1501
|
+
} });
|
|
1502
|
+
seen.add(namespace);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Per-model `namespace` validation paired with
|
|
1507
|
+
* {@link validateNamespaceDeclarations}. Mirrors the reserved-name
|
|
1508
|
+
* rules so the per-model surface stays consistent with the contract-
|
|
1509
|
+
* level surface:
|
|
1510
|
+
*
|
|
1511
|
+
* - `__unbound__` / `__unspecified__` — reserved IR sentinels on
|
|
1512
|
+
* every SQL target.
|
|
1513
|
+
* - `unbound` on Postgres — reserved for the PSL
|
|
1514
|
+
* `namespace unbound { … }` opt-in.
|
|
1515
|
+
*
|
|
1516
|
+
* Additionally enforces that each per-model `namespace` either
|
|
1517
|
+
* references an entry in the contract's declared `namespaces` list or
|
|
1518
|
+
* names the Postgres late-binding keyword (`unbound`) — the latter is
|
|
1519
|
+
* not a "declared namespace" but is a legal opt-in only via PSL today,
|
|
1520
|
+
* so the TS surface also rejects it on the per-model side and points
|
|
1521
|
+
* authors at the PSL `namespace unbound { … }` block.
|
|
1522
|
+
*
|
|
1523
|
+
* The SQLite per-model `namespace` field is rejected outright (SQLite
|
|
1524
|
+
* has no schema concept).
|
|
1525
|
+
*/
|
|
1526
|
+
function validatePerModelNamespaces(target, namespaces, models) {
|
|
1527
|
+
const declaredNamespaces = new Set(namespaces ?? []);
|
|
1528
|
+
for (const [modelKey, modelBuilder] of Object.entries(models)) {
|
|
1529
|
+
const perModelNamespace = modelBuilder.stageOne.namespace;
|
|
1530
|
+
if (perModelNamespace === void 0) continue;
|
|
1531
|
+
if (target.targetId === "sqlite") throw contractError("CONTRACT.NAMESPACE_UNSUPPORTED", `defineContract: model "${modelKey}" sets \`namespace: "${perModelNamespace}"\` but the target is SQLite (SQLite has no schema concept; remove the per-model \`namespace\` field).`, { meta: {
|
|
1532
|
+
modelKey,
|
|
1533
|
+
namespace: perModelNamespace,
|
|
1534
|
+
targetId: target.targetId
|
|
1535
|
+
} });
|
|
1536
|
+
if (perModelNamespace === "__unbound__" || perModelNamespace === "__unspecified__") throw contractError("CONTRACT.NAMESPACE_INVALID", `defineContract: model "${modelKey}" sets \`namespace: "${perModelNamespace}"\` but that name is a reserved IR sentinel and cannot appear in user code.`, { meta: {
|
|
1537
|
+
modelKey,
|
|
1538
|
+
namespace: perModelNamespace,
|
|
1539
|
+
reason: "reserved-ir-sentinel"
|
|
1540
|
+
} });
|
|
1541
|
+
if (target.targetId === "postgres" && perModelNamespace === "unbound") throw contractError("CONTRACT.NAMESPACE_INVALID", `defineContract: model "${modelKey}" sets \`namespace: "unbound"\` but that name is reserved by Postgres for the late-binding opt-in (use \`namespace unbound { … }\` in PSL instead — there is no equivalent surface in the TS builder today).`, { meta: {
|
|
1542
|
+
modelKey,
|
|
1543
|
+
namespace: perModelNamespace,
|
|
1544
|
+
reason: "reserved-by-postgres"
|
|
1545
|
+
} });
|
|
1546
|
+
if (!declaredNamespaces.has(perModelNamespace)) throw contractError("CONTRACT.NAMESPACE_UNKNOWN", `defineContract: model "${modelKey}" references namespace "${perModelNamespace}" but that name does not appear in the contract's declared \`namespaces\` list.${declaredNamespaces.size > 0 ? ` Declared namespaces: [${[...declaredNamespaces].map((name) => `"${name}"`).join(", ")}].` : " The contract does not declare any namespaces; add `namespaces: [\"…\"]` to `defineContract` first."}`, { meta: {
|
|
1547
|
+
modelKey,
|
|
1548
|
+
namespace: perModelNamespace,
|
|
1549
|
+
declared: [...declaredNamespaces]
|
|
1550
|
+
} });
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
function validateExtensionPackRefs(target, extensions) {
|
|
1554
|
+
if (!extensions) return;
|
|
1555
|
+
for (const packRef of Object.values(extensions)) {
|
|
1556
|
+
if (packRef.kind !== "extension") throw contractError("CONTRACT.PACK_REF_INVALID", `defineContract only accepts extension pack refs in extensions. Received kind "${packRef.kind}".`, { meta: {
|
|
1557
|
+
packId: packRef.id,
|
|
1558
|
+
kind: packRef.kind
|
|
1559
|
+
} });
|
|
1560
|
+
if (packRef.familyId !== target.familyId) throw contractError("CONTRACT.PACK_FAMILY_MISMATCH", `extension pack "${packRef.id}" targets family "${packRef.familyId}" but contract target family is "${target.familyId}".`, { meta: {
|
|
1561
|
+
packId: packRef.id,
|
|
1562
|
+
packFamilyId: packRef.familyId,
|
|
1563
|
+
contractFamilyId: target.familyId
|
|
1564
|
+
} });
|
|
1565
|
+
if (packRef.targetId && packRef.targetId !== target.targetId) throw contractError("CONTRACT.PACK_TARGET_MISMATCH", `extension pack "${packRef.id}" targets "${packRef.targetId}" but contract target is "${target.targetId}".`, { meta: {
|
|
1566
|
+
packId: packRef.id,
|
|
1567
|
+
packTargetId: packRef.targetId,
|
|
1568
|
+
contractTargetId: target.targetId
|
|
1569
|
+
} });
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function buildContractFromDsl(definition) {
|
|
1573
|
+
validateTargetPackRef(definition.family, definition.target);
|
|
1574
|
+
validateExtensionPackRefs(definition.target, definition.extensions);
|
|
1575
|
+
validateNamespaceDeclarations(definition.target, definition.namespaces);
|
|
1576
|
+
validatePerModelNamespaces(definition.target, definition.namespaces, definition.models ?? {});
|
|
1577
|
+
return blindCast(buildSqlContractFromDefinition(buildContractDefinition(definition), definition.codecLookup));
|
|
1578
|
+
}
|
|
1579
|
+
/** Implementation. */
|
|
1580
|
+
function buildBoundContract(family, target, definition, factory) {
|
|
1581
|
+
const full = {
|
|
1582
|
+
...definition,
|
|
1583
|
+
family,
|
|
1584
|
+
target
|
|
1585
|
+
};
|
|
1586
|
+
if (factory !== void 0) {
|
|
1587
|
+
const built = factory(createComposedAuthoringHelpers({
|
|
1588
|
+
family,
|
|
1589
|
+
target,
|
|
1590
|
+
extensions: definition.extensions
|
|
1591
|
+
}));
|
|
1592
|
+
const mergedEnums = {
|
|
1593
|
+
...definition.enums ?? {},
|
|
1594
|
+
...built.enums
|
|
1595
|
+
};
|
|
1596
|
+
const mergedEntities = [...definition.entities ?? [], ...built.entities ?? []];
|
|
1597
|
+
return buildContractFromDsl({
|
|
1598
|
+
...full,
|
|
1599
|
+
...ifDefined("types", built.types),
|
|
1600
|
+
...ifDefined("models", built.models),
|
|
1601
|
+
...ifDefined("enums", Object.keys(mergedEnums).length > 0 ? mergedEnums : void 0),
|
|
1602
|
+
...ifDefined("entities", mergedEntities.length > 0 ? mergedEntities : void 0)
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
return buildContractFromDsl(full);
|
|
1606
|
+
}
|
|
1607
|
+
function defineContract(definition, factory) {
|
|
1608
|
+
if (!isContractInput(definition)) throw contractError("CONTRACT.ARGUMENT_INVALID", "defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).");
|
|
1609
|
+
if (factory !== void 0) return buildBoundContract(definition.family, definition.target, definition, factory);
|
|
1610
|
+
return buildBoundContract(definition.family, definition.target, definition);
|
|
1611
|
+
}
|
|
1612
|
+
//#endregion
|
|
1613
|
+
export { enumType as a, member as c, defineContract as i, model as l, buildBoundContract as n, extensionModel as o, buildContractDefinition as r, field as s, bindEnumType as t, rel as u };
|
|
1614
|
+
|
|
1615
|
+
//# sourceMappingURL=contract-builder-YjMQcvZ1.mjs.map
|