@cortexkit/antigravity-auth-core 3.0.12 → 3.0.13

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 (51) hide show
  1. package/dist/account-selector.d.ts +9 -1
  2. package/dist/account-selector.d.ts.map +1 -1
  3. package/dist/account-selector.js +23 -8
  4. package/dist/account-selector.js.map +1 -1
  5. package/dist/common-auth-embedded/claustrum/consumer.d.ts +77 -2
  6. package/dist/common-auth-embedded/claustrum/consumer.js +320 -35
  7. package/dist/common-auth-embedded/claustrum/custody.d.ts +36 -1
  8. package/dist/common-auth-embedded/claustrum/custody.js +95 -11
  9. package/dist/common-auth-embedded/claustrum/enrollment.js +37 -12
  10. package/dist/common-auth-embedded/claustrum/hub.d.ts +195 -0
  11. package/dist/common-auth-embedded/claustrum/hub.js +988 -0
  12. package/dist/common-auth-embedded/claustrum/index.d.ts +13 -3
  13. package/dist/common-auth-embedded/claustrum/index.js +37 -4
  14. package/dist/common-auth-embedded/claustrum/quota-writer.d.ts +8 -1
  15. package/dist/common-auth-embedded/claustrum/quota-writer.js +34 -12
  16. package/dist/common-auth-embedded/claustrum/roster.d.ts +49 -1
  17. package/dist/common-auth-embedded/claustrum/roster.js +187 -12
  18. package/dist/common-auth-embedded/claustrum/shutdown.js +42 -0
  19. package/dist/common-auth-embedded/fs/file-read-cache.js +57 -0
  20. package/dist/common-auth-embedded/fs/refresh-file-lock.d.ts +1 -1
  21. package/dist/common-auth-embedded/fs/refresh-file-lock.js +20 -11
  22. package/dist/common-auth-embedded/quota/commit-interval.d.ts +9 -0
  23. package/dist/common-auth-embedded/quota/commit-interval.js +18 -0
  24. package/dist/common-auth-embedded/source-output.json +115 -67
  25. package/dist/common-auth-embedded/store/mutate.d.ts +2 -0
  26. package/dist/common-auth-embedded/store/mutate.js +4 -0
  27. package/dist/common-auth-embedded/store/pool.d.ts +6 -1
  28. package/dist/common-auth-embedded/store/pool.js +1 -1
  29. package/dist/common-auth-embedded/store/quota-writer.js +27 -13
  30. package/dist/common-auth-embedded/store/settings.d.ts +1 -1
  31. package/dist/common-auth-embedded/store/settings.js +16 -6
  32. package/dist/model-registry.d.ts +2 -2
  33. package/dist/model-registry.d.ts.map +1 -1
  34. package/dist/model-registry.js +6 -4
  35. package/dist/model-registry.js.map +1 -1
  36. package/dist/quota-manager.d.ts +1 -1
  37. package/dist/quota-manager.d.ts.map +1 -1
  38. package/dist/quota-manager.js +12 -4
  39. package/dist/quota-manager.js.map +1 -1
  40. package/dist/quota-presentation.d.ts.map +1 -1
  41. package/dist/quota-presentation.js +8 -0
  42. package/dist/quota-presentation.js.map +1 -1
  43. package/dist/quota-types.d.ts +1 -1
  44. package/dist/quota-types.d.ts.map +1 -1
  45. package/dist/quota-types.js +5 -0
  46. package/dist/quota-types.js.map +1 -1
  47. package/dist/vault-account-source.d.ts +6 -1
  48. package/dist/vault-account-source.d.ts.map +1 -1
  49. package/dist/vault-account-source.js +9 -0
  50. package/dist/vault-account-source.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,57 @@
