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.
- package/README.md +18 -7
- package/dist/advanced.cjs +1 -1
- package/dist/advanced.js +1 -1
- package/dist/index.cjs +395 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -11
- package/dist/index.d.ts +13 -11
- package/dist/index.js +395 -142
- package/dist/index.js.map +1 -1
- package/dist/{transform-A9Lx5R2l.js → transform-3mj0eStL.js} +147 -152
- package/dist/transform-3mj0eStL.js.map +1 -0
- package/dist/{transform-CSPGdBe5.cjs → transform-D45KWMzj.cjs} +148 -159
- package/dist/transform-D45KWMzj.cjs.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +3 -3
- package/skills/doxum-runtime/references/integrations.md +2 -2
- package/skills/doxum-runtime/references/invariants.md +6 -3
- package/skills/doxum-runtime/references/projections.md +48 -20
- package/skills/doxum-runtime/references/public-api.md +22 -21
- package/skills/doxum-runtime/references/recipes.md +25 -2
- package/dist/transform-A9Lx5R2l.js.map +0 -1
- package/dist/transform-CSPGdBe5.cjs.map +0 -1
|
@@ -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
|
|
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
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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-
|
|
1094
|
+
//# sourceMappingURL=transform-3mj0eStL.js.map
|