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