1
+ import { lstat } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ async function identity(path) {
4
+ try {
5
+ const metadata = await lstat(path);
6
+ // Do not follow a symlink: its target can change without changing the link.
7
+ return metadata.isFile() ? metadata : undefined;
8
+ }
9
+ catch (error) {
10
+ return error.code === 'ENOENT'
11
+ ? 'absent'
12
+ : undefined;
13
+ }
14
+ }
15
+ function sameIdentity(left, right) {
16
+ if (left === 'absent' || right === 'absent')
17
+ return left === right;
18
+ return (left.dev === right.dev &&
19
+ left.ino === right.ino &&
20
+ left.size === right.size &&
21
+ left.mtimeMs === right.mtimeMs &&
22
+ left.ctimeMs === right.ctimeMs);
23
+ }
24
+ /** Process-local reads, capped at 64 path sets with one value per resolved set. */
25
+ export class FileReadCache {
26
+ #entries = new Map();
27
+ async read(paths, read, validateHit) {
28
+ const resolvedPaths = paths.map((path) => resolve(path));
29
+ const key = JSON.stringify(resolvedPaths);
30
+ const probed = await Promise.all(resolvedPaths.map(identity));
31
+ const identities = probed.every((entry) => entry !== undefined)
32
+ ? probed
33
+ : undefined;
34
+ const cached = this.#entries.get(key);
35
+ if (identities &&
36
+ cached &&
37
+ identities.every((entry, index) => {
38
+ const previous = cached.identities[index];
39
+ return previous !== undefined && sameIdentity(entry, previous);
40
+ })) {
41
+ validateHit?.(identities);
42
+ return cached.value;
43
+ }
44
+ // A failed probe or read must not leave a previous successful value reusable.
45
+ this.#entries.delete(key);
46
+ const value = await read(resolvedPaths);
47
+ if (identities) {
48
+ if (this.#entries.size >= 64) {
49
+ const oldest = this.#entries.keys().next().value;
50
+ if (oldest !== undefined)
51
+ this.#entries.delete(oldest);
52
+ }
53
+ this.#entries.set(key, { identities, value });
54
+ }
55
+ return value;
56
+ }
57
+ }
@@ -28,7 +28,7 @@ type RefreshFileLockOptions = {
28
28
  onContended?: () => void;
29
29
  /** Synchronous, never awaited; throws and returned promise/thenable rejections are isolated. */
30
30
  onRenewalTimer?: (event: 'scheduled' | 'cancelled') => void;
31
- onStep?: (step: 'stale-marker-stat' | 'stale-marker-claimed' | 'stale-lock-observed' | 'lock-read-ready' | 'lock-missing' | 'stale-lock-confirmed' | 'stale-lock-removed' | 'stale-lock-recreated' | 'eviction-marker-acquired' | 'renewal-owner-confirmed' | 'renewal-marker-unavailable' | 'renewal-write-fenced' | 'renewal-write-ready' | 'renewal-write-finished' | 'relinquish-read' | 'renewal-finished' | 'release-owner-confirmed') => void | Promise<void>;
31
+ onStep?: (step: 'stale-marker-stat' | 'stale-marker-claimed' | 'stale-lock-observed' | 'lease-contention-read-ready' | 'lock-read-ready' | 'lock-missing' | 'stale-lock-confirmed' | 'stale-lock-removed' | 'stale-lock-recreated' | 'eviction-marker-acquired' | 'renewal-owner-confirmed' | 'renewal-marker-unavailable' | 'renewal-write-fenced' | 'renewal-write-ready' | 'renewal-write-finished' | 'relinquish-read' | 'renewal-finished' | 'release-owner-confirmed') => void | Promise<void>;
32
32
  };
33
33
  /** Filesystem leases share wall time across all callers and processes. */
34
34
  export declare function acquireRefreshFileLock(options: RefreshFileLockOptions): Promise<RefreshFileLock | null>;
@@ -178,19 +178,28 @@ export async function acquireRefreshFileLockWithClock(options) {
178
178
  }
