@bazariodev/docket 0.0.0-stage → 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +443 -2
  4. package/dist/browser/constants.d.cts +14 -0
  5. package/dist/browser/constants.d.ts +14 -0
  6. package/dist/browser/index.cjs +192 -0
  7. package/dist/browser/index.cjs.map +1 -0
  8. package/dist/browser/index.d.cts +29 -0
  9. package/dist/browser/index.d.ts +29 -0
  10. package/dist/browser/index.js +187 -0
  11. package/dist/browser/index.js.map +1 -0
  12. package/dist/chunk-7ld72rnl.js +376 -0
  13. package/dist/chunk-7ld72rnl.js.map +1 -0
  14. package/dist/chunk-B-SP8Od_.js +437 -0
  15. package/dist/chunk-B-SP8Od_.js.map +1 -0
  16. package/dist/chunk-BLf-eCDO.cjs +53 -0
  17. package/dist/chunk-BLf-eCDO.cjs.map +1 -0
  18. package/dist/chunk-Bvq4tF_F.cjs +440 -0
  19. package/dist/chunk-Bvq4tF_F.cjs.map +1 -0
  20. package/dist/chunk-CEhpwtwJ.cjs +379 -0
  21. package/dist/chunk-CEhpwtwJ.cjs.map +1 -0
  22. package/dist/chunk-CSS3f4_M.js +46 -0
  23. package/dist/chunk-CSS3f4_M.js.map +1 -0
  24. package/dist/chunk-CXZYNALm.cjs +124 -0
  25. package/dist/chunk-CXZYNALm.cjs.map +1 -0
  26. package/dist/chunk-DYcNnkOV.js +50 -0
  27. package/dist/chunk-DYcNnkOV.js.map +1 -0
  28. package/dist/chunk-DqK_3eFr.cjs +53 -0
  29. package/dist/chunk-DqK_3eFr.cjs.map +1 -0
  30. package/dist/chunk-LRCHwVuE.js +110 -0
  31. package/dist/chunk-LRCHwVuE.js.map +1 -0
  32. package/dist/constants.d.cts +6 -0
  33. package/dist/constants.d.ts +6 -0
  34. package/dist/core/constants.d.cts +43 -0
  35. package/dist/core/constants.d.ts +43 -0
  36. package/dist/core/errors.d.cts +8 -0
  37. package/dist/core/errors.d.ts +8 -0
  38. package/dist/devtools/index.cjs +37 -0
  39. package/dist/devtools/index.cjs.map +1 -0
  40. package/dist/devtools/index.d.cts +11 -0
  41. package/dist/devtools/index.d.ts +11 -0
  42. package/dist/devtools/index.js +35 -0
  43. package/dist/devtools/index.js.map +1 -0
  44. package/dist/export/index.cjs +111 -0
  45. package/dist/export/index.cjs.map +1 -0
  46. package/dist/export/index.d.cts +49 -0
  47. package/dist/export/index.d.ts +49 -0
  48. package/dist/export/index.js +109 -0
  49. package/dist/export/index.js.map +1 -0
  50. package/dist/history/constants.d.cts +35 -0
  51. package/dist/history/constants.d.ts +35 -0
  52. package/dist/history/index.cjs +1196 -0
  53. package/dist/history/index.cjs.map +1 -0
  54. package/dist/history/index.d.cts +113 -0
  55. package/dist/history/index.d.ts +113 -0
  56. package/dist/history/index.js +1194 -0
  57. package/dist/history/index.js.map +1 -0
  58. package/dist/index.cjs +14 -0
  59. package/dist/index.cjs.map +1 -0
  60. package/dist/index.d.cts +158 -0
  61. package/dist/index.d.ts +158 -0
  62. package/dist/index.js +6 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/indexeddb/index.cjs +15 -0
  65. package/dist/indexeddb/index.cjs.map +1 -0
  66. package/dist/indexeddb/index.d.cts +46 -0
  67. package/dist/indexeddb/index.d.ts +46 -0
  68. package/dist/indexeddb/index.js +13 -0
  69. package/dist/indexeddb/index.js.map +1 -0
  70. package/dist/redaction/index.cjs +154 -0
  71. package/dist/redaction/index.cjs.map +1 -0
  72. package/dist/redaction/index.d.cts +70 -0
  73. package/dist/redaction/index.d.ts +70 -0
  74. package/dist/redaction/index.js +152 -0
  75. package/dist/redaction/index.js.map +1 -0
  76. package/dist/sip/constants.d.cts +5 -0
  77. package/dist/sip/constants.d.ts +5 -0
  78. package/dist/sip/index.cjs +336 -0
  79. package/dist/sip/index.cjs.map +1 -0
  80. package/dist/sip/index.d.cts +34 -0
  81. package/dist/sip/index.d.ts +34 -0
  82. package/dist/sip/index.js +333 -0
  83. package/dist/sip/index.js.map +1 -0
  84. package/dist/storage/constants.d.cts +11 -0
  85. package/dist/storage/constants.d.ts +11 -0
  86. package/dist/storage/contracts.d.cts +35 -0
  87. package/dist/storage/contracts.d.ts +35 -0
  88. package/package.json +127 -3
