@noy-db/hub 0.2.0-pre.26 → 0.2.0-pre.27

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 (147) hide show
  1. package/dist/attestation/index.cjs.map +1 -1
  2. package/dist/attestation/index.d.cts +1 -1
  3. package/dist/attestation/index.d.ts +1 -1
  4. package/dist/attestation/index.js +4 -4
  5. package/dist/blobs/index.cjs.map +1 -1
  6. package/dist/blobs/index.d.cts +3 -3
  7. package/dist/blobs/index.d.ts +3 -3
  8. package/dist/blobs/index.js +4 -4
  9. package/dist/bundle/index.cjs +320 -144
  10. package/dist/bundle/index.cjs.map +1 -1
  11. package/dist/bundle/index.d.cts +3 -3
  12. package/dist/bundle/index.d.ts +3 -3
  13. package/dist/bundle/index.js +8 -8
  14. package/dist/{chunk-HUXDQIVU.js → chunk-2NYVA6FW.js} +2 -2
  15. package/dist/{chunk-OCRDV3NU.js → chunk-5EITJMUJ.js} +3 -3
  16. package/dist/{chunk-EYZJULEN.js → chunk-6H4CAHMQ.js} +2 -2
  17. package/dist/{chunk-VGAN5RLD.js → chunk-6M5JKTUQ.js} +2 -2
  18. package/dist/{chunk-N4EXCKWP.js → chunk-6ZKXFMUG.js} +2 -2
  19. package/dist/{chunk-KJ37E3R5.js → chunk-7DDTFGXY.js} +2 -2
  20. package/dist/{chunk-LR7CODVN.js → chunk-AD6RNBAW.js} +1 -1
  21. package/dist/chunk-AD6RNBAW.js.map +1 -0
  22. package/dist/{chunk-TSUICI5N.js → chunk-BMDVFBCL.js} +2 -2
  23. package/dist/{chunk-FCIZXX56.js → chunk-BOMH3637.js} +2 -2
  24. package/dist/{chunk-RZOGD7IF.js → chunk-CVTPNW2Y.js} +6 -6
  25. package/dist/{chunk-GPZHHTJU.js → chunk-DONPLWRC.js} +2 -2
  26. package/dist/{chunk-GHXOVGTX.js → chunk-HAKEZTA7.js} +3 -3
  27. package/dist/{chunk-QYQRAOEF.js → chunk-I2RX62RX.js} +2 -2
  28. package/dist/{chunk-2RHBFCWQ.js → chunk-IWGVH2RR.js} +3 -3
  29. package/dist/{chunk-Y5CTT6K5.js → chunk-K6PCTYAH.js} +2 -2
  30. package/dist/{chunk-ANLOD6IS.js → chunk-KA5A5CSD.js} +3 -3
  31. package/dist/{chunk-56ENKU46.js → chunk-KSKKLVPA.js} +97 -144
  32. package/dist/chunk-KSKKLVPA.js.map +1 -0
  33. package/dist/{chunk-UNBX2HMA.js → chunk-LXA2E3VI.js} +2 -2
  34. package/dist/{chunk-JJKXJAH2.js → chunk-PFKAT4NT.js} +3 -3
  35. package/dist/{chunk-ZCBJIDT4.js → chunk-RRCRITDM.js} +2 -2
  36. package/dist/{chunk-YP2AYE5W.js → chunk-SID2NJNF.js} +2 -2
  37. package/dist/chunk-Z3BJF7SF.js +220 -0
  38. package/dist/chunk-Z3BJF7SF.js.map +1 -0
  39. package/dist/consent/index.d.cts +2 -2
  40. package/dist/consent/index.d.ts +2 -2
  41. package/dist/{decrypt-partition-CyyJUWLR.d.ts → decrypt-partition-CptDdcCx.d.ts} +1 -1
  42. package/dist/{decrypt-partition-C71vhnND.d.cts → decrypt-partition-DmkeOB4I.d.cts} +1 -1
  43. package/dist/derivations/index.d.cts +3 -3
  44. package/dist/derivations/index.d.ts +3 -3
  45. package/dist/{dev-unlock-BdrE0kbS.d.cts → dev-unlock-DUTLA3Sc.d.cts} +1 -1
  46. package/dist/{dev-unlock-ByBkl99-.d.ts → dev-unlock-Y9znMkQ2.d.ts} +1 -1
  47. package/dist/{fanout-sidecar-ZQT4Y7PF.js → fanout-sidecar-OC4QVTS2.js} +2 -2
  48. package/dist/forget/index.js +2 -2
  49. package/dist/guards/index.d.cts +3 -3
  50. package/dist/guards/index.d.ts +3 -3
  51. package/dist/{hash-CZxVv8RH.d.ts → hash-Btl9IvQS.d.ts} +1 -1
  52. package/dist/{hash-BUkDp_8Q.d.cts → hash-DFK7cGdo.d.cts} +1 -1
  53. package/dist/history/index.cjs.map +1 -1
  54. package/dist/history/index.d.cts +3 -3
  55. package/dist/history/index.d.ts +3 -3
  56. package/dist/history/index.js +3 -3
  57. package/dist/i18n/index.cjs.map +1 -1
  58. package/dist/i18n/index.d.cts +2 -2
  59. package/dist/i18n/index.d.ts +2 -2
  60. package/dist/i18n/index.js +3 -3
  61. package/dist/{index-CBUhOmrM.d.cts → index-CqzZml-D.d.cts} +1 -1
  62. package/dist/{index-DFhKV-6A.d.ts → index-wRwJVVJQ.d.ts} +1 -1
  63. package/dist/index.cjs +316 -140
  64. package/dist/index.cjs.map +1 -1
  65. package/dist/index.d.cts +11 -11
  66. package/dist/index.d.ts +11 -11
  67. package/dist/index.js +19 -18
  68. package/dist/index.js.map +1 -1
  69. package/dist/{issue-LEBPVF3Y.js → issue-ZZ2XPOGP.js} +4 -4
  70. package/dist/kernel/index.cjs +61 -0
  71. package/dist/kernel/index.cjs.map +1 -1
  72. package/dist/kernel/index.d.cts +2 -2
  73. package/dist/kernel/index.d.ts +2 -2
  74. package/dist/kernel/index.js +7 -0
  75. package/dist/{ledger-FLRTSOYH.js → ledger-XQ4KVFQ6.js} +3 -3
  76. package/dist/materialized-views/index.d.cts +3 -3
  77. package/dist/materialized-views/index.d.ts +3 -3
  78. package/dist/{mime-magic-BAhLjkHw.d.cts → mime-magic-Co4Pyj-O.d.cts} +1 -1
  79. package/dist/{mime-magic-C1UbcBxP.d.ts → mime-magic-OiPT1qed.d.ts} +1 -1
  80. package/dist/{noydb-6FA46A4M.js → noydb-EY52NVH4.js} +15 -14
  81. package/dist/overlay-views/index.d.cts +3 -3
  82. package/dist/overlay-views/index.d.ts +3 -3
  83. package/dist/periods/index.cjs.map +1 -1
  84. package/dist/periods/index.d.cts +2 -2
  85. package/dist/periods/index.d.ts +2 -2
  86. package/dist/periods/index.js +3 -3
  87. package/dist/{public-envelope-DBKJEBBF.js → public-envelope-O6X6AUUS.js} +3 -3
  88. package/dist/{revoke-P5D3UTRX.js → revoke-QJ2HUM4W.js} +4 -4
  89. package/dist/session/index.d.cts +3 -3
  90. package/dist/session/index.d.ts +3 -3
  91. package/dist/shadow/index.d.cts +2 -2
  92. package/dist/shadow/index.d.ts +2 -2
  93. package/dist/{signer-NEQPCHMW.js → signer-SCJ6C6HQ.js} +3 -3
  94. package/dist/snapshots/index.d.cts +2 -2
  95. package/dist/snapshots/index.d.ts +2 -2
  96. package/dist/snapshots/index.js +3 -3
  97. package/dist/store/index.d.cts +2 -2
  98. package/dist/store/index.d.ts +2 -2
  99. package/dist/sync/index.cjs.map +1 -1
  100. package/dist/sync/index.d.cts +1 -1
  101. package/dist/sync/index.d.ts +1 -1
  102. package/dist/sync/index.js +2 -2
  103. package/dist/team/index.cjs.map +1 -1
  104. package/dist/team/index.d.cts +2 -2
  105. package/dist/team/index.d.ts +2 -2
  106. package/dist/team/index.js +5 -5
  107. package/dist/{transition-guard-BSLdikC_.d.ts → transition-guard-CJmb8O8q.d.ts} +1 -1
  108. package/dist/{transition-guard-DPs6al8h.d.cts → transition-guard-Dzj68JWC.d.cts} +1 -1
  109. package/dist/tx/index.d.cts +2 -2
  110. package/dist/tx/index.d.ts +2 -2
  111. package/dist/{types-CCq0WHh9.d.ts → types-CkSWJt0H.d.ts} +137 -7
  112. package/dist/{types-BCYvhKzr.d.cts → types-CwrTuYFI.d.cts} +137 -7
  113. package/dist/{with-materialized-view-DiD41wQp.d.ts → with-materialized-view-BVfcPlaE.d.ts} +1 -1
  114. package/dist/{with-materialized-view-CTHe6uh9.d.cts → with-materialized-view-dPG213gd.d.cts} +1 -1
  115. package/dist/{with-overlayed-view-Dlz5hcM8.d.cts → with-overlayed-view-CJ2UDBol.d.cts} +1 -1
  116. package/dist/{with-overlayed-view-DlbsJMhF.d.ts → with-overlayed-view-zYPQzGGh.d.ts} +1 -1
  117. package/dist/{with-rollup-BBWdrCvu.d.cts → with-rollup-B_zbRi3f.d.cts} +1 -1
  118. package/dist/{with-rollup-mT4_CWaU.d.ts → with-rollup-BvaJefEs.d.ts} +1 -1
  119. package/package.json +3 -3
  120. package/dist/chunk-56ENKU46.js.map +0 -1
  121. package/dist/chunk-LR7CODVN.js.map +0 -1
  122. /package/dist/{chunk-HUXDQIVU.js.map → chunk-2NYVA6FW.js.map} +0 -0
  123. /package/dist/{chunk-OCRDV3NU.js.map → chunk-5EITJMUJ.js.map} +0 -0
  124. /package/dist/{chunk-EYZJULEN.js.map → chunk-6H4CAHMQ.js.map} +0 -0
  125. /package/dist/{chunk-VGAN5RLD.js.map → chunk-6M5JKTUQ.js.map} +0 -0
  126. /package/dist/{chunk-N4EXCKWP.js.map → chunk-6ZKXFMUG.js.map} +0 -0
  127. /package/dist/{chunk-KJ37E3R5.js.map → chunk-7DDTFGXY.js.map} +0 -0
  128. /package/dist/{chunk-TSUICI5N.js.map → chunk-BMDVFBCL.js.map} +0 -0
  129. /package/dist/{chunk-FCIZXX56.js.map → chunk-BOMH3637.js.map} +0 -0
  130. /package/dist/{chunk-RZOGD7IF.js.map → chunk-CVTPNW2Y.js.map} +0 -0
  131. /package/dist/{chunk-GPZHHTJU.js.map → chunk-DONPLWRC.js.map} +0 -0
  132. /package/dist/{chunk-GHXOVGTX.js.map → chunk-HAKEZTA7.js.map} +0 -0
  133. /package/dist/{chunk-QYQRAOEF.js.map → chunk-I2RX62RX.js.map} +0 -0
  134. /package/dist/{chunk-2RHBFCWQ.js.map → chunk-IWGVH2RR.js.map} +0 -0
  135. /package/dist/{chunk-Y5CTT6K5.js.map → chunk-K6PCTYAH.js.map} +0 -0
  136. /package/dist/{chunk-ANLOD6IS.js.map → chunk-KA5A5CSD.js.map} +0 -0
  137. /package/dist/{chunk-UNBX2HMA.js.map → chunk-LXA2E3VI.js.map} +0 -0
  138. /package/dist/{chunk-JJKXJAH2.js.map → chunk-PFKAT4NT.js.map} +0 -0
  139. /package/dist/{chunk-ZCBJIDT4.js.map → chunk-RRCRITDM.js.map} +0 -0
  140. /package/dist/{chunk-YP2AYE5W.js.map → chunk-SID2NJNF.js.map} +0 -0
  141. /package/dist/{fanout-sidecar-ZQT4Y7PF.js.map → fanout-sidecar-OC4QVTS2.js.map} +0 -0
  142. /package/dist/{issue-LEBPVF3Y.js.map → issue-ZZ2XPOGP.js.map} +0 -0
  143. /package/dist/{ledger-FLRTSOYH.js.map → ledger-XQ4KVFQ6.js.map} +0 -0
  144. /package/dist/{noydb-6FA46A4M.js.map → noydb-EY52NVH4.js.map} +0 -0
  145. /package/dist/{public-envelope-DBKJEBBF.js.map → public-envelope-O6X6AUUS.js.map} +0 -0
  146. /package/dist/{revoke-P5D3UTRX.js.map → revoke-QJ2HUM4W.js.map} +0 -0
  147. /package/dist/{signer-NEQPCHMW.js.map → signer-SCJ6C6HQ.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  export { I as I18nMap, a as I18nTextDescriptor, b as I18nTextOptions, L as Layer, O as OnMissing, c as OnMissingPolicy, R as ResolveI18nOptions, d as applyI18nLocale, g as getAtPath, i as i18nText, e as isI18nTextDescriptor, r as resolveI18nText, f as resolvePolicy, s as setAtPathInPlace, v as validateI18nTextValue } from '../strategy-D1zjEV3n.cjs';
2
- import { I as I18nStrategy } from '../types-BCYvhKzr.cjs';
3
- export { D as DICT_COLLECTION_PREFIX, a as DictEntry, b as DictKeyDescriptor, c as DictionaryHandle, d as DictionaryOptions, S as ScriptWarning, e as StaticDictDescriptor, f as dictCollectionName, g as dictKey, h as enforceScript, i as inferScripts, j as isDictCollectionName, k as isDictKeyDescriptor, l as isStaticDictDescriptor, s as staticDict } from '../types-BCYvhKzr.cjs';
2
+ import { I as I18nStrategy } from '../types-CwrTuYFI.cjs';
3
+ export { D as DICT_COLLECTION_PREFIX, a as DictEntry, b as DictKeyDescriptor, c as DictionaryHandle, d as DictionaryOptions, S as ScriptWarning, e as StaticDictDescriptor, f as dictCollectionName, g as dictKey, h as enforceScript, i as inferScripts, j as isDictCollectionName, k as isDictKeyDescriptor, l as isStaticDictDescriptor, s as staticDict } from '../types-CwrTuYFI.cjs';
4
4
  export { D as DictKeyInUseError, a as DictKeyMissingError, L as LocaleNotSpecifiedError, M as MissingTranslationError, R as ReservedCollectionNameError, S as ScriptViolationError, b as StaticDictReadonlyError, T as TranslatorNotConfiguredError, U as UnknownDictCodeError } from '../errors-Dwk2k1xY.cjs';
5
5
  import '../lazy-builder-eYZzLEL1.cjs';
6
6
  import '../predicate-BmhBSPCH.cjs';
@@ -1,6 +1,6 @@
1
1
  export { I as I18nMap, a as I18nTextDescriptor, b as I18nTextOptions, L as Layer, O as OnMissing, c as OnMissingPolicy, R as ResolveI18nOptions, d as applyI18nLocale, g as getAtPath, i as i18nText, e as isI18nTextDescriptor, r as resolveI18nText, f as resolvePolicy, s as setAtPathInPlace, v as validateI18nTextValue } from '../strategy-YQ1qJWyq.js';
2
- import { I as I18nStrategy } from '../types-CCq0WHh9.js';
3
- export { D as DICT_COLLECTION_PREFIX, a as DictEntry, b as DictKeyDescriptor, c as DictionaryHandle, d as DictionaryOptions, S as ScriptWarning, e as StaticDictDescriptor, f as dictCollectionName, g as dictKey, h as enforceScript, i as inferScripts, j as isDictCollectionName, k as isDictKeyDescriptor, l as isStaticDictDescriptor, s as staticDict } from '../types-CCq0WHh9.js';
2
+ import { I as I18nStrategy } from '../types-CkSWJt0H.js';
3
+ export { D as DICT_COLLECTION_PREFIX, a as DictEntry, b as DictKeyDescriptor, c as DictionaryHandle, d as DictionaryOptions, S as ScriptWarning, e as StaticDictDescriptor, f as dictCollectionName, g as dictKey, h as enforceScript, i as inferScripts, j as isDictCollectionName, k as isDictKeyDescriptor, l as isStaticDictDescriptor, s as staticDict } from '../types-CkSWJt0H.js';
4
4
  export { D as DictKeyInUseError, a as DictKeyMissingError, L as LocaleNotSpecifiedError, M as MissingTranslationError, R as ReservedCollectionNameError, S as ScriptViolationError, b as StaticDictReadonlyError, T as TranslatorNotConfiguredError, U as UnknownDictCodeError } from '../errors-Dwk2k1xY.js';
5
5
  import '../lazy-builder-ChSqcF5t.js';
6
6
  import '../predicate-BmhBSPCH.js';
@@ -7,7 +7,7 @@ import {
7
7
  isDictKeyDescriptor,
8
8
  isStaticDictDescriptor,
9
9
  staticDict
10
- } from "../chunk-JJKXJAH2.js";
10
+ } from "../chunk-PFKAT4NT.js";
11
11
  import {
12
12
  applyI18nLocale,
13
13
  enforceScript,
@@ -20,9 +20,9 @@ import {
20
20
  setAtPathInPlace,
21
21
  validateI18nTextValue
22
22
  } from "../chunk-KD253AI5.js";
23
- import "../chunk-UNBX2HMA.js";
23
+ import "../chunk-LXA2E3VI.js";
24
24
  import "../chunk-PDVP3C2I.js";
25
- import "../chunk-LR7CODVN.js";
25
+ import "../chunk-AD6RNBAW.js";
26
26
  import "../chunk-7JSP3E67.js";
27
27
  import {
28
28
  DictKeyInUseError,
@@ -1,8 +1,8 @@
1
1
  import './predicate-BmhBSPCH.cjs';
2
2
  import './strategy-D1zjEV3n.cjs';
3
3
  import './ulid-DRH25k3y.cjs';
4
+ import './types-CwrTuYFI.cjs';
4
5
  import './errors-Dwk2k1xY.cjs';
5
- import './types-BCYvhKzr.cjs';
6
6
  import './index-DoxKSsMj.cjs';
7
7
 
8
8
  interface EncryptResult {
@@ -1,8 +1,8 @@
1
1
  import './predicate-BmhBSPCH.js';
2
2
  import './strategy-YQ1qJWyq.js';
3
3
  import './ulid-DRH25k3y.js';
4
+ import './types-CkSWJt0H.js';
4
5
  import './errors-Dwk2k1xY.js';
5
- import './types-CCq0WHh9.js';
6
6
  import './index-LaexBi3v.js';
7
7
 
8
8
  interface EncryptResult {
package/dist/index.cjs CHANGED
@@ -7255,6 +7255,249 @@ var init_peer_recover = __esm({
7255
7255
  }
7256
7256
  });
7257
7257
 
7258
+ // src/coordination/types.ts
7259
+ function isQuorum(writers, generation, excludeWriterId) {
7260
+ return writers.filter((w) => w.writerId !== excludeWriterId).every((w) => w.quiescedAtVersion === generation);
7261
+ }
7262
+ async function runDrainBarrier(provider, o, run) {
7263
+ await provider.setFence(o.vault, { currentSchemaVersion: o.generation, fenceState: "draining" });
7264
+ await o.onFlush();
7265
+ const deadline = o.now() + o.quiesceTimeoutMs;
7266
+ const seeded = await provider.reachableWriters(o.vault, { staleMs: o.staleMs, now: o.now() });
7267
+ if (!isQuorum(seeded, o.generation, o.writerId)) {
7268
+ await new Promise((resolve, reject) => {
7269
+ let settled = false;
7270
+ const finish = (fn) => {
7271
+ if (!settled) {
7272
+ settled = true;
7273
+ unsub();
7274
+ fn();
7275
+ }
7276
+ };
7277
+ const unsub = provider.observePresence(o.vault, (writers) => {
7278
+ if (isQuorum(writers, o.generation, o.writerId)) finish(resolve);
7279
+ });
7280
+ const tick = async () => {
7281
+ if (settled) return;
7282
+ if (o.now() >= deadline) {
7283
+ finish(
7284
+ () => reject(
7285
+ new QuiesceTimeoutError(
7286
+ `Cutover of vault "${o.vault}" to generation ${o.generation} timed out after ${o.quiesceTimeoutMs}ms waiting for active writers to quiesce.`
7287
+ )
7288
+ )
7289
+ );
7290
+ return;
7291
+ }
7292
+ if (o.onPoll) await o.onPoll();
7293
+ const w = await provider.reachableWriters(o.vault, { staleMs: o.staleMs, now: o.now() });
7294
+ if (isQuorum(w, o.generation, o.writerId)) finish(resolve);
7295
+ else setTimeout(() => void tick(), 25);
7296
+ };
7297
+ void tick();
7298
+ });
7299
+ }
7300
+ await run();
7301
+ }
7302
+ var init_types4 = __esm({
7303
+ "src/coordination/types.ts"() {
7304
+ "use strict";
7305
+ init_errors();
7306
+ }
7307
+ });
7308
+
7309
+ // src/schema-update/fence.ts
7310
+ async function loadFence(store, vault) {
7311
+ const envelope = await store.get(vault, META_COLLECTION3, FENCE_RECORD_ID);
7312
+ if (!envelope) return DEFAULT_FENCE;
7313
+ try {
7314
+ const parsed = JSON.parse(envelope._data);
7315
+ if (!isFenceDoc(parsed)) return DEFAULT_FENCE;
7316
+ return parsed;
7317
+ } catch {
7318
+ return DEFAULT_FENCE;
7319
+ }
7320
+ }
7321
+ async function saveFence(store, vault, fence) {
7322
+ const envelope = {
7323
+ _noydb: NOYDB_FORMAT_VERSION,
7324
+ _v: 1,
7325
+ _ts: (/* @__PURE__ */ new Date()).toISOString(),
7326
+ _iv: "",
7327
+ _data: JSON.stringify(fence)
7328
+ };
7329
+ await store.put(vault, META_COLLECTION3, FENCE_RECORD_ID, envelope);
7330
+ }
7331
+ function isFenceDoc(x) {
7332
+ if (x === null || typeof x !== "object") return false;
7333
+ const o = x;
7334
+ return typeof o["currentSchemaVersion"] === "number" && (o["fenceState"] === "normal" || o["fenceState"] === "draining" || o["fenceState"] === "migrating" || o["fenceState"] === "complete");
7335
+ }
7336
+ var FENCE_RECORD_ID, META_COLLECTION3, DEFAULT_FENCE;
7337
+ var init_fence = __esm({
7338
+ "src/schema-update/fence.ts"() {
7339
+ "use strict";
7340
+ init_types();
7341
+ FENCE_RECORD_ID = "schema-fence";
7342
+ META_COLLECTION3 = "_meta";
7343
+ DEFAULT_FENCE = { currentSchemaVersion: 0, fenceState: "normal" };
7344
+ }
7345
+ });
7346
+
7347
+ // src/schema-update/client-registry.ts
7348
+ async function writeClientDoc(store, vault, clientId, doc) {
7349
+ const envelope = {
7350
+ _noydb: NOYDB_FORMAT_VERSION,
7351
+ _v: 1,
7352
+ _ts: (/* @__PURE__ */ new Date()).toISOString(),
7353
+ _iv: "",
7354
+ _data: JSON.stringify({ clientId, ...doc })
7355
+ };
7356
+ await store.put(vault, META_COLLECTION4, `${CLIENT_PREFIX}${clientId}`, envelope);
7357
+ }
7358
+ async function listClientDocs(store, vault) {
7359
+ const ids = await store.list(vault, META_COLLECTION4);
7360
+ const out = [];
7361
+ for (const id of ids) {
7362
+ if (!id.startsWith(CLIENT_PREFIX)) continue;
7363
+ const env = await store.get(vault, META_COLLECTION4, id);
7364
+ if (!env) continue;
7365
+ try {
7366
+ const parsed = JSON.parse(env._data);
7367
+ if (isClientDoc(parsed)) out.push(parsed);
7368
+ } catch {
7369
+ }
7370
+ }
7371
+ return out;
7372
+ }
7373
+ function isClientDoc(x) {
7374
+ if (x === null || typeof x !== "object") return false;
7375
+ const o = x;
7376
+ return typeof o["clientId"] === "string" && typeof o["lastSeen"] === "number" && (o["quiescedAtVersion"] === null || typeof o["quiescedAtVersion"] === "number") && (o["sessionId"] === void 0 || typeof o["sessionId"] === "string");
7377
+ }
7378
+ var META_COLLECTION4, CLIENT_PREFIX;
7379
+ var init_client_registry = __esm({
7380
+ "src/schema-update/client-registry.ts"() {
7381
+ "use strict";
7382
+ init_types();
7383
+ META_COLLECTION4 = "_meta";
7384
+ CLIENT_PREFIX = "schema-fence:client:";
7385
+ }
7386
+ });
7387
+
7388
+ // src/coordination/store-provider.ts
7389
+ function toPresence(doc) {
7390
+ return {
7391
+ writerId: doc.clientId,
7392
+ // Pre-#469 docs (and explicit empty) have no session — fall back to the
7393
+ // writerId so every presence is session-addressable.
7394
+ sessionId: doc.sessionId && doc.sessionId.length > 0 ? doc.sessionId : doc.clientId,
7395
+ lastSeen: doc.lastSeen,
7396
+ quiescedAtVersion: doc.quiescedAtVersion
7397
+ };
7398
+ }
7399
+ function fenceEqual(a, b) {
7400
+ return a.currentSchemaVersion === b.currentSchemaVersion && a.fenceState === b.fenceState;
7401
+ }
7402
+ function presenceListEqual(a, b) {
7403
+ if (a.length !== b.length) return false;
7404
+ const key = (w) => `${w.writerId}\0${w.sessionId}\0${w.lastSeen}\0${String(w.quiescedAtVersion)}`;
7405
+ const bk = new Set(b.map(key));
7406
+ return a.every((w) => bk.has(key(w)));
7407
+ }
7408
+ function unref(timer) {
7409
+ const t = timer;
7410
+ if (typeof t.unref === "function") t.unref();
7411
+ }
7412
+ var DEFAULT_POLL_INTERVAL_MS, StoreCoordinationProvider;
7413
+ var init_store_provider = __esm({
7414
+ "src/coordination/store-provider.ts"() {
7415
+ "use strict";
7416
+ init_fence();
7417
+ init_client_registry();
7418
+ DEFAULT_POLL_INTERVAL_MS = 50;
7419
+ StoreCoordinationProvider = class {
7420
+ #store;
7421
+ #pollIntervalMs;
7422
+ constructor(store, opts) {
7423
+ this.#store = store;
7424
+ this.#pollIntervalMs = opts?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
7425
+ }
7426
+ async setFence(vault, fence) {
7427
+ await saveFence(this.#store, vault, fence);
7428
+ }
7429
+ async readFence(vault) {
7430
+ return loadFence(this.#store, vault);
7431
+ }
7432
+ observeFence(vault, onChange) {
7433
+ let last = null;
7434
+ let busy = false;
7435
+ const poll = async () => {
7436
+ if (busy) return;
7437
+ busy = true;
7438
+ try {
7439
+ const fence = await loadFence(this.#store, vault);
7440
+ if (last === null || !fenceEqual(last, fence)) {
7441
+ last = fence;
7442
+ onChange(fence);
7443
+ }
7444
+ } catch {
7445
+ } finally {
7446
+ busy = false;
7447
+ }
7448
+ };
7449
+ void poll();
7450
+ const timer = setInterval(() => void poll(), this.#pollIntervalMs);
7451
+ unref(timer);
7452
+ return () => clearInterval(timer);
7453
+ }
7454
+ async reportPresence(vault, p) {
7455
+ await writeClientDoc(this.#store, vault, p.writerId, {
7456
+ lastSeen: p.lastSeen,
7457
+ quiescedAtVersion: p.quiescedAtVersion,
7458
+ sessionId: p.sessionId
7459
+ });
7460
+ }
7461
+ observePresence(vault, onChange) {
7462
+ let last = null;
7463
+ let busy = false;
7464
+ const poll = async () => {
7465
+ if (busy) return;
7466
+ busy = true;
7467
+ try {
7468
+ const docs = await listClientDocs(this.#store, vault);
7469
+ const writers = docs.map(toPresence);
7470
+ if (last === null || !presenceListEqual(last, writers)) {
7471
+ last = writers;
7472
+ onChange(writers);
7473
+ }
7474
+ } catch {
7475
+ } finally {
7476
+ busy = false;
7477
+ }
7478
+ };
7479
+ void poll();
7480
+ const timer = setInterval(() => void poll(), this.#pollIntervalMs);
7481
+ unref(timer);
7482
+ return () => clearInterval(timer);
7483
+ }
7484
+ async reachableWriters(vault, o) {
7485
+ const docs = await listClientDocs(this.#store, vault);
7486
+ return docs.map(toPresence).filter((w) => o.now - w.lastSeen <= o.staleMs);
7487
+ }
7488
+ };
7489
+ }
7490
+ });
7491
+
7492
+ // src/coordination/index.ts
7493
+ var init_coordination = __esm({
7494
+ "src/coordination/index.ts"() {
7495
+ "use strict";
7496
+ init_types4();
7497
+ init_store_provider();
7498
+ }
7499
+ });
7500
+
7258
7501
  // src/crdt/strategy.ts
7259
7502
  var NOT_ENABLED, NO_CRDT;
7260
7503
  var init_strategy = __esm({
@@ -17764,108 +18007,18 @@ var init_magic_link_grant = __esm({
17764
18007
  }
17765
18008
  });
17766
18009
 
17767
- // src/schema-update/fence.ts
17768
- async function loadFence(store, vault) {
17769
- const envelope = await store.get(vault, META_COLLECTION3, FENCE_RECORD_ID);
17770
- if (!envelope) return DEFAULT_FENCE;
17771
- try {
17772
- const parsed = JSON.parse(envelope._data);
17773
- if (!isFenceDoc(parsed)) return DEFAULT_FENCE;
17774
- return parsed;
17775
- } catch {
17776
- return DEFAULT_FENCE;
17777
- }
17778
- }
17779
- async function saveFence(store, vault, fence) {
17780
- const envelope = {
17781
- _noydb: NOYDB_FORMAT_VERSION,
17782
- _v: 1,
17783
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
17784
- _iv: "",
17785
- _data: JSON.stringify(fence)
17786
- };
17787
- await store.put(vault, META_COLLECTION3, FENCE_RECORD_ID, envelope);
17788
- }
17789
- function isFenceDoc(x) {
17790
- if (x === null || typeof x !== "object") return false;
17791
- const o = x;
17792
- return typeof o["currentSchemaVersion"] === "number" && (o["fenceState"] === "normal" || o["fenceState"] === "draining" || o["fenceState"] === "migrating" || o["fenceState"] === "complete");
17793
- }
17794
- var FENCE_RECORD_ID, META_COLLECTION3, DEFAULT_FENCE;
17795
- var init_fence = __esm({
17796
- "src/schema-update/fence.ts"() {
17797
- "use strict";
17798
- init_types();
17799
- FENCE_RECORD_ID = "schema-fence";
17800
- META_COLLECTION3 = "_meta";
17801
- DEFAULT_FENCE = { currentSchemaVersion: 0, fenceState: "normal" };
17802
- }
17803
- });
17804
-
17805
- // src/schema-update/client-registry.ts
17806
- async function writeClientDoc(store, vault, clientId, doc) {
17807
- const envelope = {
17808
- _noydb: NOYDB_FORMAT_VERSION,
17809
- _v: 1,
17810
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
17811
- _iv: "",
17812
- _data: JSON.stringify({ clientId, ...doc })
17813
- };
17814
- await store.put(vault, META_COLLECTION4, `${CLIENT_PREFIX}${clientId}`, envelope);
17815
- }
17816
- async function listClientDocs(store, vault) {
17817
- const ids = await store.list(vault, META_COLLECTION4);
17818
- const out = [];
17819
- for (const id of ids) {
17820
- if (!id.startsWith(CLIENT_PREFIX)) continue;
17821
- const env = await store.get(vault, META_COLLECTION4, id);
17822
- if (!env) continue;
17823
- try {
17824
- const parsed = JSON.parse(env._data);
17825
- if (isClientDoc(parsed)) out.push(parsed);
17826
- } catch {
17827
- }
17828
- }
17829
- return out;
17830
- }
17831
- async function activeQuiesced(store, vault, opts) {
17832
- const docs = await listClientDocs(store, vault);
17833
- const active = docs.filter(
17834
- (d) => d.lastSeen >= opts.now - opts.staleMs && d.clientId !== opts.excludeClientId
17835
- );
17836
- return active.every((d) => d.quiescedAtVersion === opts.generation);
17837
- }
17838
- function isClientDoc(x) {
17839
- if (x === null || typeof x !== "object") return false;
17840
- const o = x;
17841
- return typeof o["clientId"] === "string" && typeof o["lastSeen"] === "number" && (o["quiescedAtVersion"] === null || typeof o["quiescedAtVersion"] === "number");
17842
- }
17843
- var META_COLLECTION4, CLIENT_PREFIX;
17844
- var init_client_registry = __esm({
17845
- "src/schema-update/client-registry.ts"() {
17846
- "use strict";
17847
- init_types();
17848
- META_COLLECTION4 = "_meta";
17849
- CLIENT_PREFIX = "schema-fence:client:";
17850
- }
17851
- });
17852
-
17853
18010
  // src/schema-update/fence-controller.ts
17854
- function delay(ms) {
17855
- return new Promise((resolve) => setTimeout(resolve, ms));
17856
- }
17857
18011
  var SchemaFenceController;
17858
18012
  var init_fence_controller = __esm({
17859
18013
  "src/schema-update/fence-controller.ts"() {
17860
18014
  "use strict";
17861
- init_fence();
17862
18015
  init_errors();
17863
- init_client_registry();
18016
+ init_coordination();
17864
18017
  SchemaFenceController = class {
17865
- #store;
18018
+ #coordination;
17866
18019
  #vault;
17867
18020
  #onFlush;
17868
- #clientId;
18021
+ #writerId;
17869
18022
  #now;
17870
18023
  #staleMs;
17871
18024
  #quiesceTimeoutMs;
@@ -17873,10 +18026,10 @@ var init_fence_controller = __esm({
17873
18026
  #snapshot = 0;
17874
18027
  #pending = /* @__PURE__ */ new Map();
17875
18028
  constructor(opts) {
17876
- this.#store = opts.store;
18029
+ this.#coordination = opts.coordination;
17877
18030
  this.#vault = opts.vault;
17878
18031
  this.#onFlush = opts.onFlush;
17879
- this.#clientId = opts.clientId ?? "migrator";
18032
+ this.#writerId = opts.clientId ?? "migrator";
17880
18033
  this.#now = opts.now ?? (() => Date.now());
17881
18034
  this.#staleMs = opts.staleMs ?? 3e4;
17882
18035
  this.#quiesceTimeoutMs = opts.quiesceTimeoutMs ?? 6e4;
@@ -17885,7 +18038,7 @@ var init_fence_controller = __esm({
17885
18038
  }
17886
18039
  /** Capture the generation snapshot at vault-open. */
17887
18040
  async init() {
17888
- this.#snapshot = (await loadFence(this.#store, this.#vault)).currentSchemaVersion;
18041
+ this.#snapshot = (await this.#coordination.readFence(this.#vault)).currentSchemaVersion;
17889
18042
  }
17890
18043
  /** Record a per-collection pending cutover (from a registration `cutover` decision). */
17891
18044
  registerPendingCutover(collection, transform) {
@@ -17893,7 +18046,7 @@ var init_fence_controller = __esm({
17893
18046
  }
17894
18047
  /** Write-path gate. Throws when behind, fenced, or this collection is cutover-pending. */
17895
18048
  async assertWritable(collection) {
17896
- const fence = await loadFence(this.#store, this.#vault);
18049
+ const fence = await this.#coordination.readFence(this.#vault);
17897
18050
  if (fence.currentSchemaVersion > this.#snapshot) {
17898
18051
  throw new MigrationRequiredError(
17899
18052
  `Vault "${this.#vault}" advanced to schema generation ${fence.currentSchemaVersion} (this client opened at ${this.#snapshot}). Reload to continue.`
@@ -17912,49 +18065,49 @@ var init_fence_controller = __esm({
17912
18065
  * Admin trigger. Drain → wait for the active set to quiesce (or time out)
17913
18066
  * → migrate each pending transform → bump → complete → normal. The
17914
18067
  * migrator excludes itself from the barrier (it drained synchronously
17915
- * here). `onPoll` (tests) advances other clients between barrier checks;
17916
- * production falls back to a short real delay.
18068
+ * inside {@link runDrainBarrier}). `onPoll` (tests) advances other clients
18069
+ * between barrier checks; production falls back to a short real delay.
17917
18070
  */
17918
18071
  async runCutover(run, opts) {
17919
18072
  if (this.#pending.size === 0) return { migrated: 0 };
17920
- const base = await loadFence(this.#store, this.#vault);
18073
+ const base = await this.#coordination.readFence(this.#vault);
17921
18074
  const generation = base.currentSchemaVersion;
17922
- await this.#setState(generation, "draining");
17923
- await this.#onFlush();
17924
- const deadline = this.#now() + this.#quiesceTimeoutMs;
17925
- while (!await activeQuiesced(this.#store, this.#vault, {
17926
- generation,
17927
- now: this.#now(),
17928
- staleMs: this.#staleMs,
17929
- excludeClientId: this.#clientId
17930
- })) {
17931
- if (this.#now() >= deadline) {
17932
- throw new QuiesceTimeoutError(
17933
- `Cutover on "${this.#vault}" timed out waiting for clients to quiesce at generation ${generation}.`
17934
- );
17935
- }
17936
- await (opts?.onPoll ? opts.onPoll() : delay(50));
17937
- }
17938
- await this.#setState(generation, "migrating");
18075
+ this.#emit({ currentSchemaVersion: generation, fenceState: "draining" });
17939
18076
  let migrated = 0;
17940
- for (const [collection, transform] of this.#pending) {
17941
- await run(collection, transform);
17942
- migrated++;
17943
- }
17944
- const nextVersion = generation + 1;
17945
- await this.#setState(nextVersion, "complete");
17946
- this.#pending.clear();
17947
- await this.#setState(nextVersion, "normal");
17948
- this.#snapshot = nextVersion;
18077
+ await runDrainBarrier(
18078
+ this.#coordination,
18079
+ {
18080
+ vault: this.#vault,
18081
+ generation,
18082
+ writerId: this.#writerId,
18083
+ onFlush: this.#onFlush,
18084
+ staleMs: this.#staleMs,
18085
+ quiesceTimeoutMs: this.#quiesceTimeoutMs,
18086
+ now: this.#now,
18087
+ ...opts?.onPoll ? { onPoll: opts.onPoll } : {}
18088
+ },
18089
+ async () => {
18090
+ await this.#setState(generation, "migrating");
18091
+ for (const [collection, transform] of this.#pending) {
18092
+ await run(collection, transform);
18093
+ migrated++;
18094
+ }
18095
+ const nextVersion = generation + 1;
18096
+ await this.#setState(nextVersion, "complete");
18097
+ this.#pending.clear();
18098
+ await this.#setState(nextVersion, "normal");
18099
+ this.#snapshot = nextVersion;
18100
+ }
18101
+ );
17949
18102
  return { migrated };
17950
18103
  }
17951
18104
  /** Recover a stuck drain: reset fenceState to normal at the current version (no bump). */
17952
18105
  async abort() {
17953
- const fence = await loadFence(this.#store, this.#vault);
18106
+ const fence = await this.#coordination.readFence(this.#vault);
17954
18107
  await this.#setState(fence.currentSchemaVersion, "normal");
17955
18108
  }
17956
18109
  async #setState(currentSchemaVersion, fenceState) {
17957
- await saveFence(this.#store, this.#vault, { currentSchemaVersion, fenceState });
18110
+ await this.#coordination.setFence(this.#vault, { currentSchemaVersion, fenceState });
17958
18111
  this.#emit({ currentSchemaVersion, fenceState });
17959
18112
  }
17960
18113
  };
@@ -17966,12 +18119,11 @@ var FenceWatcher;
17966
18119
  var init_fence_watcher = __esm({
17967
18120
  "src/schema-update/fence-watcher.ts"() {
17968
18121
  "use strict";
17969
- init_fence();
17970
- init_client_registry();
17971
18122
  FenceWatcher = class {
17972
- #store;
18123
+ #coordination;
17973
18124
  #vault;
17974
- #clientId;
18125
+ #writerId;
18126
+ #sessionId;
17975
18127
  #onFlush;
17976
18128
  #now;
17977
18129
  #emit;
@@ -17979,9 +18131,10 @@ var init_fence_watcher = __esm({
17979
18131
  #quiescedAtVersion = null;
17980
18132
  #timer;
17981
18133
  constructor(opts) {
17982
- this.#store = opts.store;
18134
+ this.#coordination = opts.coordination;
17983
18135
  this.#vault = opts.vault;
17984
- this.#clientId = opts.clientId;
18136
+ this.#writerId = opts.clientId;
18137
+ this.#sessionId = opts.sessionId ?? opts.clientId;
17985
18138
  this.#onFlush = opts.onFlush;
17986
18139
  this.#now = opts.now ?? (() => Date.now());
17987
18140
  this.#emit = opts.emit ?? (() => {
@@ -17989,14 +18142,16 @@ var init_fence_watcher = __esm({
17989
18142
  }
17990
18143
  /** Publish liveness (and the current ack) without changing quiesce state. */
17991
18144
  async beat() {
17992
- await writeClientDoc(this.#store, this.#vault, this.#clientId, {
18145
+ await this.#coordination.reportPresence(this.#vault, {
18146
+ writerId: this.#writerId,
18147
+ sessionId: this.#sessionId,
17993
18148
  lastSeen: this.#now(),
17994
18149
  quiescedAtVersion: this.#quiescedAtVersion
17995
18150
  });
17996
18151
  }
17997
18152
  /** Poll the fence; quiesce on draining; emit on transitions. */
17998
18153
  async check() {
17999
- const fence = await loadFence(this.#store, this.#vault);
18154
+ const fence = await this.#coordination.readFence(this.#vault);
18000
18155
  if (fence.fenceState !== this.#lastState) {
18001
18156
  this.#lastState = fence.fenceState;
18002
18157
  this.#emit({ currentSchemaVersion: fence.currentSchemaVersion, fenceState: fence.fenceState });
@@ -19308,10 +19463,11 @@ var init_vault = __esm({
19308
19463
  this.keyring = opts.keyring;
19309
19464
  this.encrypted = opts.encrypted;
19310
19465
  this.schemaFence = new SchemaFenceController({
19311
- store: this.adapter,
19466
+ coordination: this.noydb.coordination,
19312
19467
  vault: this.name,
19313
19468
  onFlush: () => this.noydb._writeQueueTracker.onFlush(),
19314
19469
  clientId: this.noydb._clientId,
19470
+ sessionId: this.noydb._sessionId,
19315
19471
  emit: (e) => this.emitter.emit("schema:fence-changed", { vault: this.name, ...e })
19316
19472
  });
19317
19473
  this.emitter = opts.emitter;
@@ -19707,9 +19863,10 @@ var init_vault = __esm({
19707
19863
  if (this.#fenceCoordinationStarted) return;
19708
19864
  this.#fenceCoordinationStarted = true;
19709
19865
  this.#fenceWatcher = new FenceWatcher({
19710
- store: this.adapter,
19866
+ coordination: this.noydb.coordination,
19711
19867
  vault: this.name,
19712
19868
  clientId: this.noydb._clientId,
19869
+ sessionId: this.noydb._sessionId,
19713
19870
  onFlush: () => this.noydb._writeQueueTracker.onFlush(),
19714
19871
  emit: (e) => this.emitter.emit("schema:fence-changed", { vault: this.name, ...e })
19715
19872
  });
@@ -23450,6 +23607,7 @@ var init_noydb = __esm({
23450
23607
  init_recovery();
23451
23608
  init_managed_passphrase();
23452
23609
  init_ulid();
23610
+ init_coordination();
23453
23611
  init_errors2();
23454
23612
  init_auth_introspection();
23455
23613
  init_public_envelope();
@@ -23493,6 +23651,10 @@ var init_noydb = __esm({
23493
23651
  writeHooks = new WriteHookRegistry();
23494
23652
  subsystemBus = new SubsystemBus();
23495
23653
  clientId = generateULID();
23654
+ /** Session that owns this instance's writers (one user's writers across vaults). */
23655
+ sessionId;
23656
+ /** Drain-barrier coordination transport for the schema fence (#469). */
23657
+ coordinationProvider;
23496
23658
  vaultCache = /* @__PURE__ */ new Map();
23497
23659
  keyringCache = /* @__PURE__ */ new Map();
23498
23660
  syncEngines = /* @__PURE__ */ new Map();
@@ -23567,6 +23729,8 @@ var init_noydb = __esm({
23567
23729
  "[noydb] debugPlaintext is ON \u2014 records are stored UNENCRYPTED and laid out for native store inspection. NEVER use this for production or client data."
23568
23730
  );
23569
23731
  }
23732
+ this.sessionId = options.sessionId ?? generateULID();
23733
+ this.coordinationProvider = options.coordinationStrategy ?? new StoreCoordinationProvider(options.store);
23570
23734
  this.txStrategy = options.txStrategy ?? NO_TX;
23571
23735
  this.forgetStrategy = options.forgetStrategy ?? NO_FORGET;
23572
23736
  this.sessionStrategy = options.sessionStrategy ?? NO_SESSION;
@@ -24620,6 +24784,18 @@ var init_noydb = __esm({
24620
24784
  get _clientId() {
24621
24785
  return this.clientId;
24622
24786
  }
24787
+ /** @internal Session that owns this instance's writers (#469). */
24788
+ get _sessionId() {
24789
+ return this.sessionId;
24790
+ }
24791
+ /**
24792
+ * @internal Drain-barrier coordination transport for the schema fence (#469).
24793
+ * The default store-backed provider reproduces today's fence behavior; a
24794
+ * `by-*` real-time transport is injected via `coordinationStrategy`.
24795
+ */
24796
+ get coordination() {
24797
+ return this.coordinationProvider;
24798
+ }
24623
24799
  /**
24624
24800
  * Soft-lock a single vault: clear its in-memory keyring, DEKs, vault
24625
24801
  * instance, sync engine, policy enforcer, and active-tier entry —
@@ -28036,8 +28212,8 @@ function withRetry(opts = {}) {
28036
28212
  } catch (err) {
28037
28213
  lastError = err;
28038
28214
  if (attempt >= maxRetries || !shouldRetry(err)) throw err;
28039
- const delay2 = backoffMs * Math.pow(2, attempt) * (1 + Math.random() * jitter);
28040
- await new Promise((r) => setTimeout(r, delay2));
28215
+ const delay = backoffMs * Math.pow(2, attempt) * (1 + Math.random() * jitter);
28216
+ await new Promise((r) => setTimeout(r, delay));
28041
28217
  }
28042
28218
  }
28043
28219
  throw lastError;