doxum 0.1.33 → 0.1.35

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.
@@ -185,6 +185,12 @@ const readNamedDependencyEntries = (dependencies, label) => {
185
185
  };
186
186
  /** Creates the durable dependency value exposed to pure derive callbacks. */
187
187
  const snapshotDependencyValue = (source) => source.kind === "value" ? source.value : snapshotCollectionView(source.read);
188
+ /** Builds the same readonly named value boundary for scalar and keyed pure computations. */
189
+ const snapshotProjectionValues = (names, sources) => {
190
+ const values = Object.create(null);
191
+ for (let index = 0; index < names.length; index++) values[names[index]] = snapshotDependencyValue(sources[index]);
192
+ return Object.freeze(values);
193
+ };
188
194
  /** Compiles the shared named Projection dependency declaration contract. */
189
195
  const compileProjectionDependencies = (dependencies, label) => {
190
196
  const names = [];
@@ -261,10 +267,11 @@ const createScheduler = (onError) => {
261
267
  const sources = /* @__PURE__ */ new Set();
262
268
  const processors = /* @__PURE__ */ new Set();
263
269
  const pendingSources = /* @__PURE__ */ new Set();
264
- const heap = [];
270
+ const dirty = /* @__PURE__ */ new Set();
265
271
  const queued = /* @__PURE__ */ new Set();
266
272
  const completed = /* @__PURE__ */ new Set();
267
273
  const emissions = /* @__PURE__ */ new Set();
274
+ const forced = /* @__PURE__ */ new Set();
268
275
  const errors = [];
269
276
  let reportingFailures = [];
270
277
  const guards = /* @__PURE__ */ new Set();
@@ -276,35 +283,19 @@ const createScheduler = (onError) => {
276
283
  if (phase !== "idle") throw new Error("Projection cannot be re-entered during input callbacks, processing or notification.");
277
284
  };
278
285
  const lock = (value) => guards.forEach((guard) => guard(value));
286
+ const markDirty = (producer) => {
287
+ const pending = [producer];
288
+ for (let index = 0; index < pending.length; index++) {
289
+ const current = pending[index];
290
+ if (current.disposed || dirty.has(current)) continue;
291
+ dirty.add(current);
292
+ for (const output of current.outputs) output.forEachConsumer((consumer) => pending.push(consumer));
293
+ }
294
+ };
279
295
  const enqueue = (processor) => {
280
296
  if (processor.disposed || queued.has(processor)) return;
281
297
  queued.add(processor);
282
298
  profile.projection("scheduledNodes");
283
- let index = heap.length;
284
- heap.push(processor);
285
- while (index > 0) {
286
- const parent = index - 1 >> 1;
287
- if (heap[parent].order < processor.order) break;
288
- heap[index] = heap[parent];
289
- index = parent;
290
- }
291
- heap[index] = processor;
292
- };
293
- const pop = () => {
294
- const first = heap[0];
295
- const last = heap.pop();
296
- if (heap.length) {
297
- let index = 0;
298
- while (index * 2 + 1 < heap.length) {
299
- let child = index * 2 + 1;
300
- if (child + 1 < heap.length && heap[child + 1].order < heap[child].order) child++;
301
- if (last.order < heap[child].order) break;
302
- heap[index] = heap[child];
303
- index = child;
304
- }
305
- heap[index] = last;
306
- }
307
- return first;
308
299
  };
309
300
  const errorFor = (producer, cause, kind = producer.kind === "source" ? "source" : "processor") => new ProjectionError(kind, cause);
310
301
  const capture = (source) => {
@@ -315,6 +306,7 @@ const createScheduler = (onError) => {
315
306
  throw source.fault;
316
307
  }
317
308
  pendingSources.add(source);
309
+ markDirty(source);
318
310
  profile.projection("sourceEvents");
319
311
  if (source.fault) errors.push(source.fault);
320
312
  };
@@ -325,88 +317,137 @@ const createScheduler = (onError) => {
325
317
  if (cause !== void 0) return cause;
326
318
  }
327
319
  };
328
- const settle = () => {
329
- if (disposed || depth || phase !== "idle") return;
330
- phase = "compute";
331
- lock(true);
332
- const cause = settleCause();
333
- try {
334
- for (const source of pendingSources) {
335
- if (source.disposed) continue;
336
- const wasFaulted = source.fault !== void 0;
337
- let changed = false;
338
- let force = false;
339
- if (!source.fault || source.recovering()) try {
340
- const prepared = source.prepare(cause);
341
- changed = prepared.changed;
342
- force = prepared.force;
343
- source.fault = void 0;
344
- } catch (failure) {
345
- source.fault = errorFor(source, failure, "source");
346
- errors.push(source.fault);
347
- }
348
- const recovered = wasFaulted && !source.fault;
349
- if (changed) source.publish();
350
- if (source.fault || changed || force || recovered) {
351
- const output = source.outputs[0];
352
- emissions.add(output);
353
- output.forEachConsumer(enqueue);
320
+ const propagate = (output, force = false) => {
321
+ emissions.add(output);
322
+ if (force) forced.add(output);
323
+ output.forEachConsumer(enqueue);
324
+ };
325
+ const advanceSource = (source, cause) => {
326
+ pendingSources.delete(source);
327
+ const wasFaulted = source.fault !== void 0;
328
+ let changed = false;
329
+ let force = false;
330
+ if (!source.fault || source.recovering()) try {
331
+ source.fault = void 0;
332
+ const prepared = source.prepare(cause);
333
+ changed = prepared.changed;
334
+ force = prepared.force;
335
+ source.fault = void 0;
336
+ } catch (failure) {
337
+ source.fault = errorFor(source, failure, "source");
338
+ errors.push(source.fault);
339
+ }
340
+ if (changed || force) source.publish();
341
+ if (source.fault || changed || force || wasFaulted) propagate(source.outputs[0], force);
342
+ source.clear();
343
+ };
344
+ const advanceProcessor = (processor, cause) => {
345
+ queued.delete(processor);
346
+ const wasFaulted = processor.fault !== void 0;
347
+ profile.projection("processedNodes");
348
+ let changedOutputs = [];
349
+ const blocked = processor.dependencies.find((output) => output.owner.fault || output.owner.disposed);
350
+ if (blocked) processor.fault = errorFor(processor, blocked.owner.fault ?? new ProjectionDisposedError(), "blocked");
351
+ else {
352
+ const reset = wasFaulted || processor.dependencies.some((output) => output.reset(processor));
353
+ try {
354
+ changedOutputs = processor.evaluate(reset, cause, wasFaulted);
355
+ const failure = processor.dependencies.find((output) => output.owner.fault)?.owner.fault;
356
+ if (failure) throw failure;
357
+ processor.fault = void 0;
358
+ } catch (failure) {
359
+ const error = errorFor(processor, failure);
360
+ processor.fault = error;
361
+ if (!reset) try {
362
+ changedOutputs = processor.evaluate(true, cause, true);
363
+ const dependencyFailure = processor.dependencies.find((output) => output.owner.fault)?.owner.fault;
364
+ if (dependencyFailure) throw dependencyFailure;
365
+ processor.fault = void 0;
366
+ errors.push(error);
367
+ } catch (rebuildFailure) {
368
+ processor.fault = errorFor(processor, rebuildFailure);
369
+ errors.push(processor.fault);
354
370
  }
371
+ else errors.push(error);
355
372
  }
356
- while (heap.length) {
357
- const processor = pop();
358
- if (processor.disposed) continue;
359
- const wasFaulted = processor.fault !== void 0;
360
- profile.projection("processedNodes");
361
- let changedOutputs = Object.freeze([]);
362
- const blocked = processor.dependencies.find((output) => output.owner.fault || output.owner.disposed);
363
- if (blocked) processor.fault = errorFor(processor, blocked.owner.fault ?? new ProjectionDisposedError(), "blocked");
364
- else {
365
- const reset = wasFaulted || processor.dependencies.some((output) => output.reset());
366
- try {
367
- changedOutputs = processor.evaluate(reset, cause, wasFaulted);
368
- const dependencyFailure = processor.dependencies.find((output) => output.owner.fault)?.owner.fault;
369
- if (dependencyFailure) throw dependencyFailure;
370
- processor.fault = void 0;
371
- } catch (failure) {
372
- const error = errorFor(processor, failure);
373
- processor.fault = error;
374
- if (!reset) try {
375
- changedOutputs = processor.evaluate(true, cause, true);
376
- const dependencyFailure = processor.dependencies.find((output) => output.owner.fault)?.owner.fault;
377
- if (dependencyFailure) throw dependencyFailure;
378
- processor.fault = void 0;
379
- errors.push(error);
380
- } catch (rebuildFailure) {
381
- processor.fault = errorFor(processor, rebuildFailure);
382
- errors.push(processor.fault);
383
- }
384
- else errors.push(error);
373
+ }
374
+ completed.add(processor);
375
+ if (processor.fault) processor.outputs.forEach((output) => propagate(output));
376
+ else {
377
+ if (changedOutputs.length) processor.publish();
378
+ (wasFaulted && !processor.fault ? processor.outputs : changedOutputs).forEach((output) => propagate(output));
379
+ }
380
+ if (processor.fault || changedOutputs.length) profile.projection("publishedNodes");
381
+ };
382
+ const advance = (target, cause) => {
383
+ const stack = [{
384
+ producer: target,
385
+ dependency: 0
386
+ }];
387
+ while (stack.length) {
388
+ const frame = stack[stack.length - 1];
389
+ const { producer } = frame;
390
+ if (producer.disposed || !dirty.has(producer)) {
391
+ dirty.delete(producer);
392
+ stack.pop();
393
+ continue;
394
+ }
395
+ if (producer.kind === "processor") {
396
+ let dependency;
397
+ while (frame.dependency < producer.dependencies.length) {
398
+ const candidate = producer.dependencies[frame.dependency++].owner;
399
+ if (dirty.has(candidate)) {
400
+ dependency = candidate;
401
+ break;
385
402
  }
386
403
  }
387
- completed.add(processor);
388
- const recovered = wasFaulted && !processor.fault;
389
- if (processor.fault) processor.outputs.forEach((output) => {
390
- emissions.add(output);
391
- output.forEachConsumer(enqueue);
392
- });
393
- else {
394
- if (changedOutputs.length) {
395
- processor.publish();
396
- changedOutputs.forEach((output) => emissions.add(output));
397
- }
398
- (recovered ? processor.outputs : changedOutputs).forEach((output) => output.forEachConsumer(enqueue));
404
+ if (dependency) {
405
+ stack.push({
406
+ producer: dependency,
407
+ dependency: 0
408
+ });
409
+ continue;
399
410
  }
400
- if (processor.fault || changedOutputs.length) profile.projection("publishedNodes");
401
411
  }
412
+ stack.pop();
413
+ dirty.delete(producer);
414
+ if (producer.kind === "source") advanceSource(producer, cause);
415
+ else if (queued.has(producer)) if (producer.fault || producer.dependencies.some((output) => output.owner.fault || output.pending(producer))) advanceProcessor(producer, cause);
416
+ else {
417
+ queued.delete(producer);
418
+ producer.dependencies.forEach((output) => output.acknowledge(producer));
419
+ }
420
+ }
421
+ };
422
+ const compute = (run) => {
423
+ phase = "compute";
424
+ lock(true);
425
+ try {
426
+ return run();
402
427
  } finally {
403
428
  lock(false);
404
429
  phase = "idle";
405
430
  }
406
431
  };
432
+ const settle = () => {
433
+ if (disposed || depth || phase !== "idle") return;
434
+ const cause = settleCause();
435
+ compute(() => {
436
+ for (const producer of dirty) advance(producer, cause);
437
+ });
438
+ };
439
+ const ensureCurrent = (output) => {
440
+ assertActive();
441
+ if (phase === "notify") return;
442
+ assertIdle();
443
+ if (dirty.has(output.owner)) {
444
+ const cause = settleCause();
445
+ compute(() => advance(output.owner, cause));
446
+ }
447
+ };
407
448
  const flush = () => {
408
449
  if (disposed || depth) return [];
409
- if (pendingSources.size || completed.size) profile.projection("flushes");
450
+ if (emissions.size || completed.size) profile.projection("flushes");
410
451
  phase = "notify";
411
452
  lock(true);
412
453
  const reportFailures = [];
@@ -417,7 +458,7 @@ const createScheduler = (onError) => {
417
458
  } catch (cause) {
418
459
  errors.push(errorFor(output.owner, cause, "listener"));
419
460
  }
420
- }));
461
+ }, forced.has(output) || Boolean(output.owner.fault)));
421
462
  for (const error of errors.slice()) try {
422
463
  onError(error);
423
464
  } catch (cause) {
@@ -431,12 +472,13 @@ const createScheduler = (onError) => {
431
472
  error
432
473
  }))]);
433
474
  } finally {
434
- pendingSources.forEach((source) => source.clear());
475
+ emissions.forEach((output) => output.finish());
435
476
  completed.forEach((processor) => processor.clear());
436
477
  pendingSources.clear();
437
478
  queued.clear();
438
479
  completed.clear();
439
480
  emissions.clear();
481
+ forced.clear();
440
482
  errors.length = 0;
441
483
  reportingFailures = reportFailures;
442
484
  lock(false);
@@ -482,6 +524,7 @@ const createScheduler = (onError) => {
482
524
  assertIdle();
483
525
  if (producer.outputs.some((output) => output.hasConsumers())) throw new Error("Projection producer is still used by another processor.");
484
526
  producer.disposed = true;
527
+ dirty.delete(producer);
485
528
  if (producer.kind === "processor") {
486
529
  producer.dependencies.forEach((output) => output.detachConsumer(producer));
487
530
  processors.delete(producer);
@@ -510,6 +553,7 @@ const createScheduler = (onError) => {
510
553
  assertIdle,
511
554
  capture,
512
555
  settle,
556
+ ensureCurrent,
513
557
  flush,
514
558
  run,
515
559
  registerGuard(guard) {
@@ -535,7 +579,7 @@ const createScheduler = (onError) => {
535
579
  processor.dependencies.forEach((output) => output.attachConsumer(processor));
536
580
  },
537
581
  releaseProducer,
538
- order: () => sequence++,
582
+ nextId: () => sequence++,
539
583
  acceptInput(callback) {
540
584
  assertIdle();
541
585
  phase = "input";
@@ -549,14 +593,7 @@ const createScheduler = (onError) => {
549
593
  },
550
594
  initialize(callback) {
551
595
  assertIdle();
552
- phase = "compute";
553
- lock(true);
554
- try {
555
- return callback();
556
- } finally {
557
- lock(false);
558
- phase = "idle";
559
- }
596
+ return compute(callback);
560
597
  },
561
598
  batch,
562
599
  batchContext: () => activeBatch,
@@ -584,10 +621,11 @@ const createScheduler = (onError) => {
584
621
  processors.clear();
585
622
  sources.clear();
586
623
  pendingSources.clear();
587
- heap.length = 0;
624
+ dirty.clear();
588
625
  queued.clear();
589
626
  completed.clear();
590
627
  emissions.clear();
628
+ forced.clear();
591
629
  guards.clear();
592
630
  errors.length = 0;
593
631
  reportingFailures = [];
@@ -921,23 +959,6 @@ const transitionOrderChanged = (before, after, removed, added) => {
921
959
  if (left.value !== right.value) return true;
922
960
  }
923
961
  };
924
- const nextPresentIn = (iterator, membership) => {
925
- while (true) {
926
- const next = iterator.next();
927
- if (next.done || membership.has(next.value)) return next;
928
- }
929
- };
930
- /** Compares common-member order between two map snapshots without key snapshots or closures. */
931
- const mapOrderChanged = (before, after) => {
932
- const beforeIterator = before.keys();
933
- const afterIterator = after.keys();
934
- while (true) {
935
- const left = nextPresentIn(beforeIterator, after);
936
- const right = nextPresentIn(afterIterator, before);
937
- if (left.done || right.done) return left.done !== right.done;
938
- if (left.value !== right.value) return true;
939
- }
940
- };
941
962
  const sealIncrementalChange = (added, updated, removed, order) => {
942
963
  if (!added.length && !updated.length && !removed.length && !order) return void 0;
943
964
  return Object.freeze({
@@ -961,32 +982,6 @@ const createCollectionChange = (input) => {
961
982
  after: input.afterOrder
962
983
  } : void 0);
963
984
  };
964
- /** Exact fallback diff between two published map-like snapshots. */
965
- const diffCollection = (previous, current, isEqual = Object.is) => {
966
- const added = [];
967
- const updated = [];
968
- const removed = [];
969
- for (const [key, before] of previous) if (!current.has(key)) removed.push({
970
- key,
971
- before
972
- });
973
- else {
974
- const after = current.get(key);
975
- if (!isEqual(before, after)) updated.push({
976
- key,
977
- before,
978
- after
979
- });
980
- }
981
- for (const [key, after] of current) if (!previous.has(key)) added.push({
982
- key,
983
- after
984
- });
985
- return sealIncrementalChange(added, updated, removed, mapOrderChanged(previous, current) ? {
986
- before: Object.freeze([...previous.keys()]),
987
- after: Object.freeze([...current.keys()])
988
- } : void 0);
989
- };
990
985
  const changedKeys = function* (change) {
991
986
  if (change.kind !== "incremental") throw new TypeError("collectionChange.keys requires an incremental CollectionChange.");
992
987
  for (const entry of change.added) yield entry.key;
@@ -1094,6 +1089,6 @@ const createKeyedTransform = (definition) => {
1094
1089
  return projection;
1095
1090
  };
1096
1091
  //#endregion
1097
- export { isProjection as A, compileProjectionDependencies as C, snapshotCollectionView as D, mapRead as E, producerOf as M, projectionRef as N, defineProcessor as O, profile as P, collectKeyedInvalidation as S, collectionView as T, assertScope as _, collectionChangeTouchesKey as a, ProjectionDisposedError as b, diffCollection as c, createKeyedDependencyRuntime as d, outputKind as f, snapshotArray as g, sameArray as h, collectionChangeIntersects as i, ownProjections as j, defineSource as k, assertKeyedProjection as l, iterableMatchesArray as m, keyedAbsent as n, collectionHasStructuralChange as o, createKeyRelation as p, collectionChange as r, createCollectionChange as s, createKeyedTransform as t, compileKeyedDependencies as u, assertSynchronous as v, snapshotDependencyValue as w, ProjectionError as x, createScheduler as y };
1092
+ export { ownProjections as A, snapshotProjectionValues as C, defineProcessor as D, snapshotCollectionView as E, projectionRef as M, profile as N, defineSource as O, compileProjectionDependencies as S, mapRead as T, assertSynchronous as _, collectionChangeTouchesKey as a, ProjectionError as b, assertKeyedProjection as c, outputKind as d, createKeyRelation as f, assertScope as g, snapshotArray as h, collectionChangeIntersects as i, producerOf as j, isProjection as k, compileKeyedDependencies as l, sameArray as m, keyedAbsent as n, collectionHasStructuralChange as o, iterableMatchesArray as p, collectionChange as r, createCollectionChange as s, createKeyedTransform as t, createKeyedDependencyRuntime as u, createScheduler as v, collectionView as w, collectKeyedInvalidation as x, ProjectionDisposedError as y };
1098
1093
 
1099
- //# sourceMappingURL=transform-A9Lx5R2l.js.map
1094
+ //# sourceMappingURL=transform-3mj0eStL.js.map