@@ -0,0 +1,1196 @@
1
+ 'use strict';
2
+
3
+ var common = require('../chunk-CXZYNALm.cjs');
4
+ var constants = require('../chunk-BLf-eCDO.cjs');
5
+ var hub = require('../chunk-Bvq4tF_F.cjs');
6
+ require('../chunk-CEhpwtwJ.cjs');
7
+
8
+ const STORE_MODES = Object.freeze({
9
+ OPENING: 'opening',
10
+ MEMORY: 'memory',
11
+ PERSISTENT: 'persistent',
12
+ FALLBACK: 'fallback',
13
+ STOPPED: 'stopped',
14
+ });
15
+
16
+ const OVERFLOW_STRATEGIES = Object.freeze({
17
+ CYCLE: 'cycle',
18
+ ROTATE: 'rotate',
19
+ });
20
+
21
+ const HISTORY_MODES = /* @__PURE__ */ Object.freeze({
22
+ OFF: 'off',
23
+ MEMORY: 'memory',
24
+ PERSISTENT: 'persistent',
25
+ });
26
+ const HISTORY_STATES = /* @__PURE__ */ Object.freeze({
27
+ IDLE: 'idle',
28
+ ACTIVE: 'active',
29
+ TRANSITIONING: 'transitioning',
30
+ BLOCKED: 'blocked',
31
+ STALE: 'stale',
32
+ STOPPED: 'stopped',
33
+ });
34
+ const HISTORY_ERROR_CODES = /* @__PURE__ */ Object.freeze({
35
+ METADATA: 'metadata',
36
+ CLEANUP: 'cleanup',
37
+ STALE: 'stale',
38
+ SETUP: 'setup',
39
+ MIGRATION: 'migration',
40
+ STOPPED: 'stopped',
41
+ ROTATION: 'rotation',
42
+ });
43
+ const HISTORY_ERRORS = /* @__PURE__ */ Object.freeze({
44
+ [HISTORY_ERROR_CODES.METADATA]: 'Log history metadata failed',
45
+ [HISTORY_ERROR_CODES.CLEANUP]: 'Log history cleanup failed',
46
+ [HISTORY_ERROR_CODES.STALE]: 'Log history stale failed',
47
+ [HISTORY_ERROR_CODES.SETUP]: 'Log history setup failed',
48
+ [HISTORY_ERROR_CODES.MIGRATION]: 'Log history migration failed',
49
+ [HISTORY_ERROR_CODES.STOPPED]: 'Log history stopped failed',
50
+ [HISTORY_ERROR_CODES.ROTATION]: 'Log history rotation failed',
51
+ });
52
+ const HISTORY_LIMITS = /* @__PURE__ */ Object.freeze({
53
+ STARTUP_DEFAULT_MAX_SIZE_PER_AREA: 250_000,
54
+ STARTUP_MAX_TOTAL_SIZE: 1_000_000,
55
+ });
56
+
57
+ function historyError(code, cause) {
58
+ return Object.assign(
59
+ new Error(
60
+ HISTORY_ERRORS[code] ?? `Log history ${code} failed`,
61
+ cause === undefined ? undefined : { cause },
62
+ ),
63
+ { code },
64
+ );
65
+ }
66
+
67
+ function readMetadata(storage, key, leaseKey) {
68
+ const text = storage.getItem(key);
69
+ if (!text) return null;
70
+ let value;
71
+ try {
72
+ value = JSON.parse(text);
73
+ } catch {
74
+ return null;
75
+ }
76
+ if (
77
+ !value ||
78
+ value.version !== 1 ||
79
+ !Number.isSafeInteger(value.revision) ||
80
+ value.revision < 1 ||
81
+ !Object.values(HISTORY_MODES).includes(value.mode) ||
82
+ typeof value.cleanupPending !== 'boolean'
83
+ )
84
+ return null;
85
+ if (value.leaseKey !== leaseKey)
86
+ throw historyError(HISTORY_ERROR_CODES.METADATA);
87
+ if (
88
+ !value.loggedOut &&
89
+ !value.ownerless &&
90
+ (!/^[a-f0-9]{32}$/.test(value.salt) ||
91
+ !/^[a-f0-9]{64}$/.test(value.ownerDigest))
92
+ )
93
+ return null;
94
+ if (
95
+ (value.mode !== HISTORY_MODES.PERSISTENT || value.cleanupPending) &&
96
+ Object.hasOwn(value, 'key')
97
+ ) {
98
+ delete value.key;
99
+ storage.setItem(key, JSON.stringify(value));
100
+ }
101
+ if (value.key && !/^[a-f0-9]{64}$/.test(value.key)) return null;
102
+ return value;
103
+ }
104
+
105
+ async function digestOwner(ownerId, salt = common.randomHex(16)) {
106
+ if (!globalThis.crypto?.subtle) throw historyError(HISTORY_ERROR_CODES.SETUP);
107
+ const input = new TextEncoder().encode(
108
+ JSON.stringify(['bazario-log-owner-v1', salt, ownerId]),
109
+ );
110
+ const digest = await crypto.subtle.digest('SHA-256', input);
111
+ return {
112
+ salt,
113
+ ownerDigest: Array.from(new Uint8Array(digest), (byte) =>
114
+ byte.toString(16).padStart(2, '0'),
115
+ ).join(''),
116
+ };
117
+ }
118
+
119
+ const STORE_OPTION_NAMES = Object.freeze([
120
+ 'flushIntervalMs',
121
+ 'maxBatchSize',
122
+ 'maxQueuedRecords',
123
+ 'maxQueuedSize',
124
+ ]);
125
+
126
+ function validateStoreOptions(options, stores, lease) {
127
+ stores.validateStoreOptions(options);
128
+ if (lease.signal.aborted) throw historyError(HISTORY_ERROR_CODES.STOPPED);
129
+ }
130
+
131
+ function validateHistoryOptions(options) {
132
+ const { lease } = options ?? {};
133
+ if (
134
+ !lease?.key ||
135
+ !lease.signal ||
136
+ lease.signal.aborted ||
137
+ typeof lease.signal.addEventListener !== 'function'
138
+ )
139
+ throw new TypeError('An active application lease is required');
140
+ if (
141
+ !Array.isArray(options.logging?.areas) ||
142
+ typeof options.createRedactor !== 'function'
143
+ )
144
+ throw new TypeError('Logging areas and createRedactor are required');
145
+ if (Object.hasOwn(options.logging, 'createRunId'))
146
+ throw new TypeError('Managed history controls its run IDs');
147
+ if (Object.hasOwn(options.logging, 'isActive'))
148
+ throw new TypeError('Managed history controls admission');
149
+ common.assertStormLimit(options.logging.stormLimit);
150
+ if (Object.hasOwn(options.persistent ?? {}, 'allowUnprotected'))
151
+ throw new TypeError('Managed history requires protected database access');
152
+ const areas = new Set();
153
+ for (const area of options.logging.areas) {
154
+ if (typeof area.id !== 'string' || !area.id || areas.has(area.id))
155
+ throw new TypeError('Invalid history area');
156
+ common.assertLevel(area.level ?? 'info');
157
+ areas.add(area.id);
158
+ }
159
+ for (const nested of [options.memory, options.startup, options.persistent])
160
+ if (
161
+ nested &&
162
+ (Object.hasOwn(nested, 'overflowStrategy') ||
163
+ Object.hasOwn(nested, 'onRotate'))
164
+ )
165
+ throw new TypeError('Rotation options belong to history');
166
+ validateStoreOptions(options, options.stores, lease);
167
+ for (const nested of [
168
+ options.memory,
169
+ options.startup || {},
170
+ options.persistent,
171
+ options.persistent?.fallback,
172
+ ])
173
+ validateStoreOptions(nested, options.stores, lease);
174
+ for (const name of STORE_OPTION_NAMES)
175
+ if (options.persistent?.[name] !== undefined)
176
+ common.positive(options.persistent[name], name);
177
+ if (options.logging.maxOverrideMs !== undefined)
178
+ common.positive(options.logging.maxOverrideMs, 'maxOverrideMs');
179
+ if (
180
+ options.databaseName !== undefined &&
181
+ (typeof options.databaseName !== 'string' || !options.databaseName)
182
+ )
183
+ throw new TypeError('Invalid history database name');
184
+ const databaseName =
185
+ options.databaseName ?? `bazario-logging:history:${lease.key}`;
186
+ const metadataKey = `bazario-logging:metadata:${databaseName}`;
187
+ const requestedMode = options.initialMode ?? HISTORY_MODES.MEMORY;
188
+ if (!Object.values(HISTORY_MODES).includes(requestedMode))
189
+ throw new TypeError('Invalid history mode');
190
+ return { lease, areas, databaseName, metadataKey, requestedMode };
191
+ }
192
+
193
+ function createHistorySession({
194
+ options,
195
+ store,
196
+ key,
197
+ isLive,
198
+ canControl,
199
+ levelStorage,
200
+ startup,
201
+ trackCleanup,
202
+ }) {
203
+ const state = {
204
+ valid: true,
205
+ paused: false,
206
+ store,
207
+ key,
208
+ startup,
209
+ hub: null,
210
+ session: null,
211
+ queries: new Set(),
212
+ };
213
+ const live = () => state.valid && !state.paused && isLive();
214
+ const check = () => {
215
+ if (!live()) throw historyError(HISTORY_ERROR_CODES.STALE);
216
+ };
217
+ const redactor = options.createRedactor(key);
218
+ check();
219
+ const guardedRedactor = {
220
+ version: redactor.version,
221
+ limits: redactor.limits,
222
+ };
223
+ for (const method of [
224
+ 'redact',
225
+ 'redactValue',
226
+ 'redactText',
227
+ 'mask',
228
+ ...(redactor.hash ? ['hash'] : []),
229
+ ])
230
+ guardedRedactor[method] = (...args) => {
231
+ check();
232
+ const result = redactor[method](...args);
233
+ check();
234
+ return result;
235
+ };
236
+ const sinks = [];
237
+ let hub$1;
238
+ try {
239
+ if (!startup)
240
+ sinks.push(
241
+ ...(options.createSinks?.(Object.freeze(guardedRedactor)) ?? []),
242
+ );
243
+ check();
244
+ hub$1 = hub.createLogging({
245
+ ...options.logging,
246
+ redactor: guardedRedactor,
247
+ sinks: [store, ...sinks],
248
+ ...(levelStorage ? { levelStorage } : {}),
249
+ isActive: live,
250
+ });
251
+ } catch (failure) {
252
+ const cleanup = Promise.allSettled(
253
+ sinks.map(async (sink) => sink.stop?.()),
254
+ );
255
+ trackCleanup(cleanup);
256
+ throw failure;
257
+ }
258
+ state.hub = hub$1;
259
+ const logging = {};
260
+ for (const method of ['setLevel', 'resetLevel', 'getLevels', 'setContext'])
261
+ logging[method] = (...args) => {
262
+ check();
263
+ if (['setLevel', 'resetLevel'].includes(method) && !canControl())
264
+ throw historyError(HISTORY_ERROR_CODES.STALE);
265
+ const result = hub$1[method](...args);
266
+ check();
267
+ return result;
268
+ };
269
+ const guardLogger = (logger) => {
270
+ const result = {};
271
+ for (const level of Object.values(constants.LEVELS))
272
+ result[level] = (message, data) => {
273
+ if (live()) logger[level](message, data);
274
+ };
275
+ result.child = (binding) => guardLogger(logger.child(binding));
276
+ return Object.freeze(result);
277
+ };
278
+ const roots = new Map();
279
+ logging.runId = hub$1.runId;
280
+ logging.getLogger = (area, scope) => {
281
+ check();
282
+ const key = JSON.stringify([area, scope]);
283
+ const cached = roots.get(key);
284
+ if (cached) return cached;
285
+ const logger = guardLogger(hub$1.getLogger(area, scope));
286
+ if (roots.size < constants.LOGGING_LIMITS.ROOT_LOGGER_CACHE_PAIRS)
287
+ roots.set(key, logger);
288
+ return logger;
289
+ };
290
+ logging.flush = async () => {
291
+ check();
292
+ await hub$1.flush();
293
+ check();
294
+ };
295
+ const source = Object.freeze({
296
+ async query(query) {
297
+ check();
298
+ const pending = Promise.resolve().then(() => store.query(query));
299
+ state.queries.add(pending);
300
+ try {
301
+ const records = await pending;
302
+ check();
303
+ return records;
304
+ } finally {
305
+ state.queries.delete(pending);
306
+ }
307
+ },
308
+
309
+ getStatus() {
310
+ check();
311
+ return store.getStatus();
312
+ },
313
+ });
314
+ state.session = Object.freeze({
315
+ logging: Object.freeze(logging),
316
+ source,
317
+ redactor: Object.freeze(guardedRedactor),
318
+ });
319
+ return state;
320
+ }
321
+
322
+ function createLogHistory(options) {
323
+ if (
324
+ !options?.stores ||
325
+ typeof options.stores.createMemoryStore !== 'function' ||
326
+ typeof options.stores.createIndexedDbStore !== 'function' ||
327
+ typeof options.stores.validateStoreOptions !== 'function' ||
328
+ typeof options.stores.copyRecords !== 'function'
329
+ )
330
+ throw new TypeError('History store providers are required');
331
+ const {
332
+ lease,
333
+ areas,
334
+ databaseName,
335
+ metadataKey,
336
+ requestedMode: initialMode,
337
+ } = validateHistoryOptions(options);
338
+ if (lease.signal.aborted) throw historyError(HISTORY_ERROR_CODES.STOPPED);
339
+ const roots = new Map();
340
+ const retired = new Set();
341
+ const sessions = new Set();
342
+ let storage;
343
+ let metadata = null;
344
+ let requestedMode = initialMode;
345
+ let state = HISTORY_STATES.IDLE;
346
+ let current = null;
347
+ let startup = null;
348
+ let ownerId;
349
+ let stopped = false;
350
+ let startupEpoch = 0;
351
+ let controlQueue = Promise.resolve();
352
+ let stopPromise;
353
+ let rotationBusy = false;
354
+ let setupFailure;
355
+ let forwarding = false;
356
+ let rootLoggerLimitReported = false;
357
+ const status = () =>
358
+ Object.freeze({
359
+ state,
360
+ requestedMode,
361
+ activeMode: current
362
+ ? current.store.getStatus().mode === STORE_MODES.PERSISTENT
363
+ ? HISTORY_MODES.PERSISTENT
364
+ : HISTORY_MODES.MEMORY
365
+ : HISTORY_MODES.OFF,
366
+ cleanupPending: metadata?.cleanupPending ?? false,
367
+ });
368
+
369
+ function notify() {
370
+ common.report(options.onChange, current?.session ?? null, status());
371
+ }
372
+
373
+ function error(code, cause) {
374
+ const failure = historyError(code, cause);
375
+ common.report(options.onError, failure);
376
+ return failure;
377
+ }
378
+
379
+ function check() {
380
+ if (stopped || lease.signal.aborted)
381
+ throw historyError(HISTORY_ERROR_CODES.STOPPED);
382
+ }
383
+
384
+ function retire(session, discard = false) {
385
+ if (!session) return Promise.resolve();
386
+ session.valid = false;
387
+ if (current === session) current = null;
388
+ if (startup === session) startup = null;
389
+ if (session.cleanupPromise) return session.cleanupPromise;
390
+ const cleanup = Promise.resolve().then(async () => {
391
+ try {
392
+ if (!discard)
393
+ try {
394
+ await session.store.flush();
395
+ } catch (failure) {
396
+ common.report(
397
+ options.onError,
398
+ historyError(HISTORY_ERROR_CODES.SETUP, failure),
399
+ );
400
+ }
401
+ try {
402
+ await session.store.stop({ discard });
403
+ } catch (failure) {
404
+ if (session.store.getStatus?.()?.mode !== STORE_MODES.STOPPED)
405
+ throw failure;
406
+ common.report(
407
+ options.onError,
408
+ historyError(HISTORY_ERROR_CODES.SETUP, failure),
409
+ );
410
+ }
411
+ try {
412
+ await session.hub?.stop();
413
+ } catch (failure) {
414
+ if (session.store.getStatus?.()?.mode !== STORE_MODES.STOPPED)
415
+ throw failure;
416
+ common.report(
417
+ options.onError,
418
+ historyError(HISTORY_ERROR_CODES.SETUP, failure),
419
+ );
420
+ }
421
+ } finally {
422
+ await Promise.allSettled(session.queries ?? []);
423
+ }
424
+ });
425
+ retired.add(cleanup);
426
+ session.cleanupPromise = cleanup;
427
+ cleanup.then(
428
+ () => {
429
+ retired.delete(cleanup);
430
+ sessions.delete(session);
431
+ },
432
+ () => {
433
+ retired.delete(cleanup);
434
+ session.cleanupPromise = null;
435
+ },
436
+ );
437
+ return cleanup;
438
+ }
439
+
440
+ function ownStore(store) {
441
+ const session = {
442
+ valid: false,
443
+ discardOnStop: true,
444
+ store,
445
+ hub: null,
446
+ cleanupPromise: null,
447
+ };
448
+ sessions.add(session);
449
+ return session;
450
+ }
451
+
452
+ async function drainInvalidSessions() {
453
+ await Promise.all(
454
+ Array.from(sessions)
455
+ .filter((session) => !session.valid)
456
+ .map((session) => retire(session, true)),
457
+ );
458
+ check();
459
+ }
460
+
461
+ function discardStartup() {
462
+ startupEpoch++;
463
+ if (startup) void retire(startup, true);
464
+ }
465
+
466
+ function block(code, cause) {
467
+ if (stopped) return historyError(HISTORY_ERROR_CODES.STOPPED);
468
+ setupFailure = code;
469
+ state = HISTORY_STATES.BLOCKED;
470
+ discardStartup();
471
+ for (const session of sessions) void retire(session, true);
472
+ notify();
473
+ return error(code, cause);
474
+ }
475
+
476
+ function read() {
477
+ try {
478
+ storage ??= options.storage ?? globalThis.localStorage;
479
+ if (!storage) throw historyError(HISTORY_ERROR_CODES.METADATA);
480
+ metadata = readMetadata(storage, metadataKey, lease.key);
481
+ requestedMode = metadata?.mode ?? requestedMode;
482
+ return metadata;
483
+ } catch {
484
+ throw block(HISTORY_ERROR_CODES.METADATA);
485
+ }
486
+ }
487
+
488
+ function save(value) {
489
+ check();
490
+ const next = { ...value };
491
+ if (
492
+ next.mode !== HISTORY_MODES.PERSISTENT ||
493
+ next.cleanupPending ||
494
+ next.loggedOut
495
+ )
496
+ delete next.key;
497
+ try {
498
+ storage.setItem(metadataKey, JSON.stringify(next));
499
+ metadata = next;
500
+ } catch {
501
+ throw block(HISTORY_ERROR_CODES.METADATA);
502
+ }
503
+ }
504
+
505
+ function privateLevels(owningSession) {
506
+ return {
507
+ key: 'levels',
508
+ storage: {
509
+ getItem: () => JSON.stringify(metadata?.levels ?? {}),
510
+ setItem(_key, text) {
511
+ if (
512
+ stopped ||
513
+ lease.signal.aborted ||
514
+ state !== HISTORY_STATES.ACTIVE ||
515
+ current !== owningSession() ||
516
+ !owningSession()?.valid
517
+ )
518
+ return;
519
+ save({ ...metadata, levels: JSON.parse(text) });
520
+ },
521
+ },
522
+ };
523
+ }
524
+
525
+ function trackCleanup(cleanup) {
526
+ retired.add(cleanup);
527
+ cleanup.then(() => retired.delete(cleanup));
528
+ }
529
+
530
+ function makeSession(mode, key = common.randomHex(32), isStartup = false) {
531
+ let session;
532
+ const rotationOptions = {
533
+ overflowStrategy: isStartup
534
+ ? OVERFLOW_STRATEGIES.CYCLE
535
+ : options.overflowStrategy,
536
+ isRotationEnabled: () =>
537
+ Boolean(
538
+ session?.valid &&
539
+ !session.paused &&
540
+ current === session &&
541
+ !rotationBusy &&
542
+ state === HISTORY_STATES.ACTIVE,
543
+ ),
544
+ onRotate: options.onRotate
545
+ ? async (source) => {
546
+ if (
547
+ !session?.valid ||
548
+ stopped ||
549
+ lease.signal.aborted ||
550
+ rotationBusy
551
+ )
552
+ return;
553
+ rotationBusy = true;
554
+ const guardedSource = {
555
+ async query(query) {
556
+ if (!session.valid || stopped || lease.signal.aborted)
557
+ throw historyError(HISTORY_ERROR_CODES.STALE);
558
+ const result = await source.query(query);
559
+ if (!session.valid || stopped || lease.signal.aborted)
560
+ throw historyError(HISTORY_ERROR_CODES.STALE);
561
+ return result;
562
+ },
563
+
564
+ getStatus() {
565
+ if (!session.valid)
566
+ throw historyError(HISTORY_ERROR_CODES.STALE);
567
+ return source.getStatus();
568
+ },
569
+ };
570
+ try {
571
+ await options.onRotate(
572
+ Object.freeze({
573
+ source: Object.freeze(guardedSource),
574
+ redactor: session.session.redactor,
575
+ }),
576
+ );
577
+ } catch {
578
+ common.report(
579
+ options.onError,
580
+ historyError(HISTORY_ERROR_CODES.ROTATION),
581
+ );
582
+ } finally {
583
+ rotationBusy = false;
584
+ }
585
+ }
586
+ : undefined,
587
+ };
588
+ let store;
589
+ if (mode === HISTORY_MODES.PERSISTENT) {
590
+ store = options.stores.createIndexedDbStore({
591
+ ...options.persistent,
592
+ ...rotationOptions,
593
+ databaseName,
594
+ indexedDB: options.indexedDB,
595
+ allowUnprotected: false,
596
+ onStatusChange: () => {
597
+ if (session?.valid && current === session) notify();
598
+ },
599
+ });
600
+ } else {
601
+ store = options.stores.createMemoryStore({
602
+ ...(isStartup
603
+ ? {
604
+ defaultMaxSizePerArea:
605
+ HISTORY_LIMITS.STARTUP_DEFAULT_MAX_SIZE_PER_AREA,
606
+ maxTotalSize: HISTORY_LIMITS.STARTUP_MAX_TOTAL_SIZE,
607
+ ...options.startup,
608
+ }
609
+ : options.memory),
610
+ ...rotationOptions,
611
+ });
612
+ }
613
+ if (stopped || lease.signal.aborted) {
614
+ void retire(ownStore(store), true).catch((failure) => {
615
+ common.report(
616
+ options.onError,
617
+ historyError(HISTORY_ERROR_CODES.CLEANUP, failure),
618
+ );
619
+ });
620
+ throw historyError(HISTORY_ERROR_CODES.STOPPED);
621
+ }
622
+ try {
623
+ session = createHistorySession({
624
+ options,
625
+ store,
626
+ key,
627
+ startup: isStartup,
628
+ isLive: () => !stopped && !lease.signal.aborted,
629
+ canControl: () => isStartup || state === HISTORY_STATES.ACTIVE,
630
+ levelStorage: isStartup ? undefined : privateLevels(() => session),
631
+ trackCleanup,
632
+ });
633
+ sessions.add(session);
634
+ return session;
635
+ } catch (failure) {
636
+ void retire(ownStore(store), true);
637
+ throw block(HISTORY_ERROR_CODES.SETUP, failure);
638
+ }
639
+ }
640
+
641
+ async function clearDisk() {
642
+ await drainInvalidSessions();
643
+ let cleanup;
644
+ try {
645
+ cleanup = ownStore(
646
+ options.stores.createIndexedDbStore({
647
+ ...options.persistent,
648
+ databaseName,
649
+ indexedDB: options.indexedDB,
650
+ allowUnprotected: false,
651
+ overflowStrategy: OVERFLOW_STRATEGIES.CYCLE,
652
+ }),
653
+ );
654
+ check();
655
+ await cleanup.store.clear();
656
+ check();
657
+ } finally {
658
+ await retire(cleanup, true);
659
+ }
660
+ }
661
+
662
+ async function copySession(source, destination) {
663
+ await options.stores.copyRecords(source, {
664
+ write(record) {
665
+ check();
666
+ destination.write(record);
667
+ },
668
+ async flush() {
669
+ try {
670
+ await destination.flush();
671
+ } catch (failure) {
672
+ if (destination.getStatus().mode !== STORE_MODES.FALLBACK)
673
+ throw failure;
674
+ }
675
+ },
676
+ });
677
+ }
678
+
679
+ async function clearAndSave(selectedOwner, mode) {
680
+ await clearDisk();
681
+ check();
682
+ let key;
683
+ try {
684
+ if (selectedOwner && mode !== HISTORY_MODES.OFF) key = common.randomHex(32);
685
+ } catch {
686
+ throw block(HISTORY_ERROR_CODES.SETUP);
687
+ }
688
+ save({
689
+ ...metadata,
690
+ cleanupPending: false,
691
+ ...(mode === HISTORY_MODES.PERSISTENT && key ? { key } : {}),
692
+ });
693
+ return key;
694
+ }
695
+
696
+ async function publish(mode, key, bufferToken, selectedOwner = ownerId) {
697
+ check();
698
+ if (mode === HISTORY_MODES.OFF) {
699
+ ownerId = selectedOwner;
700
+ state = selectedOwner ? HISTORY_STATES.ACTIVE : HISTORY_STATES.IDLE;
701
+ notify();
702
+ return status();
703
+ }
704
+ if (mode === HISTORY_MODES.PERSISTENT) await drainInvalidSessions();
705
+ const candidate = makeSession(mode, key);
706
+ try {
707
+ if (startup) {
708
+ const source = startup;
709
+ source.paused = true;
710
+ current = null;
711
+ notify();
712
+ try {
713
+ await copySession(source.store, candidate.store);
714
+ check();
715
+ if (bufferToken !== startupEpoch || startup !== source)
716
+ throw historyError(HISTORY_ERROR_CODES.STALE);
717
+ } catch {
718
+ await retire(candidate, true);
719
+ if (!stopped && source.valid && bufferToken === startupEpoch) {
720
+ source.paused = false;
721
+ current = source;
722
+ state = HISTORY_STATES.IDLE;
723
+ notify();
724
+ }
725
+ throw error(HISTORY_ERROR_CODES.MIGRATION);
726
+ }
727
+ await retire(source, true);
728
+ }
729
+ check();
730
+ if (bufferToken !== startupEpoch) {
731
+ await retire(candidate, true);
732
+ throw historyError(HISTORY_ERROR_CODES.STALE);
733
+ }
734
+ current = candidate;
735
+ ownerId = selectedOwner;
736
+ state = HISTORY_STATES.ACTIVE;
737
+ setupFailure = undefined;
738
+ notify();
739
+ return status();
740
+ } catch (failure) {
741
+ if (candidate !== current) await retire(candidate, true);
742
+ throw failure;
743
+ }
744
+ }
745
+
746
+ async function destructive(
747
+ selectedOwner,
748
+ ownerFields,
749
+ mode,
750
+ {
751
+ reset = true,
752
+ keepStartup = false,
753
+ logout = false,
754
+ remainBlocked = false,
755
+ } = {},
756
+ ) {
757
+ const previous = current;
758
+ const savedLevels = reset ? {} : (metadata?.levels ?? {});
759
+ state = HISTORY_STATES.TRANSITIONING;
760
+ if (previous && previous !== startup) {
761
+ previous.valid = false;
762
+ current = null;
763
+ }
764
+ if (!keepStartup) discardStartup();
765
+ const bufferToken = startupEpoch;
766
+ notify();
767
+ const pending = {
768
+ version: 1,
769
+ leaseKey: lease.key,
770
+ revision: (metadata?.revision ?? 0) + 1,
771
+ mode,
772
+ cleanupPending: true,
773
+ loggedOut: logout || (!selectedOwner && Boolean(metadata?.loggedOut)),
774
+ ...(selectedOwner || (!logout && ownerFields?.salt) ? ownerFields : {}),
775
+ ...(!selectedOwner && !logout && !ownerFields?.salt
776
+ ? { ownerless: true }
777
+ : {}),
778
+ levels: savedLevels,
779
+ };
780
+ save(pending);
781
+ requestedMode = mode;
782
+ if (previous && previous !== startup)
783
+ await retire(previous, true).catch((failure) => {
784
+ throw block(HISTORY_ERROR_CODES.CLEANUP, failure);
785
+ });
786
+ check();
787
+ let clean = false;
788
+ let key;
789
+ try {
790
+ key = await clearAndSave(selectedOwner, mode);
791
+ clean = true;
792
+ } catch (failure) {
793
+ check();
794
+ if (
795
+ failure.code === HISTORY_ERROR_CODES.METADATA ||
796
+ failure.code === HISTORY_ERROR_CODES.SETUP
797
+ )
798
+ throw failure;
799
+ error(HISTORY_ERROR_CODES.CLEANUP, failure);
800
+ }
801
+ check();
802
+ if (remainBlocked) {
803
+ ownerId = selectedOwner;
804
+ state = HISTORY_STATES.BLOCKED;
805
+ notify();
806
+ return status();
807
+ }
808
+ if (!selectedOwner || mode === HISTORY_MODES.OFF) {
809
+ ownerId = selectedOwner;
810
+ state = selectedOwner ? HISTORY_STATES.ACTIVE : HISTORY_STATES.IDLE;
811
+ notify();
812
+ return status();
813
+ }
814
+ return publish(
815
+ clean ? mode : HISTORY_MODES.MEMORY,
816
+ key,
817
+ bufferToken,
818
+ selectedOwner,
819
+ );
820
+ }
821
+
822
+ async function resume(id) {
823
+ const bufferToken = startupEpoch;
824
+ if (state === HISTORY_STATES.ACTIVE && ownerId && ownerId !== id)
825
+ throw error(HISTORY_ERROR_CODES.STALE);
826
+ if (ownerId === id && state === HISTORY_STATES.ACTIVE) {
827
+ if (metadata.cleanupPending) return retryCleanup();
828
+ return status();
829
+ }
830
+ const saved = read();
831
+ let fields;
832
+ try {
833
+ fields = await digestOwner(id, saved?.salt);
834
+ } catch {
835
+ throw block(HISTORY_ERROR_CODES.SETUP);
836
+ }
837
+ check();
838
+ if (bufferToken !== startupEpoch)
839
+ throw historyError(HISTORY_ERROR_CODES.STALE);
840
+ if (
841
+ saved &&
842
+ (saved.loggedOut ||
843
+ (!saved.ownerless && saved.ownerDigest !== fields.ownerDigest))
844
+ ) {
845
+ if (!ownerId) state = HISTORY_STATES.STALE;
846
+ notify();
847
+ throw error(HISTORY_ERROR_CODES.STALE);
848
+ }
849
+ if (
850
+ !saved ||
851
+ saved.ownerless ||
852
+ saved.cleanupPending ||
853
+ saved.mode !== HISTORY_MODES.PERSISTENT ||
854
+ !saved.key
855
+ )
856
+ return destructive(id, fields, requestedMode, {
857
+ reset: false,
858
+ keepStartup: true,
859
+ });
860
+ return publish(requestedMode, saved.key, startupEpoch, id);
861
+ }
862
+
863
+ async function setOwner(id) {
864
+ if (ownerId === id && state === HISTORY_STATES.ACTIVE) return status();
865
+ if (state === HISTORY_STATES.BLOCKED) read();
866
+ const bufferToken = startupEpoch;
867
+ let fields;
868
+ try {
869
+ fields = await digestOwner(id);
870
+ } catch {
871
+ throw block(HISTORY_ERROR_CODES.SETUP);
872
+ }
873
+ check();
874
+ if (bufferToken !== startupEpoch)
875
+ throw historyError(HISTORY_ERROR_CODES.STALE);
876
+ return destructive(id, fields, requestedMode, {
877
+ keepStartup: Boolean(startup) && (!ownerId || ownerId === id),
878
+ });
879
+ }
880
+
881
+ async function retryCleanup() {
882
+ if (!metadata?.cleanupPending) return status();
883
+ const previousState = state;
884
+ state = HISTORY_STATES.TRANSITIONING;
885
+ notify();
886
+ let key;
887
+ try {
888
+ key = await clearAndSave(ownerId, requestedMode);
889
+ } catch (failure) {
890
+ check();
891
+ if (
892
+ failure.code === HISTORY_ERROR_CODES.METADATA ||
893
+ failure.code === HISTORY_ERROR_CODES.SETUP
894
+ )
895
+ throw failure;
896
+ state = previousState;
897
+ notify();
898
+ error(HISTORY_ERROR_CODES.CLEANUP, failure);
899
+ return status();
900
+ }
901
+ check();
902
+ const previous = current;
903
+ if (previous && previous !== startup) await retire(previous, true);
904
+ check();
905
+ if (!ownerId || previousState === HISTORY_STATES.BLOCKED) {
906
+ state =
907
+ previousState === HISTORY_STATES.BLOCKED
908
+ ? HISTORY_STATES.BLOCKED
909
+ : HISTORY_STATES.IDLE;
910
+ notify();
911
+ return status();
912
+ }
913
+ return publish(requestedMode, key, startupEpoch, ownerId);
914
+ }
915
+
916
+ async function setMode(mode) {
917
+ if (state === HISTORY_STATES.BLOCKED)
918
+ throw historyError(setupFailure ?? HISTORY_ERROR_CODES.METADATA);
919
+ if (state === HISTORY_STATES.STALE)
920
+ throw historyError(HISTORY_ERROR_CODES.STALE);
921
+ if (mode === requestedMode) return status();
922
+ if (!ownerId) {
923
+ if (mode === HISTORY_MODES.OFF)
924
+ return destructive(
925
+ undefined,
926
+ { salt: metadata?.salt, ownerDigest: metadata?.ownerDigest },
927
+ mode,
928
+ );
929
+ requestedMode = mode;
930
+ save({
931
+ ...(metadata ?? {
932
+ version: 1,
933
+ leaseKey: lease.key,
934
+ revision: 1,
935
+ ownerless: true,
936
+ loggedOut: false,
937
+ cleanupPending: false,
938
+ levels: {},
939
+ }),
940
+ mode,
941
+ });
942
+ notify();
943
+ return status();
944
+ }
945
+ if (
946
+ requestedMode === HISTORY_MODES.MEMORY &&
947
+ mode === HISTORY_MODES.PERSISTENT &&
948
+ !metadata.cleanupPending &&
949
+ current
950
+ ) {
951
+ const previous = current;
952
+ state = HISTORY_STATES.TRANSITIONING;
953
+ notify();
954
+ let candidate;
955
+ let detach;
956
+ try {
957
+ await clearDisk();
958
+ check();
959
+ candidate = makeSession(mode, previous.key);
960
+ detach = previous.hub.addSink(candidate.store);
961
+ await copySession(previous.store, candidate.store);
962
+ check();
963
+ save({ ...metadata, mode, key: previous.key });
964
+ requestedMode = mode;
965
+ detach();
966
+ previous.valid = false;
967
+ current = candidate;
968
+ } catch (failure) {
969
+ detach?.();
970
+ if (candidate)
971
+ await retire(candidate, true).catch(() =>
972
+ error(HISTORY_ERROR_CODES.SETUP),
973
+ );
974
+ if (!stopped && state !== HISTORY_STATES.BLOCKED) {
975
+ current = previous;
976
+ state = HISTORY_STATES.ACTIVE;
977
+ notify();
978
+ }
979
+ throw failure.code === 'metadata' ||
980
+ failure.code === HISTORY_ERROR_CODES.SETUP ||
981
+ stopped ||
982
+ state === HISTORY_STATES.BLOCKED
983
+ ? failure
984
+ : error(HISTORY_ERROR_CODES.MIGRATION);
985
+ }
986
+ await retire(previous, true).catch(() =>
987
+ error(HISTORY_ERROR_CODES.CLEANUP),
988
+ );
989
+ check();
990
+ state = HISTORY_STATES.ACTIVE;
991
+ notify();
992
+ return status();
993
+ }
994
+ return destructive(
995
+ ownerId,
996
+ { salt: metadata.salt, ownerDigest: metadata.ownerDigest },
997
+ mode,
998
+ { reset: mode === HISTORY_MODES.OFF },
999
+ );
1000
+ }
1001
+ const enqueue = (action, argument, validate) => {
1002
+ if (validate && !validate(argument))
1003
+ return Promise.reject(new TypeError('Invalid history control argument'));
1004
+ if (stopped)
1005
+ return Promise.reject(historyError(HISTORY_ERROR_CODES.STOPPED));
1006
+ const operation = controlQueue.then(async () => {
1007
+ check();
1008
+ if (state === HISTORY_STATES.BLOCKED && action !== setMode)
1009
+ try {
1010
+ await drainInvalidSessions();
1011
+ } catch (failure) {
1012
+ check();
1013
+ throw error(HISTORY_ERROR_CODES.CLEANUP, failure);
1014
+ }
1015
+ return action(argument);
1016
+ });
1017
+ controlQueue = operation.catch(() => {});
1018
+ return operation;
1019
+ };
1020
+
1021
+ function forward(area, scope, binding, invalid) {
1022
+ const logger = {};
1023
+ for (const level of Object.values(constants.LEVELS))
1024
+ logger[level] = (message, data) => {
1025
+ const selected = current;
1026
+ if (
1027
+ forwarding ||
1028
+ !selected?.valid ||
1029
+ selected.paused ||
1030
+ stopped ||
1031
+ lease.signal.aborted
1032
+ )
1033
+ return;
1034
+ forwarding = true;
1035
+ try {
1036
+ if (invalid) throw invalid.error;
1037
+ let target = selected.session.logging.getLogger(area, scope);
1038
+ if (binding) target = target.child(binding);
1039
+ target[level](message, data);
1040
+ } catch (failure) {
1041
+ common.report(options.logging.onError, failure, constants.LOGGING_ERROR_SOURCES.DATA);
1042
+ } finally {
1043
+ forwarding = false;
1044
+ }
1045
+ };
1046
+ logger.child = (child = {}) => {
1047
+ if (invalid) return forward(area, scope, binding, invalid);
1048
+ try {
1049
+ const {
1050
+ scope: childScope = binding ? binding.scope : scope,
1051
+ context = {},
1052
+ } = child;
1053
+ return forward(area, scope, {
1054
+ scope: childScope,
1055
+ context: { ...binding?.context, ...common.copyBindings(context) },
1056
+ });
1057
+ } catch (failure) {
1058
+ return forward(area, scope, binding, { error: failure });
1059
+ }
1060
+ };
1061
+ return Object.freeze(logger);
1062
+ }
1063
+
1064
+ function stop() {
1065
+ if (stopPromise) return stopPromise;
1066
+ stopped = true;
1067
+ state = HISTORY_STATES.STOPPED;
1068
+ lease.signal.removeEventListener('abort', abort);
1069
+ current = null;
1070
+ const unexpected =
1071
+ lease.signal.aborted &&
1072
+ !['release', 'pagehide', 'stop'].includes(lease.signal.reason);
1073
+ discardStartup();
1074
+ for (const session of sessions)
1075
+ void retire(
1076
+ session,
1077
+ unexpected || session.startup || session.discardOnStop,
1078
+ );
1079
+ stopPromise = Promise.resolve()
1080
+ .then(async () => {
1081
+ await controlQueue;
1082
+ const cleanups = Array.from(sessions, (session) =>
1083
+ retire(
1084
+ session,
1085
+ unexpected || session.startup || session.discardOnStop,
1086
+ ),
1087
+ );
1088
+ await Promise.all([...retired, ...cleanups]);
1089
+ })
1090
+ .catch((failure) => {
1091
+ stopPromise = undefined;
1092
+ throw failure;
1093
+ });
1094
+ notify();
1095
+ return stopPromise;
1096
+ }
1097
+
1098
+ function abort() {
1099
+ void stop().catch((failure) => error(HISTORY_ERROR_CODES.SETUP, failure));
1100
+ }
1101
+ lease.signal.addEventListener('abort', abort, { once: true });
1102
+ try {
1103
+ read();
1104
+ if (requestedMode !== HISTORY_MODES.OFF && options.startup !== false) {
1105
+ startup = makeSession(HISTORY_MODES.MEMORY, common.randomHex(32), true);
1106
+ current = startup;
1107
+ }
1108
+ } catch {
1109
+ if (state !== HISTORY_STATES.BLOCKED) block(HISTORY_ERROR_CODES.SETUP);
1110
+ }
1111
+ const api = Object.freeze({
1112
+ getLogger(area, scope) {
1113
+ if (!areas.has(area) || typeof scope !== 'string')
1114
+ throw new TypeError('Invalid logger area or scope');
1115
+ const key = JSON.stringify([area, scope]);
1116
+ const cached = roots.get(key);
1117
+ if (cached) return cached;
1118
+ const logger = forward(area, scope);
1119
+ if (roots.size < constants.LOGGING_LIMITS.ROOT_LOGGER_CACHE_PAIRS) {
1120
+ roots.set(key, logger);
1121
+ return logger;
1122
+ }
1123
+ if (!rootLoggerLimitReported) {
1124
+ rootLoggerLimitReported = true;
1125
+ common.report(
1126
+ options.logging.onError,
1127
+ hub.loggingError(constants.LOGGING_ERROR_CODES.ROOT_LOGGER_CACHE_LIMIT),
1128
+ constants.LOGGING_ERROR_SOURCES.SCOPE,
1129
+ );
1130
+ }
1131
+ return logger;
1132
+ },
1133
+ resume: (id) =>
1134
+ enqueue(
1135
+ resume,
1136
+ id,
1137
+ (value) => typeof value === 'string' && value.length > 0,
1138
+ ),
1139
+ setOwner: (id) =>
1140
+ enqueue(
1141
+ setOwner,
1142
+ id,
1143
+ (value) => typeof value === 'string' && value.length > 0,
1144
+ ),
1145
+ logout() {
1146
+ discardStartup();
1147
+ return enqueue(async () => {
1148
+ if (state === HISTORY_STATES.BLOCKED) read();
1149
+ return destructive(undefined, {}, requestedMode, { logout: true });
1150
+ });
1151
+ },
1152
+
1153
+ setMode(mode) {
1154
+ if (mode === HISTORY_MODES.OFF) discardStartup();
1155
+ return enqueue(setMode, mode, (value) =>
1156
+ [
1157
+ HISTORY_MODES.OFF,
1158
+ HISTORY_MODES.MEMORY,
1159
+ HISTORY_MODES.PERSISTENT,
1160
+ ].includes(value),
1161
+ );
1162
+ },
1163
+
1164
+ clear() {
1165
+ discardStartup();
1166
+ return enqueue(async () => {
1167
+ if (state === HISTORY_STATES.STALE)
1168
+ throw historyError(HISTORY_ERROR_CODES.STALE);
1169
+ const remainBlocked = state === HISTORY_STATES.BLOCKED;
1170
+ if (remainBlocked) read();
1171
+ return destructive(
1172
+ ownerId,
1173
+ { salt: metadata?.salt, ownerDigest: metadata?.ownerDigest },
1174
+ requestedMode,
1175
+ { remainBlocked },
1176
+ );
1177
+ });
1178
+ },
1179
+ retryCleanup: () =>
1180
+ enqueue(async () => {
1181
+ if (state === HISTORY_STATES.STALE)
1182
+ throw historyError(HISTORY_ERROR_CODES.STALE);
1183
+ if (state === HISTORY_STATES.BLOCKED) read();
1184
+ return retryCleanup();
1185
+ }),
1186
+ getSession: () =>
1187
+ current?.valid && !current.paused ? current.session : null,
1188
+ getStatus: status,
1189
+ stop,
1190
+ });
1191
+ notify();
1192
+ return api;
1193
+ }
1194
+
1195
+ exports.createLogHistory = createLogHistory;
1196
+ //# sourceMappingURL=index.cjs.map