179
179
  async function leaseContention(error) {
180
180
  const code = error.code;
181
- if (code === 'EEXIST') {
182
- if (windowsPrivateFiles)
183
- await readOwner();
184
- return true;
185
- }
186
- if (code === 'EISDIR') {
187
- if (windowsPrivateFiles) {
188
- await verifyPrivate(lockPath, 'directory', true);
189
- await readPrivateText(legacyOwnerPath, true);
181
+ if (code !== 'EEXIST' && code !== 'EISDIR')
182
+ return false;
183
+ if (windowsPrivateFiles) {
184
+ if (options.onStep)
185
+ await options.onStep('lease-contention-read-ready');
186
+ try {
187
+ if (code === 'EISDIR') {
188
+ await verifyPrivate(lockPath, 'directory', true);
189
+ await readPrivateText(legacyOwnerPath, true);
190
+ }
191
+ else
192
+ await readOwner();
193
+ }
194
+ catch (readError) {
195
+ // The holder can release after exclusive creation reports a collision.
196
+ // Only absence permits another acquisition attempt; security and sharing
197
+ // refusals must still escape rather than being treated as contention.
198
+ if (readError.code !== 'ENOENT')
199
+ throw readError;
190
200
  }
191
- return true;
192
201
  }
193
- return false;
202
+ return true;
194
203
  }
195
204
  async function tryAcquire() {
196
205
  if (windowsPrivateFiles)
@@ -0,0 +1,9 @@
1
+ export declare const QUOTA_COMMIT_INTERVAL_MS = 30000;
2
+ export interface QuotaCommitTimer {
3
+ now(): number;
4
+ schedule(delayMs: number, run: () => Promise<void>): {
5
+ cancel(): void;
6
+ unref?(): void;
7
+ };
8
+ }
9
+ export declare const quotaCommitTimer: QuotaCommitTimer;
@@ -0,0 +1,18 @@
1
+ // Atomic quota commits create locks, markers and a renamed temp file. Keep
2
+ // those filesystem events at least 30 s apart under normal load; routing sees
3
+ // pending readings immediately, while other processes may lag by one interval.
4
+ // Shutdown drains and a full bounded pool queue can commit sooner.
5
+ // The vault shares one gate per resolved path; each pool handle has its own.
6
+ // N pool handles on one file can therefore make N normal commits per interval
7
+ // in one process. These gates do not coordinate with other processes.
8
+ export const QUOTA_COMMIT_INTERVAL_MS = 30_000;
9
+ export const quotaCommitTimer = {
10
+ now: () => performance.now(),
11
+ schedule(delayMs, run) {
12
+ const timer = setTimeout(() => void run(), delayMs);
13
+ return {
14
+ cancel: () => clearTimeout(timer),
15
+ unref: () => timer.unref?.(),
16
+ };
17
+ },
18
+ };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "package": "@cortexkit/common-auth",
4
- "version": "0.26.0",
4
+ "version": "0.27.1",
5
5
  "artifactStatus": "published",
6
6
  "publicRoots": {
7
7
  "./store": {
@@ -37,15 +37,15 @@
37
37
  "import": "./dist/pi-slot/index.js"
38
38
  }
39
39
  },
40
- "input": "tools/common-auth-build/inputs/cortexkit-common-auth-0.26.0.tgz",
41
- "tarballSha256": "a5a6cb7051ee8c45e86e19cf7a918d0e28ce1b6a80301e982efbc7bb4b98208d",
42
- "sri": "sha512-qTmQ9aMG+HcyDbhKie8Nq9wxKMRQ6GXH2hKq8Fab9kHqFfzswdp2NpXofGY52RhQ2VxuyMOQr5T6lUrkbny5VQ==",
43
- "canonicalFiles": 141,
44
- "canonicalBytes": 888503,
40
+ "input": "tools/common-auth-build/inputs/cortexkit-common-auth-0.27.1.tgz",
41
+ "tarballSha256": "a74507a1f746c9d591501ab24a630fe8bab20fb305c92738770902f003edb387",
42
+ "sri": "sha512-k5oKV38HxXKqwVi7qU8K9fD+P0ca7kXu52TlulHJoiGO0Q8Ebv0URtW1UXKLYawOm3ZwfVwFEBzAhuj8kMh6rg==",
43
+ "canonicalFiles": 147,
44
+ "canonicalBytes": 981509,
45
45
  "generator": {
46
46
  "path": "packages/opencode/scripts/embed-common-auth.ts",
47
47
  "version": 3,
48
- "sha256": "89dbeb07d77e0657abeb72a40fcc03a900e98292956865252349a01a15999ed0"
48
+ "sha256": "79e2db6c01d1048f7a8b90a5105848710890e68ebb0ff096ec5623ec96eb1012"
49
49
  },
50
50
  "files": [
51
51
  {
@@ -227,33 +227,33 @@
227
227
  {
228
228
  "source": "package/dist/claustrum/consumer.d.ts",
229
229
  "output": "claustrum/consumer.d.ts",
230
- "bytes": 9907,
231
- "sourceSha256": "8001ce3f928af1bc9124223071eb9c85ea3273436cd7be073c588217f942b248",
232
- "outputSha256": "8001ce3f928af1bc9124223071eb9c85ea3273436cd7be073c588217f942b248",
230
+ "bytes": 13309,
231
+ "sourceSha256": "a33f8fb2750424f1dd1e80ee6e0f260d4d5b6bdf6be3b00b2ae503bfbadb4d17",
232
+ "outputSha256": "a33f8fb2750424f1dd1e80ee6e0f260d4d5b6bdf6be3b00b2ae503bfbadb4d17",
233
233
  "transform": "verbatim"
234
234
  },
235
235
  {
236
236
  "source": "package/dist/claustrum/consumer.js",
237
237
  "output": "claustrum/consumer.js",
238
- "bytes": 26470,
239
- "sourceSha256": "b2087accd6e8759fb49173dc744243ed703520d5011386ef514672be8fe79fbe",
240
- "outputSha256": "b2087accd6e8759fb49173dc744243ed703520d5011386ef514672be8fe79fbe",
238
+ "bytes": 38516,
239
+ "sourceSha256": "46a8be88c8e5270d06bda0042235d7081dea06eaf1ae15f9a5fdf2ed93e478fd",
240
+ "outputSha256": "46a8be88c8e5270d06bda0042235d7081dea06eaf1ae15f9a5fdf2ed93e478fd",
241
241
  "transform": "verbatim"
242
242
  },
243
243
  {
244
244
  "source": "package/dist/claustrum/custody.d.ts",
245
245
  "output": "claustrum/custody.d.ts",
246
- "bytes": 14842,
247
- "sourceSha256": "f776af511c7a02f8e5c4cb0361845d1e740eec96244fee5cfa1289d2212dffd9",
248
- "outputSha256": "f776af511c7a02f8e5c4cb0361845d1e740eec96244fee5cfa1289d2212dffd9",
246
+ "bytes": 16192,
247
+ "sourceSha256": "ac8a3f2a47ec04ba0b6b5f69425db6672aa7660cd9249853f05fb2b4669698c2",
248
+ "outputSha256": "ac8a3f2a47ec04ba0b6b5f69425db6672aa7660cd9249853f05fb2b4669698c2",
249
249
  "transform": "verbatim"
250
250
  },
251
251
  {
252
252
  "source": "package/dist/claustrum/custody.js",
253
253
  "output": "claustrum/custody.js",
254
- "bytes": 27016,
255
- "sourceSha256": "5d2ead857d6e6384980dace94d308f1f47afe831c82e2b644ab434ffb0bcf151",
256
- "outputSha256": "5d2ead857d6e6384980dace94d308f1f47afe831c82e2b644ab434ffb0bcf151",
254
+ "bytes": 30242,
255
+ "sourceSha256": "cfa9c256d59ac4740f5b88fc1662a3bbb29078973cbeaf6225448c305fa580a2",
256
+ "outputSha256": "cfa9c256d59ac4740f5b88fc1662a3bbb29078973cbeaf6225448c305fa580a2",
257
257
  "transform": "verbatim"
258
258
  },
259
259
  {
@@ -267,9 +267,9 @@
267
267
  {
268
268
  "source": "package/dist/claustrum/enrollment.js",
269
269
  "output": "claustrum/enrollment.js",
270
- "bytes": 35060,
271
- "sourceSha256": "a9ca3a23778cc86ab9798051ba19dcb3b2b8e3dc4c4fda356cbd56eca50097db",
272
- "outputSha256": "a9ca3a23778cc86ab9798051ba19dcb3b2b8e3dc4c4fda356cbd56eca50097db",
270
+ "bytes": 36501,
271
+ "sourceSha256": "0a6aa09a8927ae9ba11e4cc4fbe81cd8a3248d5f7db71e8592667bc870899078",
272
+ "outputSha256": "0a6aa09a8927ae9ba11e4cc4fbe81cd8a3248d5f7db71e8592667bc870899078",
273
273
  "transform": "verbatim"
274
274
  },
275
275
  {
@@ -304,20 +304,36 @@
304
304
  "outputSha256": "59bd24af8dbf5d923750b6e2fb15de2ce8a59aa59e11a6740527e50d5f7f93d6",
305
305
  "transform": "verbatim"
306
306
  },
307
+ {
308
+ "source": "package/dist/claustrum/hub.d.ts",
309
+ "output": "claustrum/hub.d.ts",
310
+ "bytes": 9169,
311
+ "sourceSha256": "07a5c385738649ec5c03b6afb7a916011e565c9f9d5463b280b9896024d8f0c0",
312
+ "outputSha256": "07a5c385738649ec5c03b6afb7a916011e565c9f9d5463b280b9896024d8f0c0",
313
+ "transform": "verbatim"
314
+ },
315
+ {
316
+ "source": "package/dist/claustrum/hub.js",
317
+ "output": "claustrum/hub.js",
318
+ "bytes": 40718,
319
+ "sourceSha256": "50d57557ccd6e59989424e0df63df75570d97cdb5f71da37ce865d75f76297fd",
320
+ "outputSha256": "50d57557ccd6e59989424e0df63df75570d97cdb5f71da37ce865d75f76297fd",
321
+ "transform": "verbatim"
322
+ },
307
323
  {
308
324
  "source": "package/dist/claustrum/index.d.ts",
309
325
  "output": "claustrum/index.d.ts",
310
- "bytes": 3844,
311
- "sourceSha256": "36c0b94c5e18972463f48bfd7e1506d9655372aa8df065786b3a47d199cdb5e0",
312
- "outputSha256": "36c0b94c5e18972463f48bfd7e1506d9655372aa8df065786b3a47d199cdb5e0",
326
+ "bytes": 4728,
327
+ "sourceSha256": "524bec258d060b75e6aeefa1e39e102e9ad7370e6c74ba2c033fcc418a1db4d7",
328
+ "outputSha256": "524bec258d060b75e6aeefa1e39e102e9ad7370e6c74ba2c033fcc418a1db4d7",
313
329
  "transform": "verbatim"
314
330
  },
315
331
  {
316
332
  "source": "package/dist/claustrum/index.js",
317
333
  "output": "claustrum/index.js",
318
- "bytes": 2529,
319
- "sourceSha256": "42c9e67bb5ec6749de488933a56fb3ee253c3b9e3588d692f171d87c59bcbd89",
320
- "outputSha256": "42c9e67bb5ec6749de488933a56fb3ee253c3b9e3588d692f171d87c59bcbd89",
334
+ "bytes": 3938,
335
+ "sourceSha256": "9868c6c475ba9390118bd45c3b5dafe18a51c13cfe4e61700496e9096143a42b",
336
+ "outputSha256": "9868c6c475ba9390118bd45c3b5dafe18a51c13cfe4e61700496e9096143a42b",
321
337
  "transform": "verbatim"
322
338
  },
323
339
  {
@@ -355,33 +371,41 @@
355
371
  {
356
372
  "source": "package/dist/claustrum/quota-writer.d.ts",
357
373
  "output": "claustrum/quota-writer.d.ts",
358
- "bytes": 1112,
359
- "sourceSha256": "35c3193c833ef169b5ebd9fdb03a8e08c234e9154b68b04d3cbea79975fffd14",
360
- "outputSha256": "35c3193c833ef169b5ebd9fdb03a8e08c234e9154b68b04d3cbea79975fffd14",
374
+ "bytes": 1483,
375
+ "sourceSha256": "c3cafec7057092a67d443667bee50b19333dcf3a8d14b63c9fbe4ac3989ba389",
376
+ "outputSha256": "c3cafec7057092a67d443667bee50b19333dcf3a8d14b63c9fbe4ac3989ba389",
361
377
  "transform": "verbatim"
362
378
  },
363
379
  {
364
380
  "source": "package/dist/claustrum/quota-writer.js",
365
381
  "output": "claustrum/quota-writer.js",
366
- "bytes": 7579,
367
- "sourceSha256": "0054a12ca94aab2eecf50f9b5e090879ff184e7f286cc979f32d311e22011906",
368
- "outputSha256": "0054a12ca94aab2eecf50f9b5e090879ff184e7f286cc979f32d311e22011906",
382
+ "bytes": 8527,
383
+ "sourceSha256": "5375e7d8a300e73f4398d610090834095fd22ba4b3830f3c62d142a67dcbe9fd",
384
+ "outputSha256": "5375e7d8a300e73f4398d610090834095fd22ba4b3830f3c62d142a67dcbe9fd",
369
385
  "transform": "verbatim"
370
386
  },
371
387
  {
372
388
  "source": "package/dist/claustrum/roster.d.ts",
373
389
  "output": "claustrum/roster.d.ts",
374
- "bytes": 12574,
375
- "sourceSha256": "c9ab3a89e04f72de09c5f15ccf1436b91f8a245e2253f24ec5998120058aebe7",
376
- "outputSha256": "c9ab3a89e04f72de09c5f15ccf1436b91f8a245e2253f24ec5998120058aebe7",
390
+ "bytes": 15102,
391
+ "sourceSha256": "30fc3b4d572e43a08a700ff169ebcb52206d1200f7ca678d2bff0a3369aaa005",
392
+ "outputSha256": "30fc3b4d572e43a08a700ff169ebcb52206d1200f7ca678d2bff0a3369aaa005",
377
393
  "transform": "verbatim"
378
394
  },
379
395
  {
380
396
  "source": "package/dist/claustrum/roster.js",
381
397
  "output": "claustrum/roster.js",
382
- "bytes": 30876,
383
- "sourceSha256": "d02184fd4d32aa8917369919edea4c44328e7332e30f3ddc41ce350e5fd1df07",
384
- "outputSha256": "d02184fd4d32aa8917369919edea4c44328e7332e30f3ddc41ce350e5fd1df07",
398
+ "bytes": 39326,
399
+ "sourceSha256": "f46fc75edcf4e418f02a5699c7395f1f44abd2e28b77067b89f7622af5890214",
400
+ "outputSha256": "f46fc75edcf4e418f02a5699c7395f1f44abd2e28b77067b89f7622af5890214",
401
+ "transform": "verbatim"
402
+ },
403
+ {
404
+ "source": "package/dist/claustrum/shutdown.js",
405
+ "output": "claustrum/shutdown.js",
406
+ "bytes": 1270,
407
+ "sourceSha256": "6ccb19ab5e9bb84a978cb47a32b443abe432b4edfe69a318e45bb404ef75c45e",
408
+ "outputSha256": "6ccb19ab5e9bb84a978cb47a32b443abe432b4edfe69a318e45bb404ef75c45e",
385
409
  "transform": "verbatim"
386
410
  },
387
411
  {
@@ -512,6 +536,14 @@
512
536
  "outputSha256": "80779a8078e673ef68deaa7fdee3a0e1322078521a1f4156c0a595814c374044",
513
537
  "transform": "verbatim"
514
538
  },
539
+ {
540
+ "source": "package/dist/fs/file-read-cache.js",
541
+ "output": "fs/file-read-cache.js",
542
+ "bytes": 2121,
543
+ "sourceSha256": "484d18996d76c44125c880347b3d3dcd10bb352743acf44c588d0b38e8e0f1a7",
544
+ "outputSha256": "484d18996d76c44125c880347b3d3dcd10bb352743acf44c588d0b38e8e0f1a7",
545
+ "transform": "verbatim"
546
+ },
515
547
  {
516
548
  "source": "package/dist/fs/index.d.ts",
517
549
  "output": "fs/index.d.ts",
@@ -563,17 +595,17 @@
563
595
  {
564
596
  "source": "package/dist/fs/refresh-file-lock.d.ts",
565
597
  "output": "fs/refresh-file-lock.d.ts",
566
- "bytes": 2125,
567
- "sourceSha256": "ec643f4e081c108c1b42ceb17a83cf3e7e9997a4c75e1a61ffc33b5b44ccd414",
568
- "outputSha256": "ec643f4e081c108c1b42ceb17a83cf3e7e9997a4c75e1a61ffc33b5b44ccd414",
598
+ "bytes": 2157,
599
+ "sourceSha256": "cbce15eb313d425501780dd4be657cbbaa7e41064bab4ae64fea91fbd54918e9",
600
+ "outputSha256": "cbce15eb313d425501780dd4be657cbbaa7e41064bab4ae64fea91fbd54918e9",
569
601
  "transform": "verbatim"
570
602
  },
571
603
  {
572
604
  "source": "package/dist/fs/refresh-file-lock.js",
573
605
  "output": "fs/refresh-file-lock.js",
574
- "bytes": 29704,
575
- "sourceSha256": "5c52aad573e84e8065dcf06259ecf38345e8b5a2c401029a23d942f28c7a8521",
576
- "outputSha256": "5c52aad573e84e8065dcf06259ecf38345e8b5a2c401029a23d942f28c7a8521",
606
+ "bytes": 30222,
607
+ "sourceSha256": "314dadbe39b9f0664b6f2f6abff674197c8d86f61ba657e2031d5a8b08ec1030",
608
+ "outputSha256": "314dadbe39b9f0664b6f2f6abff674197c8d86f61ba657e2031d5a8b08ec1030",
577
609
  "transform": "verbatim"
578
610
  },
579
611
  {
@@ -712,6 +744,22 @@
712
744
  "outputSha256": "4e496ce03a8a23536b467168f1dc6a7ceffb06d00ae80cb8a8868f311b2f3a8a",
713
745
  "transform": "verbatim"
714
746
  },
747
+ {
748
+ "source": "package/dist/quota/commit-interval.d.ts",
749
+ "output": "quota/commit-interval.d.ts",
750
+ "bytes": 283,
751
+ "sourceSha256": "8b1d8b541a382d1ce366d65d9845e9d3848445cf075751b68ad963fb660ee04a",
752
+ "outputSha256": "8b1d8b541a382d1ce366d65d9845e9d3848445cf075751b68ad963fb660ee04a",
753
+ "transform": "verbatim"
754
+ },
755
+ {
756
+ "source": "package/dist/quota/commit-interval.js",
757
+ "output": "quota/commit-interval.js",
758
+ "bytes": 864,
759
+ "sourceSha256": "3281afb6df69d83a7c5898e1163287f34104068ddd4085b7460c4bb392967491",
760
+ "outputSha256": "3281afb6df69d83a7c5898e1163287f34104068ddd4085b7460c4bb392967491",
761
+ "transform": "verbatim"
762
+ },
715
763
  {
716
764
  "source": "package/dist/quota/format.d.ts",
717
765
  "output": "quota/format.d.ts",
@@ -963,33 +1011,33 @@
963
1011
  {
964
1012
  "source": "package/dist/store/mutate.d.ts",
965
1013
  "output": "store/mutate.d.ts",
966
- "bytes": 9006,
967
- "sourceSha256": "009fea657c03a49b37555aa3c445c8930132234f0a1f3e8808ded5c065293ead",
968
- "outputSha256": "009fea657c03a49b37555aa3c445c8930132234f0a1f3e8808ded5c065293ead",
1014
+ "bytes": 9196,
1015
+ "sourceSha256": "4a46be98d9eb24a690e95f26b69bd3c461d5f225170bd31fcb89147c7baac41c",
1016
+ "outputSha256": "4a46be98d9eb24a690e95f26b69bd3c461d5f225170bd31fcb89147c7baac41c",
969
1017
  "transform": "verbatim"
970
1018
  },
971
1019
  {
972
1020
  "source": "package/dist/store/mutate.js",
973
1021
  "output": "store/mutate.js",
974
- "bytes": 19981,
975
- "sourceSha256": "095842664fb1d805159902223b7765dd4a7349ad1a5bbb5e2c135c654739a434",
976
- "outputSha256": "095842664fb1d805159902223b7765dd4a7349ad1a5bbb5e2c135c654739a434",
1022
+ "bytes": 20171,
1023
+ "sourceSha256": "d61d08f2caed6d7a803ca4c67511e5133d28f21a90a7417f76e23b49ed00591d",
1024
+ "outputSha256": "d61d08f2caed6d7a803ca4c67511e5133d28f21a90a7417f76e23b49ed00591d",
977
1025
  "transform": "verbatim"
978
1026
  },
979
1027
  {
980
1028
  "source": "package/dist/store/pool.d.ts",
981
1029
  "output": "store/pool.d.ts",
982
- "bytes": 11743,
983
- "sourceSha256": "0693e165ef937c22c375a3f63d01209c844d101ddf1a6702c8acc99bb5fc2a9d",
984
- "outputSha256": "0693e165ef937c22c375a3f63d01209c844d101ddf1a6702c8acc99bb5fc2a9d",
1030
+ "bytes": 12144,
1031
+ "sourceSha256": "64d6e7dd5600f436422e6d309a22b7f874a6afdd866873226ab1f4d5091ebc43",
1032
+ "outputSha256": "64d6e7dd5600f436422e6d309a22b7f874a6afdd866873226ab1f4d5091ebc43",
985
1033
  "transform": "verbatim"
986
1034
  },
987
1035
  {
988
1036
  "source": "package/dist/store/pool.js",
989
1037
  "output": "store/pool.js",
990
- "bytes": 6332,
991
- "sourceSha256": "45a696753691027dfcadd8e94b38b111ad5085ba1cb4f769225b15efc68125d7",
992
- "outputSha256": "45a696753691027dfcadd8e94b38b111ad5085ba1cb4f769225b15efc68125d7",
1038
+ "bytes": 6358,
1039
+ "sourceSha256": "1503b5c0f461678f9710a5fe22e345092de70e537a114e57b0cb651795928031",
1040
+ "outputSha256": "1503b5c0f461678f9710a5fe22e345092de70e537a114e57b0cb651795928031",
993
1041
  "transform": "verbatim"
994
1042
  },
995
1043
  {
@@ -1043,9 +1091,9 @@
1043
1091
  {
1044
1092
  "source": "package/dist/store/quota-writer.js",
1045
1093
  "output": "store/quota-writer.js",
1046
- "bytes": 12396,
1047
- "sourceSha256": "ade5fdda516011baf11e40f672263364cea07c45bdcbfc11107e22794e9aef21",
1048
- "outputSha256": "ade5fdda516011baf11e40f672263364cea07c45bdcbfc11107e22794e9aef21",
1094
+ "bytes": 12960,
1095
+ "sourceSha256": "7816549913b030045e5f7848dd57ca11841a3aa6051c30c96a7d09f82341700e",
1096
+ "outputSha256": "7816549913b030045e5f7848dd57ca11841a3aa6051c30c96a7d09f82341700e",
1049
1097
  "transform": "verbatim"
1050
1098
  },
1051
1099
  {
@@ -1139,17 +1187,17 @@
1139
1187
  {
1140
1188
  "source": "package/dist/store/settings.d.ts",
1141
1189
  "output": "store/settings.d.ts",
1142
- "bytes": 2979,
1143
- "sourceSha256": "2be15ac325bcfed048f12e202eaf625a6eca3f85fc35f29dff5770a31d81b7c5",
1144
- "outputSha256": "2be15ac325bcfed048f12e202eaf625a6eca3f85fc35f29dff5770a31d81b7c5",
1190
+ "bytes": 2994,
1191
+ "sourceSha256": "32446dea1f8431e3fb7e38940d1dec7259dbd5c794b4117efe3875be92405fef",
1192
+ "outputSha256": "32446dea1f8431e3fb7e38940d1dec7259dbd5c794b4117efe3875be92405fef",
1145
1193
  "transform": "verbatim"
1146
1194
  },
1147
1195
  {
1148
1196
  "source": "package/dist/store/settings.js",
1149
1197
  "output": "store/settings.js",
1150
- "bytes": 5176,
1151
- "sourceSha256": "b96a54dfca4a59a592d6bfac82e388e9a4f4c158e4b01dc8864b4bc6becbc88c",
1152
- "outputSha256": "b96a54dfca4a59a592d6bfac82e388e9a4f4c158e4b01dc8864b4bc6becbc88c",
1198
+ "bytes": 5766,
1199
+ "sourceSha256": "5ed4e0458800923b843b9b8f0671cca0c04cb26e428453e3bcc8b6d06b51470f",
1200
+ "outputSha256": "5ed4e0458800923b843b9b8f0671cca0c04cb26e428453e3bcc8b6d06b51470f",
1153
1201
  "transform": "verbatim"
1154
1202
  },
1155
1203
  {
@@ -52,6 +52,8 @@ export type ReadResult = ({
52
52
  kind?: 'snapshot-contended';
53
53
  retryable?: true;
54
54
  };
55
+ /** Settings need only the config classification, not a joined roster snapshot. */
56
+ export declare function readPoolConfig(path: string): Promise<import("./schema.js").ConfigClassification>;
55
57
  /**
56
58
  * Validate config/state/config bytes without locks. Roster replacement writes
57
59
  * config before deleting old state credentials. Re-read a changed config so an
@@ -28,6 +28,10 @@ function parseFile(text) {
28
28
  return { exists: true, parseError };
29
29
  }
30
30
  }
31
+ /** Settings need only the config classification, not a joined roster snapshot. */
32
+ export async function readPoolConfig(path) {
33
+ return classifyConfig(parseFile(await readText(path)));
34
+ }
31
35
  /**
32
36
  * Validate config/state/config bytes without locks. Roster replacement writes
33
37
  * config before deleting old state credentials. Re-read a changed config so an
@@ -1,3 +1,4 @@
1
+ import type { QuotaCommitTimer } from '../quota/commit-interval.js';
1
2
  import { type Attribution } from './attribution.js';
2
3
  import type { PoolOperationError } from './errors.js';
3
4
  import type { PoolLogger } from './hooks.js';
@@ -24,11 +25,15 @@ export interface OpenPoolStoreOptions {
24
25
  * older reading replaces. Accepted readings appear in read/load
25
26
  * immediately, merged the same way; the returned promise still rejects
26
27
  * current-row refusals before queueing. Await dispose or close at shutdown
27
- * to drain.
28
+ * to attempt a bounded drain; readings still pending at its timeout are
29
+ * discarded. Normal quota commits are at least 30 seconds apart; a full
30
+ * queue commits early so response handling never waits for that interval.
28
31
  */
29
32
  coalesceQuota?: boolean;
30
33
  /** Bounded quota drain on close/dispose, in milliseconds; defaults to 1,000. */
31
34
  quotaDrainTimeoutMs?: number;
35
+ /** Monotonic clock and scheduling seam for detached quota commits. */
36
+ quotaCommitTimer?: QuotaCommitTimer;
32
37
  /**
33
38
  * The codec of the provider state kept beside each row's credential (see
34
39
  * `ProviderStateCodec`). Without it no row shows a provider state, and
@@ -82,7 +82,7 @@ export function openPoolStore(options) {
82
82
  firePull: (id, reason) => pulls.fire(id, reason),
83
83
  };
84
84
  const quotaWriter = options.coalesceQuota
85
- ? new PoolQuotaWriter(rt, options.quotaDrainTimeoutMs)
85
+ ? new PoolQuotaWriter(rt, options.quotaDrainTimeoutMs, options.quotaCommitTimer)
86
86
  : undefined;
87
87
  const record = (id, attribution, observation) => quotaWriter
88
88
  ? quotaWriter.record(id, attribution, observation)
@@ -1,4 +1,5 @@
1
1
  import { createLogger } from '../logger/index.js';
2
+ import { QUOTA_COMMIT_INTERVAL_MS, quotaCommitTimer, } from '../quota/commit-interval.js';
2
3
  import { assertQuotaEpoch, assertQuotaRow, mergedQuota, } from './attribution.js';
3
4
  import { PoolOperationError } from './errors.js';
4
5
  import { toFailure, withTransaction } from './mutate.js';
@@ -24,19 +25,22 @@ export const POOL_QUOTA_QUEUE_LIMIT = 128;
24
25
  export class PoolQuotaWriter {
25
26
  rt;
26
27
  timeoutMs;
28
+ clock;
27
29
  pending = new Map();
28
30
  warned = new Set();
29
31
  wake = new Set();
30
32
  logger;
31
33
  timer;
34
+ committedAt;
32
35
  running;
33
36
  closing;
34
37
  closed = false;
35
38
  stopped = false;
36
39
  failures = 0;
37
- constructor(rt, timeoutMs = 1_000) {
40
+ constructor(rt, timeoutMs = 1_000, clock = quotaCommitTimer) {
38
41
  this.rt = rt;
39
42
  this.timeoutMs = timeoutMs;
43
+ this.clock = clock;
40
44
  this.logger = rt.ctx.logger ?? createLogger('store');
41
45
  }
42
46
  async record(id, attribution, observation) {
@@ -126,25 +130,33 @@ export class PoolQuotaWriter {
126
130
  schedule(delay) {
127
131
  if (this.running || this.timer || !this.pending.size || this.stopped)
128
132
  return;
129
- this.timer = setTimeout(() => {
133
+ const remaining = this.closed
134
+ ? 0
135
+ : (this.committedAt ?? -Infinity) +
136
+ QUOTA_COMMIT_INTERVAL_MS -
137
+ this.clock.now();
138
+ this.timer = this.clock.schedule(Math.max(delay, remaining), () => {
130
139
  this.timer = undefined;
131
- this.flush();
132
- }, delay);
140
+ return this.flush();
141
+ });
133
142
  if (!this.closed)
134
143
  this.timer.unref?.();
135
144
  }
136
- // Starts a flush now rather than at the coalescing timer or retry backoff;
137
- // a flush already running is left to finish.
145
+ cancelTimer() {
146
+ this.timer?.cancel();
147
+ this.timer = undefined;
148
+ }
149
+ // A full queue overrides the interval: response handling must not wait for
150
+ // a quota commit slot. A flush already running is left to finish.
138
151
  flushNow() {
139
152
  if (this.running)
140
153
  return;
141
- clearTimeout(this.timer);
142
- this.timer = undefined;
154
+ this.cancelTimer();
143
155
  this.flush();
144
156
  }
145
157
  flush() {
146
158
  if (this.running || this.stopped || !this.pending.size)
147
- return;
159
+ return this.running ?? Promise.resolve();
148
160
  // Every queued reading, each row's in arrival order.
149
161
  const batch = [...this.pending.values()].flat();
150
162
  // Short lock attempts allow retries to pick up freshly queued readings and
@@ -208,8 +220,10 @@ export class PoolQuotaWriter {
208
220
  tx.setEntry(id, { ...entry, quota, needsFirstReading: false });
209
221
  changed = true;
210
222
  }
211
- if (changed && !this.stopped)
223
+ if (changed && !this.stopped) {
212
224
  await tx.commitConfig();
225
+ this.committedAt = this.clock.now();
226
+ }
213
227
  }, { completeTorn: false });
214
228
  const flushed = new Set(batch);
215
229
  for (const [id, queue] of this.pending) {
@@ -240,6 +254,7 @@ export class PoolQuotaWriter {
240
254
  this.schedule(delay);
241
255
  }
242
256
  })();
257
+ return this.running;
243
258
  }
244
259
  warn(message, data) {
245
260
  try {
@@ -257,8 +272,7 @@ export class PoolQuotaWriter {
257
272
  return this.closing;
258
273
  }
259
274
  async drain() {
260
- clearTimeout(this.timer);
261
- this.timer = undefined;
275
+ this.cancelTimer();
262
276
  let deadline;
263
277
  let wake;
264
278
  const drained = new Promise((done) => {
@@ -281,7 +295,7 @@ export class PoolQuotaWriter {
281
295
  finally {
282
296
  this.stopped = true;
283
297
  clearTimeout(deadline);
284
- clearTimeout(this.timer);
298
+ this.cancelTimer();
285
299
  this.wake.delete(wake);
286
300
  if (this.pending.size)
287
301
  this.warn('pool quota drain timed out; pending readings left', {
@@ -50,7 +50,7 @@ export type UpdateSettingsResult = {
50
50
  /** `unchanged` when the mutator left the settings as they were; nothing was written. */
51
51
  outcome: 'updated' | 'unchanged';
52
52
  };
53
- /** Reads the settings without taking a lock or writing anything. */
53
+ /** Reads only the config's settings, without taking a lock or writing anything. */
54
54
  export declare function readPoolSettings(rt: StoreRuntime): Promise<SettingsRead>;
55
55
  /**
56
56
  * One locked read-modify-write of the plugin's settings in the config file