@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
package/dist/runtime.mjs
ADDED
|
@@ -0,0 +1,1606 @@
|
|
|
1
|
+
import { t as createSqlOperationRegistry } from "./dist-C_q1_QR9.mjs";
|
|
2
|
+
import { A as PreparedParamRef, B as isQueryAst } from "./types-BgkZru_1-H7yfikSQ.mjs";
|
|
3
|
+
import { t as collectOrderedParamRefs } from "./util-DQQgv2j1-D6INZaRG.mjs";
|
|
4
|
+
import { t as createSqlParamRefMutator } from "./middleware-CMr4CHNz-DHIUdxlm.mjs";
|
|
5
|
+
import { t as buildCodecDescriptorRegistry } from "./codec-descriptor-registry-BJVJRftS.mjs";
|
|
6
|
+
import { isStructuredError } from "@prisma/orm-framework/utils/structured-error";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { assertNever } from "@prisma/orm-framework/utils/internal-error";
|
|
9
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
10
|
+
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
11
|
+
import { canonicalStringify } from "@prisma/orm-framework/utils/canonical-stringify";
|
|
12
|
+
import { materializeCodec, resolveCodecDescriptorOrThrow } from "@prisma/orm-framework/components/codec";
|
|
13
|
+
import { AsyncIterableResult, RuntimeCore, checkAborted, checkMiddlewareCompatibility, raceAgainstAbort, runBeforeExecuteChain, runWithMiddleware, runtimeError } from "@prisma/orm-framework/components/runtime";
|
|
14
|
+
import { canonicalizeJson } from "@prisma/orm-framework/components/utils";
|
|
15
|
+
import { checkContractComponentRequirements, mergeCapabilityMatrices } from "@prisma/orm-framework/components/components";
|
|
16
|
+
import { createExecutionStack } from "@prisma/orm-framework/components/execution";
|
|
17
|
+
import { hashContent } from "@prisma/orm-framework/utils/hash-content";
|
|
18
|
+
//#region ../../../2-sql/5-runtime/dist/exports-DcRj136z.mjs
|
|
19
|
+
/**
|
|
20
|
+
* Build an {@link AstCodecResolver} bound to a descriptor registry and a per-call instance-context factory.
|
|
21
|
+
*
|
|
22
|
+
* The instance-context factory lets callers control `name` / `usedAt` for refs the AST supplies (e.g. AST-embedded migration ops where the materialisation site is the AST node, not a contract column). The contract-walk pre-population path constructs its own contexts and invokes the resolver with those refs to seed the cache.
|
|
23
|
+
*/
|
|
24
|
+
function createAstCodecResolver(descriptors, instanceContextFor) {
|
|
25
|
+
const cache = /* @__PURE__ */ new Map();
|
|
26
|
+
return { forCodecRef(ref) {
|
|
27
|
+
const key = `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
|
|
28
|
+
const cached = cache.get(key);
|
|
29
|
+
if (cached) return cached;
|
|
30
|
+
const codec = materializeCodec(resolveCodecDescriptorOrThrow((id) => descriptors.descriptorFor(id), ref, "RUNTIME.CODEC_DESCRIPTOR_MISSING"), ref, instanceContextFor(ref));
|
|
31
|
+
cache.set(key, codec);
|
|
32
|
+
return codec;
|
|
33
|
+
} };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a contract-free {@link ContractCodecRegistry} that resolves codecs
|
|
37
|
+
* purely from AST-supplied {@link import('@internal/framework-components/codec').CodecRef}s
|
|
38
|
+
* against a target's descriptor registry.
|
|
39
|
+
*
|
|
40
|
+
* Dispatch is driven entirely by `CodecRef`s embedded in AST nodes; no
|
|
41
|
+
* contract walk is needed. `forColumn` always returns `undefined` — this
|
|
42
|
+
* registry carries no column-to-codec mappings.
|
|
43
|
+
*/
|
|
44
|
+
function createAstCodecRegistry(descriptors) {
|
|
45
|
+
const resolver = createAstCodecResolver(descriptors, (ref) => ({
|
|
46
|
+
name: ref.codecId,
|
|
47
|
+
usedAt: []
|
|
48
|
+
}));
|
|
49
|
+
return {
|
|
50
|
+
forColumn: () => void 0,
|
|
51
|
+
forCodecRef: (ref) => resolver.forCodecRef(ref)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const NO_METADATA = Object.freeze({
|
|
55
|
+
codec: void 0,
|
|
56
|
+
name: void 0
|
|
57
|
+
});
|
|
58
|
+
function deriveParamMetadata(ast) {
|
|
59
|
+
return collectOrderedParamRefs(ast).map((ref) => {
|
|
60
|
+
return {
|
|
61
|
+
codec: ref.codec,
|
|
62
|
+
name: ref.name
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function resolveParamCodec(metadata, contractCodecs) {
|
|
67
|
+
if (metadata.codec && contractCodecs) return contractCodecs.forCodecRef(metadata.codec);
|
|
68
|
+
}
|
|
69
|
+
function paramLabel(metadata, paramIndex) {
|
|
70
|
+
return metadata.name ?? `param[${paramIndex}]`;
|
|
71
|
+
}
|
|
72
|
+
function wrapEncodeFailure(error, metadata, paramIndex, codecId) {
|
|
73
|
+
const label = paramLabel(metadata, paramIndex);
|
|
74
|
+
const wrapped = runtimeError("RUNTIME.ENCODE_FAILED", `Failed to encode parameter ${label} with codec '${codecId}': ${error instanceof Error ? error.message : String(error)}`, {
|
|
75
|
+
label,
|
|
76
|
+
codec: codecId,
|
|
77
|
+
paramIndex
|
|
78
|
+
});
|
|
79
|
+
wrapped.cause = error;
|
|
80
|
+
throw wrapped;
|
|
81
|
+
}
|
|
82
|
+
async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs) {
|
|
83
|
+
if (value === null || value === void 0) return null;
|
|
84
|
+
const codec = resolveParamCodec(metadata, contractCodecs);
|
|
85
|
+
if (!codec) return value;
|
|
86
|
+
if (metadata.codec?.many) {
|
|
87
|
+
if (!Array.isArray(value)) wrapEncodeFailure(/* @__PURE__ */ new TypeError(`expected an array for many-typed parameter, got ${typeof value}`), metadata, paramIndex, codec.id);
|
|
88
|
+
const encoded = [];
|
|
89
|
+
for (let i = 0; i < value.length; i++) {
|
|
90
|
+
const elem = value[i];
|
|
91
|
+
if (elem === null || elem === void 0) {
|
|
92
|
+
encoded.push(null);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
encoded.push(await codec.encode(elem, ctx));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (isStructuredError(error)) throw error;
|
|
99
|
+
wrapEncodeFailure(error, metadata, paramIndex, codec.id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return encoded;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await codec.encode(value, ctx);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (isStructuredError(error)) throw error;
|
|
108
|
+
wrapEncodeFailure(error, metadata, paramIndex, codec.id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Encodes all parameters concurrently via `Promise.all`. Per parameter, sync-and async-authored codecs share the same path: `codec.encode → await → return`. Param-level failures are wrapped in `RUNTIME.ENCODE_FAILED`.
|
|
113
|
+
*
|
|
114
|
+
* When `ctx.signal` is provided:
|
|
115
|
+
*
|
|
116
|
+
* - **Already-aborted at entry** short-circuits with `RUNTIME.ABORTED` (`{ phase: 'encode' }`) before any `codec.encode` call is made — codecs can pin this with a per-call counter that stays at zero.
|
|
117
|
+
* - **Mid-flight abort** races the per-param `Promise.all` against `abortable(ctx.signal)`. The runtime returns `RUNTIME.ABORTED` promptly even if codec bodies ignore the signal; the in-flight bodies are abandoned and run to completion in the background (cooperative cancellation, see ADR 204).
|
|
118
|
+
* - Existing structured envelopes (any dotted-code error passing `isStructuredError`, e.g. `RUNTIME.ENCODE_FAILED`) that surface from a codec body before the runtime observes the abort pass through unchanged (no double wrap).
|
|
119
|
+
*/
|
|
120
|
+
async function encodeParams(plan, ctx, contractCodecs) {
|
|
121
|
+
return encodeParamsWithMetadata(plan.params, deriveParamMetadata(plan.ast), ctx, contractCodecs);
|
|
122
|
+
}
|
|
123
|
+
async function encodeParamsWithMetadata(values, metadata, ctx, contractCodecs) {
|
|
124
|
+
checkAborted(ctx, "encode");
|
|
125
|
+
const signal = ctx.signal;
|
|
126
|
+
if (values.length === 0) return values;
|
|
127
|
+
const tasks = values.map((value, i) => encodeParamValue(value, metadata[i] ?? NO_METADATA, i, ctx, contractCodecs));
|
|
128
|
+
const settled = await raceAgainstAbort(Promise.all(tasks), signal, "encode");
|
|
129
|
+
return Object.freeze(settled);
|
|
130
|
+
}
|
|
131
|
+
function extractCodecIds(contract) {
|
|
132
|
+
const codecIds = /* @__PURE__ */ new Set();
|
|
133
|
+
for (const ns of Object.values(contract.storage.namespaces)) for (const table of Object.values(ns.entries.table ?? {})) for (const column of Object.values(table.columns)) {
|
|
134
|
+
const codecId = column.codecId;
|
|
135
|
+
codecIds.add(codecId);
|
|
136
|
+
}
|
|
137
|
+
return codecIds;
|
|
138
|
+
}
|
|
139
|
+
function extractColumnCodecRefs(contract) {
|
|
140
|
+
const refs = [];
|
|
141
|
+
for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) for (const [columnName, column] of Object.entries(table.columns)) refs.push({
|
|
142
|
+
namespaceId,
|
|
143
|
+
table: tableName,
|
|
144
|
+
column: columnName,
|
|
145
|
+
codecId: column.codecId
|
|
146
|
+
});
|
|
147
|
+
return refs;
|
|
148
|
+
}
|
|
149
|
+
function validateContractCodecMappings(registry, contract) {
|
|
150
|
+
const invalidCodecs = extractColumnCodecRefs(contract).filter((ref) => registry.descriptorFor(ref.codecId) === void 0);
|
|
151
|
+
if (invalidCodecs.length > 0) {
|
|
152
|
+
const details = {
|
|
153
|
+
contractTarget: contract.target,
|
|
154
|
+
invalidCodecs
|
|
155
|
+
};
|
|
156
|
+
throw runtimeError("RUNTIME.CODEC_MISSING", `Missing codec implementations for column codecIds: ${invalidCodecs.map((c) => `${c.namespaceId}.${c.table}.${c.column} (${c.codecId})`).join(", ")}`, details);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function validateCodecRegistryCompleteness(registry, contract) {
|
|
160
|
+
validateContractCodecMappings(registry, contract);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Lowers a SQL query plan to an executable Plan by calling the adapter's lower method.
|
|
164
|
+
*
|
|
165
|
+
* Ad-hoc lowerings produce only `{kind: 'literal'}` slots; this helper
|
|
166
|
+
* unwraps them into the bare-value array `SqlExecutionPlan` exposes.
|
|
167
|
+
* Encountering a `{kind: 'bind'}` slot here means the caller passed an
|
|
168
|
+
* AST containing `PreparedParamRef` to the ad-hoc execute path — that's a
|
|
169
|
+
* caller error, surfaced as `RUNTIME.PREPARE_BIND_ON_ADHOC`.
|
|
170
|
+
*/
|
|
171
|
+
function lowerSqlPlan(adapter, contract, queryPlan) {
|
|
172
|
+
const lowered = adapter.lower(queryPlan.ast, {
|
|
173
|
+
contract,
|
|
174
|
+
params: queryPlan.params
|
|
175
|
+
});
|
|
176
|
+
const params = lowered.params.map((slot) => {
|
|
177
|
+
if (slot.kind === "literal") return slot.value;
|
|
178
|
+
throw runtimeError("RUNTIME.PREPARE_BIND_ON_ADHOC", `Ad-hoc execute received a bind-site slot for '${slot.name}' — bind-site references are only valid inside runtime.prepare(...).`, { name: slot.name });
|
|
179
|
+
});
|
|
180
|
+
return Object.freeze({
|
|
181
|
+
sql: lowered.sql,
|
|
182
|
+
params,
|
|
183
|
+
ast: queryPlan.ast,
|
|
184
|
+
meta: queryPlan.meta
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function hasAggregateWithoutGroupBy(ast) {
|
|
188
|
+
if (ast.groupBy !== void 0) return false;
|
|
189
|
+
return ast.projection.some((item) => item.expr.kind === "aggregate");
|
|
190
|
+
}
|
|
191
|
+
function primaryTableFromAst(ast) {
|
|
192
|
+
if (ast.from === void 0) return void 0;
|
|
193
|
+
switch (ast.from.kind) {
|
|
194
|
+
case "table-source": return ast.from.name;
|
|
195
|
+
case "derived-table-source": return ast.from.alias;
|
|
196
|
+
case "function-source": return ast.from.fn;
|
|
197
|
+
// v8 ignore next 4
|
|
198
|
+
default: return assertNever(ast.from, `Unsupported source kind: ${ast.from.kind}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function estimateRowsFromAst(ast, tableRows, defaultTableRows, hasAggregateWithoutGroup) {
|
|
202
|
+
if (hasAggregateWithoutGroup) return 1;
|
|
203
|
+
const primaryTable = primaryTableFromAst(ast);
|
|
204
|
+
const tableEstimate = (primaryTable !== void 0 ? tableRows[primaryTable] : void 0) ?? defaultTableRows;
|
|
205
|
+
if (typeof ast.limit === "number") return Math.min(ast.limit, tableEstimate);
|
|
206
|
+
return tableEstimate;
|
|
207
|
+
}
|
|
208
|
+
function emitBudgetViolation(error, shouldBlock, ctx) {
|
|
209
|
+
if (shouldBlock) throw error;
|
|
210
|
+
ctx.log.warn({
|
|
211
|
+
code: error.code,
|
|
212
|
+
message: error.message,
|
|
213
|
+
details: error.details
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function budgets(options) {
|
|
217
|
+
const maxRows = options?.maxRows ?? 1e4;
|
|
218
|
+
const defaultTableRows = options?.defaultTableRows ?? 1e4;
|
|
219
|
+
const tableRows = options?.tableRows ?? {};
|
|
220
|
+
const maxLatencyMs = options?.maxLatencyMs ?? 1e3;
|
|
221
|
+
const rowSeverity = options?.severities?.rowCount ?? "error";
|
|
222
|
+
const latencySeverity = options?.severities?.latency ?? "warn";
|
|
223
|
+
const observedRowsByPlan = /* @__PURE__ */ new WeakMap();
|
|
224
|
+
return Object.freeze({
|
|
225
|
+
name: "budgets",
|
|
226
|
+
familyId: "sql",
|
|
227
|
+
async beforeExecute(plan, ctx) {
|
|
228
|
+
observedRowsByPlan.set(plan, { count: 0 });
|
|
229
|
+
if (isQueryAst(plan.ast) && plan.ast.kind === "select") return evaluateSelectAst(plan.ast, ctx);
|
|
230
|
+
},
|
|
231
|
+
async onRow(_row, plan, _ctx) {
|
|
232
|
+
const state = observedRowsByPlan.get(plan);
|
|
233
|
+
if (!state) return;
|
|
234
|
+
state.count += 1;
|
|
235
|
+
if (state.count > maxRows) throw runtimeError("BUDGET.ROWS_EXCEEDED", "Observed row count exceeds budget", {
|
|
236
|
+
source: "observed",
|
|
237
|
+
observedRows: state.count,
|
|
238
|
+
maxRows
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
async afterExecute(_plan, result, ctx) {
|
|
242
|
+
const latencyMs = result.latencyMs;
|
|
243
|
+
if (latencyMs > maxLatencyMs) {
|
|
244
|
+
const shouldBlock = latencySeverity === "error" || ctx.mode === "strict";
|
|
245
|
+
emitBudgetViolation(runtimeError("BUDGET.TIME_EXCEEDED", "Query latency exceeds budget", {
|
|
246
|
+
latencyMs,
|
|
247
|
+
maxLatencyMs
|
|
248
|
+
}), shouldBlock, ctx);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
function evaluateSelectAst(ast, ctx) {
|
|
253
|
+
const hasAggNoGroup = hasAggregateWithoutGroupBy(ast);
|
|
254
|
+
const estimated = estimateRowsFromAst(ast, tableRows, defaultTableRows, hasAggNoGroup);
|
|
255
|
+
const isUnbounded = ast.limit === void 0 && !hasAggNoGroup;
|
|
256
|
+
const shouldBlock = rowSeverity === "error" || ctx.mode === "strict";
|
|
257
|
+
if (isUnbounded) {
|
|
258
|
+
emitBudgetViolation(runtimeError("BUDGET.ROWS_EXCEEDED", "Unbounded SELECT query exceeds budget", estimated >= maxRows ? {
|
|
259
|
+
source: "ast",
|
|
260
|
+
estimatedRows: estimated,
|
|
261
|
+
maxRows
|
|
262
|
+
} : {
|
|
263
|
+
source: "ast",
|
|
264
|
+
maxRows
|
|
265
|
+
}), shouldBlock, ctx);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (estimated > maxRows) emitBudgetViolation(runtimeError("BUDGET.ROWS_EXCEEDED", "Estimated row count exceeds budget", {
|
|
269
|
+
source: "ast",
|
|
270
|
+
estimatedRows: estimated,
|
|
271
|
+
maxRows
|
|
272
|
+
}), shouldBlock, ctx);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const SELECT_STAR_REGEX = /select\s+\*/i;
|
|
276
|
+
const LIMIT_REGEX = /\blimit\b/i;
|
|
277
|
+
const MUTATION_PREFIX_REGEX = /^(insert|update|delete|create|alter|drop|truncate)\b/i;
|
|
278
|
+
const READ_ONLY_INTENTS = /* @__PURE__ */ new Set([
|
|
279
|
+
"read",
|
|
280
|
+
"report",
|
|
281
|
+
"readonly"
|
|
282
|
+
]);
|
|
283
|
+
function evaluateRawGuardrails(plan, config) {
|
|
284
|
+
const lints = [];
|
|
285
|
+
const budgets = [];
|
|
286
|
+
const normalized = normalizeWhitespace(plan.sql);
|
|
287
|
+
const statementType = classifyStatement(normalized);
|
|
288
|
+
if (statementType === "select") {
|
|
289
|
+
if (SELECT_STAR_REGEX.test(normalized)) lints.push(createLint("LINT.SELECT_STAR", "error", "Raw SQL plan selects all columns via *", { sql: snippet(plan.sql) }));
|
|
290
|
+
if (!LIMIT_REGEX.test(normalized)) {
|
|
291
|
+
const severity = config?.budgets?.unboundedSelectSeverity ?? "error";
|
|
292
|
+
lints.push(createLint("LINT.NO_LIMIT", "warn", "Raw SQL plan omits LIMIT clause", { sql: snippet(plan.sql) }));
|
|
293
|
+
budgets.push(createBudget("BUDGET.ROWS_EXCEEDED", severity, "Raw SQL plan is unbounded and may exceed row budget", {
|
|
294
|
+
sql: snippet(plan.sql),
|
|
295
|
+
...config?.budgets?.estimatedRows !== void 0 ? { estimatedRows: config.budgets.estimatedRows } : {}
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (isMutationStatement(statementType) && isReadOnlyIntent(plan.meta)) lints.push(createLint("LINT.READ_ONLY_MUTATION", "error", "Raw SQL plan mutates data despite read-only intent", {
|
|
300
|
+
sql: snippet(plan.sql),
|
|
301
|
+
intent: plan.meta.annotations?.["intent"]
|
|
302
|
+
}));
|
|
303
|
+
return {
|
|
304
|
+
lints,
|
|
305
|
+
budgets,
|
|
306
|
+
statement: statementType
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function classifyStatement(sql) {
|
|
310
|
+
const trimmed = sql.trim();
|
|
311
|
+
const lower = trimmed.toLowerCase();
|
|
312
|
+
if (lower.startsWith("with")) {
|
|
313
|
+
if (lower.includes("select")) return "select";
|
|
314
|
+
}
|
|
315
|
+
if (lower.startsWith("select")) return "select";
|
|
316
|
+
if (MUTATION_PREFIX_REGEX.test(trimmed)) return "mutation";
|
|
317
|
+
return "other";
|
|
318
|
+
}
|
|
319
|
+
function isMutationStatement(statement) {
|
|
320
|
+
return statement === "mutation";
|
|
321
|
+
}
|
|
322
|
+
function isReadOnlyIntent(meta) {
|
|
323
|
+
const annotations = meta.annotations;
|
|
324
|
+
const intent = typeof annotations?.intent === "string" ? annotations.intent.toLowerCase() : void 0;
|
|
325
|
+
return intent !== void 0 && READ_ONLY_INTENTS.has(intent);
|
|
326
|
+
}
|
|
327
|
+
function normalizeWhitespace(value) {
|
|
328
|
+
return value.replace(/\s+/g, " ").trim();
|
|
329
|
+
}
|
|
330
|
+
function snippet(sql) {
|
|
331
|
+
return normalizeWhitespace(sql).slice(0, 200);
|
|
332
|
+
}
|
|
333
|
+
function createLint(code, severity, message, details) {
|
|
334
|
+
return {
|
|
335
|
+
code,
|
|
336
|
+
severity,
|
|
337
|
+
message,
|
|
338
|
+
...details ? { details } : {}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function createBudget(code, severity, message, details) {
|
|
342
|
+
return {
|
|
343
|
+
code,
|
|
344
|
+
severity,
|
|
345
|
+
message,
|
|
346
|
+
...details ? { details } : {}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function getFromSourceTableDetail(source) {
|
|
350
|
+
switch (source.kind) {
|
|
351
|
+
case "table-source": return source.name;
|
|
352
|
+
case "derived-table-source": return source.alias;
|
|
353
|
+
case "function-source": return source.fn;
|
|
354
|
+
// v8 ignore next 4
|
|
355
|
+
default: return assertNever(source, `Unsupported source kind: ${source.kind}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function evaluateAstLints(ast) {
|
|
359
|
+
const findings = [];
|
|
360
|
+
switch (ast.kind) {
|
|
361
|
+
case "delete":
|
|
362
|
+
if (ast.where === void 0) findings.push({
|
|
363
|
+
code: "LINT.DELETE_WITHOUT_WHERE",
|
|
364
|
+
severity: "error",
|
|
365
|
+
message: "DELETE without WHERE clause blocks execution to prevent accidental full-table deletion",
|
|
366
|
+
details: { table: ast.table.name }
|
|
367
|
+
});
|
|
368
|
+
break;
|
|
369
|
+
case "update":
|
|
370
|
+
if (ast.where === void 0) findings.push({
|
|
371
|
+
code: "LINT.UPDATE_WITHOUT_WHERE",
|
|
372
|
+
severity: "error",
|
|
373
|
+
message: "UPDATE without WHERE clause blocks execution to prevent accidental full-table update",
|
|
374
|
+
details: { table: ast.table.name }
|
|
375
|
+
});
|
|
376
|
+
break;
|
|
377
|
+
case "select":
|
|
378
|
+
if (ast.limit === void 0) {
|
|
379
|
+
const table = ast.from !== void 0 ? getFromSourceTableDetail(ast.from) : void 0;
|
|
380
|
+
findings.push({
|
|
381
|
+
code: "LINT.NO_LIMIT",
|
|
382
|
+
severity: "warn",
|
|
383
|
+
message: "Unbounded SELECT may return large result sets",
|
|
384
|
+
...ifDefined("details", table !== void 0 ? { table } : void 0)
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
if (ast.selectAllIntent !== void 0) {
|
|
388
|
+
const table = ast.selectAllIntent.table;
|
|
389
|
+
findings.push({
|
|
390
|
+
code: "LINT.SELECT_STAR",
|
|
391
|
+
severity: "warn",
|
|
392
|
+
message: "Query selects all columns via selectAll intent",
|
|
393
|
+
...ifDefined("details", table !== void 0 ? { table } : void 0)
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
break;
|
|
397
|
+
case "insert": break;
|
|
398
|
+
case "raw-sql": break;
|
|
399
|
+
// v8 ignore next 2
|
|
400
|
+
default: assertNever(ast, `Unsupported AST kind: ${ast.kind}`);
|
|
401
|
+
}
|
|
402
|
+
return findings;
|
|
403
|
+
}
|
|
404
|
+
function getConfiguredSeverity(code, options) {
|
|
405
|
+
const severities = options?.severities;
|
|
406
|
+
if (!severities) return void 0;
|
|
407
|
+
switch (code) {
|
|
408
|
+
case "LINT.SELECT_STAR": return severities.selectStar;
|
|
409
|
+
case "LINT.NO_LIMIT": return severities.noLimit;
|
|
410
|
+
case "LINT.DELETE_WITHOUT_WHERE": return severities.deleteWithoutWhere;
|
|
411
|
+
case "LINT.UPDATE_WITHOUT_WHERE": return severities.updateWithoutWhere;
|
|
412
|
+
case "LINT.READ_ONLY_MUTATION": return severities.readOnlyMutation;
|
|
413
|
+
default: return;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* AST-first lint middleware for SQL plans. When `plan.ast` is a SQL QueryAst, inspects
|
|
418
|
+
* the AST structurally. When `plan.ast` is missing, falls back to raw heuristic
|
|
419
|
+
* guardrails or skips linting depending on `fallbackWhenAstMissing`.
|
|
420
|
+
*
|
|
421
|
+
* Rules (AST-based):
|
|
422
|
+
* - DELETE without WHERE: blocks execution (configurable severity, default error)
|
|
423
|
+
* - UPDATE without WHERE: blocks execution (configurable severity, default error)
|
|
424
|
+
* - Unbounded SELECT: warn/error (severity from noLimit)
|
|
425
|
+
* - SELECT * intent: warn/error (severity from selectStar)
|
|
426
|
+
*
|
|
427
|
+
* Fallback: When ast is missing, `fallbackWhenAstMissing: 'raw'` uses heuristic
|
|
428
|
+
* SQL parsing; `'skip'` skips all lints. Default is `'raw'`.
|
|
429
|
+
*/
|
|
430
|
+
function lints(options) {
|
|
431
|
+
const fallback = options?.fallbackWhenAstMissing ?? "raw";
|
|
432
|
+
return Object.freeze({
|
|
433
|
+
name: "lints",
|
|
434
|
+
familyId: "sql",
|
|
435
|
+
async beforeExecute(plan, ctx) {
|
|
436
|
+
const findings = [];
|
|
437
|
+
if (isQueryAst(plan.ast)) {
|
|
438
|
+
findings.push(...evaluateAstLints(plan.ast));
|
|
439
|
+
if (plan.ast.kind === "raw-sql") findings.push(...evaluateRawGuardrails(plan).lints);
|
|
440
|
+
} else if (fallback !== "skip") findings.push(...evaluateRawGuardrails(plan).lints);
|
|
441
|
+
for (const lint of findings) {
|
|
442
|
+
const effectiveSeverity = getConfiguredSeverity(lint.code, options) ?? lint.severity;
|
|
443
|
+
if (effectiveSeverity === "error") throw runtimeError(lint.code, lint.message, lint.details);
|
|
444
|
+
if (effectiveSeverity === "warn") ctx.log.warn({
|
|
445
|
+
code: lint.code,
|
|
446
|
+
message: lint.message,
|
|
447
|
+
details: lint.details
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
var PreparedStatementImpl = class {
|
|
454
|
+
sql;
|
|
455
|
+
ast;
|
|
456
|
+
meta;
|
|
457
|
+
slots;
|
|
458
|
+
decodeContext;
|
|
459
|
+
paramMetadata;
|
|
460
|
+
constructor(internals) {
|
|
461
|
+
this.sql = internals.sql;
|
|
462
|
+
this.ast = internals.ast;
|
|
463
|
+
this.meta = internals.meta;
|
|
464
|
+
this.slots = internals.slots;
|
|
465
|
+
this.decodeContext = internals.decodeContext;
|
|
466
|
+
this.paramMetadata = internals.paramMetadata;
|
|
467
|
+
Object.freeze(this);
|
|
468
|
+
}
|
|
469
|
+
execute(target, params, options) {
|
|
470
|
+
return target.executePrepared(this, params, options);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
function documentScopedCodecTypes(contract) {
|
|
474
|
+
return blindCast(contract.storage.types);
|
|
475
|
+
}
|
|
476
|
+
function createSqlExecutionStack(options) {
|
|
477
|
+
return createExecutionStack({
|
|
478
|
+
target: options.target,
|
|
479
|
+
adapter: options.adapter,
|
|
480
|
+
driver: options.driver,
|
|
481
|
+
extensions: options.extensions
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
function assertExecutionStackContractRequirements(contract, stack) {
|
|
485
|
+
const providedComponentIds = /* @__PURE__ */ new Set([
|
|
486
|
+
stack.target.id,
|
|
487
|
+
stack.adapter.id,
|
|
488
|
+
...stack.extensions.map((pack) => pack.id)
|
|
489
|
+
]);
|
|
490
|
+
const result = checkContractComponentRequirements({
|
|
491
|
+
contract,
|
|
492
|
+
expectedTargetFamily: "sql",
|
|
493
|
+
expectedTargetId: stack.target.targetId,
|
|
494
|
+
providedComponentIds
|
|
495
|
+
});
|
|
496
|
+
if (result.familyMismatch) throw runtimeError("RUNTIME.CONTRACT_FAMILY_MISMATCH", `Contract target family '${result.familyMismatch.actual}' does not match runtime family '${result.familyMismatch.expected}'.`, {
|
|
497
|
+
actual: result.familyMismatch.actual,
|
|
498
|
+
expected: result.familyMismatch.expected
|
|
499
|
+
});
|
|
500
|
+
if (result.targetMismatch) throw runtimeError("RUNTIME.CONTRACT_TARGET_MISMATCH", `Contract target '${result.targetMismatch.actual}' does not match runtime target descriptor '${result.targetMismatch.expected}'.`, {
|
|
501
|
+
actual: result.targetMismatch.actual,
|
|
502
|
+
expected: result.targetMismatch.expected
|
|
503
|
+
});
|
|
504
|
+
if (result.missingExtensionPackIds.length > 0) {
|
|
505
|
+
const packIds = result.missingExtensionPackIds;
|
|
506
|
+
throw runtimeError("RUNTIME.MISSING_EXTENSION_PACK", `Contract requires extension pack(s) ${packIds.map((id) => `'${id}'`).join(", ")}, but runtime descriptors do not provide matching component(s).`, { packIds });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function validateTypeParams(typeParams, descriptor, context) {
|
|
510
|
+
const result = descriptor.paramsSchema["~standard"].validate(typeParams);
|
|
511
|
+
if (result instanceof Promise) throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `paramsSchema for codec '${descriptor.codecId}' returned a Promise; runtime validation requires a synchronous Standard Schema validator.`, {
|
|
512
|
+
...context,
|
|
513
|
+
codecId: descriptor.codecId,
|
|
514
|
+
typeParams
|
|
515
|
+
});
|
|
516
|
+
if (result.issues) {
|
|
517
|
+
const messages = result.issues.map((issue) => issue.message).join("; ");
|
|
518
|
+
throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Invalid typeParams for ${context.typeName ? `type '${context.typeName}'` : `column '${context.tableName}.${context.columnName}'`} (codecId: ${descriptor.codecId}): ${messages}`, {
|
|
519
|
+
...context,
|
|
520
|
+
codecId: descriptor.codecId,
|
|
521
|
+
typeParams
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
return result.value;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Collect every {@link CodecDescriptor} contributed by the SQL stack and partition into "parameterized" vs "non-parameterized" via the descriptor's own {@link CodecDescriptorImpl.isParameterized} getter. The getter is the canonical discriminator — a `paramsSchema` identity check would misroute any descriptor that doesn't reuse the exact `voidParamsSchema` singleton (e.g. a non-parameterized codec authoring its own no-op schema).
|
|
528
|
+
*
|
|
529
|
+
* The unified descriptor list collapses the legacy split (a separate slot used to register parameterized codecs) — every codec id resolves through the same map (codec-registry-unification spec § Decision).
|
|
530
|
+
*/
|
|
531
|
+
function collectCodecDescriptors(contributors) {
|
|
532
|
+
const all = [];
|
|
533
|
+
const parameterized = /* @__PURE__ */ new Map();
|
|
534
|
+
const seen = /* @__PURE__ */ new Set();
|
|
535
|
+
for (const contributor of contributors) for (const descriptor of contributor.codecs()) {
|
|
536
|
+
if (seen.has(descriptor.codecId)) throw runtimeError("RUNTIME.DUPLICATE_CODEC", `Duplicate codec descriptor for codecId '${descriptor.codecId}'.`, { codecId: descriptor.codecId });
|
|
537
|
+
seen.add(descriptor.codecId);
|
|
538
|
+
all.push(descriptor);
|
|
539
|
+
if (descriptor.isParameterized) parameterized.set(descriptor.codecId, descriptor);
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
all,
|
|
543
|
+
parameterized
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function collectTypeRefSites(storage) {
|
|
547
|
+
const sites = /* @__PURE__ */ new Map();
|
|
548
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) for (const [columnName, column] of Object.entries(table.columns)) {
|
|
549
|
+
if (typeof column.typeRef !== "string") continue;
|
|
550
|
+
const list = sites.get(column.typeRef);
|
|
551
|
+
const entry = {
|
|
552
|
+
table: tableName,
|
|
553
|
+
column: columnName
|
|
554
|
+
};
|
|
555
|
+
if (list) list.push(entry);
|
|
556
|
+
else sites.set(column.typeRef, [entry]);
|
|
557
|
+
}
|
|
558
|
+
return sites;
|
|
559
|
+
}
|
|
560
|
+
function initializeTypeHelpers(storage, documentTypes, codecDescriptors) {
|
|
561
|
+
const helpers = {};
|
|
562
|
+
if (!documentTypes) return helpers;
|
|
563
|
+
const typeRefSites = collectTypeRefSites(storage);
|
|
564
|
+
for (const [typeName, typeInstance] of Object.entries(documentTypes)) {
|
|
565
|
+
const codecId = typeInstance.codecId;
|
|
566
|
+
const typeParams = typeInstance.typeParams;
|
|
567
|
+
const descriptor = codecDescriptors.get(codecId);
|
|
568
|
+
if (!descriptor) {
|
|
569
|
+
helpers[typeName] = typeInstance;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const validatedParams = validateTypeParams(typeParams ?? {}, descriptor, { typeName });
|
|
573
|
+
const ctx = {
|
|
574
|
+
name: typeName,
|
|
575
|
+
usedAt: typeRefSites.get(typeName) ?? []
|
|
576
|
+
};
|
|
577
|
+
helpers[typeName] = descriptor.factory(validatedParams)(ctx);
|
|
578
|
+
}
|
|
579
|
+
return helpers;
|
|
580
|
+
}
|
|
581
|
+
function validateColumnTypeParams(storage, codecDescriptors) {
|
|
582
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) for (const [columnName, column] of Object.entries(table.columns)) if (column.typeParams) {
|
|
583
|
+
const descriptor = codecDescriptors.get(column.codecId);
|
|
584
|
+
if (descriptor) validateTypeParams(column.typeParams, descriptor, {
|
|
585
|
+
tableName,
|
|
586
|
+
columnName
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Build-time contract-integrity check: every `(table, column)` resolves to a {@link CodecRef} whose `codecId` is registered and whose `typeParams` presence matches the descriptor's `isParameterized` flag.
|
|
592
|
+
*
|
|
593
|
+
* Surfaces three classes of malformed contract that AST-bound codec resolution would otherwise mask silently:
|
|
594
|
+
*
|
|
595
|
+
* - column references a codecId no contributor registered → `RUNTIME.CODEC_DESCRIPTOR_MISSING`.
|
|
596
|
+
* - parameterized codec, no `typeParams` (legacy "tolerate refs without params" shape) → `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH`.
|
|
597
|
+
* - non-parameterized codec, `typeParams` supplied → `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH`.
|
|
598
|
+
*
|
|
599
|
+
* Runs unconditionally from `createExecutionContext` so contract bugs fail fast at construction time instead of silently skipping affected columns in the codec registry's pre-population walk.
|
|
600
|
+
*/
|
|
601
|
+
function assertColumnCodecIntegrity(storage, codecDescriptors) {
|
|
602
|
+
for (const [namespaceId, ns] of Object.entries(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) for (const columnName of Object.keys(table.columns)) {
|
|
603
|
+
const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
|
|
604
|
+
if (!ref) continue;
|
|
605
|
+
const descriptor = codecDescriptors.descriptorFor(ref.codecId);
|
|
606
|
+
if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `Column '${tableName}.${columnName}' references codec '${ref.codecId}' but no contributor registered a codec descriptor for that codecId. Add the extension pack that owns the codec to the runtime stack.`, {
|
|
607
|
+
table: tableName,
|
|
608
|
+
column: columnName,
|
|
609
|
+
codecId: ref.codecId
|
|
610
|
+
});
|
|
611
|
+
if (descriptor.isParameterized && ref.typeParams === void 0) {
|
|
612
|
+
const probe = descriptor.paramsSchema["~standard"].validate({});
|
|
613
|
+
if (probe instanceof Promise) {
|
|
614
|
+
probe.catch(() => {});
|
|
615
|
+
throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Column '${tableName}.${columnName}' uses parameterized codec '${ref.codecId}' whose paramsSchema returned a Promise; paramsSchema must be a synchronous Standard Schema validator. Return a value/issues result directly instead of a Promise.`, {
|
|
616
|
+
table: tableName,
|
|
617
|
+
column: columnName,
|
|
618
|
+
codecId: ref.codecId
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
if ("issues" in probe && !!probe.issues) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' uses parameterized codec '${ref.codecId}' but no typeParams are supplied. Provide typeParams on the column, or use a typeRef pointing at a storage.types entry that carries them.`, {
|
|
622
|
+
table: tableName,
|
|
623
|
+
column: columnName,
|
|
624
|
+
codecId: ref.codecId,
|
|
625
|
+
expected: "parameterized",
|
|
626
|
+
actual: "no typeParams"
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
const refTypeParams = ref.typeParams;
|
|
630
|
+
const refHasTypeParamKeys = refTypeParams !== void 0 && refTypeParams !== null && typeof refTypeParams === "object" && !Array.isArray(refTypeParams) && Object.keys(refTypeParams).length > 0;
|
|
631
|
+
if (!descriptor.isParameterized && refHasTypeParamKeys) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' supplies typeParams to non-parameterized codec '${ref.codecId}'. Remove the typeParams or switch to a parameterized codec id.`, {
|
|
632
|
+
table: tableName,
|
|
633
|
+
column: columnName,
|
|
634
|
+
codecId: ref.codecId,
|
|
635
|
+
expected: "non-parameterized",
|
|
636
|
+
actual: "has typeParams"
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Build a {@link ContractCodecRegistry} that resolves codecs exclusively through the `forCodecRef` content-keyed cache.
|
|
642
|
+
*
|
|
643
|
+
* One pre-population pass walks `storage.types` and `storage.tables[].columns[]` to seed the resolver's per-ref instance context with the *aggregated* `usedAt` set for each canonical `(codecId, typeParams)` key. The same codec materialised through `forColumn` or `forCodecRef` is therefore one instance with one `SqlCodecInstanceContext` — stateful codecs reading `usedAt` see the full column set regardless of which surface the caller used.
|
|
644
|
+
*
|
|
645
|
+
* Per-key instance-name policy:
|
|
646
|
+
*
|
|
647
|
+
* - typeRef-shared columns use the `storage.types[name]` name.
|
|
648
|
+
* - inline-`typeParams` columns use `<col:Table.column>` (the first column observed at that key; additional columns sharing the key extend `usedAt`).
|
|
649
|
+
* - non-parameterized codec ids use `<codec:codecId>`, aggregating every column on that codec id into one `usedAt` set.
|
|
650
|
+
* - ad-hoc refs the contract walk did not pre-populate (e.g. AST-supplied refs from deserialised migration ops) fall back to the canonical cache key `${codecId}:${canonicalizeJson(typeParams)}` — the only structurally honest identity for an ad-hoc ref, distinct per `(codecId, typeParams)`.
|
|
651
|
+
*
|
|
652
|
+
* Contract integrity is enforced upstream by {@link assertColumnCodecIntegrity}: every column must reference a registered `codecId` whose `descriptor.isParameterized` flag matches the presence of `typeParams` (via `codecRefForColumn`). The pre-population walk and `forColumn` therefore make no defensive checks — malformed columns fail fast at `createExecutionContext` construction with `RUNTIME.CODEC_DESCRIPTOR_MISSING` or `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH` rather than being silently skipped here.
|
|
653
|
+
*
|
|
654
|
+
* `forColumn(ns, t, c)` is a thin delegate over `forCodecRef(codecRefForColumn(ns, t, c))`; encode/decode hot paths read the resolver directly via `forCodecRef`. The only `undefined` `forColumn` returns is the legitimate "no such column in the contract" case.
|
|
655
|
+
*/
|
|
656
|
+
function buildContractCodecRegistry(contract, codecDescriptors) {
|
|
657
|
+
const refKeyOf = (ref) => `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
|
|
658
|
+
const usedAtByKey = /* @__PURE__ */ new Map();
|
|
659
|
+
const nameByKey = /* @__PURE__ */ new Map();
|
|
660
|
+
const typeRefSites = collectTypeRefSites(contract.storage);
|
|
661
|
+
for (const [typeName, typeInstance] of Object.entries(documentScopedCodecTypes(contract) ?? {})) {
|
|
662
|
+
const instanceTypeParams = typeInstance.typeParams;
|
|
663
|
+
const key = refKeyOf(instanceTypeParams !== void 0 && Object.keys(instanceTypeParams).length > 0 ? {
|
|
664
|
+
codecId: typeInstance.codecId,
|
|
665
|
+
typeParams: instanceTypeParams
|
|
666
|
+
} : { codecId: typeInstance.codecId });
|
|
667
|
+
const sites = typeRefSites.get(typeName) ?? [];
|
|
668
|
+
const existing = usedAtByKey.get(key);
|
|
669
|
+
if (existing) existing.push(...sites);
|
|
670
|
+
else {
|
|
671
|
+
usedAtByKey.set(key, [...sites]);
|
|
672
|
+
nameByKey.set(key, typeName);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) for (const [columnName, column] of Object.entries(table.columns)) {
|
|
676
|
+
if (column.typeRef !== void 0) continue;
|
|
677
|
+
const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
|
|
678
|
+
if (!ref) continue;
|
|
679
|
+
const key = refKeyOf(ref);
|
|
680
|
+
const site = {
|
|
681
|
+
table: tableName,
|
|
682
|
+
column: columnName
|
|
683
|
+
};
|
|
684
|
+
const existing = usedAtByKey.get(key);
|
|
685
|
+
if (existing) existing.push(site);
|
|
686
|
+
else {
|
|
687
|
+
usedAtByKey.set(key, [site]);
|
|
688
|
+
const name = ref.typeParams !== void 0 ? `<col:${tableName}.${columnName}>` : `<codec:${ref.codecId}>`;
|
|
689
|
+
nameByKey.set(key, name);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const resolver = createAstCodecResolver(codecDescriptors, (ref) => {
|
|
693
|
+
const key = refKeyOf(ref);
|
|
694
|
+
return {
|
|
695
|
+
name: nameByKey.get(key) ?? key,
|
|
696
|
+
usedAt: usedAtByKey.get(key) ?? []
|
|
697
|
+
};
|
|
698
|
+
});
|
|
699
|
+
for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) for (const columnName of Object.keys(table.columns)) {
|
|
700
|
+
const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
|
|
701
|
+
if (!ref) continue;
|
|
702
|
+
resolver.forCodecRef(ref);
|
|
703
|
+
}
|
|
704
|
+
return {
|
|
705
|
+
forColumn(namespaceId, table, column) {
|
|
706
|
+
const ref = codecDescriptors.codecRefForColumn(namespaceId, table, column);
|
|
707
|
+
return ref ? resolver.forCodecRef(ref) : void 0;
|
|
708
|
+
},
|
|
709
|
+
forCodecRef(ref) {
|
|
710
|
+
return resolver.forCodecRef(ref);
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
function assertMutationDefaultGeneratorsAvailable(contract, generatorRegistry) {
|
|
715
|
+
const defaults = contract.execution?.mutations.defaults ?? [];
|
|
716
|
+
if (defaults.length === 0) return;
|
|
717
|
+
const missing = /* @__PURE__ */ new Set();
|
|
718
|
+
for (const mutationDefault of defaults) for (const phase of [mutationDefault.onCreate, mutationDefault.onUpdate]) {
|
|
719
|
+
if (!phase) continue;
|
|
720
|
+
if (phase.kind === "generator" && !generatorRegistry.has(phase.id)) missing.add(phase.id);
|
|
721
|
+
}
|
|
722
|
+
if (missing.size === 0) return;
|
|
723
|
+
const ids = Array.from(missing);
|
|
724
|
+
throw runtimeError("RUNTIME.MUTATION_DEFAULT_GENERATOR_MISSING", `Contract requires mutation default generator(s) ${ids.map((id) => `'${id}'`).join(", ")}, but no runtime component provides them.`, { ids });
|
|
725
|
+
}
|
|
726
|
+
function collectMutationDefaultGenerators(contributors) {
|
|
727
|
+
const generators = /* @__PURE__ */ new Map();
|
|
728
|
+
const owners = /* @__PURE__ */ new Map();
|
|
729
|
+
for (const contributor of contributors) {
|
|
730
|
+
const nextGenerators = contributor.mutationDefaultGenerators?.() ?? [];
|
|
731
|
+
for (const generator of nextGenerators) {
|
|
732
|
+
const existingOwner = owners.get(generator.id);
|
|
733
|
+
if (existingOwner !== void 0) throw runtimeError("RUNTIME.DUPLICATE_MUTATION_DEFAULT_GENERATOR", `Duplicate mutation default generator '${generator.id}'.`, {
|
|
734
|
+
id: generator.id,
|
|
735
|
+
existingOwner,
|
|
736
|
+
incomingOwner: contributor.id
|
|
737
|
+
});
|
|
738
|
+
generators.set(generator.id, generator);
|
|
739
|
+
owners.set(generator.id, contributor.id);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return generators;
|
|
743
|
+
}
|
|
744
|
+
function computeExecutionDefaultValue(spec, generatorRegistry) {
|
|
745
|
+
switch (spec.kind) {
|
|
746
|
+
case "generator": {
|
|
747
|
+
const generator = generatorRegistry.get(spec.id);
|
|
748
|
+
if (!generator) throw runtimeError("RUNTIME.MUTATION_DEFAULT_GENERATOR_MISSING", `Contract references mutation default generator '${spec.id}' but no runtime component provides it.`, { id: spec.id });
|
|
749
|
+
return generator.generate(spec.params);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
function applyMutationDefaults(contract, generatorRegistry, options) {
|
|
754
|
+
const defaults = contract.execution?.mutations.defaults ?? [];
|
|
755
|
+
if (defaults.length === 0) return [];
|
|
756
|
+
const isEmptyUpdate = options.op === "update" && Object.keys(options.values).length === 0;
|
|
757
|
+
const applied = [];
|
|
758
|
+
const appliedColumns = /* @__PURE__ */ new Set();
|
|
759
|
+
const rowCache = /* @__PURE__ */ new Map();
|
|
760
|
+
for (const mutationDefault of defaults) {
|
|
761
|
+
if (mutationDefault.ref.table !== options.table) continue;
|
|
762
|
+
if (mutationDefault.ref.namespace !== options.namespace) continue;
|
|
763
|
+
const defaultSpec = options.op === "create" ? mutationDefault.onCreate : mutationDefault.onUpdate;
|
|
764
|
+
if (!defaultSpec) continue;
|
|
765
|
+
if (isEmptyUpdate) continue;
|
|
766
|
+
const columnName = mutationDefault.ref.column;
|
|
767
|
+
if (Object.hasOwn(options.values, columnName) || appliedColumns.has(columnName)) continue;
|
|
768
|
+
applied.push({
|
|
769
|
+
column: columnName,
|
|
770
|
+
value: resolveScopedValue(defaultSpec, generatorRegistry, rowCache, options.defaultValueCache)
|
|
771
|
+
});
|
|
772
|
+
appliedColumns.add(columnName);
|
|
773
|
+
}
|
|
774
|
+
return applied;
|
|
775
|
+
}
|
|
776
|
+
function resolveScopedValue(spec, generatorRegistry, rowCache, queryCache) {
|
|
777
|
+
if (spec.kind !== "generator") return computeExecutionDefaultValue(spec, generatorRegistry);
|
|
778
|
+
const cache = scopedCache(generatorRegistry.get(spec.id)?.stability, rowCache, queryCache);
|
|
779
|
+
if (!cache) return computeExecutionDefaultValue(spec, generatorRegistry);
|
|
780
|
+
if (cache.has(spec.id)) return cache.get(spec.id);
|
|
781
|
+
const value = computeExecutionDefaultValue(spec, generatorRegistry);
|
|
782
|
+
cache.set(spec.id, value);
|
|
783
|
+
return value;
|
|
784
|
+
}
|
|
785
|
+
function scopedCache(stability, rowCache, queryCache) {
|
|
786
|
+
switch (stability) {
|
|
787
|
+
case "row": return rowCache;
|
|
788
|
+
case "query": return queryCache;
|
|
789
|
+
default: return;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function createExecutionContext(options) {
|
|
793
|
+
const { stack, driver } = options;
|
|
794
|
+
assertExecutionStackContractRequirements(options.contract, stack);
|
|
795
|
+
const capabilityContributors = [
|
|
796
|
+
stack.target,
|
|
797
|
+
stack.adapter,
|
|
798
|
+
...driver ? [driver] : [],
|
|
799
|
+
...stack.extensions
|
|
800
|
+
];
|
|
801
|
+
const mergedCapabilities = mergeCapabilityMatrices(options.contract.capabilities, capabilityContributors);
|
|
802
|
+
const contract = {
|
|
803
|
+
...options.contract,
|
|
804
|
+
capabilities: mergedCapabilities
|
|
805
|
+
};
|
|
806
|
+
const contributors = [
|
|
807
|
+
stack.target,
|
|
808
|
+
stack.adapter,
|
|
809
|
+
...stack.extensions
|
|
810
|
+
];
|
|
811
|
+
const { all: allCodecDescriptors, parameterized: parameterizedCodecDescriptors } = collectCodecDescriptors(contributors);
|
|
812
|
+
const queryOperationRegistry = createSqlOperationRegistry();
|
|
813
|
+
for (const contributor of contributors) {
|
|
814
|
+
const ops = contributor.queryOperations?.() ?? {};
|
|
815
|
+
for (const [name, op] of Object.entries(ops)) queryOperationRegistry.register(name, op);
|
|
816
|
+
}
|
|
817
|
+
const codecDescriptors = buildCodecDescriptorRegistry(allCodecDescriptors, contract.storage);
|
|
818
|
+
assertColumnCodecIntegrity(contract.storage, codecDescriptors);
|
|
819
|
+
const mutationDefaultGeneratorRegistry = collectMutationDefaultGenerators(contributors);
|
|
820
|
+
assertMutationDefaultGeneratorsAvailable(contract, mutationDefaultGeneratorRegistry);
|
|
821
|
+
if (parameterizedCodecDescriptors.size > 0) validateColumnTypeParams(contract.storage, parameterizedCodecDescriptors);
|
|
822
|
+
const types = initializeTypeHelpers(contract.storage, documentScopedCodecTypes(contract), parameterizedCodecDescriptors);
|
|
823
|
+
return {
|
|
824
|
+
contract,
|
|
825
|
+
contractCodecs: buildContractCodecRegistry(contract, codecDescriptors),
|
|
826
|
+
codecDescriptors,
|
|
827
|
+
queryOperations: queryOperationRegistry,
|
|
828
|
+
types,
|
|
829
|
+
applyMutationDefaults: (options) => applyMutationDefaults(contract, mutationDefaultGeneratorRegistry, options)
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
const WIRE_PREVIEW_LIMIT = 100;
|
|
833
|
+
const EMPTY_INCLUDE_ALIASES = /* @__PURE__ */ new Set();
|
|
834
|
+
function projectionListFromAst(ast) {
|
|
835
|
+
if (ast.kind === "select") return ast.projection;
|
|
836
|
+
if (ast.kind === "raw-sql") return;
|
|
837
|
+
return ast.returning;
|
|
838
|
+
}
|
|
839
|
+
function resolveProjectionCodec(item, contractCodecs) {
|
|
840
|
+
if (item.codec && contractCodecs) return contractCodecs.forCodecRef(item.codec);
|
|
841
|
+
}
|
|
842
|
+
const EMPTY_MANY_ALIASES = /* @__PURE__ */ new Set();
|
|
843
|
+
function buildDecodeContext(ast, contractCodecs) {
|
|
844
|
+
const projection = projectionListFromAst(ast);
|
|
845
|
+
if (!projection || projection.length === 0) return {
|
|
846
|
+
aliases: void 0,
|
|
847
|
+
codecs: /* @__PURE__ */ new Map(),
|
|
848
|
+
columnRefs: /* @__PURE__ */ new Map(),
|
|
849
|
+
includeAliases: EMPTY_INCLUDE_ALIASES,
|
|
850
|
+
manyAliases: EMPTY_MANY_ALIASES
|
|
851
|
+
};
|
|
852
|
+
const aliases = [];
|
|
853
|
+
const codecs = /* @__PURE__ */ new Map();
|
|
854
|
+
const columnRefs = /* @__PURE__ */ new Map();
|
|
855
|
+
const includeAliases = /* @__PURE__ */ new Set();
|
|
856
|
+
const manyAliases = /* @__PURE__ */ new Set();
|
|
857
|
+
for (const item of projection) {
|
|
858
|
+
aliases.push(item.alias);
|
|
859
|
+
const codec = resolveProjectionCodec(item, contractCodecs);
|
|
860
|
+
if (codec) codecs.set(item.alias, codec);
|
|
861
|
+
if (item.codec?.many) manyAliases.add(item.alias);
|
|
862
|
+
if (item.expr.kind === "column-ref") columnRefs.set(item.alias, {
|
|
863
|
+
table: item.expr.table,
|
|
864
|
+
column: item.expr.column
|
|
865
|
+
});
|
|
866
|
+
else if (item.expr.kind === "subquery" || item.expr.kind === "json-array-agg") includeAliases.add(item.alias);
|
|
867
|
+
}
|
|
868
|
+
return {
|
|
869
|
+
aliases,
|
|
870
|
+
codecs,
|
|
871
|
+
columnRefs,
|
|
872
|
+
includeAliases,
|
|
873
|
+
manyAliases
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function previewWireValue(wireValue) {
|
|
877
|
+
if (typeof wireValue === "string") return wireValue.length > WIRE_PREVIEW_LIMIT ? `${wireValue.substring(0, WIRE_PREVIEW_LIMIT)}...` : wireValue;
|
|
878
|
+
return String(wireValue).substring(0, WIRE_PREVIEW_LIMIT);
|
|
879
|
+
}
|
|
880
|
+
function wrapDecodeFailure(error, alias, ref, codec, wireValue) {
|
|
881
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
882
|
+
const wrapped = runtimeError("RUNTIME.DECODE_FAILED", `Failed to decode column ${ref ? `${ref.table}.${ref.column}` : alias} with codec '${codec.id}': ${message}`, {
|
|
883
|
+
...ref ? {
|
|
884
|
+
table: ref.table,
|
|
885
|
+
column: ref.column
|
|
886
|
+
} : { alias },
|
|
887
|
+
codec: codec.id,
|
|
888
|
+
wirePreview: previewWireValue(wireValue)
|
|
889
|
+
});
|
|
890
|
+
wrapped.cause = error;
|
|
891
|
+
throw wrapped;
|
|
892
|
+
}
|
|
893
|
+
function wrapIncludeAggregateFailure(error, alias, wireValue) {
|
|
894
|
+
const wrapped = runtimeError("RUNTIME.DECODE_FAILED", `Failed to parse JSON array for include alias '${alias}': ${error instanceof Error ? error.message : String(error)}`, {
|
|
895
|
+
alias,
|
|
896
|
+
wirePreview: previewWireValue(wireValue)
|
|
897
|
+
});
|
|
898
|
+
wrapped.cause = error;
|
|
899
|
+
throw wrapped;
|
|
900
|
+
}
|
|
901
|
+
function decodeIncludeAggregate(alias, wireValue) {
|
|
902
|
+
if (wireValue === null || wireValue === void 0) return [];
|
|
903
|
+
try {
|
|
904
|
+
if (typeof wireValue === "string") return JSON.parse(wireValue);
|
|
905
|
+
if (typeof wireValue === "object") return wireValue;
|
|
906
|
+
return JSON.parse(String(wireValue));
|
|
907
|
+
} catch (error) {
|
|
908
|
+
wrapIncludeAggregateFailure(error, alias, wireValue);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Decodes a single field. Single-armed: every cell takes the same path — `codec.decode → await → return plain value` — so sync- and async-authored codecs are indistinguishable to callers. JSON-Schema validation, when required, lives inside the resolved codec's `decode` body (e.g. `arktype-json` validates against its rehydrated schema and throws `RUNTIME.JSON_SCHEMA_VALIDATION_FAILED` from `decode` directly); there is
|
|
913
|
+
* no separate validator-registry pass.
|
|
914
|
+
*
|
|
915
|
+
* The row-level `rowCtx` is repackaged into a per-cell `SqlCodecCallContext` whose `column = { table, name }` is a structural projection of the per-cell `ColumnRef = { table, column }` resolved from the AST-backed `DecodeContext` (the same resolution `wrapDecodeFailure` uses for envelope construction — one resolution per cell, two consumers). Cells the runtime cannot resolve to a single underlying column (aggregate
|
|
916
|
+
* aliases, computed projections without a simple ref) get `column: undefined`, matching the spec contract that the runtime never silently defaults this field.
|
|
917
|
+
*
|
|
918
|
+
* For `many`-flagged aliases the driver has already parsed the wire form into a JS array; this function maps the element codec over that array element-by-element, passing `null` elements through unchanged. Element-level failures surface through the existing `RUNTIME.DECODE_FAILED` envelope with the column/codec context from the parent cell.
|
|
919
|
+
*/
|
|
920
|
+
async function decodeField(alias, wireValue, decodeCtx, rowCtx) {
|
|
921
|
+
if (wireValue === null) return null;
|
|
922
|
+
const codec = decodeCtx.codecs.get(alias);
|
|
923
|
+
if (!codec) return wireValue;
|
|
924
|
+
const ref = decodeCtx.columnRefs.get(alias);
|
|
925
|
+
let cellCtx;
|
|
926
|
+
if (ref) cellCtx = {
|
|
927
|
+
...rowCtx,
|
|
928
|
+
column: {
|
|
929
|
+
table: ref.table,
|
|
930
|
+
name: ref.column
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
else {
|
|
934
|
+
const { column: _drop, ...rowCtxWithoutColumn } = rowCtx;
|
|
935
|
+
cellCtx = rowCtxWithoutColumn;
|
|
936
|
+
}
|
|
937
|
+
if (decodeCtx.manyAliases.has(alias)) {
|
|
938
|
+
if (!Array.isArray(wireValue)) wrapDecodeFailure(/* @__PURE__ */ new TypeError(`expected an array from the driver for many-typed column, got ${typeof wireValue}`), alias, ref, codec, wireValue);
|
|
939
|
+
const decoded = [];
|
|
940
|
+
for (const elem of wireValue) {
|
|
941
|
+
if (elem === null || elem === void 0) {
|
|
942
|
+
decoded.push(null);
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
try {
|
|
946
|
+
decoded.push(await codec.decode(elem, cellCtx));
|
|
947
|
+
} catch (error) {
|
|
948
|
+
if (isStructuredError(error)) throw error;
|
|
949
|
+
wrapDecodeFailure(error, alias, ref, codec, elem);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return decoded;
|
|
953
|
+
}
|
|
954
|
+
try {
|
|
955
|
+
return await codec.decode(wireValue, cellCtx);
|
|
956
|
+
} catch (error) {
|
|
957
|
+
if (isStructuredError(error)) throw error;
|
|
958
|
+
wrapDecodeFailure(error, alias, ref, codec, wireValue);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Decodes a row by dispatching all per-cell codec calls concurrently via `Promise.all`. Each cell follows the single-armed `decodeField` path. Structured envelopes thrown by codec bodies (anything passing `isStructuredError`) pass through unchanged; all other failures are wrapped in `RUNTIME.DECODE_FAILED` with `{ table, column, codec }` (or `{ alias, codec }` when no column ref is resolvable) and the original error attached on `cause`.
|
|
963
|
+
*
|
|
964
|
+
* When `rowCtx.signal` is provided:
|
|
965
|
+
*
|
|
966
|
+
* - **Already-aborted at entry** short-circuits with `RUNTIME.ABORTED` (`{ phase: 'decode' }`) before any `codec.decode` call is made.
|
|
967
|
+
* - **Mid-flight aborts** race the per-cell `Promise.all` against the signal so the runtime returns promptly even when codec bodies ignore it. In-flight bodies that ignore the signal complete in the background (cooperative cancellation).
|
|
968
|
+
* - Existing structured envelopes (any dotted-code error passing `isStructuredError`, e.g. `RUNTIME.DECODE_FAILED`) from codec bodies pass through unchanged (no double wrap).
|
|
969
|
+
*/
|
|
970
|
+
async function decodeRow(row, decodeCtx, rowCtx) {
|
|
971
|
+
checkAborted(rowCtx, "decode");
|
|
972
|
+
const signal = rowCtx.signal;
|
|
973
|
+
const aliases = decodeCtx.aliases ?? Object.keys(row);
|
|
974
|
+
if (decodeCtx.aliases !== void 0) {
|
|
975
|
+
for (const alias of decodeCtx.aliases) if (!Object.hasOwn(row, alias)) throw runtimeError("RUNTIME.DECODE_FAILED", `Row missing projection alias "${alias}"`, {
|
|
976
|
+
alias,
|
|
977
|
+
expectedAliases: decodeCtx.aliases,
|
|
978
|
+
presentKeys: Object.keys(row)
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
const tasks = [];
|
|
982
|
+
const includeIndices = [];
|
|
983
|
+
for (let i = 0; i < aliases.length; i++) {
|
|
984
|
+
const alias = aliases[i];
|
|
985
|
+
const wireValue = row[alias];
|
|
986
|
+
if (decodeCtx.includeAliases.has(alias)) {
|
|
987
|
+
includeIndices.push({
|
|
988
|
+
index: i,
|
|
989
|
+
alias,
|
|
990
|
+
value: wireValue
|
|
991
|
+
});
|
|
992
|
+
tasks.push(Promise.resolve(void 0));
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
tasks.push(decodeField(alias, wireValue, decodeCtx, rowCtx));
|
|
996
|
+
}
|
|
997
|
+
const settled = await raceAgainstAbort(Promise.all(tasks), signal, "decode");
|
|
998
|
+
for (const entry of includeIndices) settled[entry.index] = decodeIncludeAggregate(entry.alias, entry.value);
|
|
999
|
+
const decoded = {};
|
|
1000
|
+
for (let i = 0; i < aliases.length; i++) decoded[aliases[i]] = settled[i];
|
|
1001
|
+
return decoded;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Computes a stable content hash for a lowered SQL execution plan.
|
|
1005
|
+
*
|
|
1006
|
+
* Internally builds an unambiguous canonical-stringified preimage from
|
|
1007
|
+
* three components:
|
|
1008
|
+
*
|
|
1009
|
+
* 1. `meta.storageHash` — discriminates by schema. A migration changes the
|
|
1010
|
+
* storage hash, which invalidates cached entries automatically.
|
|
1011
|
+
* 2. `exec.sql` — the raw lowered SQL text. Two queries with different
|
|
1012
|
+
* structure produce different keys. Note that we deliberately do **not**
|
|
1013
|
+
* use `computeSqlFingerprint` here: that helper strips literals to group
|
|
1014
|
+
* executions by statement shape (used by telemetry), which is the
|
|
1015
|
+
* opposite of what a content hash needs — we want per-execution
|
|
1016
|
+
* discrimination, not per-statement-shape grouping.
|
|
1017
|
+
* 3. `exec.params` — the bound parameters. `canonicalStringify` produces a
|
|
1018
|
+
* deterministic serialization that is stable across object key
|
|
1019
|
+
* insertion order and that distinguishes types JSON would otherwise
|
|
1020
|
+
* conflate (e.g. `BigInt(1)` vs `1`).
|
|
1021
|
+
*
|
|
1022
|
+
* The components are wrapped in an object and canonicalized as a single
|
|
1023
|
+
* unit (rather than concatenated with a delimiter) so component
|
|
1024
|
+
* boundaries are unambiguous: any character appearing inside `sql` or
|
|
1025
|
+
* `storageHash` cannot bleed across components and produce a collision
|
|
1026
|
+
* with a different split of the same characters.
|
|
1027
|
+
*
|
|
1028
|
+
* The canonical string is then piped through `hashContent` to produce a
|
|
1029
|
+
* bounded, opaque digest. See `@internal/utils/hash-content` for the
|
|
1030
|
+
* rationale.
|
|
1031
|
+
*
|
|
1032
|
+
* @internal
|
|
1033
|
+
*/
|
|
1034
|
+
function computeSqlContentHash(exec) {
|
|
1035
|
+
return hashContent(canonicalStringify({
|
|
1036
|
+
storageHash: exec.meta.storageHash,
|
|
1037
|
+
sql: exec.sql,
|
|
1038
|
+
params: exec.params
|
|
1039
|
+
}));
|
|
1040
|
+
}
|
|
1041
|
+
const STRING_LITERAL_REGEX = /'(?:''|[^'])*'/g;
|
|
1042
|
+
const NUMERIC_LITERAL_REGEX = /\b\d+(?:\.\d+)?\b/g;
|
|
1043
|
+
const WHITESPACE_REGEX = /\s+/g;
|
|
1044
|
+
/**
|
|
1045
|
+
* Computes a literal-stripped, normalized fingerprint of a SQL statement.
|
|
1046
|
+
*
|
|
1047
|
+
* The function strips string and numeric literals, collapses whitespace, and
|
|
1048
|
+
* lowercases the result before hashing — so two structurally equivalent
|
|
1049
|
+
* statements (with different parameter values) produce the same fingerprint.
|
|
1050
|
+
* Used by SQL telemetry to group queries.
|
|
1051
|
+
*/
|
|
1052
|
+
function computeSqlFingerprint(sql) {
|
|
1053
|
+
const normalized = sql.replace(STRING_LITERAL_REGEX, "?").replace(NUMERIC_LITERAL_REGEX, "?").replace(WHITESPACE_REGEX, " ").trim().toLowerCase();
|
|
1054
|
+
return createHash("sha256").update(normalized).digest("hex");
|
|
1055
|
+
}
|
|
1056
|
+
async function runBeforeCompileChain(middleware, initial, ctx) {
|
|
1057
|
+
let current = initial;
|
|
1058
|
+
for (const mw of middleware) {
|
|
1059
|
+
if (!mw.beforeCompile) continue;
|
|
1060
|
+
const result = await mw.beforeCompile(current, ctx);
|
|
1061
|
+
if (result === void 0) continue;
|
|
1062
|
+
if (result.ast === current.ast) continue;
|
|
1063
|
+
ctx.log.debug?.({
|
|
1064
|
+
event: "middleware.rewrite",
|
|
1065
|
+
middleware: mw.name,
|
|
1066
|
+
lane: current.meta.lane
|
|
1067
|
+
});
|
|
1068
|
+
current = result;
|
|
1069
|
+
}
|
|
1070
|
+
return current;
|
|
1071
|
+
}
|
|
1072
|
+
function normalizeSpec(spec) {
|
|
1073
|
+
if (typeof spec === "string") return {
|
|
1074
|
+
codec: { codecId: spec },
|
|
1075
|
+
nullable: false
|
|
1076
|
+
};
|
|
1077
|
+
return {
|
|
1078
|
+
codec: spec.typeParams !== void 0 ? {
|
|
1079
|
+
codecId: spec.codecId,
|
|
1080
|
+
typeParams: spec.typeParams
|
|
1081
|
+
} : { codecId: spec.codecId },
|
|
1082
|
+
nullable: spec.nullable === true
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
var BindSiteExpression = class {
|
|
1086
|
+
returnType;
|
|
1087
|
+
#ast;
|
|
1088
|
+
constructor(ref, returnType) {
|
|
1089
|
+
this.#ast = ref;
|
|
1090
|
+
this.returnType = returnType;
|
|
1091
|
+
}
|
|
1092
|
+
buildAst() {
|
|
1093
|
+
return this.#ast;
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
function buildBindSiteParams(declaration) {
|
|
1097
|
+
const params = {};
|
|
1098
|
+
for (const [name, spec] of Object.entries(declaration)) {
|
|
1099
|
+
const { codec, nullable } = normalizeSpec(spec);
|
|
1100
|
+
params[name] = new BindSiteExpression(PreparedParamRef.of(name, codec), {
|
|
1101
|
+
codecId: codec.codecId,
|
|
1102
|
+
nullable
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
return Object.freeze(params);
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Resolve a PreparedStatement's slot order to the unencoded values it
|
|
1109
|
+
* will carry into encode. Literal slots come from the lowered AST;
|
|
1110
|
+
* bind slots are looked up by name on `userParams`. Missing user params
|
|
1111
|
+
* surface `RUNTIME.PREPARE_MISSING_PARAM` so the caller cannot silently
|
|
1112
|
+
* bind `undefined`.
|
|
1113
|
+
*/
|
|
1114
|
+
function resolvePreparedSlotValues(ps, userParams) {
|
|
1115
|
+
return ps.slots.map((slot) => {
|
|
1116
|
+
if (slot.kind === "literal") return slot.value;
|
|
1117
|
+
if (!Object.hasOwn(userParams, slot.name)) throw runtimeError("RUNTIME.PREPARE_MISSING_PARAM", `Prepared statement execute is missing parameter '${slot.name}'`, { name: slot.name });
|
|
1118
|
+
return userParams[slot.name];
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
var SqlFamilyAdapter = class {
|
|
1122
|
+
contract;
|
|
1123
|
+
markerReader;
|
|
1124
|
+
constructor(contract, adapterProfile) {
|
|
1125
|
+
this.contract = contract;
|
|
1126
|
+
this.markerReader = adapterProfile;
|
|
1127
|
+
}
|
|
1128
|
+
validatePlan(plan, contract) {
|
|
1129
|
+
if (plan.meta.target !== contract.target) throw runtimeError("PLAN.TARGET_MISMATCH", "Plan target does not match runtime target", {
|
|
1130
|
+
planTarget: plan.meta.target,
|
|
1131
|
+
runtimeTarget: contract.target
|
|
1132
|
+
});
|
|
1133
|
+
if (plan.meta.storageHash !== contract.storage.storageHash) throw runtimeError("PLAN.HASH_MISMATCH", "Plan storage hash does not match runtime contract", {
|
|
1134
|
+
planStorageHash: plan.meta.storageHash,
|
|
1135
|
+
runtimeStorageHash: contract.storage.storageHash
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
function isExecutionPlan(plan) {
|
|
1140
|
+
return "sql" in plan;
|
|
1141
|
+
}
|
|
1142
|
+
// v8 ignore next 2
|
|
1143
|
+
const noopLogSink = () => {};
|
|
1144
|
+
const noopLog = {
|
|
1145
|
+
info: noopLogSink,
|
|
1146
|
+
warn: noopLogSink,
|
|
1147
|
+
error: noopLogSink
|
|
1148
|
+
};
|
|
1149
|
+
/**
|
|
1150
|
+
* Abstract family-layer base for SQL runtimes. Subclass to build a target runtime
|
|
1151
|
+
* (e.g. `PostgresRuntimeImpl`); app code should consume the `Runtime` interface returned
|
|
1152
|
+
* by the target factories, never this class directly.
|
|
1153
|
+
*/
|
|
1154
|
+
var SqlRuntimeBase = class extends RuntimeCore {
|
|
1155
|
+
contract;
|
|
1156
|
+
adapter;
|
|
1157
|
+
driver;
|
|
1158
|
+
familyAdapter;
|
|
1159
|
+
contractCodecs;
|
|
1160
|
+
codecDescriptors;
|
|
1161
|
+
sqlCtx;
|
|
1162
|
+
verifyMarkerOption;
|
|
1163
|
+
verifyMarkerPromise;
|
|
1164
|
+
#preparedStatementHandles = /* @__PURE__ */ new WeakMap();
|
|
1165
|
+
codecRegistryValidated;
|
|
1166
|
+
_telemetry;
|
|
1167
|
+
constructor(options) {
|
|
1168
|
+
const { context, adapter, driver, verifyMarker, middleware, mode, log } = options;
|
|
1169
|
+
if (middleware) for (const mw of middleware) checkMiddlewareCompatibility(mw, "sql", context.contract.target);
|
|
1170
|
+
const sqlCtx = {
|
|
1171
|
+
contract: context.contract,
|
|
1172
|
+
mode: mode ?? "strict",
|
|
1173
|
+
now: () => Date.now(),
|
|
1174
|
+
log: log ?? noopLog,
|
|
1175
|
+
contentHash: (exec) => computeSqlContentHash(exec),
|
|
1176
|
+
scope: "runtime",
|
|
1177
|
+
planExecutionId: ""
|
|
1178
|
+
};
|
|
1179
|
+
super({
|
|
1180
|
+
middleware: middleware ?? [],
|
|
1181
|
+
ctx: sqlCtx
|
|
1182
|
+
});
|
|
1183
|
+
this.contract = context.contract;
|
|
1184
|
+
this.adapter = adapter;
|
|
1185
|
+
this.driver = driver;
|
|
1186
|
+
this.familyAdapter = new SqlFamilyAdapter(context.contract, adapter.profile);
|
|
1187
|
+
this.contractCodecs = context.contractCodecs;
|
|
1188
|
+
this.codecDescriptors = context.codecDescriptors;
|
|
1189
|
+
this.sqlCtx = sqlCtx;
|
|
1190
|
+
this.verifyMarkerOption = verifyMarker ?? "onFirstUse";
|
|
1191
|
+
this.codecRegistryValidated = false;
|
|
1192
|
+
this.verifyMarkerPromise = this.verifyMarkerOption === false ? Promise.resolve() : null;
|
|
1193
|
+
this._telemetry = null;
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Lower a `SqlQueryPlan` (AST + meta) into a `SqlExecutionPlan`
|
|
1197
|
+
* with encoded parameters ready for the driver.
|
|
1198
|
+
*
|
|
1199
|
+
* Implementation note: SQL splits lower-then-encode across
|
|
1200
|
+
* {@link lowerToDraft} + {@link encodeDraftParams} so the runtime
|
|
1201
|
+
* can fire the `beforeExecute` middleware chain between them
|
|
1202
|
+
* (cipherstash bulk-encrypt, for example, mutates pre-encode
|
|
1203
|
+
* `ParamRef.value` slots). This protected hook composes the two
|
|
1204
|
+
* back into the cross-family `lower()` shape `RuntimeCore.execute`
|
|
1205
|
+
* expects, and is called from the no-middleware fast paths /
|
|
1206
|
+
* fixtures that hit `RuntimeCore`'s default template directly.
|
|
1207
|
+
* `execute()` overrides the template and uses the split form so
|
|
1208
|
+
* `beforeExecute` lands between the two halves.
|
|
1209
|
+
*
|
|
1210
|
+
* `ctx: SqlCodecCallContext` is forwarded to `encodeParams` so
|
|
1211
|
+
* per-query cancellation reaches every codec body during parameter
|
|
1212
|
+
* encoding. SQL params do not populate `ctx.column` — encode-side
|
|
1213
|
+
* column metadata is the middleware's domain.
|
|
1214
|
+
*/
|
|
1215
|
+
async lower(plan, ctx) {
|
|
1216
|
+
const draft = this.lowerToDraft(plan);
|
|
1217
|
+
return await this.encodeDraftParams(draft, ctx);
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* AST → pre-encode draft. The returned plan has `sql` rendered and
|
|
1221
|
+
* `params` populated with the user-domain values the lowering site
|
|
1222
|
+
* collected from `ParamRef` nodes. No codec encode has happened
|
|
1223
|
+
* yet; consumers can mutate `params` via the `SqlParamRefMutator`
|
|
1224
|
+
* before {@link encodeDraftParams} runs.
|
|
1225
|
+
*/
|
|
1226
|
+
lowerToDraft(plan) {
|
|
1227
|
+
return lowerSqlPlan(this.adapter, this.contract, plan);
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Encode a draft plan's params through the per-column codecs and
|
|
1231
|
+
* freeze the result into the final `SqlExecutionPlan` the driver
|
|
1232
|
+
* sees. Errors surface as `RUNTIME.ENCODE_FAILED` envelopes from
|
|
1233
|
+
* {@link encodeParams}.
|
|
1234
|
+
*/
|
|
1235
|
+
async encodeDraftParams(draft, ctx) {
|
|
1236
|
+
return Object.freeze({
|
|
1237
|
+
...draft,
|
|
1238
|
+
params: await encodeParams(draft, ctx, this.contractCodecs)
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Default driver invocation required by the abstract `RuntimeCore` contract. Every production path overrides `execute()` and routes through `executeAgainstQueryable`, so this hook is defensive only — subclasses that delegate back to `super.execute()` would land here.
|
|
1243
|
+
*/
|
|
1244
|
+
// v8 ignore next 6
|
|
1245
|
+
runDriver(exec) {
|
|
1246
|
+
return this.driver.execute({
|
|
1247
|
+
sql: exec.sql,
|
|
1248
|
+
params: exec.params
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* SQL pre-compile hook. Runs the registered middleware `beforeCompile` chain over the plan's draft (AST + meta). Returns the original plan unchanged when no middleware rewrote the AST; otherwise returns a new plan carrying the rewritten AST and meta. The AST is the authoritative source of execution metadata, so a rewrite needs no sidecar reconciliation here — the lowering adapter and the encoder both walk the rewritten
|
|
1253
|
+
* AST directly.
|
|
1254
|
+
*/
|
|
1255
|
+
async runBeforeCompile(plan) {
|
|
1256
|
+
const rewrittenDraft = await runBeforeCompileChain(this.middleware, {
|
|
1257
|
+
ast: plan.ast,
|
|
1258
|
+
meta: plan.meta
|
|
1259
|
+
}, this.sqlCtx);
|
|
1260
|
+
return rewrittenDraft.ast === plan.ast ? plan : {
|
|
1261
|
+
...plan,
|
|
1262
|
+
ast: rewrittenDraft.ast,
|
|
1263
|
+
meta: rewrittenDraft.meta
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
execute(plan, options) {
|
|
1267
|
+
return this.executeAgainstQueryable(plan, this.driver, options);
|
|
1268
|
+
}
|
|
1269
|
+
executePrepared(ps, params, options) {
|
|
1270
|
+
return this.executePreparedAgainstQueryable(ps, params, this.driver, options);
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Returns the raw driver connection. The connection is a `SqlQueryable` — SQL
|
|
1274
|
+
* issued on it runs below the middleware/codec/telemetry pipeline. It carries
|
|
1275
|
+
* its own lifecycle (`release`/`destroy`/`beginTransaction`); the caller owns
|
|
1276
|
+
* disposal.
|
|
1277
|
+
*/
|
|
1278
|
+
acquireRawConnection() {
|
|
1279
|
+
return this.driver.acquireConnection();
|
|
1280
|
+
}
|
|
1281
|
+
async *streamRows(exec, decodeContext, driverCall, codecCtx, execMiddlewareCtx) {
|
|
1282
|
+
this.familyAdapter.validatePlan(exec, this.contract);
|
|
1283
|
+
this._telemetry = null;
|
|
1284
|
+
if (this.verifyMarkerPromise === null) this.verifyMarkerPromise = this.verifyMarker();
|
|
1285
|
+
await this.verifyMarkerPromise;
|
|
1286
|
+
const startedAt = Date.now();
|
|
1287
|
+
let outcome = null;
|
|
1288
|
+
try {
|
|
1289
|
+
const iterator = runWithMiddleware(exec, this.middleware, execMiddlewareCtx, driverCall)[Symbol.asyncIterator]();
|
|
1290
|
+
try {
|
|
1291
|
+
while (true) {
|
|
1292
|
+
checkAborted(codecCtx, "stream");
|
|
1293
|
+
const next = await iterator.next();
|
|
1294
|
+
if (next.done) break;
|
|
1295
|
+
yield await decodeRow(next.value, decodeContext, codecCtx);
|
|
1296
|
+
}
|
|
1297
|
+
} finally {
|
|
1298
|
+
await iterator.return?.();
|
|
1299
|
+
}
|
|
1300
|
+
outcome = "success";
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
outcome = "runtime-error";
|
|
1303
|
+
throw error;
|
|
1304
|
+
} finally {
|
|
1305
|
+
if (outcome !== null) this.recordTelemetry(exec, outcome, Date.now() - startedAt);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Execute a plan against a caller-supplied queryable, running the full
|
|
1310
|
+
* middleware/codec/telemetry pipeline. Use `acquireRawConnection` to obtain a
|
|
1311
|
+
* queryable that subclasses can bind typed plans to.
|
|
1312
|
+
*/
|
|
1313
|
+
executeAgainstQueryable(plan, queryable, options) {
|
|
1314
|
+
this.ensureCodecRegistryValidated();
|
|
1315
|
+
const self = this;
|
|
1316
|
+
const signal = options?.signal;
|
|
1317
|
+
const scope = options?.scope ?? "runtime";
|
|
1318
|
+
const codecCtx = signal === void 0 ? {} : { signal };
|
|
1319
|
+
const execMiddlewareCtx = {
|
|
1320
|
+
...self.ctx,
|
|
1321
|
+
...ifDefined("signal", signal),
|
|
1322
|
+
...scope !== "runtime" ? { scope } : {},
|
|
1323
|
+
planExecutionId: crypto.randomUUID()
|
|
1324
|
+
};
|
|
1325
|
+
const generator = async function* () {
|
|
1326
|
+
checkAborted(codecCtx, "stream");
|
|
1327
|
+
let exec;
|
|
1328
|
+
if (isExecutionPlan(plan)) {
|
|
1329
|
+
const preEncodeMutator = createSqlParamRefMutator(plan);
|
|
1330
|
+
await runBeforeExecuteChain(plan, self.middleware, execMiddlewareCtx, preEncodeMutator);
|
|
1331
|
+
exec = Object.freeze({
|
|
1332
|
+
...plan,
|
|
1333
|
+
params: await encodeParams({
|
|
1334
|
+
...plan,
|
|
1335
|
+
params: preEncodeMutator.currentParams()
|
|
1336
|
+
}, codecCtx, self.contractCodecs)
|
|
1337
|
+
});
|
|
1338
|
+
} else {
|
|
1339
|
+
const compiled = await self.runBeforeCompile(plan);
|
|
1340
|
+
const draft = self.lowerToDraft(compiled);
|
|
1341
|
+
const preEncodeMutator = createSqlParamRefMutator(draft);
|
|
1342
|
+
await runBeforeExecuteChain(draft, self.middleware, execMiddlewareCtx, preEncodeMutator);
|
|
1343
|
+
const draftWithMutations = Object.freeze({
|
|
1344
|
+
...draft,
|
|
1345
|
+
params: preEncodeMutator.currentParams()
|
|
1346
|
+
});
|
|
1347
|
+
exec = await self.encodeDraftParams(draftWithMutations, codecCtx);
|
|
1348
|
+
}
|
|
1349
|
+
const decodeContext = buildDecodeContext(exec.ast, self.contractCodecs);
|
|
1350
|
+
yield* self.streamRows(exec, decodeContext, () => queryable.execute({
|
|
1351
|
+
sql: exec.sql,
|
|
1352
|
+
params: exec.params
|
|
1353
|
+
}), codecCtx, execMiddlewareCtx);
|
|
1354
|
+
};
|
|
1355
|
+
return new AsyncIterableResult(generator());
|
|
1356
|
+
}
|
|
1357
|
+
async prepare(declaration, callback) {
|
|
1358
|
+
this.ensureCodecRegistryValidated();
|
|
1359
|
+
const userPlan = callback(buildBindSiteParams(declaration));
|
|
1360
|
+
const finalPlan = await this.runBeforeCompile(userPlan);
|
|
1361
|
+
const orderedRefs = collectOrderedParamRefs(finalPlan.ast);
|
|
1362
|
+
const referencedNames = /* @__PURE__ */ new Set();
|
|
1363
|
+
for (const ref of orderedRefs) if (ref.kind === "prepared-param-ref") referencedNames.add(ref.name);
|
|
1364
|
+
const missing = Object.keys(declaration).filter((name) => !referencedNames.has(name));
|
|
1365
|
+
if (missing.length > 0) throw runtimeError("RUNTIME.PREPARE_UNUSED_PARAM", `Prepared statement declaration includes parameter${missing.length === 1 ? "" : "s"} not referenced by the callback's plan: ${missing.join(", ")}`, { unused: missing });
|
|
1366
|
+
const lowered = this.adapter.lower(finalPlan.ast, {
|
|
1367
|
+
contract: this.contract,
|
|
1368
|
+
params: orderedRefs.map((r) => r.kind === "param-ref" ? r.value : void 0)
|
|
1369
|
+
});
|
|
1370
|
+
const decodeContext = buildDecodeContext(finalPlan.ast, this.contractCodecs);
|
|
1371
|
+
const paramMetadata = deriveParamMetadata(finalPlan.ast);
|
|
1372
|
+
return new PreparedStatementImpl(Object.freeze({
|
|
1373
|
+
sql: lowered.sql,
|
|
1374
|
+
ast: finalPlan.ast,
|
|
1375
|
+
meta: finalPlan.meta,
|
|
1376
|
+
slots: lowered.params,
|
|
1377
|
+
decodeContext,
|
|
1378
|
+
paramMetadata
|
|
1379
|
+
}));
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Execute a prepared statement against a caller-supplied queryable, running
|
|
1383
|
+
* the full middleware/codec/telemetry pipeline.
|
|
1384
|
+
*/
|
|
1385
|
+
executePreparedAgainstQueryable(ps, userParams, queryable, options) {
|
|
1386
|
+
this.ensureCodecRegistryValidated();
|
|
1387
|
+
const self = this;
|
|
1388
|
+
const signal = options?.signal;
|
|
1389
|
+
const scope = options?.scope ?? "runtime";
|
|
1390
|
+
const codecCtx = signal === void 0 ? {} : { signal };
|
|
1391
|
+
const execMiddlewareCtx = {
|
|
1392
|
+
...self.ctx,
|
|
1393
|
+
...ifDefined("signal", signal),
|
|
1394
|
+
...scope !== "runtime" ? { scope } : {},
|
|
1395
|
+
planExecutionId: crypto.randomUUID()
|
|
1396
|
+
};
|
|
1397
|
+
const generator = async function* () {
|
|
1398
|
+
checkAborted(codecCtx, "stream");
|
|
1399
|
+
const preEncodeValues = resolvePreparedSlotValues(ps, userParams);
|
|
1400
|
+
const preEncodeExec = {
|
|
1401
|
+
sql: ps.sql,
|
|
1402
|
+
params: preEncodeValues,
|
|
1403
|
+
ast: ps.ast,
|
|
1404
|
+
meta: ps.meta
|
|
1405
|
+
};
|
|
1406
|
+
const mutator = createSqlParamRefMutator(preEncodeExec);
|
|
1407
|
+
await runBeforeExecuteChain(preEncodeExec, self.middleware, execMiddlewareCtx, mutator);
|
|
1408
|
+
const encodedParams = await encodeParamsWithMetadata(mutator.currentParams(), ps.paramMetadata, codecCtx, self.contractCodecs);
|
|
1409
|
+
const exec = {
|
|
1410
|
+
sql: ps.sql,
|
|
1411
|
+
params: encodedParams,
|
|
1412
|
+
ast: ps.ast,
|
|
1413
|
+
meta: ps.meta
|
|
1414
|
+
};
|
|
1415
|
+
const handles = self.#preparedStatementHandles;
|
|
1416
|
+
const request = {
|
|
1417
|
+
sql: exec.sql,
|
|
1418
|
+
params: exec.params,
|
|
1419
|
+
handle: {
|
|
1420
|
+
get: () => handles.get(ps),
|
|
1421
|
+
set: (value) => {
|
|
1422
|
+
handles.set(ps, value);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
yield* self.streamRows(exec, ps.decodeContext, () => queryable.executePrepared(request), codecCtx, execMiddlewareCtx);
|
|
1427
|
+
};
|
|
1428
|
+
return new AsyncIterableResult(generator());
|
|
1429
|
+
}
|
|
1430
|
+
async connection() {
|
|
1431
|
+
const driverConn = await this.driver.acquireConnection();
|
|
1432
|
+
const self = this;
|
|
1433
|
+
return {
|
|
1434
|
+
async transaction() {
|
|
1435
|
+
const driverTx = await driverConn.beginTransaction();
|
|
1436
|
+
return self.wrapTransaction(driverTx);
|
|
1437
|
+
},
|
|
1438
|
+
async release() {
|
|
1439
|
+
await driverConn.release();
|
|
1440
|
+
},
|
|
1441
|
+
async destroy(reason) {
|
|
1442
|
+
await driverConn.destroy(reason);
|
|
1443
|
+
},
|
|
1444
|
+
execute(plan, options) {
|
|
1445
|
+
return self.executeAgainstQueryable(plan, driverConn, {
|
|
1446
|
+
...options,
|
|
1447
|
+
scope: "connection"
|
|
1448
|
+
});
|
|
1449
|
+
},
|
|
1450
|
+
executePrepared(ps, params, options) {
|
|
1451
|
+
return self.executePreparedAgainstQueryable(ps, params, driverConn, {
|
|
1452
|
+
...options,
|
|
1453
|
+
scope: "connection"
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
wrapTransaction(driverTx) {
|
|
1459
|
+
const self = this;
|
|
1460
|
+
return {
|
|
1461
|
+
async commit() {
|
|
1462
|
+
await driverTx.commit();
|
|
1463
|
+
},
|
|
1464
|
+
async rollback() {
|
|
1465
|
+
await driverTx.rollback();
|
|
1466
|
+
},
|
|
1467
|
+
execute(plan, options) {
|
|
1468
|
+
return self.executeAgainstQueryable(plan, driverTx, {
|
|
1469
|
+
...options,
|
|
1470
|
+
scope: "transaction"
|
|
1471
|
+
});
|
|
1472
|
+
},
|
|
1473
|
+
executePrepared(ps, params, options) {
|
|
1474
|
+
return self.executePreparedAgainstQueryable(ps, params, driverTx, {
|
|
1475
|
+
...options,
|
|
1476
|
+
scope: "transaction"
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
telemetry() {
|
|
1482
|
+
return this._telemetry;
|
|
1483
|
+
}
|
|
1484
|
+
async close() {
|
|
1485
|
+
await this.driver.close();
|
|
1486
|
+
}
|
|
1487
|
+
ensureCodecRegistryValidated() {
|
|
1488
|
+
if (!this.codecRegistryValidated) {
|
|
1489
|
+
validateCodecRegistryCompleteness(this.codecDescriptors, this.contract);
|
|
1490
|
+
this.codecRegistryValidated = true;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
async verifyMarker() {
|
|
1494
|
+
const readResult = await this.familyAdapter.markerReader.readMarker(this.driver);
|
|
1495
|
+
const expectedStorageHash = this.contract.storage.storageHash;
|
|
1496
|
+
const expectedProfileHash = this.contract.profileHash ?? null;
|
|
1497
|
+
const expected = {
|
|
1498
|
+
storageHash: expectedStorageHash,
|
|
1499
|
+
profileHash: expectedProfileHash
|
|
1500
|
+
};
|
|
1501
|
+
if (readResult.kind !== "present") {
|
|
1502
|
+
this.sqlCtx.log.warn({
|
|
1503
|
+
code: "CONTRACT.MARKER_MISSING",
|
|
1504
|
+
scope: "marker-verification",
|
|
1505
|
+
expected,
|
|
1506
|
+
actual: null,
|
|
1507
|
+
message: "Contract marker not found in database"
|
|
1508
|
+
});
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
const marker = readResult.record;
|
|
1512
|
+
const storageHashMatch = marker.storageHash === expectedStorageHash;
|
|
1513
|
+
const profileHashMatch = expectedProfileHash === null || marker.profileHash === expectedProfileHash;
|
|
1514
|
+
if (!storageHashMatch || !profileHashMatch) this.sqlCtx.log.warn({
|
|
1515
|
+
code: "CONTRACT.MARKER_MISMATCH",
|
|
1516
|
+
scope: "marker-verification",
|
|
1517
|
+
expected,
|
|
1518
|
+
actual: {
|
|
1519
|
+
storageHash: marker.storageHash,
|
|
1520
|
+
profileHash: marker.profileHash ?? null
|
|
1521
|
+
},
|
|
1522
|
+
message: "Contract marker hash does not match runtime contract"
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
recordTelemetry(plan, outcome, durationMs) {
|
|
1526
|
+
const contract = this.contract;
|
|
1527
|
+
this._telemetry = Object.freeze({
|
|
1528
|
+
lane: plan.meta.lane,
|
|
1529
|
+
target: contract.target,
|
|
1530
|
+
fingerprint: computeSqlFingerprint(plan.sql),
|
|
1531
|
+
outcome,
|
|
1532
|
+
...durationMs !== void 0 ? { durationMs } : {}
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
function transactionClosedError() {
|
|
1537
|
+
return runtimeError("RUNTIME.TRANSACTION_CLOSED", "Cannot read from a query result after the transaction has ended. Await the result or call .toArray() inside the transaction callback.", {});
|
|
1538
|
+
}
|
|
1539
|
+
async function withTransaction(runtime, fn) {
|
|
1540
|
+
const connection = await runtime.connection();
|
|
1541
|
+
const transaction = await connection.transaction();
|
|
1542
|
+
let invalidated = false;
|
|
1543
|
+
async function* guardedStream(inner) {
|
|
1544
|
+
if (invalidated) throw transactionClosedError();
|
|
1545
|
+
for await (const row of inner) {
|
|
1546
|
+
yield row;
|
|
1547
|
+
if (invalidated) throw transactionClosedError();
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
const txContext = {
|
|
1551
|
+
get invalidated() {
|
|
1552
|
+
return invalidated;
|
|
1553
|
+
},
|
|
1554
|
+
execute(plan, options) {
|
|
1555
|
+
if (invalidated) throw transactionClosedError();
|
|
1556
|
+
return new AsyncIterableResult(guardedStream(transaction.execute(plan, options)));
|
|
1557
|
+
},
|
|
1558
|
+
executePrepared(ps, params, options) {
|
|
1559
|
+
if (invalidated) throw transactionClosedError();
|
|
1560
|
+
return new AsyncIterableResult(guardedStream(transaction.executePrepared(ps, params, options)));
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
let connectionDisposed = false;
|
|
1564
|
+
const destroyConnection = async (reason) => {
|
|
1565
|
+
if (connectionDisposed) return;
|
|
1566
|
+
connectionDisposed = true;
|
|
1567
|
+
await connection.destroy(reason).catch(() => void 0);
|
|
1568
|
+
};
|
|
1569
|
+
try {
|
|
1570
|
+
let result;
|
|
1571
|
+
try {
|
|
1572
|
+
result = await fn(txContext);
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
try {
|
|
1575
|
+
await transaction.rollback();
|
|
1576
|
+
} catch (rollbackError) {
|
|
1577
|
+
await destroyConnection(rollbackError);
|
|
1578
|
+
const wrapped = runtimeError("RUNTIME.TRANSACTION_ROLLBACK_FAILED", "Transaction rollback failed after callback error", { rollbackError });
|
|
1579
|
+
wrapped.cause = error;
|
|
1580
|
+
throw wrapped;
|
|
1581
|
+
}
|
|
1582
|
+
throw error;
|
|
1583
|
+
} finally {
|
|
1584
|
+
invalidated = true;
|
|
1585
|
+
}
|
|
1586
|
+
try {
|
|
1587
|
+
await transaction.commit();
|
|
1588
|
+
} catch (commitError) {
|
|
1589
|
+
try {
|
|
1590
|
+
await transaction.rollback();
|
|
1591
|
+
} catch {
|
|
1592
|
+
await destroyConnection(commitError);
|
|
1593
|
+
}
|
|
1594
|
+
const wrapped = runtimeError("RUNTIME.TRANSACTION_COMMIT_FAILED", "Transaction commit failed", { commitError });
|
|
1595
|
+
wrapped.cause = commitError;
|
|
1596
|
+
throw wrapped;
|
|
1597
|
+
}
|
|
1598
|
+
return result;
|
|
1599
|
+
} finally {
|
|
1600
|
+
if (!connectionDisposed) await connection.release();
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
//#endregion
|
|
1604
|
+
export { PreparedStatementImpl, SqlRuntimeBase, budgets, createAstCodecRegistry, createExecutionContext, createSqlExecutionStack, deriveParamMetadata, encodeParamsWithMetadata, extractCodecIds, lints, lowerSqlPlan, validateCodecRegistryCompleteness, validateContractCodecMappings, withTransaction };
|
|
1605
|
+
|
|
1606
|
+
//# sourceMappingURL=runtime.mjs.map
|