@supalive/core 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index-0XwMOQBo.d.ts +2110 -0
- package/dist/index-0XwMOQBo.d.ts.map +1 -0
- package/dist/index-BHwZ0TXc.d.ts +2126 -0
- package/dist/index-BHwZ0TXc.d.ts.map +1 -0
- package/dist/index-BSqGOgxz.d.ts +2126 -0
- package/dist/index-BSqGOgxz.d.ts.map +1 -0
- package/dist/index-BSvfTwmf.d.ts +2127 -0
- package/dist/index-BSvfTwmf.d.ts.map +1 -0
- package/dist/index-BX33pXeZ.d.ts +2126 -0
- package/dist/index-BX33pXeZ.d.ts.map +1 -0
- package/dist/index-B_eYxS62.d.ts +2127 -0
- package/dist/index-B_eYxS62.d.ts.map +1 -0
- package/dist/index-BbGO7Ejp.d.ts +2126 -0
- package/dist/index-BbGO7Ejp.d.ts.map +1 -0
- package/dist/index-C2ZMPgsI.d.ts +2114 -0
- package/dist/index-C2ZMPgsI.d.ts.map +1 -0
- package/dist/index-CTFFQWz1.d.ts +2126 -0
- package/dist/index-CTFFQWz1.d.ts.map +1 -0
- package/dist/index-CjOTzplF.d.ts +2126 -0
- package/dist/index-CjOTzplF.d.ts.map +1 -0
- package/dist/index-CmFZetc-.d.ts +2127 -0
- package/dist/index-CmFZetc-.d.ts.map +1 -0
- package/dist/index-Cmrr9Tec.d.ts +2126 -0
- package/dist/index-Cmrr9Tec.d.ts.map +1 -0
- package/dist/index-CqVR7zll.d.ts +2125 -0
- package/dist/index-CqVR7zll.d.ts.map +1 -0
- package/dist/index-CtHo5xAL.d.ts +2125 -0
- package/dist/index-CtHo5xAL.d.ts.map +1 -0
- package/dist/index-Ctw7txMT.d.ts +2166 -0
- package/dist/index-Ctw7txMT.d.ts.map +1 -0
- package/dist/index-Cwbbu-XP.d.ts +2166 -0
- package/dist/index-Cwbbu-XP.d.ts.map +1 -0
- package/dist/index-Cy8IkuGR.d.ts +2125 -0
- package/dist/index-Cy8IkuGR.d.ts.map +1 -0
- package/dist/index-D4_QHo7q.d.ts +2126 -0
- package/dist/index-D4_QHo7q.d.ts.map +1 -0
- package/dist/index-D87c8EVr.d.ts +2125 -0
- package/dist/index-D87c8EVr.d.ts.map +1 -0
- package/dist/index-D9vqKJO_.d.ts +2126 -0
- package/dist/index-D9vqKJO_.d.ts.map +1 -0
- package/dist/index-DJnKNJ6y.d.ts +2110 -0
- package/dist/index-DJnKNJ6y.d.ts.map +1 -0
- package/dist/index-DTPJn741.d.ts +2126 -0
- package/dist/index-DTPJn741.d.ts.map +1 -0
- package/dist/index-DeM-i96w.d.ts +2109 -0
- package/dist/index-DeM-i96w.d.ts.map +1 -0
- package/dist/index-DfMHVkVC.d.ts +2126 -0
- package/dist/index-DfMHVkVC.d.ts.map +1 -0
- package/dist/index-DgZeS3JK.d.ts +2166 -0
- package/dist/index-DgZeS3JK.d.ts.map +1 -0
- package/dist/index-DkfqkzsC.d.ts +2125 -0
- package/dist/index-DkfqkzsC.d.ts.map +1 -0
- package/dist/index-DzGkzEaO.d.ts +2126 -0
- package/dist/index-DzGkzEaO.d.ts.map +1 -0
- package/dist/index-Dzbvt3C2.d.ts +2127 -0
- package/dist/index-Dzbvt3C2.d.ts.map +1 -0
- package/dist/index-WvntPU0e.d.ts +2111 -0
- package/dist/index-WvntPU0e.d.ts.map +1 -0
- package/dist/index-YnlFOYMJ.d.ts +2127 -0
- package/dist/index-YnlFOYMJ.d.ts.map +1 -0
- package/dist/mysql-6XsvNwxp.d.ts +109 -0
- package/dist/mysql-6XsvNwxp.d.ts.map +1 -0
- package/dist/mysql-9euRTU3L.d.ts +109 -0
- package/dist/mysql-9euRTU3L.d.ts.map +1 -0
- package/dist/mysql-B2kTH1jJ.d.ts +109 -0
- package/dist/mysql-B2kTH1jJ.d.ts.map +1 -0
- package/dist/mysql-B49q-Cmk.d.ts +109 -0
- package/dist/mysql-B49q-Cmk.d.ts.map +1 -0
- package/dist/mysql-B5FzK4eP.js +602 -0
- package/dist/mysql-B5FzK4eP.js.map +1 -0
- package/dist/mysql-B94uJ3tN.js +602 -0
- package/dist/mysql-B94uJ3tN.js.map +1 -0
- package/dist/mysql-BB7xHRP5.d.ts +109 -0
- package/dist/mysql-BB7xHRP5.d.ts.map +1 -0
- package/dist/mysql-BF2gBly8.js +602 -0
- package/dist/mysql-BF2gBly8.js.map +1 -0
- package/dist/mysql-BK0abZ2z.d.ts +109 -0
- package/dist/mysql-BK0abZ2z.d.ts.map +1 -0
- package/dist/mysql-BK5D6KCY.d.ts +109 -0
- package/dist/mysql-BK5D6KCY.d.ts.map +1 -0
- package/dist/mysql-BNIwKbOq.js +602 -0
- package/dist/mysql-BNIwKbOq.js.map +1 -0
- package/dist/mysql-BNoXrafv.js +602 -0
- package/dist/mysql-BNoXrafv.js.map +1 -0
- package/dist/mysql-BPQInRAe.js +602 -0
- package/dist/mysql-BPQInRAe.js.map +1 -0
- package/dist/mysql-BYGEXm2C.d.ts +109 -0
- package/dist/mysql-BYGEXm2C.d.ts.map +1 -0
- package/dist/mysql-BYI8YXkW.d.ts +109 -0
- package/dist/mysql-BYI8YXkW.d.ts.map +1 -0
- package/dist/mysql-BYcoH18V.js +602 -0
- package/dist/mysql-BYcoH18V.js.map +1 -0
- package/dist/mysql-BkVE_Ujv.d.ts +109 -0
- package/dist/mysql-BkVE_Ujv.d.ts.map +1 -0
- package/dist/mysql-Bm2p56cX.d.ts +109 -0
- package/dist/mysql-Bm2p56cX.d.ts.map +1 -0
- package/dist/mysql-BpXbhT7n.d.ts +109 -0
- package/dist/mysql-BpXbhT7n.d.ts.map +1 -0
- package/dist/mysql-C5mY2vsv.d.ts +109 -0
- package/dist/mysql-C5mY2vsv.d.ts.map +1 -0
- package/dist/mysql-CA7GCR62.js +602 -0
- package/dist/mysql-CA7GCR62.js.map +1 -0
- package/dist/mysql-CB92MLis.d.ts +109 -0
- package/dist/mysql-CB92MLis.d.ts.map +1 -0
- package/dist/mysql-CJzUHhkD.d.ts +109 -0
- package/dist/mysql-CJzUHhkD.d.ts.map +1 -0
- package/dist/mysql-CMxoLJKU.js +602 -0
- package/dist/mysql-CMxoLJKU.js.map +1 -0
- package/dist/mysql-CWUc2OMX.js +602 -0
- package/dist/mysql-CWUc2OMX.js.map +1 -0
- package/dist/mysql-CXjC0ysZ.js +602 -0
- package/dist/mysql-CXjC0ysZ.js.map +1 -0
- package/dist/mysql-Cki0N6RJ.d.ts +109 -0
- package/dist/mysql-Cki0N6RJ.d.ts.map +1 -0
- package/dist/mysql-CmEj_TDt.d.ts +109 -0
- package/dist/mysql-CmEj_TDt.d.ts.map +1 -0
- package/dist/mysql-CqIRmBp7.d.ts +109 -0
- package/dist/mysql-CqIRmBp7.d.ts.map +1 -0
- package/dist/mysql-CtEorg4f.d.ts +109 -0
- package/dist/mysql-CtEorg4f.d.ts.map +1 -0
- package/dist/mysql-CuOauMxY.d.ts +109 -0
- package/dist/mysql-CuOauMxY.d.ts.map +1 -0
- package/dist/mysql-CuXo3LRu.js +602 -0
- package/dist/mysql-CuXo3LRu.js.map +1 -0
- package/dist/mysql-CzTpgBoO.js +602 -0
- package/dist/mysql-CzTpgBoO.js.map +1 -0
- package/dist/mysql-D8wL7pst.js +602 -0
- package/dist/mysql-D8wL7pst.js.map +1 -0
- package/dist/mysql-DA72G9SS.d.ts +109 -0
- package/dist/mysql-DA72G9SS.d.ts.map +1 -0
- package/dist/mysql-DGE7rJ0V.js +602 -0
- package/dist/mysql-DGE7rJ0V.js.map +1 -0
- package/dist/mysql-DVAc6aZz.d.ts +109 -0
- package/dist/mysql-DVAc6aZz.d.ts.map +1 -0
- package/dist/mysql-DVwWeE_Y.d.ts +109 -0
- package/dist/mysql-DVwWeE_Y.d.ts.map +1 -0
- package/dist/mysql-Db5cdkrK.d.ts +109 -0
- package/dist/mysql-Db5cdkrK.d.ts.map +1 -0
- package/dist/mysql-DfJaMwhW.d.ts +109 -0
- package/dist/mysql-DfJaMwhW.d.ts.map +1 -0
- package/dist/mysql-DifqBSa_.d.ts +109 -0
- package/dist/mysql-DifqBSa_.d.ts.map +1 -0
- package/dist/mysql-Dir8ENr2.d.ts +109 -0
- package/dist/mysql-Dir8ENr2.d.ts.map +1 -0
- package/dist/mysql-DpjO1Ytp.d.ts +109 -0
- package/dist/mysql-DpjO1Ytp.d.ts.map +1 -0
- package/dist/mysql-DswRsxyn.js +602 -0
- package/dist/mysql-DswRsxyn.js.map +1 -0
- package/dist/mysql-Dt8SVcVr.js +602 -0
- package/dist/mysql-Dt8SVcVr.js.map +1 -0
- package/dist/mysql-DtvfcLez.js +602 -0
- package/dist/mysql-DtvfcLez.js.map +1 -0
- package/dist/mysql-Dx64LagV.d.ts +109 -0
- package/dist/mysql-Dx64LagV.d.ts.map +1 -0
- package/dist/mysql-EDJ_yHHV.d.ts +109 -0
- package/dist/mysql-EDJ_yHHV.d.ts.map +1 -0
- package/dist/mysql-EgLnP40l.js +602 -0
- package/dist/mysql-EgLnP40l.js.map +1 -0
- package/dist/mysql-HDKtlHA7.js +602 -0
- package/dist/mysql-HDKtlHA7.js.map +1 -0
- package/dist/mysql-JpthxaEC.js +602 -0
- package/dist/mysql-JpthxaEC.js.map +1 -0
- package/dist/mysql-P_b5BFqG.js +602 -0
- package/dist/mysql-P_b5BFqG.js.map +1 -0
- package/dist/mysql-SLS-B4FA.js +602 -0
- package/dist/mysql-SLS-B4FA.js.map +1 -0
- package/dist/mysql-W2O36FZp.js +602 -0
- package/dist/mysql-W2O36FZp.js.map +1 -0
- package/dist/mysql-e29bahXC.js +602 -0
- package/dist/mysql-e29bahXC.js.map +1 -0
- package/dist/mysql-xrp4lX9M.js +602 -0
- package/dist/mysql-xrp4lX9M.js.map +1 -0
- package/dist/mysql-yzsN4kja.js +602 -0
- package/dist/mysql-yzsN4kja.js.map +1 -0
- package/dist/overlap-checker-BGIygypG.js +235 -0
- package/dist/overlap-checker-BGIygypG.js.map +1 -0
- package/dist/overlap-checker-BZShba_4.js +235 -0
- package/dist/overlap-checker-BZShba_4.js.map +1 -0
- package/dist/overlap-checker-BqR9qCfy.js +235 -0
- package/dist/overlap-checker-BqR9qCfy.js.map +1 -0
- package/dist/overlap-checker-BsSy8AzX.js +235 -0
- package/dist/overlap-checker-BsSy8AzX.js.map +1 -0
- package/dist/overlap-checker-C7ZufVA_.js +235 -0
- package/dist/overlap-checker-C7ZufVA_.js.map +1 -0
- package/dist/overlap-checker-CVOLquoJ.js +235 -0
- package/dist/overlap-checker-CVOLquoJ.js.map +1 -0
- package/dist/overlap-checker-C_TrIsl9.js +235 -0
- package/dist/overlap-checker-C_TrIsl9.js.map +1 -0
- package/dist/overlap-checker-CdY929rB.js +235 -0
- package/dist/overlap-checker-CdY929rB.js.map +1 -0
- package/dist/overlap-checker-CexhOI6d.js +235 -0
- package/dist/overlap-checker-CexhOI6d.js.map +1 -0
- package/dist/overlap-checker-CiQ0i2he.js +235 -0
- package/dist/overlap-checker-CiQ0i2he.js.map +1 -0
- package/dist/overlap-checker-Clg2Tvh1.js +235 -0
- package/dist/overlap-checker-Clg2Tvh1.js.map +1 -0
- package/dist/overlap-checker-CmH6Q_Zr.js +235 -0
- package/dist/overlap-checker-CmH6Q_Zr.js.map +1 -0
- package/dist/overlap-checker-Con95unw.js +235 -0
- package/dist/overlap-checker-Con95unw.js.map +1 -0
- package/dist/overlap-checker-D3fIwrEe.js +235 -0
- package/dist/overlap-checker-D3fIwrEe.js.map +1 -0
- package/dist/overlap-checker-D8aJEZhc.js +235 -0
- package/dist/overlap-checker-D8aJEZhc.js.map +1 -0
- package/dist/overlap-checker-DF8mpxy4.js +235 -0
- package/dist/overlap-checker-DF8mpxy4.js.map +1 -0
- package/dist/overlap-checker-DMkMOPz8.js +235 -0
- package/dist/overlap-checker-DMkMOPz8.js.map +1 -0
- package/dist/overlap-checker-DNqu1-af.js +235 -0
- package/dist/overlap-checker-DNqu1-af.js.map +1 -0
- package/dist/overlap-checker-DO4z3CWn.js +235 -0
- package/dist/overlap-checker-DO4z3CWn.js.map +1 -0
- package/dist/overlap-checker-DOuPHIda.js +235 -0
- package/dist/overlap-checker-DOuPHIda.js.map +1 -0
- package/dist/overlap-checker-DPlDexLT.js +235 -0
- package/dist/overlap-checker-DPlDexLT.js.map +1 -0
- package/dist/overlap-checker-DvVGLMNQ.js +235 -0
- package/dist/overlap-checker-DvVGLMNQ.js.map +1 -0
- package/dist/overlap-checker-FEC4bTk3.js +235 -0
- package/dist/overlap-checker-FEC4bTk3.js.map +1 -0
- package/dist/overlap-checker-JLCNx5q4.js +235 -0
- package/dist/overlap-checker-JLCNx5q4.js.map +1 -0
- package/dist/overlap-checker-fpoPpTQY.js +235 -0
- package/dist/overlap-checker-fpoPpTQY.js.map +1 -0
- package/dist/overlap-checker-j0GcZW77.js +235 -0
- package/dist/overlap-checker-j0GcZW77.js.map +1 -0
- package/dist/overlap-checker-zUQdaIYv.js +235 -0
- package/dist/overlap-checker-zUQdaIYv.js.map +1 -0
- package/dist/postgres-AyB5fPHb.js +857 -0
- package/dist/postgres-AyB5fPHb.js.map +1 -0
- package/dist/postgres-B-OZJdD9.d.ts +113 -0
- package/dist/postgres-B-OZJdD9.d.ts.map +1 -0
- package/dist/postgres-B1ybwr2I.d.ts +113 -0
- package/dist/postgres-B1ybwr2I.d.ts.map +1 -0
- package/dist/postgres-B2396cQX.js +857 -0
- package/dist/postgres-B2396cQX.js.map +1 -0
- package/dist/postgres-B7w7AW5q.d.ts +113 -0
- package/dist/postgres-B7w7AW5q.d.ts.map +1 -0
- package/dist/postgres-BE2VC-lV.d.ts +113 -0
- package/dist/postgres-BE2VC-lV.d.ts.map +1 -0
- package/dist/postgres-BIVafBnG.js +857 -0
- package/dist/postgres-BIVafBnG.js.map +1 -0
- package/dist/postgres-BJ3N1ARq.d.ts +113 -0
- package/dist/postgres-BJ3N1ARq.d.ts.map +1 -0
- package/dist/postgres-BKCshpjz.d.ts +113 -0
- package/dist/postgres-BKCshpjz.d.ts.map +1 -0
- package/dist/postgres-BLDfi_c1.js +857 -0
- package/dist/postgres-BLDfi_c1.js.map +1 -0
- package/dist/postgres-BLsYmvBT.js +857 -0
- package/dist/postgres-BLsYmvBT.js.map +1 -0
- package/dist/postgres-BOHVFFRE.d.ts +113 -0
- package/dist/postgres-BOHVFFRE.d.ts.map +1 -0
- package/dist/postgres-BSm7hkOq.js +857 -0
- package/dist/postgres-BSm7hkOq.js.map +1 -0
- package/dist/postgres-Bc0E1TrW.d.ts +113 -0
- package/dist/postgres-Bc0E1TrW.d.ts.map +1 -0
- package/dist/postgres-BoMAdlCX.d.ts +113 -0
- package/dist/postgres-BoMAdlCX.d.ts.map +1 -0
- package/dist/postgres-C4x9dHnD.js +857 -0
- package/dist/postgres-C4x9dHnD.js.map +1 -0
- package/dist/postgres-C7JBu6rW.d.ts +113 -0
- package/dist/postgres-C7JBu6rW.d.ts.map +1 -0
- package/dist/postgres-C7K6Ylb-.d.ts +113 -0
- package/dist/postgres-C7K6Ylb-.d.ts.map +1 -0
- package/dist/postgres-CECxtb8l.js +857 -0
- package/dist/postgres-CECxtb8l.js.map +1 -0
- package/dist/postgres-CGHUivER.d.ts +113 -0
- package/dist/postgres-CGHUivER.d.ts.map +1 -0
- package/dist/postgres-CH2UJrpO.js +857 -0
- package/dist/postgres-CH2UJrpO.js.map +1 -0
- package/dist/postgres-CJAn-jlg.js +857 -0
- package/dist/postgres-CJAn-jlg.js.map +1 -0
- package/dist/postgres-CPZDIBB0.d.ts +113 -0
- package/dist/postgres-CPZDIBB0.d.ts.map +1 -0
- package/dist/postgres-CaxNPOOz.js +857 -0
- package/dist/postgres-CaxNPOOz.js.map +1 -0
- package/dist/postgres-Chws2Anm.js +857 -0
- package/dist/postgres-Chws2Anm.js.map +1 -0
- package/dist/postgres-Cjmj7ESy.js +857 -0
- package/dist/postgres-Cjmj7ESy.js.map +1 -0
- package/dist/postgres-Clc1i2dP.d.ts +113 -0
- package/dist/postgres-Clc1i2dP.d.ts.map +1 -0
- package/dist/postgres-Cm-bmEN9.js +857 -0
- package/dist/postgres-Cm-bmEN9.js.map +1 -0
- package/dist/postgres-CotH5IcJ.d.ts +113 -0
- package/dist/postgres-CotH5IcJ.d.ts.map +1 -0
- package/dist/postgres-CtCEIxj_.js +857 -0
- package/dist/postgres-CtCEIxj_.js.map +1 -0
- package/dist/postgres-Cwq5EPBg.js +857 -0
- package/dist/postgres-Cwq5EPBg.js.map +1 -0
- package/dist/postgres-CzE7AZii.js +857 -0
- package/dist/postgres-CzE7AZii.js.map +1 -0
- package/dist/postgres-D-9VfxTJ.js +857 -0
- package/dist/postgres-D-9VfxTJ.js.map +1 -0
- package/dist/postgres-D3yPBJPF.d.ts +113 -0
- package/dist/postgres-D3yPBJPF.d.ts.map +1 -0
- package/dist/postgres-D5r-bNx9.d.ts +113 -0
- package/dist/postgres-D5r-bNx9.d.ts.map +1 -0
- package/dist/postgres-D9uq7-pc.d.ts +113 -0
- package/dist/postgres-D9uq7-pc.d.ts.map +1 -0
- package/dist/postgres-DC-_vGdw.js +857 -0
- package/dist/postgres-DC-_vGdw.js.map +1 -0
- package/dist/postgres-DKcSOpXu.js +857 -0
- package/dist/postgres-DKcSOpXu.js.map +1 -0
- package/dist/postgres-DKrGqh_j.d.ts +113 -0
- package/dist/postgres-DKrGqh_j.d.ts.map +1 -0
- package/dist/postgres-DPje6yca.js +857 -0
- package/dist/postgres-DPje6yca.js.map +1 -0
- package/dist/postgres-DUfgpwmk.js +857 -0
- package/dist/postgres-DUfgpwmk.js.map +1 -0
- package/dist/postgres-DkxzH2Dg.d.ts +113 -0
- package/dist/postgres-DkxzH2Dg.d.ts.map +1 -0
- package/dist/postgres-Dm2hJj_z.d.ts +113 -0
- package/dist/postgres-Dm2hJj_z.d.ts.map +1 -0
- package/dist/postgres-DmvXfcDq.d.ts +113 -0
- package/dist/postgres-DmvXfcDq.d.ts.map +1 -0
- package/dist/postgres-DpJbDdVo.d.ts +113 -0
- package/dist/postgres-DpJbDdVo.d.ts.map +1 -0
- package/dist/postgres-DqMmjAnq.js +857 -0
- package/dist/postgres-DqMmjAnq.js.map +1 -0
- package/dist/postgres-Dqo6TmkC.d.ts +113 -0
- package/dist/postgres-Dqo6TmkC.d.ts.map +1 -0
- package/dist/postgres-DuJMKfZT.js +857 -0
- package/dist/postgres-DuJMKfZT.js.map +1 -0
- package/dist/postgres-DuXHCyLM.d.ts +113 -0
- package/dist/postgres-DuXHCyLM.d.ts.map +1 -0
- package/dist/postgres-GjRa0yUX.d.ts +113 -0
- package/dist/postgres-GjRa0yUX.d.ts.map +1 -0
- package/dist/postgres-QmkUq1wW.js +857 -0
- package/dist/postgres-QmkUq1wW.js.map +1 -0
- package/dist/postgres-RYL0Miyw.js +857 -0
- package/dist/postgres-RYL0Miyw.js.map +1 -0
- package/dist/postgres-TiV0hbea.d.ts +113 -0
- package/dist/postgres-TiV0hbea.d.ts.map +1 -0
- package/dist/postgres-oXKx8meT.d.ts +113 -0
- package/dist/postgres-oXKx8meT.d.ts.map +1 -0
- package/dist/postgres-sIfan-wV.d.ts +113 -0
- package/dist/postgres-sIfan-wV.d.ts.map +1 -0
- package/dist/postgres-t-CmTR4w.js +857 -0
- package/dist/postgres-t-CmTR4w.js.map +1 -0
- package/dist/postgres-wSYI5UGF.d.ts +113 -0
- package/dist/postgres-wSYI5UGF.d.ts.map +1 -0
- package/dist/query-BLQJ9SWs.js +738 -0
- package/dist/query-BLQJ9SWs.js.map +1 -0
- package/dist/query-BMkGnyXV.js +722 -0
- package/dist/query-BMkGnyXV.js.map +1 -0
- package/dist/query-BXFo-xCO.js +705 -0
- package/dist/query-BXFo-xCO.js.map +1 -0
- package/dist/query-BXMj4jr-.js +718 -0
- package/dist/query-BXMj4jr-.js.map +1 -0
- package/dist/query-BuL8f9F8.js +724 -0
- package/dist/query-BuL8f9F8.js.map +1 -0
- package/dist/query-C10FzUWC.js +721 -0
- package/dist/query-C10FzUWC.js.map +1 -0
- package/dist/query-C4u8tD4H.js +698 -0
- package/dist/query-C4u8tD4H.js.map +1 -0
- package/dist/query-CLJCQlyA.js +722 -0
- package/dist/query-CLJCQlyA.js.map +1 -0
- package/dist/query-CMzRDKYw.js +714 -0
- package/dist/query-CMzRDKYw.js.map +1 -0
- package/dist/query-CNOc_XzM.js +709 -0
- package/dist/query-CNOc_XzM.js.map +1 -0
- package/dist/query-CVKqKDJE.js +737 -0
- package/dist/query-CVKqKDJE.js.map +1 -0
- package/dist/query-CbY0iPoj.js +724 -0
- package/dist/query-CbY0iPoj.js.map +1 -0
- package/dist/query-ClVEugE4.js +693 -0
- package/dist/query-ClVEugE4.js.map +1 -0
- package/dist/query-Cx7F-ete.js +740 -0
- package/dist/query-Cx7F-ete.js.map +1 -0
- package/dist/query-Cz7nBVio.js +738 -0
- package/dist/query-Cz7nBVio.js.map +1 -0
- package/dist/query-DC40-Yjv.js +737 -0
- package/dist/query-DC40-Yjv.js.map +1 -0
- package/dist/query-DQQ-PgGW.js +724 -0
- package/dist/query-DQQ-PgGW.js.map +1 -0
- package/dist/query-DleaAa4_.js +734 -0
- package/dist/query-DleaAa4_.js.map +1 -0
- package/dist/query-EzBrnggP.js +721 -0
- package/dist/query-EzBrnggP.js.map +1 -0
- package/dist/query-LVDVZXYz.js +729 -0
- package/dist/query-LVDVZXYz.js.map +1 -0
- package/dist/query-RyqMp8z7.js +697 -0
- package/dist/query-RyqMp8z7.js.map +1 -0
- package/dist/query-UQIzz6l1.js +722 -0
- package/dist/query-UQIzz6l1.js.map +1 -0
- package/dist/query-UzuYEXCD.js +724 -0
- package/dist/query-UzuYEXCD.js.map +1 -0
- package/dist/query-a1sRfDbW.js +739 -0
- package/dist/query-a1sRfDbW.js.map +1 -0
- package/dist/query-dsNEpb6t.js +706 -0
- package/dist/query-dsNEpb6t.js.map +1 -0
- package/dist/query-ur-jCtVr.js +740 -0
- package/dist/query-ur-jCtVr.js.map +1 -0
- package/dist/realtime_db-1gVIBhXB.js +272 -0
- package/dist/realtime_db-1gVIBhXB.js.map +1 -0
- package/dist/realtime_db-B7y4VvqK.js +272 -0
- package/dist/realtime_db-B7y4VvqK.js.map +1 -0
- package/dist/realtime_db-B9p7nVqA.js +272 -0
- package/dist/realtime_db-B9p7nVqA.js.map +1 -0
- package/dist/realtime_db-BGbxIA49.js +272 -0
- package/dist/realtime_db-BGbxIA49.js.map +1 -0
- package/dist/realtime_db-BHcg-hrk.js +272 -0
- package/dist/realtime_db-BHcg-hrk.js.map +1 -0
- package/dist/realtime_db-BNGcb0XU.js +272 -0
- package/dist/realtime_db-BNGcb0XU.js.map +1 -0
- package/dist/realtime_db-BeSfp0TC.js +272 -0
- package/dist/realtime_db-BeSfp0TC.js.map +1 -0
- package/dist/realtime_db-Bqjr53bi.js +272 -0
- package/dist/realtime_db-Bqjr53bi.js.map +1 -0
- package/dist/realtime_db-CC7YVTXO.js +272 -0
- package/dist/realtime_db-CC7YVTXO.js.map +1 -0
- package/dist/realtime_db-CKC01P0a.js +272 -0
- package/dist/realtime_db-CKC01P0a.js.map +1 -0
- package/dist/realtime_db-COWVCIVH.js +272 -0
- package/dist/realtime_db-COWVCIVH.js.map +1 -0
- package/dist/realtime_db-CRzNVBAz.js +272 -0
- package/dist/realtime_db-CRzNVBAz.js.map +1 -0
- package/dist/realtime_db-Cfkgkm9D.js +272 -0
- package/dist/realtime_db-Cfkgkm9D.js.map +1 -0
- package/dist/realtime_db-D3D8n8z1.js +272 -0
- package/dist/realtime_db-D3D8n8z1.js.map +1 -0
- package/dist/realtime_db-DI5Z16wS.js +272 -0
- package/dist/realtime_db-DI5Z16wS.js.map +1 -0
- package/dist/realtime_db-DbLFDG_j.js +272 -0
- package/dist/realtime_db-DbLFDG_j.js.map +1 -0
- package/dist/realtime_db-DiYQbfeA.js +272 -0
- package/dist/realtime_db-DiYQbfeA.js.map +1 -0
- package/dist/realtime_db-DnJ7fr2H.js +272 -0
- package/dist/realtime_db-DnJ7fr2H.js.map +1 -0
- package/dist/realtime_db-DpGFkUHy.js +272 -0
- package/dist/realtime_db-DpGFkUHy.js.map +1 -0
- package/dist/realtime_db-DpZLibPQ.js +272 -0
- package/dist/realtime_db-DpZLibPQ.js.map +1 -0
- package/dist/realtime_db-DwxYj-Si.js +272 -0
- package/dist/realtime_db-DwxYj-Si.js.map +1 -0
- package/dist/realtime_db-F0o5WP5z.js +272 -0
- package/dist/realtime_db-F0o5WP5z.js.map +1 -0
- package/dist/realtime_db-G-9ZPdwx.js +272 -0
- package/dist/realtime_db-G-9ZPdwx.js.map +1 -0
- package/dist/realtime_db-NPtQRElK.js +272 -0
- package/dist/realtime_db-NPtQRElK.js.map +1 -0
- package/dist/realtime_db-Zs7Ox8YY.js +272 -0
- package/dist/realtime_db-Zs7Ox8YY.js.map +1 -0
- package/dist/realtime_db-jQ7mhwvO.js +272 -0
- package/dist/realtime_db-jQ7mhwvO.js.map +1 -0
- package/dist/realtime_db-xIwkcoDg.js +272 -0
- package/dist/realtime_db-xIwkcoDg.js.map +1 -0
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.js +3 -3
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/mysql.js +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/postgres.js +1 -1
- package/dist/src/exports/procedure.d.ts +2 -2
- package/dist/src/exports/procedure.js +1 -1
- package/dist/src/exports/schema-sql.d.ts +1 -1
- package/dist/src/exports/server.d.ts +4 -4
- package/dist/src/exports/server.js +3 -3
- package/dist/src/exports/subscription-manager-worker-entry.js +2 -2
- package/dist/src/exports/types.d.ts +2 -2
- package/dist/src/exports/types.js +1 -1
- package/dist/subscription-worker-5l4qavz7.js +749 -0
- package/dist/subscription-worker-5l4qavz7.js.map +1 -0
- package/dist/subscription-worker-B7sDMXsu.js +749 -0
- package/dist/subscription-worker-B7sDMXsu.js.map +1 -0
- package/dist/subscription-worker-BCXO8Lwm.js +749 -0
- package/dist/subscription-worker-BCXO8Lwm.js.map +1 -0
- package/dist/subscription-worker-BEIjeBhb.js +749 -0
- package/dist/subscription-worker-BEIjeBhb.js.map +1 -0
- package/dist/subscription-worker-BQv_WD7J.js +749 -0
- package/dist/subscription-worker-BQv_WD7J.js.map +1 -0
- package/dist/subscription-worker-Beayghw3.js +749 -0
- package/dist/subscription-worker-Beayghw3.js.map +1 -0
- package/dist/subscription-worker-C4b0RWPI.js +749 -0
- package/dist/subscription-worker-C4b0RWPI.js.map +1 -0
- package/dist/subscription-worker-CFPiS-lO.js +749 -0
- package/dist/subscription-worker-CFPiS-lO.js.map +1 -0
- package/dist/subscription-worker-CKQad7zS.js +749 -0
- package/dist/subscription-worker-CKQad7zS.js.map +1 -0
- package/dist/subscription-worker-CUGSAwmV.js +749 -0
- package/dist/subscription-worker-CUGSAwmV.js.map +1 -0
- package/dist/subscription-worker-CjYVbFq2.js +749 -0
- package/dist/subscription-worker-CjYVbFq2.js.map +1 -0
- package/dist/subscription-worker-Cm-wPcp-.js +749 -0
- package/dist/subscription-worker-Cm-wPcp-.js.map +1 -0
- package/dist/subscription-worker-Cu6-TIg3.js +749 -0
- package/dist/subscription-worker-Cu6-TIg3.js.map +1 -0
- package/dist/subscription-worker-D02vQ0YL.js +749 -0
- package/dist/subscription-worker-D02vQ0YL.js.map +1 -0
- package/dist/subscription-worker-D8TcRMuX.js +749 -0
- package/dist/subscription-worker-D8TcRMuX.js.map +1 -0
- package/dist/subscription-worker-DC9v3_Xn.js +749 -0
- package/dist/subscription-worker-DC9v3_Xn.js.map +1 -0
- package/dist/subscription-worker-DHOkFYtt.js +749 -0
- package/dist/subscription-worker-DHOkFYtt.js.map +1 -0
- package/dist/subscription-worker-DrR2G0UY.js +749 -0
- package/dist/subscription-worker-DrR2G0UY.js.map +1 -0
- package/dist/subscription-worker-Dwdp_WGy.js +749 -0
- package/dist/subscription-worker-Dwdp_WGy.js.map +1 -0
- package/dist/subscription-worker-Dy1rzhyX.js +749 -0
- package/dist/subscription-worker-Dy1rzhyX.js.map +1 -0
- package/dist/subscription-worker-DyOE3VkC.js +749 -0
- package/dist/subscription-worker-DyOE3VkC.js.map +1 -0
- package/dist/subscription-worker-O731E8Bh.js +749 -0
- package/dist/subscription-worker-O731E8Bh.js.map +1 -0
- package/dist/subscription-worker-Sj1M8tbn.js +749 -0
- package/dist/subscription-worker-Sj1M8tbn.js.map +1 -0
- package/dist/subscription-worker-Zrw5q6qO.js +749 -0
- package/dist/subscription-worker-Zrw5q6qO.js.map +1 -0
- package/dist/subscription-worker-exWsillu.js +749 -0
- package/dist/subscription-worker-exWsillu.js.map +1 -0
- package/dist/subscription-worker-pTVrvvmW.js +749 -0
- package/dist/subscription-worker-pTVrvvmW.js.map +1 -0
- package/dist/subscription-worker-zvtbinBF.js +749 -0
- package/dist/subscription-worker-zvtbinBF.js.map +1 -0
- package/dist/types_db-B039N8Em.js +173 -0
- package/dist/types_db-B039N8Em.js.map +1 -0
- package/dist/types_db-NY-7zuhP.js +173 -0
- package/dist/types_db-NY-7zuhP.js.map +1 -0
- package/dist/types_server-6U7QWGuL.d.ts +328 -0
- package/dist/types_server-6U7QWGuL.d.ts.map +1 -0
- package/dist/types_server-B2rOOZ0z.d.ts +328 -0
- package/dist/types_server-B2rOOZ0z.d.ts.map +1 -0
- package/dist/types_server-B49_-V9c.d.ts +328 -0
- package/dist/types_server-B49_-V9c.d.ts.map +1 -0
- package/dist/types_server-BDHd_YTS.d.ts +328 -0
- package/dist/types_server-BDHd_YTS.d.ts.map +1 -0
- package/dist/types_server-BEX-uJc5.d.ts +328 -0
- package/dist/types_server-BEX-uJc5.d.ts.map +1 -0
- package/dist/types_server-BH5h91D0.d.ts +328 -0
- package/dist/types_server-BH5h91D0.d.ts.map +1 -0
- package/dist/types_server-BNuTxu-E.d.ts +328 -0
- package/dist/types_server-BNuTxu-E.d.ts.map +1 -0
- package/dist/types_server-BQ33p2LM.d.ts +328 -0
- package/dist/types_server-BQ33p2LM.d.ts.map +1 -0
- package/dist/types_server-BiXcNKA3.d.ts +328 -0
- package/dist/types_server-BiXcNKA3.d.ts.map +1 -0
- package/dist/types_server-BkbGHQzW.d.ts +328 -0
- package/dist/types_server-BkbGHQzW.d.ts.map +1 -0
- package/dist/types_server-BkeSAZmH.d.ts +328 -0
- package/dist/types_server-BkeSAZmH.d.ts.map +1 -0
- package/dist/types_server-BlpFFYCI.d.ts +328 -0
- package/dist/types_server-BlpFFYCI.d.ts.map +1 -0
- package/dist/types_server-BtvZjizR.d.ts +328 -0
- package/dist/types_server-BtvZjizR.d.ts.map +1 -0
- package/dist/types_server-C38fddo_.d.ts +328 -0
- package/dist/types_server-C38fddo_.d.ts.map +1 -0
- package/dist/types_server-CN02ApQ_.d.ts +328 -0
- package/dist/types_server-CN02ApQ_.d.ts.map +1 -0
- package/dist/types_server-CPd_Fuhq.d.ts +328 -0
- package/dist/types_server-CPd_Fuhq.d.ts.map +1 -0
- package/dist/types_server-Ce4bZgQJ.d.ts +328 -0
- package/dist/types_server-Ce4bZgQJ.d.ts.map +1 -0
- package/dist/types_server-CexXO5sW.d.ts +328 -0
- package/dist/types_server-CexXO5sW.d.ts.map +1 -0
- package/dist/types_server-Cq8HGFGy.d.ts +328 -0
- package/dist/types_server-Cq8HGFGy.d.ts.map +1 -0
- package/dist/types_server-CuHsqi4p.d.ts +328 -0
- package/dist/types_server-CuHsqi4p.d.ts.map +1 -0
- package/dist/types_server-D8PzF-79.d.ts +328 -0
- package/dist/types_server-D8PzF-79.d.ts.map +1 -0
- package/dist/types_server-DB3IrC2o.d.ts +328 -0
- package/dist/types_server-DB3IrC2o.d.ts.map +1 -0
- package/dist/types_server-DUV3oRXr.d.ts +328 -0
- package/dist/types_server-DUV3oRXr.d.ts.map +1 -0
- package/dist/types_server-DYNpoqfL.d.ts +328 -0
- package/dist/types_server-DYNpoqfL.d.ts.map +1 -0
- package/dist/types_server-MkSVQQVM.d.ts +328 -0
- package/dist/types_server-MkSVQQVM.d.ts.map +1 -0
- package/dist/types_server-N_coWvBq.d.ts +328 -0
- package/dist/types_server-N_coWvBq.d.ts.map +1 -0
- package/dist/types_server-XPVdOY-I.d.ts +328 -0
- package/dist/types_server-XPVdOY-I.d.ts.map +1 -0
- package/dist/types_server-eQqn3f_C.d.ts +328 -0
- package/dist/types_server-eQqn3f_C.d.ts.map +1 -0
- package/dist/types_server-hAXcDGD4.d.ts +328 -0
- package/dist/types_server-hAXcDGD4.d.ts.map +1 -0
- package/dist/types_server-sKy9tI8G.d.ts +328 -0
- package/dist/types_server-sKy9tI8G.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlap-checker-BZShba_4.js","names":[],"sources":["../src/server/overlap-checker.ts"],"sourcesContent":["import type {\n ReadEntry, CommitLogEntry,\n Predicate, WriteEntry, WriteOp,\n RawReadEntry, RawPointRead, RawRangeRead, LeafPredicate,\n QueryCacheMetadata,\n} from \"../db/types_db\";\nimport { normalizeIdToBytes } from \"../db/types_db\";\nimport { bytesEqual, utf8Decode } from \"../db/bytes\";\nimport { jsonContains, jsonPathExists, jsonPathExtract } from \"../db/query\";\nimport type { Database } from \"../db/db\";\n\nexport interface RawWriteEntry {\n table: string;\n tableId: Uint8Array;\n id: Uint8Array;\n op: WriteOp;\n data?: Record<string, unknown>;\n prevData?: Record<string, unknown>;\n}\n\nexport function mapReadEntryToRaw(entry: ReadEntry): RawReadEntry {\n if (entry.kind === \"point\") {\n return {\n kind: \"point\",\n tableId: entry.tableId,\n id: normalizeIdToBytes(entry.id),\n table: entry.table,\n };\n } else {\n return {\n kind: \"range\",\n tableId: entry.tableId,\n table: entry.table,\n predicate: entry.predicate,\n columns: entry.columns,\n offset: entry.offset,\n orderBy: entry.orderBy,\n limit: entry.limit,\n };\n }\n}\n\nexport function mapReadSetToRaw(readSet: ReadEntry[]): RawReadEntry[] {\n return readSet.map(mapReadEntryToRaw);\n}\n\nexport function mapWriteEntryToRaw(entry: WriteEntry): RawWriteEntry {\n return {\n table: entry.table,\n tableId: entry.tableId,\n id: normalizeIdToBytes(entry.id),\n op: entry.op,\n data: entry.data,\n prevData: entry.prevData,\n };\n}\n\nexport function mapWriteSetToRaw(writeSet: WriteEntry[]): RawWriteEntry[] {\n return writeSet.map(mapWriteEntryToRaw);\n}\n\nexport type OverlapVerdict = \"yes\" | \"no\" | \"unknown\";\n\nexport function evaluatePredicateAgainstRow(\n rowData: Record<string, unknown>,\n predicate: Predicate\n): OverlapVerdict {\n switch (predicate.kind) {\n case \"and\": {\n let hasUnknown = false;\n for (const child of predicate.children) {\n const r = evaluatePredicateAgainstRow(rowData, child);\n if (r === \"no\") return \"no\";\n if (r === \"unknown\") hasUnknown = true;\n }\n return hasUnknown ? \"unknown\" : \"yes\";\n }\n case \"or\": {\n let hasUnknown = false;\n for (const child of predicate.children) {\n const r = evaluatePredicateAgainstRow(rowData, child);\n if (r === \"yes\") return \"yes\";\n if (r === \"unknown\") hasUnknown = true;\n }\n return hasUnknown ? \"unknown\" : \"no\";\n }\n case \"leaf\": {\n if (!(predicate.column in rowData)) return \"unknown\";\n const val = rowData[predicate.column];\n switch (predicate.operator) {\n case \"=\": return val === predicate.value ? \"yes\" : \"no\";\n case \"!=\": return val !== predicate.value ? \"yes\" : \"no\";\n case \">\": return (val as number) > (predicate.value as number) ? \"yes\" : \"no\";\n case \">=\": return (val as number) >= (predicate.value as number) ? \"yes\" : \"no\";\n case \"<\": return (val as number) < (predicate.value as number) ? \"yes\" : \"no\";\n case \"<=\": return (val as number) <= (predicate.value as number) ? \"yes\" : \"no\";\n case \"between\":\n return (val as number) >= (predicate.value as number) &&\n (val as number) <= (predicate.value2 as number) ? \"yes\" : \"no\";\n case \"in\":\n return Array.isArray(predicate.value) && (predicate.value as unknown[]).includes(val) ? \"yes\" : \"no\";\n case \"like\":\n case \"contains\":\n case \"ilike\":\n case \"icontains\": {\n if (typeof val !== \"string\" || typeof predicate.value !== \"string\") return \"no\";\n const haystack = (predicate.operator === \"ilike\" || predicate.operator === \"icontains\")\n ? val.toLowerCase() : val;\n const needle = (predicate.operator === \"ilike\" || predicate.operator === \"icontains\")\n ? (predicate.value as string).toLowerCase() : predicate.value as string;\n const pattern = (predicate.operator === \"contains\" || predicate.operator === \"icontains\")\n ? `%${needle}%` : needle;\n const regex = new RegExp(\"^\" + pattern.replace(/%/g, \".*\").replace(/_/g, \".\") + \"$\");\n return regex.test(haystack) ? \"yes\" : \"no\";\n }\n case \"jsonContains\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (target === undefined) return \"no\";\n return jsonContains(target, candidate) ? \"yes\" : \"no\";\n }\n case \"jsonContainedBy\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (target === undefined) return \"no\";\n return jsonContains(candidate, target) ? \"yes\" : \"no\";\n }\n case \"jsonHasKey\": {\n if (predicate.mode && Array.isArray(predicate.value)) {\n const paths = predicate.value as string[];\n if (paths.length === 0) return \"no\";\n const verdict = predicate.mode === \"all\"\n ? paths.every(p => jsonPathExists(val, p))\n : paths.some(p => jsonPathExists(val, p));\n return verdict ? \"yes\" : \"no\";\n }\n if (predicate.raw) {\n return jsonPathExists(val, predicate.value as string) ? \"yes\" : \"no\";\n }\n const key = predicate.value as string;\n if (val === null || typeof val !== \"object\") return \"no\";\n if (Array.isArray(val)) return (val as unknown[]).includes(key) ? \"yes\" : \"no\";\n return key in (val as object) ? \"yes\" : \"no\";\n }\n case \"jsonArrayContains\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (!Array.isArray(target)) return \"no\";\n return (target as unknown[]).some(el => jsonContains(el, candidate)) ? \"yes\" : \"no\";\n }\n case \"isNull\": return val === null ? \"yes\" : \"no\";\n case \"isNotNull\": return val !== null ? \"yes\" : \"no\";\n default: return \"unknown\";\n }\n }\n }\n}\n\nfunction decodeJsonLeafValue(leaf: LeafPredicate): unknown {\n if (!leaf.raw) return leaf.value;\n if (typeof leaf.value !== \"string\") return leaf.value;\n try { return JSON.parse(leaf.value); } catch { return leaf.value; }\n}\n\nfunction parseLogData(log: CommitLogEntry): Record<string, unknown> | null {\n if (log._parsedData !== undefined) {\n return log._parsedData;\n }\n const data = log.data.length > 0\n ? JSON.parse(utf8Decode(log.data)) as Record<string, unknown>\n : null;\n log._parsedData = data;\n return data;\n}\n\n\nexport function checkLogsAffectReadSet(\n logs: CommitLogEntry[],\n rawReadSet: RawReadEntry[]\n): boolean {\n for (const log of logs) {\n for (const rawRead of rawReadSet) {\n if (!bytesEqual(log.tableId, rawRead.tableId)) continue;\n\n const res = rawRead.kind === \"point\" ?\n checkPointReadOverlap(log, rawRead) :\n checkRangeReadOverlap(log, rawRead);\n\n if (res) {\n return true;\n }\n }\n }\n return false;\n}\n\nexport interface CacheFreshness {\n /** true → the cached data for this key is still valid as of `currentTs`. */\n fresh: boolean;\n /** The cached read set (empty when there is no usable metadata). */\n readSet: ReadEntry[];\n rawReadSet: RawReadEntry[];\n}\n\n/**\n * Stateless freshness check shared by the subscription worker's `register` and\n * the main server's one-shot query path. Decides whether a query's cached\n * result (described by `metadata`) is still valid at `currentTs`.\n *\n * It is a pure function of Redis (`metadata`, `minRetainedTs`) + Postgres\n * (`getCommitLogsBetweenTs`) + the read set — it holds no worker-local state,\n * so it is safe to run outside the sub-manager.\n *\n * `minRetainedTs` is the retention watermark: the oldest commit-log ts still\n * retained after prune.\n *\n * Two ways a cache entry is deemed stale:\n * 1. **Retention guard** — `metadata.lastSnapshotTs < minRetainedTs` (or the\n * watermark is unknown). The between-ts scan would start below the oldest\n * retained commit log, so it could silently miss pruned commits → we cannot\n * prove freshness and must recompute. This is the case that a one-shot\n * (untracked by any subscription, so nothing holds the retention watermark\n * down) can hit.\n * 2. **Overlap** — a commit in `(lastSnapshotTs, currentTs]` touched the read\n * set.\n */\nexport async function evaluateCacheFreshness(\n db: Pick<Database, \"getCommitLogsBetweenTs\">,\n metadata: QueryCacheMetadata | null,\n currentTs: bigint,\n minRetainedTs: bigint | null,\n): Promise<CacheFreshness> {\n if (!metadata) return { fresh: false, readSet: [], rawReadSet: [] };\n\n const readSet = metadata.readSet;\n const rawReadSet = mapReadSetToRaw(readSet);\n\n if (minRetainedTs === null || metadata.lastSnapshotTs < minRetainedTs) {\n return { fresh: false, readSet, rawReadSet };\n }\n\n // A query that read nothing can't be invalidated by any commit — permanently\n // fresh (until TTL eviction). Skip the commit-log scan entirely.\n const tableIds = Array.from(new Set(readSet.map((r) => r.tableId)));\n if (tableIds.length === 0) return { fresh: true, readSet, rawReadSet };\n\n // Scan the window (lastSnapshotTs, currentTs]: commits strictly AFTER the\n // cached snapshot (the data already reflects everything <= lastSnapshotTs) up\n // to and INCLUDING currentTs. getCommitLogsBetweenTs is a half-open [begin,\n // end), so +1n on both ends models that inclusive upper bound. Missing the\n // upper bound would drop a mutation committed at exactly currentTs — and a\n // one-shot query has no push-invalidation backstop, so it would serve stale.\n const logs = await db.getCommitLogsBetweenTs(metadata.lastSnapshotTs + 1n, currentTs + 1n, tableIds);\n const affected = checkLogsAffectReadSet(logs, rawReadSet);\n return { fresh: !affected, readSet, rawReadSet };\n}\n\nfunction checkPointReadOverlap(\n log: CommitLogEntry,\n rawRead: RawPointRead,\n): boolean {\n return bytesEqual(log.id, rawRead.id);\n}\n\nfunction checkRangeReadOverlap(\n log: CommitLogEntry,\n rawRead: RawRangeRead\n): boolean {\n const logData = parseLogData(log);\n\n // No envelope at all → can't reason, must conflict-out.\n if (!logData) { \n return true;\n }\n\n // Commit logs serialize the full WriteEntry (see PgDatabase.insertCommitLogs),\n // so extract the row state from it. Same prev-OR-post check as the\n // in-memory invalidator path.\n const op = (logData.op as WriteOp | undefined) ?? \"update\";\n const data = logData.data as Record<string, unknown> | undefined;\n const prevData = logData.prevData as Record<string, unknown> | undefined;\n\n return checkRowWriteAffectsRange(op, prevData, data, rawRead.predicate);\n}\n\nexport function checkWriteSetAffectsReadSet(\n writeSet: RawWriteEntry[],\n rawReadSet: RawReadEntry[]\n): boolean {\n for (const write of writeSet) {\n for (const rawRead of rawReadSet) {\n if (!bytesEqual(write.tableId, rawRead.tableId)) continue;\n\n if (checkWriteAffectsRawReadEntry(write, rawRead)) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction checkWriteAffectsRawReadEntry(\n write: RawWriteEntry,\n rawRead: RawReadEntry\n): boolean {\n if (rawRead.kind === \"point\") {\n return bytesEqual(write.id, rawRead.id);\n }\n return checkRowWriteAffectsRange(\n write.op,\n write.prevData,\n write.data,\n rawRead.predicate,\n );\n}\n\n/**\n * Evaluate whether a write affects a range subscription, given the row's\n * prior and post state. The write is considered affecting if EITHER side\n * matched the predicate — that catches:\n * - rows entering the result set (prev \"no\", post \"yes\")\n * - rows leaving the result set (prev \"yes\", post \"no\")\n * - rows changing while still in the set (prev \"yes\", post \"yes\")\n * Skipped only if both sides are a definite \"no\".\n *\n * When prevData is missing (e.g. inserts, or commit logs predating this\n * change), we fall back to conservative \"unknown\" on that side.\n */\nfunction checkRowWriteAffectsRange(\n op: WriteOp,\n prevData: Record<string, unknown> | undefined,\n data: Record<string, unknown> | undefined,\n predicate: RawRangeRead[\"predicate\"],\n): boolean {\n const prevVerdict: OverlapVerdict = prevData\n ? evaluatePredicateAgainstRow(prevData, predicate)\n : (op === \"insert\" ? \"no\" : \"unknown\");\n\n let postVerdict: OverlapVerdict;\n if (op === \"delete\") {\n postVerdict = \"no\";\n } else if (data) {\n // Merge over prevData so unchanged columns carry over — otherwise a\n // partial update (e.g. {title: \"x\"}) would be \"unknown\" on every\n // predicate column it doesn't touch.\n const post = prevData ? { ...prevData, ...data } : data;\n postVerdict = evaluatePredicateAgainstRow(post, predicate);\n } else {\n postVerdict = \"unknown\";\n }\n\n return prevVerdict !== \"no\" || postVerdict !== \"no\";\n}"],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { n as utf8Decode, t as bytesEqual } from "./bytes-AuEkyxeU.js";
|
|
2
|
+
import { a as jsonPathExtract, i as jsonPathExists, r as jsonContains } from "./query-ur-jCtVr.js";
|
|
3
|
+
import { C as normalizeIdToBytes } from "./types_db-B039N8Em.js";
|
|
4
|
+
//#region src/server/overlap-checker.ts
|
|
5
|
+
function mapReadEntryToRaw(entry) {
|
|
6
|
+
if (entry.kind === "point") return {
|
|
7
|
+
kind: "point",
|
|
8
|
+
tableId: entry.tableId,
|
|
9
|
+
id: normalizeIdToBytes(entry.id),
|
|
10
|
+
table: entry.table
|
|
11
|
+
};
|
|
12
|
+
else return {
|
|
13
|
+
kind: "range",
|
|
14
|
+
tableId: entry.tableId,
|
|
15
|
+
table: entry.table,
|
|
16
|
+
predicate: entry.predicate,
|
|
17
|
+
columns: entry.columns,
|
|
18
|
+
offset: entry.offset,
|
|
19
|
+
orderBy: entry.orderBy,
|
|
20
|
+
limit: entry.limit
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function mapReadSetToRaw(readSet) {
|
|
24
|
+
return readSet.map(mapReadEntryToRaw);
|
|
25
|
+
}
|
|
26
|
+
function mapWriteEntryToRaw(entry) {
|
|
27
|
+
return {
|
|
28
|
+
table: entry.table,
|
|
29
|
+
tableId: entry.tableId,
|
|
30
|
+
id: normalizeIdToBytes(entry.id),
|
|
31
|
+
op: entry.op,
|
|
32
|
+
data: entry.data,
|
|
33
|
+
prevData: entry.prevData
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function mapWriteSetToRaw(writeSet) {
|
|
37
|
+
return writeSet.map(mapWriteEntryToRaw);
|
|
38
|
+
}
|
|
39
|
+
function evaluatePredicateAgainstRow(rowData, predicate) {
|
|
40
|
+
switch (predicate.kind) {
|
|
41
|
+
case "and": {
|
|
42
|
+
let hasUnknown = false;
|
|
43
|
+
for (const child of predicate.children) {
|
|
44
|
+
const r = evaluatePredicateAgainstRow(rowData, child);
|
|
45
|
+
if (r === "no") return "no";
|
|
46
|
+
if (r === "unknown") hasUnknown = true;
|
|
47
|
+
}
|
|
48
|
+
return hasUnknown ? "unknown" : "yes";
|
|
49
|
+
}
|
|
50
|
+
case "or": {
|
|
51
|
+
let hasUnknown = false;
|
|
52
|
+
for (const child of predicate.children) {
|
|
53
|
+
const r = evaluatePredicateAgainstRow(rowData, child);
|
|
54
|
+
if (r === "yes") return "yes";
|
|
55
|
+
if (r === "unknown") hasUnknown = true;
|
|
56
|
+
}
|
|
57
|
+
return hasUnknown ? "unknown" : "no";
|
|
58
|
+
}
|
|
59
|
+
case "leaf": {
|
|
60
|
+
if (!(predicate.column in rowData)) return "unknown";
|
|
61
|
+
const val = rowData[predicate.column];
|
|
62
|
+
switch (predicate.operator) {
|
|
63
|
+
case "=": return val === predicate.value ? "yes" : "no";
|
|
64
|
+
case "!=": return val !== predicate.value ? "yes" : "no";
|
|
65
|
+
case ">": return val > predicate.value ? "yes" : "no";
|
|
66
|
+
case ">=": return val >= predicate.value ? "yes" : "no";
|
|
67
|
+
case "<": return val < predicate.value ? "yes" : "no";
|
|
68
|
+
case "<=": return val <= predicate.value ? "yes" : "no";
|
|
69
|
+
case "between": return val >= predicate.value && val <= predicate.value2 ? "yes" : "no";
|
|
70
|
+
case "in": return Array.isArray(predicate.value) && predicate.value.includes(val) ? "yes" : "no";
|
|
71
|
+
case "like":
|
|
72
|
+
case "contains":
|
|
73
|
+
case "ilike":
|
|
74
|
+
case "icontains": {
|
|
75
|
+
if (typeof val !== "string" || typeof predicate.value !== "string") return "no";
|
|
76
|
+
const haystack = predicate.operator === "ilike" || predicate.operator === "icontains" ? val.toLowerCase() : val;
|
|
77
|
+
const needle = predicate.operator === "ilike" || predicate.operator === "icontains" ? predicate.value.toLowerCase() : predicate.value;
|
|
78
|
+
const pattern = predicate.operator === "contains" || predicate.operator === "icontains" ? `%${needle}%` : needle;
|
|
79
|
+
return new RegExp("^" + pattern.replace(/%/g, ".*").replace(/_/g, ".") + "$").test(haystack) ? "yes" : "no";
|
|
80
|
+
}
|
|
81
|
+
case "jsonContains": {
|
|
82
|
+
const candidate = decodeJsonLeafValue(predicate);
|
|
83
|
+
const target = predicate.path !== void 0 ? jsonPathExtract(val, predicate.path) : val;
|
|
84
|
+
if (target === void 0) return "no";
|
|
85
|
+
return jsonContains(target, candidate) ? "yes" : "no";
|
|
86
|
+
}
|
|
87
|
+
case "jsonContainedBy": {
|
|
88
|
+
const candidate = decodeJsonLeafValue(predicate);
|
|
89
|
+
const target = predicate.path !== void 0 ? jsonPathExtract(val, predicate.path) : val;
|
|
90
|
+
if (target === void 0) return "no";
|
|
91
|
+
return jsonContains(candidate, target) ? "yes" : "no";
|
|
92
|
+
}
|
|
93
|
+
case "jsonHasKey": {
|
|
94
|
+
if (predicate.mode && Array.isArray(predicate.value)) {
|
|
95
|
+
const paths = predicate.value;
|
|
96
|
+
if (paths.length === 0) return "no";
|
|
97
|
+
return (predicate.mode === "all" ? paths.every((p) => jsonPathExists(val, p)) : paths.some((p) => jsonPathExists(val, p))) ? "yes" : "no";
|
|
98
|
+
}
|
|
99
|
+
if (predicate.raw) return jsonPathExists(val, predicate.value) ? "yes" : "no";
|
|
100
|
+
const key = predicate.value;
|
|
101
|
+
if (val === null || typeof val !== "object") return "no";
|
|
102
|
+
if (Array.isArray(val)) return val.includes(key) ? "yes" : "no";
|
|
103
|
+
return key in val ? "yes" : "no";
|
|
104
|
+
}
|
|
105
|
+
case "jsonArrayContains": {
|
|
106
|
+
const candidate = decodeJsonLeafValue(predicate);
|
|
107
|
+
const target = predicate.path !== void 0 ? jsonPathExtract(val, predicate.path) : val;
|
|
108
|
+
if (!Array.isArray(target)) return "no";
|
|
109
|
+
return target.some((el) => jsonContains(el, candidate)) ? "yes" : "no";
|
|
110
|
+
}
|
|
111
|
+
case "isNull": return val === null ? "yes" : "no";
|
|
112
|
+
case "isNotNull": return val !== null ? "yes" : "no";
|
|
113
|
+
default: return "unknown";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function decodeJsonLeafValue(leaf) {
|
|
119
|
+
if (!leaf.raw) return leaf.value;
|
|
120
|
+
if (typeof leaf.value !== "string") return leaf.value;
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(leaf.value);
|
|
123
|
+
} catch {
|
|
124
|
+
return leaf.value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function parseLogData(log) {
|
|
128
|
+
if (log._parsedData !== void 0) return log._parsedData;
|
|
129
|
+
const data = log.data.length > 0 ? JSON.parse(utf8Decode(log.data)) : null;
|
|
130
|
+
log._parsedData = data;
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
function checkLogsAffectReadSet(logs, rawReadSet) {
|
|
134
|
+
for (const log of logs) for (const rawRead of rawReadSet) {
|
|
135
|
+
if (!bytesEqual(log.tableId, rawRead.tableId)) continue;
|
|
136
|
+
if (rawRead.kind === "point" ? checkPointReadOverlap(log, rawRead) : checkRangeReadOverlap(log, rawRead)) return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Stateless freshness check shared by the subscription worker's `register` and
|
|
142
|
+
* the main server's one-shot query path. Decides whether a query's cached
|
|
143
|
+
* result (described by `metadata`) is still valid at `currentTs`.
|
|
144
|
+
*
|
|
145
|
+
* It is a pure function of Redis (`metadata`, `minRetainedTs`) + Postgres
|
|
146
|
+
* (`getCommitLogsBetweenTs`) + the read set — it holds no worker-local state,
|
|
147
|
+
* so it is safe to run outside the sub-manager.
|
|
148
|
+
*
|
|
149
|
+
* `minRetainedTs` is the retention watermark: the oldest commit-log ts still
|
|
150
|
+
* retained after prune.
|
|
151
|
+
*
|
|
152
|
+
* Two ways a cache entry is deemed stale:
|
|
153
|
+
* 1. **Retention guard** — `metadata.lastSnapshotTs < minRetainedTs` (or the
|
|
154
|
+
* watermark is unknown). The between-ts scan would start below the oldest
|
|
155
|
+
* retained commit log, so it could silently miss pruned commits → we cannot
|
|
156
|
+
* prove freshness and must recompute. This is the case that a one-shot
|
|
157
|
+
* (untracked by any subscription, so nothing holds the retention watermark
|
|
158
|
+
* down) can hit.
|
|
159
|
+
* 2. **Overlap** — a commit in `(lastSnapshotTs, currentTs]` touched the read
|
|
160
|
+
* set.
|
|
161
|
+
*/
|
|
162
|
+
async function evaluateCacheFreshness(db, metadata, currentTs, minRetainedTs) {
|
|
163
|
+
if (!metadata) return {
|
|
164
|
+
fresh: false,
|
|
165
|
+
readSet: [],
|
|
166
|
+
rawReadSet: []
|
|
167
|
+
};
|
|
168
|
+
const readSet = metadata.readSet;
|
|
169
|
+
const rawReadSet = mapReadSetToRaw(readSet);
|
|
170
|
+
if (minRetainedTs === null || metadata.lastSnapshotTs < minRetainedTs) return {
|
|
171
|
+
fresh: false,
|
|
172
|
+
readSet,
|
|
173
|
+
rawReadSet
|
|
174
|
+
};
|
|
175
|
+
const tableIds = Array.from(new Set(readSet.map((r) => r.tableId)));
|
|
176
|
+
if (tableIds.length === 0) return {
|
|
177
|
+
fresh: true,
|
|
178
|
+
readSet,
|
|
179
|
+
rawReadSet
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
fresh: !checkLogsAffectReadSet(await db.getCommitLogsBetweenTs(metadata.lastSnapshotTs + 1n, currentTs + 1n, tableIds), rawReadSet),
|
|
183
|
+
readSet,
|
|
184
|
+
rawReadSet
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function checkPointReadOverlap(log, rawRead) {
|
|
188
|
+
return bytesEqual(log.id, rawRead.id);
|
|
189
|
+
}
|
|
190
|
+
function checkRangeReadOverlap(log, rawRead) {
|
|
191
|
+
const logData = parseLogData(log);
|
|
192
|
+
if (!logData) return true;
|
|
193
|
+
const op = logData.op ?? "update";
|
|
194
|
+
const data = logData.data;
|
|
195
|
+
const prevData = logData.prevData;
|
|
196
|
+
return checkRowWriteAffectsRange(op, prevData, data, rawRead.predicate);
|
|
197
|
+
}
|
|
198
|
+
function checkWriteSetAffectsReadSet(writeSet, rawReadSet) {
|
|
199
|
+
for (const write of writeSet) for (const rawRead of rawReadSet) {
|
|
200
|
+
if (!bytesEqual(write.tableId, rawRead.tableId)) continue;
|
|
201
|
+
if (checkWriteAffectsRawReadEntry(write, rawRead)) return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
function checkWriteAffectsRawReadEntry(write, rawRead) {
|
|
206
|
+
if (rawRead.kind === "point") return bytesEqual(write.id, rawRead.id);
|
|
207
|
+
return checkRowWriteAffectsRange(write.op, write.prevData, write.data, rawRead.predicate);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Evaluate whether a write affects a range subscription, given the row's
|
|
211
|
+
* prior and post state. The write is considered affecting if EITHER side
|
|
212
|
+
* matched the predicate — that catches:
|
|
213
|
+
* - rows entering the result set (prev "no", post "yes")
|
|
214
|
+
* - rows leaving the result set (prev "yes", post "no")
|
|
215
|
+
* - rows changing while still in the set (prev "yes", post "yes")
|
|
216
|
+
* Skipped only if both sides are a definite "no".
|
|
217
|
+
*
|
|
218
|
+
* When prevData is missing (e.g. inserts, or commit logs predating this
|
|
219
|
+
* change), we fall back to conservative "unknown" on that side.
|
|
220
|
+
*/
|
|
221
|
+
function checkRowWriteAffectsRange(op, prevData, data, predicate) {
|
|
222
|
+
const prevVerdict = prevData ? evaluatePredicateAgainstRow(prevData, predicate) : op === "insert" ? "no" : "unknown";
|
|
223
|
+
let postVerdict;
|
|
224
|
+
if (op === "delete") postVerdict = "no";
|
|
225
|
+
else if (data) postVerdict = evaluatePredicateAgainstRow(prevData ? {
|
|
226
|
+
...prevData,
|
|
227
|
+
...data
|
|
228
|
+
} : data, predicate);
|
|
229
|
+
else postVerdict = "unknown";
|
|
230
|
+
return prevVerdict !== "no" || postVerdict !== "no";
|
|
231
|
+
}
|
|
232
|
+
//#endregion
|
|
233
|
+
export { mapWriteSetToRaw as a, mapReadSetToRaw as i, checkWriteSetAffectsReadSet as n, evaluateCacheFreshness as r, checkLogsAffectReadSet as t };
|
|
234
|
+
|
|
235
|
+
//# sourceMappingURL=overlap-checker-BqR9qCfy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlap-checker-BqR9qCfy.js","names":[],"sources":["../src/server/overlap-checker.ts"],"sourcesContent":["import type {\n ReadEntry, CommitLogEntry,\n Predicate, WriteEntry, WriteOp,\n RawReadEntry, RawPointRead, RawRangeRead, LeafPredicate,\n QueryCacheMetadata,\n} from \"../db/types_db\";\nimport { normalizeIdToBytes } from \"../db/types_db\";\nimport { bytesEqual, utf8Decode } from \"../db/bytes\";\nimport { jsonContains, jsonPathExists, jsonPathExtract } from \"../db/query\";\nimport type { Database } from \"../db/db\";\n\nexport interface RawWriteEntry {\n table: string;\n tableId: Uint8Array;\n id: Uint8Array;\n op: WriteOp;\n data?: Record<string, unknown>;\n prevData?: Record<string, unknown>;\n}\n\nexport function mapReadEntryToRaw(entry: ReadEntry): RawReadEntry {\n if (entry.kind === \"point\") {\n return {\n kind: \"point\",\n tableId: entry.tableId,\n id: normalizeIdToBytes(entry.id),\n table: entry.table,\n };\n } else {\n return {\n kind: \"range\",\n tableId: entry.tableId,\n table: entry.table,\n predicate: entry.predicate,\n columns: entry.columns,\n offset: entry.offset,\n orderBy: entry.orderBy,\n limit: entry.limit,\n };\n }\n}\n\nexport function mapReadSetToRaw(readSet: ReadEntry[]): RawReadEntry[] {\n return readSet.map(mapReadEntryToRaw);\n}\n\nexport function mapWriteEntryToRaw(entry: WriteEntry): RawWriteEntry {\n return {\n table: entry.table,\n tableId: entry.tableId,\n id: normalizeIdToBytes(entry.id),\n op: entry.op,\n data: entry.data,\n prevData: entry.prevData,\n };\n}\n\nexport function mapWriteSetToRaw(writeSet: WriteEntry[]): RawWriteEntry[] {\n return writeSet.map(mapWriteEntryToRaw);\n}\n\nexport type OverlapVerdict = \"yes\" | \"no\" | \"unknown\";\n\nexport function evaluatePredicateAgainstRow(\n rowData: Record<string, unknown>,\n predicate: Predicate\n): OverlapVerdict {\n switch (predicate.kind) {\n case \"and\": {\n let hasUnknown = false;\n for (const child of predicate.children) {\n const r = evaluatePredicateAgainstRow(rowData, child);\n if (r === \"no\") return \"no\";\n if (r === \"unknown\") hasUnknown = true;\n }\n return hasUnknown ? \"unknown\" : \"yes\";\n }\n case \"or\": {\n let hasUnknown = false;\n for (const child of predicate.children) {\n const r = evaluatePredicateAgainstRow(rowData, child);\n if (r === \"yes\") return \"yes\";\n if (r === \"unknown\") hasUnknown = true;\n }\n return hasUnknown ? \"unknown\" : \"no\";\n }\n case \"leaf\": {\n if (!(predicate.column in rowData)) return \"unknown\";\n const val = rowData[predicate.column];\n switch (predicate.operator) {\n case \"=\": return val === predicate.value ? \"yes\" : \"no\";\n case \"!=\": return val !== predicate.value ? \"yes\" : \"no\";\n case \">\": return (val as number) > (predicate.value as number) ? \"yes\" : \"no\";\n case \">=\": return (val as number) >= (predicate.value as number) ? \"yes\" : \"no\";\n case \"<\": return (val as number) < (predicate.value as number) ? \"yes\" : \"no\";\n case \"<=\": return (val as number) <= (predicate.value as number) ? \"yes\" : \"no\";\n case \"between\":\n return (val as number) >= (predicate.value as number) &&\n (val as number) <= (predicate.value2 as number) ? \"yes\" : \"no\";\n case \"in\":\n return Array.isArray(predicate.value) && (predicate.value as unknown[]).includes(val) ? \"yes\" : \"no\";\n case \"like\":\n case \"contains\":\n case \"ilike\":\n case \"icontains\": {\n if (typeof val !== \"string\" || typeof predicate.value !== \"string\") return \"no\";\n const haystack = (predicate.operator === \"ilike\" || predicate.operator === \"icontains\")\n ? val.toLowerCase() : val;\n const needle = (predicate.operator === \"ilike\" || predicate.operator === \"icontains\")\n ? (predicate.value as string).toLowerCase() : predicate.value as string;\n const pattern = (predicate.operator === \"contains\" || predicate.operator === \"icontains\")\n ? `%${needle}%` : needle;\n const regex = new RegExp(\"^\" + pattern.replace(/%/g, \".*\").replace(/_/g, \".\") + \"$\");\n return regex.test(haystack) ? \"yes\" : \"no\";\n }\n case \"jsonContains\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (target === undefined) return \"no\";\n return jsonContains(target, candidate) ? \"yes\" : \"no\";\n }\n case \"jsonContainedBy\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (target === undefined) return \"no\";\n return jsonContains(candidate, target) ? \"yes\" : \"no\";\n }\n case \"jsonHasKey\": {\n if (predicate.mode && Array.isArray(predicate.value)) {\n const paths = predicate.value as string[];\n if (paths.length === 0) return \"no\";\n const verdict = predicate.mode === \"all\"\n ? paths.every(p => jsonPathExists(val, p))\n : paths.some(p => jsonPathExists(val, p));\n return verdict ? \"yes\" : \"no\";\n }\n if (predicate.raw) {\n return jsonPathExists(val, predicate.value as string) ? \"yes\" : \"no\";\n }\n const key = predicate.value as string;\n if (val === null || typeof val !== \"object\") return \"no\";\n if (Array.isArray(val)) return (val as unknown[]).includes(key) ? \"yes\" : \"no\";\n return key in (val as object) ? \"yes\" : \"no\";\n }\n case \"jsonArrayContains\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (!Array.isArray(target)) return \"no\";\n return (target as unknown[]).some(el => jsonContains(el, candidate)) ? \"yes\" : \"no\";\n }\n case \"isNull\": return val === null ? \"yes\" : \"no\";\n case \"isNotNull\": return val !== null ? \"yes\" : \"no\";\n default: return \"unknown\";\n }\n }\n }\n}\n\nfunction decodeJsonLeafValue(leaf: LeafPredicate): unknown {\n if (!leaf.raw) return leaf.value;\n if (typeof leaf.value !== \"string\") return leaf.value;\n try { return JSON.parse(leaf.value); } catch { return leaf.value; }\n}\n\nfunction parseLogData(log: CommitLogEntry): Record<string, unknown> | null {\n if (log._parsedData !== undefined) {\n return log._parsedData;\n }\n const data = log.data.length > 0\n ? JSON.parse(utf8Decode(log.data)) as Record<string, unknown>\n : null;\n log._parsedData = data;\n return data;\n}\n\n\nexport function checkLogsAffectReadSet(\n logs: CommitLogEntry[],\n rawReadSet: RawReadEntry[]\n): boolean {\n for (const log of logs) {\n for (const rawRead of rawReadSet) {\n if (!bytesEqual(log.tableId, rawRead.tableId)) continue;\n\n const res = rawRead.kind === \"point\" ?\n checkPointReadOverlap(log, rawRead) :\n checkRangeReadOverlap(log, rawRead);\n\n if (res) {\n return true;\n }\n }\n }\n return false;\n}\n\nexport interface CacheFreshness {\n /** true → the cached data for this key is still valid as of `currentTs`. */\n fresh: boolean;\n /** The cached read set (empty when there is no usable metadata). */\n readSet: ReadEntry[];\n rawReadSet: RawReadEntry[];\n}\n\n/**\n * Stateless freshness check shared by the subscription worker's `register` and\n * the main server's one-shot query path. Decides whether a query's cached\n * result (described by `metadata`) is still valid at `currentTs`.\n *\n * It is a pure function of Redis (`metadata`, `minRetainedTs`) + Postgres\n * (`getCommitLogsBetweenTs`) + the read set — it holds no worker-local state,\n * so it is safe to run outside the sub-manager.\n *\n * `minRetainedTs` is the retention watermark: the oldest commit-log ts still\n * retained after prune.\n *\n * Two ways a cache entry is deemed stale:\n * 1. **Retention guard** — `metadata.lastSnapshotTs < minRetainedTs` (or the\n * watermark is unknown). The between-ts scan would start below the oldest\n * retained commit log, so it could silently miss pruned commits → we cannot\n * prove freshness and must recompute. This is the case that a one-shot\n * (untracked by any subscription, so nothing holds the retention watermark\n * down) can hit.\n * 2. **Overlap** — a commit in `(lastSnapshotTs, currentTs]` touched the read\n * set.\n */\nexport async function evaluateCacheFreshness(\n db: Pick<Database, \"getCommitLogsBetweenTs\">,\n metadata: QueryCacheMetadata | null,\n currentTs: bigint,\n minRetainedTs: bigint | null,\n): Promise<CacheFreshness> {\n if (!metadata) return { fresh: false, readSet: [], rawReadSet: [] };\n\n const readSet = metadata.readSet;\n const rawReadSet = mapReadSetToRaw(readSet);\n\n if (minRetainedTs === null || metadata.lastSnapshotTs < minRetainedTs) {\n return { fresh: false, readSet, rawReadSet };\n }\n\n // A query that read nothing can't be invalidated by any commit — permanently\n // fresh (until TTL eviction). Skip the commit-log scan entirely.\n const tableIds = Array.from(new Set(readSet.map((r) => r.tableId)));\n if (tableIds.length === 0) return { fresh: true, readSet, rawReadSet };\n\n // Scan the window (lastSnapshotTs, currentTs]: commits strictly AFTER the\n // cached snapshot (the data already reflects everything <= lastSnapshotTs) up\n // to and INCLUDING currentTs. getCommitLogsBetweenTs is a half-open [begin,\n // end), so +1n on both ends models that inclusive upper bound. Missing the\n // upper bound would drop a mutation committed at exactly currentTs — and a\n // one-shot query has no push-invalidation backstop, so it would serve stale.\n const logs = await db.getCommitLogsBetweenTs(metadata.lastSnapshotTs + 1n, currentTs + 1n, tableIds);\n const affected = checkLogsAffectReadSet(logs, rawReadSet);\n return { fresh: !affected, readSet, rawReadSet };\n}\n\nfunction checkPointReadOverlap(\n log: CommitLogEntry,\n rawRead: RawPointRead,\n): boolean {\n return bytesEqual(log.id, rawRead.id);\n}\n\nfunction checkRangeReadOverlap(\n log: CommitLogEntry,\n rawRead: RawRangeRead\n): boolean {\n const logData = parseLogData(log);\n\n // No envelope at all → can't reason, must conflict-out.\n if (!logData) { \n return true;\n }\n\n // Commit logs serialize the full WriteEntry (see PgDatabase.insertCommitLogs),\n // so extract the row state from it. Same prev-OR-post check as the\n // in-memory invalidator path.\n const op = (logData.op as WriteOp | undefined) ?? \"update\";\n const data = logData.data as Record<string, unknown> | undefined;\n const prevData = logData.prevData as Record<string, unknown> | undefined;\n\n return checkRowWriteAffectsRange(op, prevData, data, rawRead.predicate);\n}\n\nexport function checkWriteSetAffectsReadSet(\n writeSet: RawWriteEntry[],\n rawReadSet: RawReadEntry[]\n): boolean {\n for (const write of writeSet) {\n for (const rawRead of rawReadSet) {\n if (!bytesEqual(write.tableId, rawRead.tableId)) continue;\n\n if (checkWriteAffectsRawReadEntry(write, rawRead)) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction checkWriteAffectsRawReadEntry(\n write: RawWriteEntry,\n rawRead: RawReadEntry\n): boolean {\n if (rawRead.kind === \"point\") {\n return bytesEqual(write.id, rawRead.id);\n }\n return checkRowWriteAffectsRange(\n write.op,\n write.prevData,\n write.data,\n rawRead.predicate,\n );\n}\n\n/**\n * Evaluate whether a write affects a range subscription, given the row's\n * prior and post state. The write is considered affecting if EITHER side\n * matched the predicate — that catches:\n * - rows entering the result set (prev \"no\", post \"yes\")\n * - rows leaving the result set (prev \"yes\", post \"no\")\n * - rows changing while still in the set (prev \"yes\", post \"yes\")\n * Skipped only if both sides are a definite \"no\".\n *\n * When prevData is missing (e.g. inserts, or commit logs predating this\n * change), we fall back to conservative \"unknown\" on that side.\n */\nfunction checkRowWriteAffectsRange(\n op: WriteOp,\n prevData: Record<string, unknown> | undefined,\n data: Record<string, unknown> | undefined,\n predicate: RawRangeRead[\"predicate\"],\n): boolean {\n const prevVerdict: OverlapVerdict = prevData\n ? evaluatePredicateAgainstRow(prevData, predicate)\n : (op === \"insert\" ? \"no\" : \"unknown\");\n\n let postVerdict: OverlapVerdict;\n if (op === \"delete\") {\n postVerdict = \"no\";\n } else if (data) {\n // Merge over prevData so unchanged columns carry over — otherwise a\n // partial update (e.g. {title: \"x\"}) would be \"unknown\" on every\n // predicate column it doesn't touch.\n const post = prevData ? { ...prevData, ...data } : data;\n postVerdict = evaluatePredicateAgainstRow(post, predicate);\n } else {\n postVerdict = \"unknown\";\n }\n\n return prevVerdict !== \"no\" || postVerdict !== \"no\";\n}"],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { n as utf8Decode, t as bytesEqual } from "./bytes-AuEkyxeU.js";
|
|
2
|
+
import { a as jsonPathExtract, i as jsonPathExists, r as jsonContains } from "./query-Cx7F-ete.js";
|
|
3
|
+
import { C as normalizeIdToBytes } from "./types_db-NY-7zuhP.js";
|
|
4
|
+
//#region src/server/overlap-checker.ts
|
|
5
|
+
function mapReadEntryToRaw(entry) {
|
|
6
|
+
if (entry.kind === "point") return {
|
|
7
|
+
kind: "point",
|
|
8
|
+
tableId: entry.tableId,
|
|
9
|
+
id: normalizeIdToBytes(entry.id),
|
|
10
|
+
table: entry.table
|
|
11
|
+
};
|
|
12
|
+
else return {
|
|
13
|
+
kind: "range",
|
|
14
|
+
tableId: entry.tableId,
|
|
15
|
+
table: entry.table,
|
|
16
|
+
predicate: entry.predicate,
|
|
17
|
+
columns: entry.columns,
|
|
18
|
+
offset: entry.offset,
|
|
19
|
+
orderBy: entry.orderBy,
|
|
20
|
+
limit: entry.limit
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function mapReadSetToRaw(readSet) {
|
|
24
|
+
return readSet.map(mapReadEntryToRaw);
|
|
25
|
+
}
|
|
26
|
+
function mapWriteEntryToRaw(entry) {
|
|
27
|
+
return {
|
|
28
|
+
table: entry.table,
|
|
29
|
+
tableId: entry.tableId,
|
|
30
|
+
id: normalizeIdToBytes(entry.id),
|
|
31
|
+
op: entry.op,
|
|
32
|
+
data: entry.data,
|
|
33
|
+
prevData: entry.prevData
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function mapWriteSetToRaw(writeSet) {
|
|
37
|
+
return writeSet.map(mapWriteEntryToRaw);
|
|
38
|
+
}
|
|
39
|
+
function evaluatePredicateAgainstRow(rowData, predicate) {
|
|
40
|
+
switch (predicate.kind) {
|
|
41
|
+
case "and": {
|
|
42
|
+
let hasUnknown = false;
|
|
43
|
+
for (const child of predicate.children) {
|
|
44
|
+
const r = evaluatePredicateAgainstRow(rowData, child);
|
|
45
|
+
if (r === "no") return "no";
|
|
46
|
+
if (r === "unknown") hasUnknown = true;
|
|
47
|
+
}
|
|
48
|
+
return hasUnknown ? "unknown" : "yes";
|
|
49
|
+
}
|
|
50
|
+
case "or": {
|
|
51
|
+
let hasUnknown = false;
|
|
52
|
+
for (const child of predicate.children) {
|
|
53
|
+
const r = evaluatePredicateAgainstRow(rowData, child);
|
|
54
|
+
if (r === "yes") return "yes";
|
|
55
|
+
if (r === "unknown") hasUnknown = true;
|
|
56
|
+
}
|
|
57
|
+
return hasUnknown ? "unknown" : "no";
|
|
58
|
+
}
|
|
59
|
+
case "leaf": {
|
|
60
|
+
if (!(predicate.column in rowData)) return "unknown";
|
|
61
|
+
const val = rowData[predicate.column];
|
|
62
|
+
switch (predicate.operator) {
|
|
63
|
+
case "=": return val === predicate.value ? "yes" : "no";
|
|
64
|
+
case "!=": return val !== predicate.value ? "yes" : "no";
|
|
65
|
+
case ">": return val > predicate.value ? "yes" : "no";
|
|
66
|
+
case ">=": return val >= predicate.value ? "yes" : "no";
|
|
67
|
+
case "<": return val < predicate.value ? "yes" : "no";
|
|
68
|
+
case "<=": return val <= predicate.value ? "yes" : "no";
|
|
69
|
+
case "between": return val >= predicate.value && val <= predicate.value2 ? "yes" : "no";
|
|
70
|
+
case "in": return Array.isArray(predicate.value) && predicate.value.includes(val) ? "yes" : "no";
|
|
71
|
+
case "like":
|
|
72
|
+
case "contains":
|
|
73
|
+
case "ilike":
|
|
74
|
+
case "icontains": {
|
|
75
|
+
if (typeof val !== "string" || typeof predicate.value !== "string") return "no";
|
|
76
|
+
const haystack = predicate.operator === "ilike" || predicate.operator === "icontains" ? val.toLowerCase() : val;
|
|
77
|
+
const needle = predicate.operator === "ilike" || predicate.operator === "icontains" ? predicate.value.toLowerCase() : predicate.value;
|
|
78
|
+
const pattern = predicate.operator === "contains" || predicate.operator === "icontains" ? `%${needle}%` : needle;
|
|
79
|
+
return new RegExp("^" + pattern.replace(/%/g, ".*").replace(/_/g, ".") + "$").test(haystack) ? "yes" : "no";
|
|
80
|
+
}
|
|
81
|
+
case "jsonContains": {
|
|
82
|
+
const candidate = decodeJsonLeafValue(predicate);
|
|
83
|
+
const target = predicate.path !== void 0 ? jsonPathExtract(val, predicate.path) : val;
|
|
84
|
+
if (target === void 0) return "no";
|
|
85
|
+
return jsonContains(target, candidate) ? "yes" : "no";
|
|
86
|
+
}
|
|
87
|
+
case "jsonContainedBy": {
|
|
88
|
+
const candidate = decodeJsonLeafValue(predicate);
|
|
89
|
+
const target = predicate.path !== void 0 ? jsonPathExtract(val, predicate.path) : val;
|
|
90
|
+
if (target === void 0) return "no";
|
|
91
|
+
return jsonContains(candidate, target) ? "yes" : "no";
|
|
92
|
+
}
|
|
93
|
+
case "jsonHasKey": {
|
|
94
|
+
if (predicate.mode && Array.isArray(predicate.value)) {
|
|
95
|
+
const paths = predicate.value;
|
|
96
|
+
if (paths.length === 0) return "no";
|
|
97
|
+
return (predicate.mode === "all" ? paths.every((p) => jsonPathExists(val, p)) : paths.some((p) => jsonPathExists(val, p))) ? "yes" : "no";
|
|
98
|
+
}
|
|
99
|
+
if (predicate.raw) return jsonPathExists(val, predicate.value) ? "yes" : "no";
|
|
100
|
+
const key = predicate.value;
|
|
101
|
+
if (val === null || typeof val !== "object") return "no";
|
|
102
|
+
if (Array.isArray(val)) return val.includes(key) ? "yes" : "no";
|
|
103
|
+
return key in val ? "yes" : "no";
|
|
104
|
+
}
|
|
105
|
+
case "jsonArrayContains": {
|
|
106
|
+
const candidate = decodeJsonLeafValue(predicate);
|
|
107
|
+
const target = predicate.path !== void 0 ? jsonPathExtract(val, predicate.path) : val;
|
|
108
|
+
if (!Array.isArray(target)) return "no";
|
|
109
|
+
return target.some((el) => jsonContains(el, candidate)) ? "yes" : "no";
|
|
110
|
+
}
|
|
111
|
+
case "isNull": return val === null ? "yes" : "no";
|
|
112
|
+
case "isNotNull": return val !== null ? "yes" : "no";
|
|
113
|
+
default: return "unknown";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function decodeJsonLeafValue(leaf) {
|
|
119
|
+
if (!leaf.raw) return leaf.value;
|
|
120
|
+
if (typeof leaf.value !== "string") return leaf.value;
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(leaf.value);
|
|
123
|
+
} catch {
|
|
124
|
+
return leaf.value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function parseLogData(log) {
|
|
128
|
+
if (log._parsedData !== void 0) return log._parsedData;
|
|
129
|
+
const data = log.data.length > 0 ? JSON.parse(utf8Decode(log.data)) : null;
|
|
130
|
+
log._parsedData = data;
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
function checkLogsAffectReadSet(logs, rawReadSet) {
|
|
134
|
+
for (const log of logs) for (const rawRead of rawReadSet) {
|
|
135
|
+
if (!bytesEqual(log.tableId, rawRead.tableId)) continue;
|
|
136
|
+
if (rawRead.kind === "point" ? checkPointReadOverlap(log, rawRead) : checkRangeReadOverlap(log, rawRead)) return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Stateless freshness check shared by the subscription worker's `register` and
|
|
142
|
+
* the main server's one-shot query path. Decides whether a query's cached
|
|
143
|
+
* result (described by `metadata`) is still valid at `currentTs`.
|
|
144
|
+
*
|
|
145
|
+
* It is a pure function of Redis (`metadata`, `minRetainedTs`) + Postgres
|
|
146
|
+
* (`getCommitLogsBetweenTs`) + the read set — it holds no worker-local state,
|
|
147
|
+
* so it is safe to run outside the sub-manager.
|
|
148
|
+
*
|
|
149
|
+
* `minRetainedTs` is the retention watermark: the oldest commit-log ts still
|
|
150
|
+
* retained after prune.
|
|
151
|
+
*
|
|
152
|
+
* Two ways a cache entry is deemed stale:
|
|
153
|
+
* 1. **Retention guard** — `metadata.lastSnapshotTs < minRetainedTs` (or the
|
|
154
|
+
* watermark is unknown). The between-ts scan would start below the oldest
|
|
155
|
+
* retained commit log, so it could silently miss pruned commits → we cannot
|
|
156
|
+
* prove freshness and must recompute. This is the case that a one-shot
|
|
157
|
+
* (untracked by any subscription, so nothing holds the retention watermark
|
|
158
|
+
* down) can hit.
|
|
159
|
+
* 2. **Overlap** — a commit in `(lastSnapshotTs, currentTs]` touched the read
|
|
160
|
+
* set.
|
|
161
|
+
*/
|
|
162
|
+
async function evaluateCacheFreshness(db, metadata, currentTs, minRetainedTs) {
|
|
163
|
+
if (!metadata) return {
|
|
164
|
+
fresh: false,
|
|
165
|
+
readSet: [],
|
|
166
|
+
rawReadSet: []
|
|
167
|
+
};
|
|
168
|
+
const readSet = metadata.readSet;
|
|
169
|
+
const rawReadSet = mapReadSetToRaw(readSet);
|
|
170
|
+
if (minRetainedTs === null || metadata.lastSnapshotTs < minRetainedTs) return {
|
|
171
|
+
fresh: false,
|
|
172
|
+
readSet,
|
|
173
|
+
rawReadSet
|
|
174
|
+
};
|
|
175
|
+
const tableIds = Array.from(new Set(readSet.map((r) => r.tableId)));
|
|
176
|
+
if (tableIds.length === 0) return {
|
|
177
|
+
fresh: true,
|
|
178
|
+
readSet,
|
|
179
|
+
rawReadSet
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
fresh: !checkLogsAffectReadSet(await db.getCommitLogsBetweenTs(metadata.lastSnapshotTs + 1n, currentTs + 1n, tableIds), rawReadSet),
|
|
183
|
+
readSet,
|
|
184
|
+
rawReadSet
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function checkPointReadOverlap(log, rawRead) {
|
|
188
|
+
return bytesEqual(log.id, rawRead.id);
|
|
189
|
+
}
|
|
190
|
+
function checkRangeReadOverlap(log, rawRead) {
|
|
191
|
+
const logData = parseLogData(log);
|
|
192
|
+
if (!logData) return true;
|
|
193
|
+
const op = logData.op ?? "update";
|
|
194
|
+
const data = logData.data;
|
|
195
|
+
const prevData = logData.prevData;
|
|
196
|
+
return checkRowWriteAffectsRange(op, prevData, data, rawRead.predicate);
|
|
197
|
+
}
|
|
198
|
+
function checkWriteSetAffectsReadSet(writeSet, rawReadSet) {
|
|
199
|
+
for (const write of writeSet) for (const rawRead of rawReadSet) {
|
|
200
|
+
if (!bytesEqual(write.tableId, rawRead.tableId)) continue;
|
|
201
|
+
if (checkWriteAffectsRawReadEntry(write, rawRead)) return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
function checkWriteAffectsRawReadEntry(write, rawRead) {
|
|
206
|
+
if (rawRead.kind === "point") return bytesEqual(write.id, rawRead.id);
|
|
207
|
+
return checkRowWriteAffectsRange(write.op, write.prevData, write.data, rawRead.predicate);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Evaluate whether a write affects a range subscription, given the row's
|
|
211
|
+
* prior and post state. The write is considered affecting if EITHER side
|
|
212
|
+
* matched the predicate — that catches:
|
|
213
|
+
* - rows entering the result set (prev "no", post "yes")
|
|
214
|
+
* - rows leaving the result set (prev "yes", post "no")
|
|
215
|
+
* - rows changing while still in the set (prev "yes", post "yes")
|
|
216
|
+
* Skipped only if both sides are a definite "no".
|
|
217
|
+
*
|
|
218
|
+
* When prevData is missing (e.g. inserts, or commit logs predating this
|
|
219
|
+
* change), we fall back to conservative "unknown" on that side.
|
|
220
|
+
*/
|
|
221
|
+
function checkRowWriteAffectsRange(op, prevData, data, predicate) {
|
|
222
|
+
const prevVerdict = prevData ? evaluatePredicateAgainstRow(prevData, predicate) : op === "insert" ? "no" : "unknown";
|
|
223
|
+
let postVerdict;
|
|
224
|
+
if (op === "delete") postVerdict = "no";
|
|
225
|
+
else if (data) postVerdict = evaluatePredicateAgainstRow(prevData ? {
|
|
226
|
+
...prevData,
|
|
227
|
+
...data
|
|
228
|
+
} : data, predicate);
|
|
229
|
+
else postVerdict = "unknown";
|
|
230
|
+
return prevVerdict !== "no" || postVerdict !== "no";
|
|
231
|
+
}
|
|
232
|
+
//#endregion
|
|
233
|
+
export { mapWriteSetToRaw as a, mapReadSetToRaw as i, checkWriteSetAffectsReadSet as n, evaluateCacheFreshness as r, checkLogsAffectReadSet as t };
|
|
234
|
+
|
|
235
|
+
//# sourceMappingURL=overlap-checker-BsSy8AzX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlap-checker-BsSy8AzX.js","names":[],"sources":["../src/server/overlap-checker.ts"],"sourcesContent":["import type {\n ReadEntry, CommitLogEntry,\n Predicate, WriteEntry, WriteOp,\n RawReadEntry, RawPointRead, RawRangeRead, LeafPredicate,\n QueryCacheMetadata,\n} from \"../db/types_db\";\nimport { normalizeIdToBytes } from \"../db/types_db\";\nimport { bytesEqual, utf8Decode } from \"../db/bytes\";\nimport { jsonContains, jsonPathExists, jsonPathExtract } from \"../db/query\";\nimport type { Database } from \"../db/db\";\n\nexport interface RawWriteEntry {\n table: string;\n tableId: Uint8Array;\n id: Uint8Array;\n op: WriteOp;\n data?: Record<string, unknown>;\n prevData?: Record<string, unknown>;\n}\n\nexport function mapReadEntryToRaw(entry: ReadEntry): RawReadEntry {\n if (entry.kind === \"point\") {\n return {\n kind: \"point\",\n tableId: entry.tableId,\n id: normalizeIdToBytes(entry.id),\n table: entry.table,\n };\n } else {\n return {\n kind: \"range\",\n tableId: entry.tableId,\n table: entry.table,\n predicate: entry.predicate,\n columns: entry.columns,\n offset: entry.offset,\n orderBy: entry.orderBy,\n limit: entry.limit,\n };\n }\n}\n\nexport function mapReadSetToRaw(readSet: ReadEntry[]): RawReadEntry[] {\n return readSet.map(mapReadEntryToRaw);\n}\n\nexport function mapWriteEntryToRaw(entry: WriteEntry): RawWriteEntry {\n return {\n table: entry.table,\n tableId: entry.tableId,\n id: normalizeIdToBytes(entry.id),\n op: entry.op,\n data: entry.data,\n prevData: entry.prevData,\n };\n}\n\nexport function mapWriteSetToRaw(writeSet: WriteEntry[]): RawWriteEntry[] {\n return writeSet.map(mapWriteEntryToRaw);\n}\n\nexport type OverlapVerdict = \"yes\" | \"no\" | \"unknown\";\n\nexport function evaluatePredicateAgainstRow(\n rowData: Record<string, unknown>,\n predicate: Predicate\n): OverlapVerdict {\n switch (predicate.kind) {\n case \"and\": {\n let hasUnknown = false;\n for (const child of predicate.children) {\n const r = evaluatePredicateAgainstRow(rowData, child);\n if (r === \"no\") return \"no\";\n if (r === \"unknown\") hasUnknown = true;\n }\n return hasUnknown ? \"unknown\" : \"yes\";\n }\n case \"or\": {\n let hasUnknown = false;\n for (const child of predicate.children) {\n const r = evaluatePredicateAgainstRow(rowData, child);\n if (r === \"yes\") return \"yes\";\n if (r === \"unknown\") hasUnknown = true;\n }\n return hasUnknown ? \"unknown\" : \"no\";\n }\n case \"leaf\": {\n if (!(predicate.column in rowData)) return \"unknown\";\n const val = rowData[predicate.column];\n switch (predicate.operator) {\n case \"=\": return val === predicate.value ? \"yes\" : \"no\";\n case \"!=\": return val !== predicate.value ? \"yes\" : \"no\";\n case \">\": return (val as number) > (predicate.value as number) ? \"yes\" : \"no\";\n case \">=\": return (val as number) >= (predicate.value as number) ? \"yes\" : \"no\";\n case \"<\": return (val as number) < (predicate.value as number) ? \"yes\" : \"no\";\n case \"<=\": return (val as number) <= (predicate.value as number) ? \"yes\" : \"no\";\n case \"between\":\n return (val as number) >= (predicate.value as number) &&\n (val as number) <= (predicate.value2 as number) ? \"yes\" : \"no\";\n case \"in\":\n return Array.isArray(predicate.value) && (predicate.value as unknown[]).includes(val) ? \"yes\" : \"no\";\n case \"like\":\n case \"contains\":\n case \"ilike\":\n case \"icontains\": {\n if (typeof val !== \"string\" || typeof predicate.value !== \"string\") return \"no\";\n const haystack = (predicate.operator === \"ilike\" || predicate.operator === \"icontains\")\n ? val.toLowerCase() : val;\n const needle = (predicate.operator === \"ilike\" || predicate.operator === \"icontains\")\n ? (predicate.value as string).toLowerCase() : predicate.value as string;\n const pattern = (predicate.operator === \"contains\" || predicate.operator === \"icontains\")\n ? `%${needle}%` : needle;\n const regex = new RegExp(\"^\" + pattern.replace(/%/g, \".*\").replace(/_/g, \".\") + \"$\");\n return regex.test(haystack) ? \"yes\" : \"no\";\n }\n case \"jsonContains\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (target === undefined) return \"no\";\n return jsonContains(target, candidate) ? \"yes\" : \"no\";\n }\n case \"jsonContainedBy\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (target === undefined) return \"no\";\n return jsonContains(candidate, target) ? \"yes\" : \"no\";\n }\n case \"jsonHasKey\": {\n if (predicate.mode && Array.isArray(predicate.value)) {\n const paths = predicate.value as string[];\n if (paths.length === 0) return \"no\";\n const verdict = predicate.mode === \"all\"\n ? paths.every(p => jsonPathExists(val, p))\n : paths.some(p => jsonPathExists(val, p));\n return verdict ? \"yes\" : \"no\";\n }\n if (predicate.raw) {\n return jsonPathExists(val, predicate.value as string) ? \"yes\" : \"no\";\n }\n const key = predicate.value as string;\n if (val === null || typeof val !== \"object\") return \"no\";\n if (Array.isArray(val)) return (val as unknown[]).includes(key) ? \"yes\" : \"no\";\n return key in (val as object) ? \"yes\" : \"no\";\n }\n case \"jsonArrayContains\": {\n const candidate = decodeJsonLeafValue(predicate);\n const target = predicate.path !== undefined ? jsonPathExtract(val, predicate.path) : val;\n if (!Array.isArray(target)) return \"no\";\n return (target as unknown[]).some(el => jsonContains(el, candidate)) ? \"yes\" : \"no\";\n }\n case \"isNull\": return val === null ? \"yes\" : \"no\";\n case \"isNotNull\": return val !== null ? \"yes\" : \"no\";\n default: return \"unknown\";\n }\n }\n }\n}\n\nfunction decodeJsonLeafValue(leaf: LeafPredicate): unknown {\n if (!leaf.raw) return leaf.value;\n if (typeof leaf.value !== \"string\") return leaf.value;\n try { return JSON.parse(leaf.value); } catch { return leaf.value; }\n}\n\nfunction parseLogData(log: CommitLogEntry): Record<string, unknown> | null {\n if (log._parsedData !== undefined) {\n return log._parsedData;\n }\n const data = log.data.length > 0\n ? JSON.parse(utf8Decode(log.data)) as Record<string, unknown>\n : null;\n log._parsedData = data;\n return data;\n}\n\n\nexport function checkLogsAffectReadSet(\n logs: CommitLogEntry[],\n rawReadSet: RawReadEntry[]\n): boolean {\n for (const log of logs) {\n for (const rawRead of rawReadSet) {\n if (!bytesEqual(log.tableId, rawRead.tableId)) continue;\n\n const res = rawRead.kind === \"point\" ?\n checkPointReadOverlap(log, rawRead) :\n checkRangeReadOverlap(log, rawRead);\n\n if (res) {\n return true;\n }\n }\n }\n return false;\n}\n\nexport interface CacheFreshness {\n /** true → the cached data for this key is still valid as of `currentTs`. */\n fresh: boolean;\n /** The cached read set (empty when there is no usable metadata). */\n readSet: ReadEntry[];\n rawReadSet: RawReadEntry[];\n}\n\n/**\n * Stateless freshness check shared by the subscription worker's `register` and\n * the main server's one-shot query path. Decides whether a query's cached\n * result (described by `metadata`) is still valid at `currentTs`.\n *\n * It is a pure function of Redis (`metadata`, `minRetainedTs`) + Postgres\n * (`getCommitLogsBetweenTs`) + the read set — it holds no worker-local state,\n * so it is safe to run outside the sub-manager.\n *\n * `minRetainedTs` is the retention watermark: the oldest commit-log ts still\n * retained after prune.\n *\n * Two ways a cache entry is deemed stale:\n * 1. **Retention guard** — `metadata.lastSnapshotTs < minRetainedTs` (or the\n * watermark is unknown). The between-ts scan would start below the oldest\n * retained commit log, so it could silently miss pruned commits → we cannot\n * prove freshness and must recompute. This is the case that a one-shot\n * (untracked by any subscription, so nothing holds the retention watermark\n * down) can hit.\n * 2. **Overlap** — a commit in `(lastSnapshotTs, currentTs]` touched the read\n * set.\n */\nexport async function evaluateCacheFreshness(\n db: Pick<Database, \"getCommitLogsBetweenTs\">,\n metadata: QueryCacheMetadata | null,\n currentTs: bigint,\n minRetainedTs: bigint | null,\n): Promise<CacheFreshness> {\n if (!metadata) return { fresh: false, readSet: [], rawReadSet: [] };\n\n const readSet = metadata.readSet;\n const rawReadSet = mapReadSetToRaw(readSet);\n\n if (minRetainedTs === null || metadata.lastSnapshotTs < minRetainedTs) {\n return { fresh: false, readSet, rawReadSet };\n }\n\n // A query that read nothing can't be invalidated by any commit — permanently\n // fresh (until TTL eviction). Skip the commit-log scan entirely.\n const tableIds = Array.from(new Set(readSet.map((r) => r.tableId)));\n if (tableIds.length === 0) return { fresh: true, readSet, rawReadSet };\n\n // Scan the window (lastSnapshotTs, currentTs]: commits strictly AFTER the\n // cached snapshot (the data already reflects everything <= lastSnapshotTs) up\n // to and INCLUDING currentTs. getCommitLogsBetweenTs is a half-open [begin,\n // end), so +1n on both ends models that inclusive upper bound. Missing the\n // upper bound would drop a mutation committed at exactly currentTs — and a\n // one-shot query has no push-invalidation backstop, so it would serve stale.\n const logs = await db.getCommitLogsBetweenTs(metadata.lastSnapshotTs + 1n, currentTs + 1n, tableIds);\n const affected = checkLogsAffectReadSet(logs, rawReadSet);\n return { fresh: !affected, readSet, rawReadSet };\n}\n\nfunction checkPointReadOverlap(\n log: CommitLogEntry,\n rawRead: RawPointRead,\n): boolean {\n return bytesEqual(log.id, rawRead.id);\n}\n\nfunction checkRangeReadOverlap(\n log: CommitLogEntry,\n rawRead: RawRangeRead\n): boolean {\n const logData = parseLogData(log);\n\n // No envelope at all → can't reason, must conflict-out.\n if (!logData) { \n return true;\n }\n\n // Commit logs serialize the full WriteEntry (see PgDatabase.insertCommitLogs),\n // so extract the row state from it. Same prev-OR-post check as the\n // in-memory invalidator path.\n const op = (logData.op as WriteOp | undefined) ?? \"update\";\n const data = logData.data as Record<string, unknown> | undefined;\n const prevData = logData.prevData as Record<string, unknown> | undefined;\n\n return checkRowWriteAffectsRange(op, prevData, data, rawRead.predicate);\n}\n\nexport function checkWriteSetAffectsReadSet(\n writeSet: RawWriteEntry[],\n rawReadSet: RawReadEntry[]\n): boolean {\n for (const write of writeSet) {\n for (const rawRead of rawReadSet) {\n if (!bytesEqual(write.tableId, rawRead.tableId)) continue;\n\n if (checkWriteAffectsRawReadEntry(write, rawRead)) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction checkWriteAffectsRawReadEntry(\n write: RawWriteEntry,\n rawRead: RawReadEntry\n): boolean {\n if (rawRead.kind === \"point\") {\n return bytesEqual(write.id, rawRead.id);\n }\n return checkRowWriteAffectsRange(\n write.op,\n write.prevData,\n write.data,\n rawRead.predicate,\n );\n}\n\n/**\n * Evaluate whether a write affects a range subscription, given the row's\n * prior and post state. The write is considered affecting if EITHER side\n * matched the predicate — that catches:\n * - rows entering the result set (prev \"no\", post \"yes\")\n * - rows leaving the result set (prev \"yes\", post \"no\")\n * - rows changing while still in the set (prev \"yes\", post \"yes\")\n * Skipped only if both sides are a definite \"no\".\n *\n * When prevData is missing (e.g. inserts, or commit logs predating this\n * change), we fall back to conservative \"unknown\" on that side.\n */\nfunction checkRowWriteAffectsRange(\n op: WriteOp,\n prevData: Record<string, unknown> | undefined,\n data: Record<string, unknown> | undefined,\n predicate: RawRangeRead[\"predicate\"],\n): boolean {\n const prevVerdict: OverlapVerdict = prevData\n ? evaluatePredicateAgainstRow(prevData, predicate)\n : (op === \"insert\" ? \"no\" : \"unknown\");\n\n let postVerdict: OverlapVerdict;\n if (op === \"delete\") {\n postVerdict = \"no\";\n } else if (data) {\n // Merge over prevData so unchanged columns carry over — otherwise a\n // partial update (e.g. {title: \"x\"}) would be \"unknown\" on every\n // predicate column it doesn't touch.\n const post = prevData ? { ...prevData, ...data } : data;\n postVerdict = evaluatePredicateAgainstRow(post, predicate);\n } else {\n postVerdict = \"unknown\";\n }\n\n return prevVerdict !== \"no\" || postVerdict !== \"no\";\n}"],"mappings":"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"}
|