@noy-db/hub 0.6.0-pre.1 → 0.6.0-pre.11

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.
Files changed (298) hide show
  1. package/codemods/0.4.0-pre.json +699 -0
  2. package/dist/{api-67SBPMMH.js → api-RXUMEKT3.js} +6 -6
  3. package/dist/attestation/index.js +12 -12
  4. package/dist/attestation/index.js.map +1 -1
  5. package/dist/{backup-GAYWQAFN.js → backup-AVAGHAJE.js} +25 -6
  6. package/dist/backup-AVAGHAJE.js.map +1 -0
  7. package/dist/blobs/index.js +5 -5
  8. package/dist/broker/index.js +6 -6
  9. package/dist/{bundle-handle-5OUAI7CV.js → bundle-handle-GFNHT52M.js} +2 -2
  10. package/dist/cargo/index.js +18 -18
  11. package/dist/{chunk-CYQOUTMY.js → chunk-2BMRJ6LQ.js} +2 -2
  12. package/dist/{chunk-HS7HNYBD.js → chunk-2DHSSLOD.js} +6 -6
  13. package/dist/{chunk-JH3OHQM4.js → chunk-2Z3NCLNX.js} +6 -6
  14. package/dist/{chunk-NVU7ZMPN.js → chunk-3IXIU6FP.js} +2 -2
  15. package/dist/{chunk-XBLKM73C.js → chunk-3JVXVJ7L.js} +8 -8
  16. package/dist/{chunk-FY343IZR.js → chunk-3URQC7EH.js} +3 -3
  17. package/dist/{chunk-BPTZ4K5L.js → chunk-4GZ6XOVW.js} +7 -7
  18. package/dist/{chunk-ROWL352P.js → chunk-4I72F5DF.js} +4 -4
  19. package/dist/{chunk-EK7DT5BT.js → chunk-4VUPLMZ6.js} +39 -3
  20. package/dist/chunk-4VUPLMZ6.js.map +1 -0
  21. package/dist/{chunk-LBGPO5RL.js → chunk-52JPHN5N.js} +4 -4
  22. package/dist/{chunk-27TCYH6G.js → chunk-5JY5TSVY.js} +3 -3
  23. package/dist/{chunk-27TCYH6G.js.map → chunk-5JY5TSVY.js.map} +1 -1
  24. package/dist/chunk-5O5HRRTV.js +201 -0
  25. package/dist/chunk-5O5HRRTV.js.map +1 -0
  26. package/dist/{chunk-ZXJXDHNK.js → chunk-5ZKS6ZE2.js} +2 -2
  27. package/dist/{chunk-ZXJXDHNK.js.map → chunk-5ZKS6ZE2.js.map} +1 -1
  28. package/dist/{chunk-MVOWXQHN.js → chunk-6BU3L4UE.js} +2 -2
  29. package/dist/{chunk-3IIPWA75.js → chunk-6BVER473.js} +28 -3
  30. package/dist/chunk-6BVER473.js.map +1 -0
  31. package/dist/{chunk-OSFPNKY4.js → chunk-6FJWSWFT.js} +2 -2
  32. package/dist/chunk-6FJWSWFT.js.map +1 -0
  33. package/dist/{chunk-CJ73OLMW.js → chunk-6HFM7EIT.js} +1 -1
  34. package/dist/chunk-6HFM7EIT.js.map +1 -0
  35. package/dist/{chunk-PGAPBZEK.js → chunk-6NVPFKN3.js} +4 -4
  36. package/dist/{chunk-24UICKTB.js → chunk-6SAFL5YE.js} +2 -2
  37. package/dist/chunk-6SAFL5YE.js.map +1 -0
  38. package/dist/{chunk-NQRJ5AJO.js → chunk-7H73RIE3.js} +2 -2
  39. package/dist/{chunk-NQCNSVGI.js → chunk-7MNBICB5.js} +3 -3
  40. package/dist/{chunk-T4GPR2IR.js → chunk-7TSYBD3E.js} +1 -1
  41. package/dist/chunk-7TSYBD3E.js.map +1 -0
  42. package/dist/{chunk-TLNNHG3A.js → chunk-ADW22QSB.js} +3 -3
  43. package/dist/{chunk-TLNNHG3A.js.map → chunk-ADW22QSB.js.map} +1 -1
  44. package/dist/{chunk-3737A6EA.js → chunk-BGQ63MCT.js} +74 -17
  45. package/dist/chunk-BGQ63MCT.js.map +1 -0
  46. package/dist/{chunk-TFZ7LLOQ.js → chunk-BRVW2GS5.js} +5 -5
  47. package/dist/{chunk-3UK6STWO.js → chunk-CBXHHCZT.js} +3 -3
  48. package/dist/{chunk-MXZ5TSTR.js → chunk-CCVYQFLU.js} +6 -6
  49. package/dist/{chunk-V5C5NNBR.js → chunk-CDB2HVQW.js} +3 -3
  50. package/dist/{chunk-3HYWYLXL.js → chunk-CEJWMBXO.js} +1 -1
  51. package/dist/chunk-CEJWMBXO.js.map +1 -0
  52. package/dist/{chunk-2KIV7BEK.js → chunk-FDX7OWWI.js} +1 -1
  53. package/dist/chunk-FDX7OWWI.js.map +1 -0
  54. package/dist/{chunk-4MQDV3HZ.js → chunk-FFDNKRCY.js} +2 -2
  55. package/dist/{chunk-PP5CO2PR.js → chunk-FMQJZH4R.js} +2 -2
  56. package/dist/{chunk-JSRU5W2M.js → chunk-FZ6NYJ7L.js} +3 -3
  57. package/dist/{chunk-ZGGVOADG.js → chunk-G6S5VVHP.js} +2 -2
  58. package/dist/{chunk-OMSAULZM.js → chunk-GU7A7MQA.js} +9 -9
  59. package/dist/{chunk-E6427JU2.js → chunk-H4YQSDOI.js} +17 -1
  60. package/dist/chunk-H4YQSDOI.js.map +1 -0
  61. package/dist/{chunk-JXIDGFKS.js → chunk-HPLWFNG4.js} +3 -1
  62. package/dist/chunk-HPLWFNG4.js.map +1 -0
  63. package/dist/{chunk-ZWMO5GL5.js → chunk-JJNOLSG5.js} +3 -3
  64. package/dist/{chunk-RLWY6Z44.js → chunk-JRKZTEZZ.js} +62 -6
  65. package/dist/chunk-JRKZTEZZ.js.map +1 -0
  66. package/dist/chunk-N35NC4JH.js +123 -0
  67. package/dist/chunk-N35NC4JH.js.map +1 -0
  68. package/dist/{chunk-SRGJET2F.js → chunk-NJDQ4YSV.js} +2 -2
  69. package/dist/{chunk-JEM2IGS3.js → chunk-NPP5R7FF.js} +5 -5
  70. package/dist/{chunk-DPJTDNUF.js → chunk-O2ERS4UJ.js} +5 -5
  71. package/dist/{chunk-F3GVO44K.js → chunk-PDLLCOGD.js} +3 -3
  72. package/dist/{chunk-VBY6ZLJT.js → chunk-PL2GHSCM.js} +3 -3
  73. package/dist/{chunk-DCMQR46R.js → chunk-PNKHAKQ7.js} +3 -3
  74. package/dist/{chunk-ZK4CO2KZ.js → chunk-Q6TEJXBX.js} +4 -4
  75. package/dist/{chunk-GS5RT2KK.js → chunk-QULVCV3M.js} +2 -2
  76. package/dist/chunk-QULVCV3M.js.map +1 -0
  77. package/dist/{chunk-KZ3KMDN3.js → chunk-RTAMLDWP.js} +6 -2
  78. package/dist/chunk-RTAMLDWP.js.map +1 -0
  79. package/dist/{chunk-MZ5ZFO2D.js → chunk-RXYYFSBB.js} +82 -110
  80. package/dist/chunk-RXYYFSBB.js.map +1 -0
  81. package/dist/{chunk-4VCWI42L.js → chunk-SUFG5E73.js} +2 -2
  82. package/dist/{chunk-SKONR2OL.js → chunk-U5O6GIWZ.js} +2 -2
  83. package/dist/{chunk-3CLZY46Y.js → chunk-UV4O7FAL.js} +296 -110
  84. package/dist/chunk-UV4O7FAL.js.map +1 -0
  85. package/dist/{chunk-HFKGIBMD.js → chunk-VZ3XFUQ7.js} +2 -2
  86. package/dist/{chunk-3EO7RZLJ.js → chunk-W77KZHCW.js} +4 -4
  87. package/dist/{chunk-P7NGLLUF.js → chunk-WASHZV34.js} +2 -2
  88. package/dist/{chunk-XB7LUDKM.js → chunk-WFUIE565.js} +3 -3
  89. package/dist/{chunk-RPXL6KSX.js → chunk-WLZ47GSL.js} +3 -3
  90. package/dist/{chunk-MJWCLSQL.js → chunk-X56RTUK3.js} +3 -3
  91. package/dist/{chunk-MC2LFTQM.js → chunk-XWX7GL56.js} +2 -2
  92. package/dist/{chunk-46V67XP5.js → chunk-YJ6IONGA.js} +2 -2
  93. package/dist/chunk-YJ6IONGA.js.map +1 -0
  94. package/dist/{chunk-24ZLWWEO.js → chunk-YPYHVOXP.js} +1 -1
  95. package/dist/chunk-YPYHVOXP.js.map +1 -0
  96. package/dist/{chunk-2LGDCNFJ.js → chunk-Z45YRGFO.js} +1 -1
  97. package/dist/chunk-Z45YRGFO.js.map +1 -0
  98. package/dist/classified/index.js +3 -3
  99. package/dist/{classified-marker-MH3ON5OL.js → classified-marker-2KJAXRRO.js} +3 -3
  100. package/dist/{collection-facade-AHT7FATG.js → collection-facade-QEJIM6BC.js} +8 -8
  101. package/dist/consent/index.js +4 -4
  102. package/dist/cover/index.js +2 -2
  103. package/dist/custody/index.js +6 -6
  104. package/dist/{dead-filter-U575NGSH.js → dead-filter-DCB62VWW.js} +2 -2
  105. package/dist/{delegation-PDYL6RWT.js → delegation-MO7YZYJS.js} +4 -4
  106. package/dist/derivations/index.js +6 -6
  107. package/dist/{derive-AKT4KU36.js → derive-IUA6JFLB.js} +5 -5
  108. package/dist/directory/index.js +5 -5
  109. package/dist/{dispatch-4XIJSFTT.js → dispatch-23SKSHPD.js} +5 -5
  110. package/dist/{dispatch-N5HGVUGU.js → dispatch-MJ6SHB3Y.js} +9 -9
  111. package/dist/{enclave-OPK24QDR.js → enclave-JU4YIEPB.js} +4 -4
  112. package/dist/{executor-3JZ4POTW.js → executor-FKZURXVV.js} +10 -9
  113. package/dist/{export-accessible-CPTP5STN.js → export-accessible-4S36PQNR.js} +7 -7
  114. package/dist/{extract-partition-TWXPHVU7.js → extract-partition-IO7FDZ2P.js} +10 -10
  115. package/dist/{fanout-sidecar-T2ZGLQFE.js → fanout-sidecar-NNAJQXMX.js} +4 -4
  116. package/dist/forget/index.js +4 -4
  117. package/dist/guards/index.js +1 -1
  118. package/dist/history/index.js +6 -6
  119. package/dist/i18n/index.js +6 -6
  120. package/dist/index.d.ts +3 -2
  121. package/dist/index.js +71 -65
  122. package/dist/index.js.map +1 -1
  123. package/dist/introspection/index.js +6 -6
  124. package/dist/{issue-N74C55X6.js → issue-RIVHSY2S.js} +6 -6
  125. package/dist/kernel/query/builder.d.ts +15 -2
  126. package/dist/kernel/query/join.d.ts +27 -0
  127. package/dist/kernel/secret-mode.d.ts +1 -1
  128. package/dist/kernel/types.d.ts +41 -6
  129. package/dist/kernel/vault.d.ts +19 -15
  130. package/dist/{ledger-DONWFC5M.js → ledger-AETNF4OM.js} +6 -6
  131. package/dist/liberate-GFN4C7RR.js +24 -0
  132. package/dist/{link-set-2TTP4VLO.js → link-set-E5HNY67I.js} +5 -5
  133. package/dist/{managed-secret-EAOYWRYF.js → managed-secret-CWQ6PND5.js} +3 -3
  134. package/dist/materialized-views/index.js +11 -10
  135. package/dist/money/index.js +10 -6
  136. package/dist/{noydb-6W2XGQCS.js → noydb-FYTCH2GD.js} +30 -30
  137. package/dist/overlay-views/index.js +1 -1
  138. package/dist/periods/index.js +17 -7
  139. package/dist/pod/index.js +33 -33
  140. package/dist/policy/index.js +3 -3
  141. package/dist/port/to/index.d.ts +2 -2
  142. package/dist/port/to/locator.d.ts +54 -8
  143. package/dist/port/with/collection-options.d.ts +1 -1
  144. package/dist/portability/index.js +6 -6
  145. package/dist/{post-register-ORQOQJCS.js → post-register-2XSSMGEQ.js} +4 -4
  146. package/dist/query/index.js +2 -2
  147. package/dist/{register-LHETZRJM.js → register-4EFJCVYD.js} +11 -11
  148. package/dist/{registry-JRIN447B.js → registry-4SDHHOJ7.js} +6 -6
  149. package/dist/{registry-AMAZ6QRZ.js → registry-OYTFGWVK.js} +4 -4
  150. package/dist/{request-withdrawal-2DN2RGCO.js → request-withdrawal-KGXGDPVQ.js} +9 -9
  151. package/dist/{reveal-AG4SCHUQ.js → reveal-BW36KCOQ.js} +3 -3
  152. package/dist/{revoke-ZX4NM2CJ.js → revoke-JFN773KF.js} +6 -6
  153. package/dist/sealed-record/index.js +6 -6
  154. package/dist/search/index.js +1 -1
  155. package/dist/{seed-IRCDGIRK.js → seed-GDV5PT4R.js} +6 -6
  156. package/dist/sequence/index.js +4 -4
  157. package/dist/session/index.js +3 -3
  158. package/dist/{signer-7KHD4OL2.js → signer-TA55QB6R.js} +5 -5
  159. package/dist/snapshots/index.js +5 -5
  160. package/dist/{stale-K734VACF.js → stale-4PUTWI2H.js} +5 -5
  161. package/dist/{storage-4H2GF6RQ.js → storage-V7MTFAQJ.js} +5 -5
  162. package/dist/{store-coordination-provider-4SKLSFUG.js → store-coordination-provider-GFCUOBR6.js} +3 -3
  163. package/dist/sync/index.js +5 -5
  164. package/dist/team/index.js +8 -8
  165. package/dist/tiers/index.js +4 -4
  166. package/dist/to/index.js +16 -6
  167. package/dist/to/index.js.map +1 -1
  168. package/dist/via/classified/descriptor.d.ts +1 -1
  169. package/dist/via/money/exact.d.ts +44 -0
  170. package/dist/via/money/index.d.ts +2 -0
  171. package/dist/via/money/normalize.d.ts +20 -0
  172. package/dist/{walk-MQHYNDLZ.js → walk-3ZECBFDA.js} +7 -7
  173. package/dist/with-audit/attestation/index.d.ts +1 -1
  174. package/dist/with-audit/guards/immutable-guard.d.ts +14 -0
  175. package/dist/with-audit/guards/with-guard.d.ts +1 -1
  176. package/dist/with-audit/periods/active.d.ts +28 -1
  177. package/dist/with-audit/periods/index.d.ts +4 -2
  178. package/dist/with-audit/periods/periods.d.ts +160 -5
  179. package/dist/with-audit/periods/strategy.d.ts +13 -4
  180. package/dist/with-audit/periods/vault-facade.d.ts +89 -10
  181. package/dist/with-audit/periods/window.d.ts +10 -0
  182. package/dist/with-cargo/index.d.ts +2 -2
  183. package/dist/with-commit/history/ledger/entry.d.ts +1 -1
  184. package/dist/with-commit/numbering/descriptor.d.ts +1 -1
  185. package/dist/with-formula/derivations/with-derivation.d.ts +1 -1
  186. package/dist/with-formula/materialized-views/types.d.ts +52 -0
  187. package/dist/with-formula/materialized-views/with-materialized-view.d.ts +3 -3
  188. package/dist/with-formula/overlay-views/with-overlayed-view.d.ts +1 -1
  189. package/dist/with-party/custody/index.d.ts +1 -1
  190. package/dist/with-party/directory/user-envelope/index.d.ts +1 -1
  191. package/dist/with-party/directory/user-envelope/storage.d.ts +1 -1
  192. package/dist/with-party/team/echo-ceremony.d.ts +3 -3
  193. package/dist/with-party/team/echo-secret.d.ts +2 -2
  194. package/dist/with-party/team/keyring.d.ts +10 -1
  195. package/dist/with-pod/bundle.d.ts +15 -0
  196. package/dist/with-shape/introspection/index.d.ts +2 -1
  197. package/dist/with-shape/introspection/types.d.ts +1 -1
  198. package/dist/with-shape/persisted-schemas/derive.d.ts +1 -1
  199. package/dist/with-shape/persisted-schemas/index.d.ts +1 -1
  200. package/dist/with-shape/persisted-schemas/types.d.ts +1 -1
  201. package/dist/{withdraw-accessible-JSDQHXHE.js → withdraw-accessible-7PG4DYQK.js} +8 -8
  202. package/package.json +6 -4
  203. package/dist/backup-GAYWQAFN.js.map +0 -1
  204. package/dist/chunk-24UICKTB.js.map +0 -1
  205. package/dist/chunk-24ZLWWEO.js.map +0 -1
  206. package/dist/chunk-2KIV7BEK.js.map +0 -1
  207. package/dist/chunk-2LGDCNFJ.js.map +0 -1
  208. package/dist/chunk-3737A6EA.js.map +0 -1
  209. package/dist/chunk-3CLZY46Y.js.map +0 -1
  210. package/dist/chunk-3HYWYLXL.js.map +0 -1
  211. package/dist/chunk-3IIPWA75.js.map +0 -1
  212. package/dist/chunk-46V67XP5.js.map +0 -1
  213. package/dist/chunk-CJ73OLMW.js.map +0 -1
  214. package/dist/chunk-E6427JU2.js.map +0 -1
  215. package/dist/chunk-EK7DT5BT.js.map +0 -1
  216. package/dist/chunk-GS5RT2KK.js.map +0 -1
  217. package/dist/chunk-JXIDGFKS.js.map +0 -1
  218. package/dist/chunk-KZ3KMDN3.js.map +0 -1
  219. package/dist/chunk-LTU4FLHG.js +0 -131
  220. package/dist/chunk-LTU4FLHG.js.map +0 -1
  221. package/dist/chunk-MZ5ZFO2D.js.map +0 -1
  222. package/dist/chunk-OSFPNKY4.js.map +0 -1
  223. package/dist/chunk-RLWY6Z44.js.map +0 -1
  224. package/dist/chunk-T4GPR2IR.js.map +0 -1
  225. package/dist/liberate-KICI7AJN.js +0 -24
  226. /package/dist/{api-67SBPMMH.js.map → api-RXUMEKT3.js.map} +0 -0
  227. /package/dist/{bundle-handle-5OUAI7CV.js.map → bundle-handle-GFNHT52M.js.map} +0 -0
  228. /package/dist/{chunk-CYQOUTMY.js.map → chunk-2BMRJ6LQ.js.map} +0 -0
  229. /package/dist/{chunk-HS7HNYBD.js.map → chunk-2DHSSLOD.js.map} +0 -0
  230. /package/dist/{chunk-JH3OHQM4.js.map → chunk-2Z3NCLNX.js.map} +0 -0
  231. /package/dist/{chunk-NVU7ZMPN.js.map → chunk-3IXIU6FP.js.map} +0 -0
  232. /package/dist/{chunk-XBLKM73C.js.map → chunk-3JVXVJ7L.js.map} +0 -0
  233. /package/dist/{chunk-FY343IZR.js.map → chunk-3URQC7EH.js.map} +0 -0
  234. /package/dist/{chunk-BPTZ4K5L.js.map → chunk-4GZ6XOVW.js.map} +0 -0
  235. /package/dist/{chunk-ROWL352P.js.map → chunk-4I72F5DF.js.map} +0 -0
  236. /package/dist/{chunk-LBGPO5RL.js.map → chunk-52JPHN5N.js.map} +0 -0
  237. /package/dist/{chunk-MVOWXQHN.js.map → chunk-6BU3L4UE.js.map} +0 -0
  238. /package/dist/{chunk-PGAPBZEK.js.map → chunk-6NVPFKN3.js.map} +0 -0
  239. /package/dist/{chunk-NQRJ5AJO.js.map → chunk-7H73RIE3.js.map} +0 -0
  240. /package/dist/{chunk-NQCNSVGI.js.map → chunk-7MNBICB5.js.map} +0 -0
  241. /package/dist/{chunk-TFZ7LLOQ.js.map → chunk-BRVW2GS5.js.map} +0 -0
  242. /package/dist/{chunk-3UK6STWO.js.map → chunk-CBXHHCZT.js.map} +0 -0
  243. /package/dist/{chunk-MXZ5TSTR.js.map → chunk-CCVYQFLU.js.map} +0 -0
  244. /package/dist/{chunk-V5C5NNBR.js.map → chunk-CDB2HVQW.js.map} +0 -0
  245. /package/dist/{chunk-4MQDV3HZ.js.map → chunk-FFDNKRCY.js.map} +0 -0
  246. /package/dist/{chunk-PP5CO2PR.js.map → chunk-FMQJZH4R.js.map} +0 -0
  247. /package/dist/{chunk-JSRU5W2M.js.map → chunk-FZ6NYJ7L.js.map} +0 -0
  248. /package/dist/{chunk-ZGGVOADG.js.map → chunk-G6S5VVHP.js.map} +0 -0
  249. /package/dist/{chunk-OMSAULZM.js.map → chunk-GU7A7MQA.js.map} +0 -0
  250. /package/dist/{chunk-ZWMO5GL5.js.map → chunk-JJNOLSG5.js.map} +0 -0
  251. /package/dist/{chunk-SRGJET2F.js.map → chunk-NJDQ4YSV.js.map} +0 -0
  252. /package/dist/{chunk-JEM2IGS3.js.map → chunk-NPP5R7FF.js.map} +0 -0
  253. /package/dist/{chunk-DPJTDNUF.js.map → chunk-O2ERS4UJ.js.map} +0 -0
  254. /package/dist/{chunk-F3GVO44K.js.map → chunk-PDLLCOGD.js.map} +0 -0
  255. /package/dist/{chunk-VBY6ZLJT.js.map → chunk-PL2GHSCM.js.map} +0 -0
  256. /package/dist/{chunk-DCMQR46R.js.map → chunk-PNKHAKQ7.js.map} +0 -0
  257. /package/dist/{chunk-ZK4CO2KZ.js.map → chunk-Q6TEJXBX.js.map} +0 -0
  258. /package/dist/{chunk-4VCWI42L.js.map → chunk-SUFG5E73.js.map} +0 -0
  259. /package/dist/{chunk-SKONR2OL.js.map → chunk-U5O6GIWZ.js.map} +0 -0
  260. /package/dist/{chunk-HFKGIBMD.js.map → chunk-VZ3XFUQ7.js.map} +0 -0
  261. /package/dist/{chunk-3EO7RZLJ.js.map → chunk-W77KZHCW.js.map} +0 -0
  262. /package/dist/{chunk-P7NGLLUF.js.map → chunk-WASHZV34.js.map} +0 -0
  263. /package/dist/{chunk-XB7LUDKM.js.map → chunk-WFUIE565.js.map} +0 -0
  264. /package/dist/{chunk-RPXL6KSX.js.map → chunk-WLZ47GSL.js.map} +0 -0
  265. /package/dist/{chunk-MJWCLSQL.js.map → chunk-X56RTUK3.js.map} +0 -0
  266. /package/dist/{chunk-MC2LFTQM.js.map → chunk-XWX7GL56.js.map} +0 -0
  267. /package/dist/{classified-marker-MH3ON5OL.js.map → classified-marker-2KJAXRRO.js.map} +0 -0
  268. /package/dist/{collection-facade-AHT7FATG.js.map → collection-facade-QEJIM6BC.js.map} +0 -0
  269. /package/dist/{dead-filter-U575NGSH.js.map → dead-filter-DCB62VWW.js.map} +0 -0
  270. /package/dist/{delegation-PDYL6RWT.js.map → delegation-MO7YZYJS.js.map} +0 -0
  271. /package/dist/{derive-AKT4KU36.js.map → derive-IUA6JFLB.js.map} +0 -0
  272. /package/dist/{dispatch-4XIJSFTT.js.map → dispatch-23SKSHPD.js.map} +0 -0
  273. /package/dist/{dispatch-N5HGVUGU.js.map → dispatch-MJ6SHB3Y.js.map} +0 -0
  274. /package/dist/{enclave-OPK24QDR.js.map → enclave-JU4YIEPB.js.map} +0 -0
  275. /package/dist/{executor-3JZ4POTW.js.map → executor-FKZURXVV.js.map} +0 -0
  276. /package/dist/{export-accessible-CPTP5STN.js.map → export-accessible-4S36PQNR.js.map} +0 -0
  277. /package/dist/{extract-partition-TWXPHVU7.js.map → extract-partition-IO7FDZ2P.js.map} +0 -0
  278. /package/dist/{fanout-sidecar-T2ZGLQFE.js.map → fanout-sidecar-NNAJQXMX.js.map} +0 -0
  279. /package/dist/{issue-N74C55X6.js.map → issue-RIVHSY2S.js.map} +0 -0
  280. /package/dist/{ledger-DONWFC5M.js.map → ledger-AETNF4OM.js.map} +0 -0
  281. /package/dist/{liberate-KICI7AJN.js.map → liberate-GFN4C7RR.js.map} +0 -0
  282. /package/dist/{link-set-2TTP4VLO.js.map → link-set-E5HNY67I.js.map} +0 -0
  283. /package/dist/{managed-secret-EAOYWRYF.js.map → managed-secret-CWQ6PND5.js.map} +0 -0
  284. /package/dist/{noydb-6W2XGQCS.js.map → noydb-FYTCH2GD.js.map} +0 -0
  285. /package/dist/{post-register-ORQOQJCS.js.map → post-register-2XSSMGEQ.js.map} +0 -0
  286. /package/dist/{register-LHETZRJM.js.map → register-4EFJCVYD.js.map} +0 -0
  287. /package/dist/{registry-AMAZ6QRZ.js.map → registry-4SDHHOJ7.js.map} +0 -0
  288. /package/dist/{registry-JRIN447B.js.map → registry-OYTFGWVK.js.map} +0 -0
  289. /package/dist/{request-withdrawal-2DN2RGCO.js.map → request-withdrawal-KGXGDPVQ.js.map} +0 -0
  290. /package/dist/{reveal-AG4SCHUQ.js.map → reveal-BW36KCOQ.js.map} +0 -0
  291. /package/dist/{revoke-ZX4NM2CJ.js.map → revoke-JFN773KF.js.map} +0 -0
  292. /package/dist/{seed-IRCDGIRK.js.map → seed-GDV5PT4R.js.map} +0 -0
  293. /package/dist/{signer-7KHD4OL2.js.map → signer-TA55QB6R.js.map} +0 -0
  294. /package/dist/{stale-K734VACF.js.map → stale-4PUTWI2H.js.map} +0 -0
  295. /package/dist/{storage-4H2GF6RQ.js.map → storage-V7MTFAQJ.js.map} +0 -0
  296. /package/dist/{store-coordination-provider-4SKLSFUG.js.map → store-coordination-provider-GFCUOBR6.js.map} +0 -0
  297. /package/dist/{walk-MQHYNDLZ.js.map → walk-3ZECBFDA.js.map} +0 -0
  298. /package/dist/{withdraw-accessible-JSDQHXHE.js.map → withdraw-accessible-7PG4DYQK.js.map} +0 -0
@@ -1,29 +1,29 @@
1
1
  import {
2
2
  walkClosure
3
- } from "./chunk-BPTZ4K5L.js";
3
+ } from "./chunk-4GZ6XOVW.js";
4
4
  import {
5
5
  resolveManagedSecret
6
- } from "./chunk-MC2LFTQM.js";
6
+ } from "./chunk-XWX7GL56.js";
7
7
  import {
8
8
  parseExtractedPartitionBody,
9
9
  readPod,
10
10
  readPodHeader
11
- } from "./chunk-EK7DT5BT.js";
11
+ } from "./chunk-4VUPLMZ6.js";
12
12
  import {
13
13
  createOwnerKeyring
14
- } from "./chunk-RLWY6Z44.js";
14
+ } from "./chunk-JRKZTEZZ.js";
15
15
  import {
16
16
  diff
17
17
  } from "./chunk-SVLR4POP.js";
18
18
  import {
19
19
  LedgerStore
20
- } from "./chunk-ZK4CO2KZ.js";
20
+ } from "./chunk-Q6TEJXBX.js";
21
21
  import {
22
22
  LEDGER_COLLECTION
23
23
  } from "./chunk-I7FD46FF.js";
24
24
  import {
25
25
  openEnvelopeJson
26
- } from "./chunk-FY343IZR.js";
26
+ } from "./chunk-3URQC7EH.js";
27
27
  import {
28
28
  base64ToBuffer,
29
29
  wrapKey
@@ -193,7 +193,7 @@ function shortJSON(value) {
193
193
  function withCargo() {
194
194
  return {
195
195
  async extractPartition(vault, opts) {
196
- const { extractPartitionCore } = await import("./extract-partition-TWXPHVU7.js");
196
+ const { extractPartitionCore } = await import("./extract-partition-IO7FDZ2P.js");
197
197
  return extractPartitionCore(vault, opts);
198
198
  }
199
199
  };
@@ -399,7 +399,7 @@ async function createOwnerOnAdoptedPartition(store, vaultName, opts) {
399
399
  }
400
400
  }
401
401
  if (isManaged(opts)) {
402
- const { createNoydb } = await import("./noydb-6W2XGQCS.js");
402
+ const { createNoydb } = await import("./noydb-FYTCH2GD.js");
403
403
  const db = await createNoydb({
404
404
  store,
405
405
  user: userId,
@@ -455,4 +455,4 @@ export {
455
455
  withCargo,
456
456
  describeExtraction
457
457
  };
458
- //# sourceMappingURL=chunk-OMSAULZM.js.map
458
+ //# sourceMappingURL=chunk-GU7A7MQA.js.map
@@ -9,6 +9,21 @@ import {
9
9
  // src/kernel/query/join.ts
10
10
  var DEFAULT_JOIN_MAX_ROWS = 5e4;
11
11
  var JOIN_WARN_FRACTION = 0.8;
12
+ function referencesJoinAlias(clause, aliases) {
13
+ if (clause.type !== "field") return false;
14
+ return aliases.has(clause.field.split(".")[0]);
15
+ }
16
+ function splitAroundJoins(clauses, joins) {
17
+ if (joins.length === 0 || clauses.length === 0) return { preJoin: clauses, postJoin: [] };
18
+ const aliases = new Set(joins.map((leg) => leg.as));
19
+ const preJoin = [];
20
+ const postJoin = [];
21
+ for (const clause of clauses) {
22
+ ;
23
+ (referencesJoinAlias(clause, aliases) ? postJoin : preJoin).push(clause);
24
+ }
25
+ return { preJoin, postJoin };
26
+ }
12
27
  function coerceRefKey(value) {
13
28
  if (value === null || value === void 0) return null;
14
29
  if (typeof value === "string") return value;
@@ -167,7 +182,8 @@ function resetJoinWarnings() {
167
182
 
168
183
  export {
169
184
  DEFAULT_JOIN_MAX_ROWS,
185
+ splitAroundJoins,
170
186
  applyJoins,
171
187
  resetJoinWarnings
172
188
  };
173
- //# sourceMappingURL=chunk-E6427JU2.js.map
189
+ //# sourceMappingURL=chunk-H4YQSDOI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kernel/query/join.ts"],"sourcesContent":["/**\n * Query DSL `.join()` — eager, single-FK, intra-vault joins.\n *\n * resolves a ref()-declared foreign key into an attached\n * right-side record under an alias, using one of two planner paths\n * selected automatically:\n *\n * - **nested-loop** — right-side source exposes `lookupById`, so\n * each left row costs O(1). This is the common path for joins\n * against a Collection, which backs `lookupById` with a Map\n * lookup.\n * - **hash** — right-side has only `snapshot()`. Build a\n * `Map<id, record>` once, probe per left row. Same asymptotic\n * cost for our collections, but the path exists as a fallback\n * for custom QuerySource implementations and as an explicit\n * test-only override via `{ strategy: 'hash' }`.\n *\n * Scope:\n *\n * - Equi-joins on declared `ref()` fields only. Joins on\n * undeclared fields throw at plan time with an actionable error\n * naming the field and collection.\n * - Same-vault only. Cross-vault correlation goes\n * through `queryAcross`; this is an architectural\n * invariant, not a limitation we plan to lift.\n * - Hard row ceiling via `JoinTooLargeError` — default 50k per\n * side, override via `{ maxRows }`. Warns at 80% of the ceiling\n * on the existing warn channel.\n * - Three ref-mode behaviors on dangling refs:\n * strict → `DanglingReferenceError`,\n * warn → attach `null` with a one-shot warning,\n * cascade → attach `null` silently (cascade is a delete-time\n * mode; any dangling refs still present at read time are\n * mid-flight cascades or orphans from earlier, not a DSL error).\n *\n * Partition-awareness seam:\n *\n * Every `JoinLeg` carries a `partitionScope` field that is always\n * `'all'` in. The executor never reads this field.\n * partition-aware joins will start populating it from `where()`\n * predicates on the partition key without changing the planner's\n * external shape — this is the whole reason it exists now.\n *\n * Joins stay OUT of the ledger: reads don't touch `_ledger/`,\n * including joined reads.\n */\n\nimport type { RefDescriptor, RefMode } from '../refs.js'\nimport type { Clause } from './predicate.js'\nimport { readPath } from './predicate.js'\nimport { JoinTooLargeError, DanglingReferenceError } from '../errors.js'\n\n/** Planner strategy for a single join leg. Auto-selected unless overridden. */\nexport type JoinStrategy = 'hash' | 'nested'\n\n/** Default per-side row ceiling before `.join()` throws `JoinTooLargeError`. */\nexport const DEFAULT_JOIN_MAX_ROWS = 50_000\n\n/**\n * Fraction of the row ceiling at which a one-shot warning is emitted.\n * At 80% we warn; at 100% we throw. The warn gives consumers a\n * heads-up before the hard error so they can raise the ceiling or\n * filter further without first hitting a broken query.\n */\nconst JOIN_WARN_FRACTION = 0.8\n\n/**\n * Internal representation of a single join leg in the query plan.\n *\n * This is the primary place where constraint #1 is honored:\n * every leg carries a `partitionScope` field that is always `'all'`\n * in and is never read by the executor. partition-aware\n * joins will start populating it from `where()` predicates on the\n * partition key without changing the planner's external shape.\n */\nexport interface JoinLeg {\n /** Field on the left-side record holding the foreign key value. */\n readonly field: string\n /** Alias key under which the joined right-side record attaches. */\n readonly as: string\n /** Target collection name, resolved from the `ref()` declaration. */\n readonly target: string\n /** Ref mode controlling behavior on dangling refs at read time. */\n readonly mode: RefMode\n /** Manual planner strategy override. `undefined` → auto-select. */\n readonly strategy: JoinStrategy | undefined\n /** Per-side row ceiling override. `undefined` → DEFAULT_JOIN_MAX_ROWS. */\n readonly maxRows: number | undefined\n /**\n * Partition scope for future partition-aware joins. Always `'all'`\n * today — the executor never reads this field. Future versions will\n * populate it from `where()` predicates without breaking the\n * planner's external shape. Do not remove even though it looks\n * unused today — that's the whole point of having it.\n */\n readonly partitionScope: 'all' | readonly string[]\n /**\n * When `true`, this is a dictionary join. The executor\n * resolves the left-field value against the dict snapshot and\n * attaches `{ ...labels, key }` rather than a right-side record.\n * `target` holds the dictionary name (not a collection name).\n */\n readonly isDictJoin?: true\n}\n\n/**\n * Minimal shape of a joinable right-side record source.\n *\n * Collections implement this structurally via their `QuerySource`;\n * sources without `lookupById` force the hash-join fallback. Kept as\n * a thin interface so tests can wire up plain-object sources without\n * pulling in the full Collection class.\n *\n * The optional `subscribe` is used by `Query.live()` to merge\n * right-side change streams into the live re-run trigger. Sources\n * that omit `subscribe` still work for live joins — they just\n * don't drive re-fires when their right side mutates. Collection\n * implements `subscribe` by hooking into the existing per-\n * vault event emitter.\n */\nexport interface JoinableSource {\n snapshot(): readonly unknown[]\n lookupById?(id: string): unknown\n /**\n * Default locale a label-resolving query falls back to when the query\n * itself is locale-less. Set by a `staticDict()`-backed source from its\n * `displayLocale` so `{ by: 'label' }` resolves under a locale-less read.\n * Plain `_dict_*`-backed sources omit it.\n */\n readonly displayLocale?: string\n /**\n * Sync present-for-join dressing (#626 retirement, #650 Task 6) — when\n * present and the query carries a locale, each joined right-side record\n * is passed through this hook (built by the right-side `Collection` from\n * its own i18n-text + lookup-label bindings) BEFORE it is attached under\n * the leg's alias — so a joined `i18nText` field resolves to a string\n * (not a raw `{ locale }` map) and a joined lookup field gains its\n * `<field>Label`. Locale-less queries leave joined fields raw\n * (consistent with a locale-less read). Replaces the old `i18nFields`\n * data field — the join executor no longer resolves i18n locale itself,\n * it just calls this hook.\n */\n readonly presentForJoin?: (record: unknown, locale: string) => unknown\n /**\n * Subscribe to mutations on this source. The callback fires\n * AFTER the underlying record set has been updated. Returns an\n * unsubscribe function. Optional — sources without this method\n * cannot trigger live-join re-fires from their side.\n */\n subscribe?(cb: () => void): () => void\n}\n\n/**\n * Join resolution context attached to a `Query` when it's constructed\n * from a `Collection`. Holds everything the `.join()` method needs to\n * translate a field name into a target collection + ref mode, and\n * everything the executor needs to read the right side.\n *\n * Kept as a structural interface so `Vault` can implement it\n * without `Query` needing to import `Vault` (circular-import\n * avoid). The Collection wires this up in its `query()` method using\n * the `joinResolver` back-reference the Vault passes in.\n */\nexport interface JoinContext {\n /** Name of the left-side (owning) collection. */\n readonly leftCollection: string\n /**\n * The owning collection's default locale. Used to resolve joined\n * i18n fields at the `join` layer when a terminal call doesn't pass an\n * explicit locale — so `openVault({ locale })` flows to joins like it does\n * to `get`/`list`. A per-call `toArray({ locale })` overrides it.\n */\n readonly defaultLocale?: string\n /** Look up a `RefDescriptor` by field name on the left collection. */\n resolveRef(field: string): RefDescriptor | null\n /** Resolve a right-side source by target collection name. */\n resolveSource(collectionName: string): JoinableSource | null\n /**\n * Resolve a dictKey join source. Returns a `JoinableSource`\n * whose snapshot exposes `{ key, ...labels }` records, keyed by the\n * stable dictionary key. `null` when the field is not a dictKey.\n *\n * The source is built from the compartment's in-memory dictionary\n * snapshot — same data as `DictionaryHandle.list()`, O(1) per lookup.\n */\n resolveDictSource?(field: string): JoinableSource | null\n}\n\n/**\n * Does this clause address a field that only exists once join legs are\n * attached — i.e. is its path rooted at a join alias?\n *\n * Only `FieldClause` is inspectable. A `FilterClause` carries an opaque\n * function and a `WherePredicateClause` a named one, so neither can be\n * classified; both stay on the pre-join side, which is where they have\n * always run. See `splitAroundJoins`.\n */\nfunction referencesJoinAlias(clause: Clause, aliases: ReadonlySet<string>): boolean {\n if (clause.type !== 'field') return false\n // `where('client.name', …)` addresses the alias; so does the anti-join\n // form `where('client', '==', null)`, where the path IS the alias.\n return aliases.has(clause.field.split('.')[0]!)\n}\n\n/**\n * Split a plan's clauses into those evaluable before the join legs run and\n * those that need the joined shape (#1030).\n *\n * Join legs attach after `where` so the left set can be narrowed (and\n * index-driven) first. That is the right default, but it silently broke any\n * predicate addressing a joined alias: the field did not exist yet, so\n * `readPath` returned `undefined`, nothing matched, and the query returned an\n * empty result with no error.\n *\n * The split is deliberately narrow. When no clause addresses an alias — every\n * query written against the previous behaviour — `postJoin` is empty and the\n * caller takes its original path unchanged. The reordered pipeline therefore\n * only ever runs for queries that match nothing today.\n *\n * KNOWN RESIDUAL: `.filter(r => r.client?.name === 'Ann')` cannot be\n * classified (the predicate is a closure), so it still runs pre-join and still\n * sees no alias. Prefer `.where()` for anything addressing a joined field.\n *\n * Shared by the eager `Query` and the streaming `ScanBuilder` so the two\n * cannot drift on which side a clause belongs to.\n */\nexport function splitAroundJoins(\n clauses: readonly Clause[],\n joins: readonly JoinLeg[],\n): { readonly preJoin: readonly Clause[]; readonly postJoin: readonly Clause[] } {\n if (joins.length === 0 || clauses.length === 0) return { preJoin: clauses, postJoin: [] }\n const aliases = new Set(joins.map(leg => leg.as))\n const preJoin: Clause[] = []\n const postJoin: Clause[] = []\n for (const clause of clauses) {\n ;(referencesJoinAlias(clause, aliases) ? postJoin : preJoin).push(clause)\n }\n return { preJoin, postJoin }\n}\n\n/**\n * Coerce an unknown FK value into a lookup key string.\n *\n * Legitimate ref values are strings or numbers — the same narrowing\n * the write-time `enforceRefsOnPut` path applies. Anything else\n * (objects, arrays, booleans, null, undefined) is treated as \"no\n * ref\" and returns `null`, so the join attaches `null` instead of\n * running `String({})` and producing `'[object Object]'` as a\n * bucket key. This matches the lint rule guidance and keeps\n * bizarre FK values from producing silently-wrong lookups.\n */\nfunction coerceRefKey(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n return null\n}\n\n/**\n * Warn-channel deduplication for dangling-ref `'warn'` mode. Keyed\n * by `field → target:refId` so the same dangling ref only produces\n * one warning even across many rows or repeated queries.\n */\nconst warnedDanglingKeys = new Set<string>()\nfunction warnOnceDangling(field: string, target: string, refId: string): void {\n const key = `${field}→${target}:${refId}`\n if (warnedDanglingKeys.has(key)) return\n warnedDanglingKeys.add(key)\n console.warn(\n `[noy-db] .join() encountered dangling ref in 'warn' mode: ` +\n `field \"${field}\" → \"${target}:${refId}\" not found. Attaching null.`,\n )\n}\n\n/**\n * Track row-ceiling warnings to fire only once per (target, side).\n * Prevents per-query spam when a consumer is running the same query\n * repeatedly (e.g. in a reactive loop).\n */\nconst warnedCeilingKeys = new Set<string>()\nfunction warnCeilingApproaching(\n target: string,\n side: 'left' | 'right',\n rows: number,\n maxRows: number,\n): void {\n const key = `${target}:${side}`\n if (warnedCeilingKeys.has(key)) return\n warnedCeilingKeys.add(key)\n const pct = Math.round((rows / maxRows) * 100)\n console.warn(\n `[noy-db] .join() ${side} side is at ${pct}% of the ${maxRows}-row ` +\n `ceiling for target \"${target}\" (${rows} rows). Streaming joins over ` +\n `scan() are not yet supported for collections that need to exceed this.`,\n )\n}\n\n/**\n * Apply every join leg in the plan against a base set of left-side\n * rows. Called by the query executor after `where` / `orderBy` /\n * `offset` / `limit` have narrowed the left set.\n *\n * Each leg attaches a `leg.as` field to every row. Returns a new\n * array of plain objects — the original left rows are not mutated\n * (structural sharing is fine for the inner fields, but the\n * top-level object is a fresh clone so consumers can further mutate\n * safely).\n *\n * **Ordering:** joins run AFTER orderBy / limit / offset in v1.\n * This keeps the planner simple and means queries like \"top 10\n * invoices with client\" sort and paginate the left side first, then\n * join. Sorting *by* a joined field is out of scope for — users\n * can post-sort the result array in userland or wait for \n * (multi-FK chaining) which can be layered on top.\n *\n * **Multi-FK chaining:** each leg's `maxRows` is enforced\n * against the current left-row count independently. Because\n * joins are equi-joins on the target's primary key (one-to-one or\n * one-to-null), the left row count is constant across legs — no\n * cartesian blowup. The per-leg left-side check is still necessary\n * so that a later leg with a tighter ceiling correctly fires on a\n * query like `.join('a', { maxRows: 100_000 }).join('b', { maxRows: 50 })`,\n * which should throw on the second leg if the left set exceeds 50.\n */\nexport function applyJoins(\n rows: readonly unknown[],\n joins: readonly JoinLeg[],\n context: JoinContext,\n locale?: string,\n): unknown[] {\n if (joins.length === 0) return [...rows]\n\n let result: unknown[] = [...rows]\n for (const leg of joins) {\n result = applyOneJoin(result, leg, context, locale)\n }\n return result\n}\n\nfunction applyOneJoin(\n leftRows: readonly unknown[],\n leg: JoinLeg,\n context: JoinContext,\n locale?: string,\n): unknown[] {\n // Dict join path — resolve left-field value against the\n // dictionary snapshot and attach { key, ...labels } under leg.as.\n if (leg.isDictJoin) {\n const dictSource = context.resolveDictSource?.(leg.field)\n if (!dictSource) {\n throw new Error(\n `.join() field \"${leg.field}\" on \"${context.leftCollection}\" is declared as a ` +\n `dictKey join but the dict source could not be resolved. ` +\n `Ensure the dictionary has at least one entry.`,\n )\n }\n const out: unknown[] = []\n const snapshot = dictSource.snapshot()\n const dictMap = new Map<string, unknown>()\n for (const entry of snapshot) {\n const k = readPath(entry, 'key')\n if (typeof k === 'string') dictMap.set(k, entry)\n }\n for (const left of leftRows) {\n const rawId = readPath(left, leg.field)\n const key = coerceRefKey(rawId)\n const dictEntry = key === null ? undefined : dictMap.get(key)\n out.push({ ...(left as Record<string, unknown>), [leg.as]: dictEntry ?? null })\n }\n return out\n }\n\n const source = context.resolveSource(leg.target)\n if (!source) {\n throw new Error(\n `.join() cannot resolve target collection \"${leg.target}\" ` +\n `(referenced from field \"${leg.field}\" on \"${context.leftCollection}\"). ` +\n `Make sure the target collection has been opened via vault.collection() ` +\n `at least once before running the query.`,\n )\n }\n\n const maxRows = leg.maxRows ?? DEFAULT_JOIN_MAX_ROWS\n\n // Per-leg left-side ceiling check. In a\n // multi-FK chain, each leg's `maxRows` is enforced independently\n // against the current left-row count, so\n // `.join('a', { maxRows: 100_000 }).join('b', { maxRows: 50 })`\n // correctly throws on the second leg if the left set exceeds 50.\n if (leftRows.length > maxRows) {\n throw new JoinTooLargeError({\n leftRows: leftRows.length,\n rightRows: -1,\n maxRows,\n side: 'left',\n message:\n `.join() left side has ${leftRows.length} rows, exceeding the ${maxRows}-row ` +\n `ceiling for target \"${leg.target}\". Filter the left side further with ` +\n `where()/limit() before joining, or raise the ceiling via { maxRows }. ` +\n `Streaming joins over scan() are not yet supported.`,\n })\n }\n if (leftRows.length > maxRows * JOIN_WARN_FRACTION) {\n warnCeilingApproaching(leg.target, 'left', leftRows.length, maxRows)\n }\n\n const rightSnapshot = source.snapshot()\n if (rightSnapshot.length > maxRows) {\n throw new JoinTooLargeError({\n leftRows: leftRows.length,\n rightRows: rightSnapshot.length,\n maxRows,\n side: 'right',\n message:\n `.join() right side \"${leg.target}\" has ${rightSnapshot.length} rows, ` +\n `exceeding the ${maxRows}-row ceiling. Raise the ceiling via { maxRows } ` +\n `if the data genuinely fits in memory, or track for streaming joins.`,\n })\n }\n if (rightSnapshot.length > maxRows * JOIN_WARN_FRACTION) {\n warnCeilingApproaching(leg.target, 'right', rightSnapshot.length, maxRows)\n }\n\n // `join`-layer dressing (#650 Task 6, #626 retirement). When the query\n // carries a locale (per-call or the owning collection's default) and the\n // right side declares a `presentForJoin` hook, resolve each matched right\n // record through it before it's attached. Locale-less → leave raw.\n const effLocale = locale ?? context.defaultLocale\n const presentResolve: ((right: unknown) => unknown) | undefined =\n effLocale !== undefined && source.presentForJoin !== undefined\n ? (right) =>\n right !== null && typeof right === 'object'\n ? source.presentForJoin!(right, effLocale)\n : right\n : undefined\n\n // Strategy selection: explicit override wins; otherwise prefer\n // nested-loop when the source exposes lookupById (O(1) per row),\n // falling back to hash join when it doesn't.\n const strategy: JoinStrategy =\n leg.strategy ?? (source.lookupById ? 'nested' : 'hash')\n\n if (strategy === 'nested' && source.lookupById) {\n // Bind through an arrow so the `this` context of lookupById\n // doesn't drift — same pattern as the existing candidateRecords\n // helper in builder.ts.\n const lookup = (id: string): unknown => source.lookupById?.(id)\n return nestedLoopJoin(leftRows, leg, lookup, presentResolve)\n }\n return hashJoin(leftRows, leg, rightSnapshot, presentResolve)\n}\n\nfunction nestedLoopJoin(\n leftRows: readonly unknown[],\n leg: JoinLeg,\n lookupById: (id: string) => unknown,\n presentResolve?: (right: unknown) => unknown,\n): unknown[] {\n const out: unknown[] = []\n for (const left of leftRows) {\n const rawId = readPath(left, leg.field)\n const key = coerceRefKey(rawId)\n let right = key === null ? undefined : lookupById(key)\n if (presentResolve && right !== undefined) right = presentResolve(right)\n out.push(attachJoin(left, leg, right, rawId))\n }\n return out\n}\n\nfunction hashJoin(\n leftRows: readonly unknown[],\n leg: JoinLeg,\n rightSnapshot: readonly unknown[],\n presentResolve?: (right: unknown) => unknown,\n): unknown[] {\n // Build the right-side hash once per query execution. We key on\n // the `id` field because ref() always points to a target's primary\n // key — non-equi and non-id joins are out of scope for.\n const rightMap = new Map<string, unknown>()\n for (const record of rightSnapshot) {\n const rawId = readPath(record, 'id')\n const key = coerceRefKey(rawId)\n if (key !== null) {\n rightMap.set(key, record)\n }\n }\n const out: unknown[] = []\n for (const left of leftRows) {\n const rawId = readPath(left, leg.field)\n const key = coerceRefKey(rawId)\n let right = key === null ? undefined : rightMap.get(key)\n if (presentResolve && right !== undefined) right = presentResolve(right)\n out.push(attachJoin(left, leg, right, rawId))\n }\n return out\n}\n\n/**\n * Attach the resolved right-side record (or null) to the left row\n * under the alias, applying ref-mode semantics for the dangling\n * case.\n *\n * A left-side record whose FK field is null/undefined is NOT a\n * dangling ref — it's \"no reference at all\", which is always\n * allowed regardless of mode. This matches the write-time\n * `enforceRefsOnPut` behavior: \"Nullish ref values are allowed —\n * treat them as 'no reference'.\"\n *\n * Only non-null FKs pointing at non-existent targets trigger the\n * mode behavior.\n */\nfunction attachJoin(\n left: unknown,\n leg: JoinLeg,\n right: unknown,\n rawId: unknown,\n): unknown {\n if (left === null || typeof left !== 'object') {\n // Pathological input — return as-is. Shouldn't happen in\n // practice because QuerySource yields objects, but defensive\n // because plan execution is untyped at this layer.\n return left\n }\n const merged: Record<string, unknown> = { ...(left as Record<string, unknown>) }\n\n // \"No ref at all\" — null/undefined FK value, or a non-string/non-\n // number FK that coerceRefKey treated as no-ref. Never throws\n // regardless of mode; matches the write-time policy that nullish\n // refs are allowed.\n const refKey = coerceRefKey(rawId)\n if (right === undefined) {\n if (refKey !== null && leg.mode === 'strict') {\n throw new DanglingReferenceError({\n field: leg.field,\n target: leg.target,\n refId: refKey,\n message:\n `.join() strict dangling: record references \"${leg.target}:${refKey}\" ` +\n `via field \"${leg.field}\", but no such record exists. Use ref() mode 'warn' ` +\n `or 'cascade' if dangling refs are acceptable, or run ` +\n `vault.checkIntegrity() to find and fix the orphans.`,\n })\n }\n if (refKey !== null && leg.mode === 'warn') {\n warnOnceDangling(leg.field, leg.target, refKey)\n }\n // For 'cascade' and null refs we attach null silently. Cascade\n // is a delete-time mode; any dangling refs visible at read time\n // are either mid-flight or pre-existing orphans, not a DSL error.\n merged[leg.as] = null\n } else {\n merged[leg.as] = right\n }\n return merged\n}\n\n/**\n * Test-only: reset the join warning deduplication state between\n * tests. Production code never calls this — the dedup state is\n * intentionally process-scoped so a noisy query doesn't spam the\n * console once per component render.\n */\nexport function resetJoinWarnings(): void {\n warnedDanglingKeys.clear()\n warnedCeilingKeys.clear()\n}\n"],"mappings":"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","names":[]}
@@ -7,6 +7,7 @@ var PERIODS_COLLECTION = "_periods";
7
7
  var PERIOD_FREEZES_COLLECTION = "_period_freezes";
8
8
  var PERIOD_ARCHIVES_COLLECTION = "_period_archives";
9
9
  var PERIOD_TARGET_PURGES_COLLECTION = "_period_target_purges";
10
+ var PERIOD_REOPENS_COLLECTION = "_period_reopens";
10
11
  function periodExclusiveUpperBound(endDate) {
11
12
  const ms = Date.parse(endDate);
12
13
  if (Number.isNaN(ms)) throw new ValidationError(`freezePeriod: unparseable period endDate "${endDate}".`);
@@ -19,6 +20,7 @@ export {
19
20
  PERIOD_FREEZES_COLLECTION,
20
21
  PERIOD_ARCHIVES_COLLECTION,
21
22
  PERIOD_TARGET_PURGES_COLLECTION,
23
+ PERIOD_REOPENS_COLLECTION,
22
24
  periodExclusiveUpperBound
23
25
  };
24
- //# sourceMappingURL=chunk-JXIDGFKS.js.map
26
+ //# sourceMappingURL=chunk-HPLWFNG4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-audit/periods/window.ts"],"sourcesContent":["/**\n * Pure period-window primitives — the reserved collection names and the\n * exclusive-upper-bound computation shared by freeze (#604), archive (#613),\n * target-purge (#615) and the period-scoped sync pull (#807).\n *\n * Deliberately dependency-light (kernel errors only): the sync engine\n * (`with-sync/period-scope.ts`) imports THIS module, not\n * `periods.ts`, so a period-scoped pull never drags the ledger hash-chain\n * machinery (`sha256Hex`/`canonicalJson`) into a bundle that opted out of\n * the periods service.\n */\nimport { ValidationError } from '../../kernel/errors.js'\n\n/** The reserved collection name holding closed-period metadata. */\nexport const PERIODS_COLLECTION = '_periods'\n\n/** Sibling of {@link PERIODS_COLLECTION} holding freeze companions (#604). */\nexport const PERIOD_FREEZES_COLLECTION = '_period_freezes'\n\n/** Sibling of {@link PERIODS_COLLECTION} holding archive companions (#613). */\nexport const PERIOD_ARCHIVES_COLLECTION = '_period_archives'\n\n/** Sibling of {@link PERIODS_COLLECTION} holding target-purge companions (#615). */\nexport const PERIOD_TARGET_PURGES_COLLECTION = '_period_target_purges'\n\n/**\n * Sibling of {@link PERIODS_COLLECTION} holding reopen/reclose companions (#1022).\n *\n * Unlike the freeze / archive / target-purge companions — each a single,\n * idempotent, once-only record — this one holds an APPEND-ONLY EVENT LIST,\n * because reopening is a repeating cycle: a month can be closed, reopened,\n * reclosed, and reopened again. The audit value is the sequence, not the\n * latest state.\n */\nexport const PERIOD_REOPENS_COLLECTION = '_period_reopens'\n\n/**\n * Exclusive upper bound for a period's delete-marker purge window (#604).\n * Markers carry no business date (empty body), only write-time `_ts`, so freeze\n * purges markers with `_ts < bound`, `bound` being the instant just after the\n * period's inclusive `endDate`: a date-only `endDate` seals through end-of-day\n * → next midnight; a full-timestamp `endDate` seals through that instant → +1ms.\n */\nexport function periodExclusiveUpperBound(endDate: string): string {\n const ms = Date.parse(endDate)\n if (Number.isNaN(ms)) throw new ValidationError(`freezePeriod: unparseable period endDate \"${endDate}\".`)\n const dateOnly = /^\\d{4}-\\d{2}-\\d{2}$/.test(endDate)\n return new Date(ms + (dateOnly ? 86_400_000 : 1)).toISOString()\n}\n"],"mappings":";;;;;AAcO,IAAM,qBAAqB;AAG3B,IAAM,4BAA4B;AAGlC,IAAM,6BAA6B;AAGnC,IAAM,kCAAkC;AAWxC,IAAM,4BAA4B;AASlC,SAAS,0BAA0B,SAAyB;AACjE,QAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,MAAI,OAAO,MAAM,EAAE,EAAG,OAAM,IAAI,gBAAgB,6CAA6C,OAAO,IAAI;AACxG,QAAM,WAAW,sBAAsB,KAAK,OAAO;AACnD,SAAO,IAAI,KAAK,MAAM,WAAW,QAAa,EAAE,EAAE,YAAY;AAChE;","names":[]}
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  openEnvelopeJson
3
- } from "./chunk-FY343IZR.js";
3
+ } from "./chunk-3URQC7EH.js";
4
4
  import {
5
5
  encrypt
6
6
  } from "./chunk-VCETL77O.js";
7
7
  import {
8
8
  NOYDB_FORMAT_VERSION
9
- } from "./chunk-24ZLWWEO.js";
9
+ } from "./chunk-YPYHVOXP.js";
10
10
  import {
11
11
  ConflictError
12
12
  } from "./chunk-HNGSV2YC.js";
@@ -56,4 +56,4 @@ export {
56
56
  loadSigner,
57
57
  loadOrCreateSigner
58
58
  };
59
- //# sourceMappingURL=chunk-ZWMO5GL5.js.map
59
+ //# sourceMappingURL=chunk-JJNOLSG5.js.map
@@ -5,7 +5,7 @@ import {
5
5
  readDirectoryConfig,
6
6
  readUserVisibility,
7
7
  saveUserEnvelope
8
- } from "./chunk-27TCYH6G.js";
8
+ } from "./chunk-5JY5TSVY.js";
9
9
  import {
10
10
  base64ToBuffer,
11
11
  bufferToBase64,
@@ -24,7 +24,7 @@ import {
24
24
  import {
25
25
  NOYDB_FORMAT_VERSION,
26
26
  NOYDB_KEYRING_VERSION
27
- } from "./chunk-24ZLWWEO.js";
27
+ } from "./chunk-YPYHVOXP.js";
28
28
  import {
29
29
  DirectoryDisabledError,
30
30
  EchoCeremonyRequiredError,
@@ -449,6 +449,12 @@ async function grant(store, vault, callerKeyring, options) {
449
449
  `Role "${callerKeyring.role}" cannot grant role "${options.role}"`
450
450
  );
451
451
  }
452
+ const secret = options.secret;
453
+ if (typeof secret !== "string" || secret.trim() === "") {
454
+ throw new ValidationError(
455
+ `grant: \`secret\` is required and must be a non-empty string (got ${secret === void 0 ? "undefined" : typeof secret === "string" ? "an empty string" : typeof secret}). The grantee's key is derived from it, so a missing secret produces a keyring slot nobody can unlock. Note the 0.4.0-pre rename: the option is \`secret\`, not \`passphrase\`.`
456
+ );
457
+ }
452
458
  if (options.validateSecret && !options.allowWeakSecret) {
453
459
  assertStrongSecret(options.secret);
454
460
  }
@@ -456,15 +462,26 @@ async function grant(store, vault, callerKeyring, options) {
456
462
  const newSalt = generateSalt();
457
463
  const newKek = await deriveKey(options.secret, newSalt);
458
464
  const granteeMayHoldSecrets = options.role === "owner" || options.role === "admin";
465
+ let mintedForGrant = false;
466
+ for (const collName of Object.keys(permissions)) {
467
+ if (collName === PERMISSION_WILDCARD) continue;
468
+ if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
469
+ if (callerKeyring.deks.has(collName)) continue;
470
+ if (await collectionHasRecords(store, vault, collName)) continue;
471
+ callerKeyring.deks.set(collName, await generateDEK());
472
+ mintedForGrant = true;
473
+ }
474
+ if (mintedForGrant) await persistKeyring(store, vault, callerKeyring);
459
475
  const wrappedDeks = {};
460
476
  for (const collName of Object.keys(permissions)) {
477
+ if (collName === PERMISSION_WILDCARD) continue;
461
478
  if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
462
479
  const dek = callerKeyring.deks.get(collName);
463
480
  if (dek) {
464
481
  wrappedDeks[collName] = await wrapKey(dek, newKek);
465
482
  }
466
483
  }
467
- if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer") {
484
+ if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer" || permissionsAreWildcard(options.permissions)) {
468
485
  for (const [collName, dek] of callerKeyring.deks) {
469
486
  if (collName in wrappedDeks) continue;
470
487
  if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
@@ -817,6 +834,23 @@ async function listUsersWithEnvelopes(store, vault, userEnvelopeDek, callerRole,
817
834
  }
818
835
  return out;
819
836
  }
837
+ async function collectionHasRecords(store, vault, collectionName) {
838
+ const ids = await store.list(vault, collectionName);
839
+ return ids.length > 0;
840
+ }
841
+ async function reloadPersistedDEK(store, vault, keyring, collectionName) {
842
+ if (!keyring.kek) return null;
843
+ const found = await readKeyringFile(store, vault, keyring.userId);
844
+ const wrapped = found?.file.deks[collectionName];
845
+ if (wrapped === void 0) return null;
846
+ try {
847
+ const dek = await unwrapKey(wrapped, keyring.kek);
848
+ keyring.deks.set(collectionName, dek);
849
+ return dek;
850
+ } catch {
851
+ return null;
852
+ }
853
+ }
820
854
  async function ensureCollectionDEK(store, vault, keyring) {
821
855
  const inFlight = /* @__PURE__ */ new Map();
822
856
  return async (collectionName) => {
@@ -825,6 +859,20 @@ async function ensureCollectionDEK(store, vault, keyring) {
825
859
  const pending = inFlight.get(collectionName);
826
860
  if (pending) return pending;
827
861
  const promise = (async () => {
862
+ if (!collectionName.startsWith("_")) {
863
+ if (!hasAccess(keyring, collectionName)) {
864
+ throw new NoAccessError(
865
+ `No access \u2014 user does not have a key for collection "${collectionName}". Grant them this collection in \`permissions\` to give them one.`
866
+ );
867
+ }
868
+ if (await collectionHasRecords(store, vault, collectionName)) {
869
+ const refreshed = await reloadPersistedDEK(store, vault, keyring, collectionName);
870
+ if (refreshed) return refreshed;
871
+ throw new NoAccessError(
872
+ `No access \u2014 user "${keyring.userId}" is entitled to collection "${collectionName}" but holds no key for it, because the collection was created AFTER their grant. A collection DEK can only be wrapped at grant time (wrapping needs the grantee's secret, which the vault never stores), so this cannot be back-filled \u2014 re-grant the user to give them the key.`
873
+ );
874
+ }
875
+ }
828
876
  const dek = await generateDEK();
829
877
  keyring.deks.set(collectionName, dek);
830
878
  await persistKeyring(store, vault, keyring);
@@ -838,14 +886,20 @@ async function ensureCollectionDEK(store, vault, keyring) {
838
886
  }
839
887
  };
840
888
  }
889
+ var PERMISSION_WILDCARD = "*";
890
+ function permissionsAreWildcard(permissions) {
891
+ return permissions !== void 0 && PERMISSION_WILDCARD in permissions;
892
+ }
841
893
  function hasWritePermission(keyring, collectionName) {
842
894
  if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian") return true;
843
895
  if (keyring.role === "viewer" || keyring.role === "client") return false;
896
+ if (keyring.permissions[PERMISSION_WILDCARD] === "rw") return true;
844
897
  return keyring.permissions[collectionName] === "rw";
845
898
  }
846
899
  function hasAccess(keyring, collectionName) {
847
900
  if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian" || keyring.role === "viewer")
848
901
  return true;
902
+ if (PERMISSION_WILDCARD in keyring.permissions) return true;
849
903
  return collectionName in keyring.permissions;
850
904
  }
851
905
  async function persistKeyring(store, vault, keyring) {
@@ -856,6 +910,8 @@ async function persistKeyring(store, vault, keyring) {
856
910
  }
857
911
  const existingFound = await readKeyringFile(store, vault, keyring.userId);
858
912
  const existingEcho = existingFound?.file.echo;
913
+ const existingGrantedBy = existingFound?.file.granted_by;
914
+ const existingCreatedAt = existingFound?.file.created_at;
859
915
  const wrappedDeks = {};
860
916
  for (const [collName, dek] of keyring.deks) {
861
917
  wrappedDeks[collName] = await wrapKey(dek, keyring.kek);
@@ -869,8 +925,8 @@ async function persistKeyring(store, vault, keyring) {
869
925
  permissions: keyring.permissions,
870
926
  deks: wrappedDeks,
871
927
  salt: bufferToBase64(keyring.salt),
872
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
873
- granted_by: keyring.userId,
928
+ created_at: existingCreatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
929
+ granted_by: existingGrantedBy ?? keyring.userId,
874
930
  canary,
875
931
  ...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
876
932
  ...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability },
@@ -968,4 +1024,4 @@ export {
968
1024
  hasImportCapability,
969
1025
  evaluateImportCapability
970
1026
  };
971
- //# sourceMappingURL=chunk-RLWY6Z44.js.map
1027
+ //# sourceMappingURL=chunk-JRKZTEZZ.js.map