@mmstack/primitives 22.10.3 → 22.11.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.
- package/README.md +22 -1
- package/fesm2022/mmstack-primitives.mjs +137 -29
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/package.json +1 -1
- package/types/mmstack-primitives.d.ts +90 -8
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ npm install @mmstack/primitives
|
|
|
21
21
|
- [Effects](#effects) — `nestedEffect`
|
|
22
22
|
- [Concurrency & transitions](#concurrency--transitions) — `keepPrevious`, keep-alive (`MmActivity`), `pausable*` / `providePausableOptions`, Suspense (`mm-suspense`), hold-and-swap (`*mmTransition`), per-element morphs (`mmViewTransitionName`), async derivations (`latest` / `use`), `deferredValue`, `startTransition` / `startTransaction`, `holdUntilReady`
|
|
23
23
|
- [History & persistence](#history--persistence) — `withHistory`, `storeHistory`, `stored`, `persistedStore`, `tabSync`, `opLog`
|
|
24
|
-
- [Sync & convergence](#sync--convergence) — `opSync`, `tabSync(store)`, merge policies (`lww`, `mergeThree`, `keyedArray`, `preserve`), `Conflicted`, keyed containers (`
|
|
24
|
+
- [Sync & convergence](#sync--convergence) — `opSync`, `tabSync(store)`, merge policies (`lww`, `mergeThree`, `keyedArray`, `preserve`), `Conflicted`, keyed containers (`keyedContainer`, `wrappedContainer`, `orderedEntries`, `posBetween`), `rebaseOps`, `policyStrategy`, `syncedFork`
|
|
25
25
|
- [Observability](#observability) — `provideConcurrencyInstrumentation`, `perfCustomTracks`
|
|
26
26
|
- [Performance helpers](#performance-helpers) — `chunked`, `pooled` / `pooledArray` / `pooledMap` / `pooledSet`
|
|
27
27
|
- [Sensors](#sensors) — `sensor()` facade + browser-state signals
|
|
@@ -369,8 +369,11 @@ The foundation of stale-while-revalidate. Wraps a signal so it **holds its last
|
|
|
369
369
|
import { keepPrevious } from '@mmstack/primitives';
|
|
370
370
|
|
|
371
371
|
const held = keepPrevious(resource.value); // drops to undefined mid-reload → keeps last value
|
|
372
|
+
const rows = keepPrevious(resource.value, { fallback: [] }); // [] only until the first value lands
|
|
372
373
|
```
|
|
373
374
|
|
|
375
|
+
`fallback` is yielded only while nothing has ever been defined; after the first defined value the previous value covers every gap, never the fallback. Like any linked signal the hold is lazy: it carries a value it has computed with, so place it over the value your readers read — reading is what feeds it. `@mmstack/resource` does exactly that for its `keepPrevious` option.
|
|
376
|
+
|
|
374
377
|
If the source is writable, `set` / `update` / `asReadonly` (and `mutate` / `inline` / `from` for mutable / derived sources) are forwarded through, so it stays a drop-in replacement. `@mmstack/resource` uses it under the hood for its `keepPrevious` option.
|
|
375
378
|
|
|
376
379
|
### Keep-alive — `MmActivity` / `injectPaused` / `providePaused`
|
|
@@ -792,6 +795,24 @@ const board = tabSync(store({ title: 'Board', todos: [] }), {
|
|
|
792
795
|
|
|
793
796
|
A **merge policy** decides the result when two peers change one path at once: `lww` (default), `mergeThree` (three-way against the common ancestor), `keyedArray(idFn)` (list reconcile by identity), or `preserve` (both sides survive as a `Conflicted` value; `isConflicted(v)` narrows it, resolution is a later write). `rebaseOps(root, pending, remote, policies)` is the pure invert-apply-reapply routine behind optimistic updates and offline queues, and `policyStrategy(policies)` gives a `forkStore` the same per-path resolution. This is what [`@mmstack/mesh`](https://www.npmjs.com/package/@mmstack/mesh) wraps for multiplayer.
|
|
794
797
|
|
|
798
|
+
### Keyed containers
|
|
799
|
+
|
|
800
|
+
**A list several peers reorder is a record keyed by element id, never an array.** Each element carries a fractional position at `~pos`, so an insert is one write at `[list, id]` and a move is one write at `[list, id, '~pos']` — two peers inserting into the same list at once keep both elements, where one whole-array write would have folded over the other.
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
import { keyedContainer } from '@mmstack/primitives';
|
|
804
|
+
|
|
805
|
+
const board = store<{ todos: Record<string, Todo> }>({ todos: {} });
|
|
806
|
+
const todos = keyedContainer({ key: (t: Todo) => t.id }); // or pass the key to insert
|
|
807
|
+
|
|
808
|
+
todos.insert(board.todos, { id: 't1', title: 'Ship it' }, 0);
|
|
809
|
+
todos.move(board.todos, 't1', 3); // writes the position and nothing else
|
|
810
|
+
todos.entries(board.todos()); // reading order: by ~pos, key breaking ties
|
|
811
|
+
todos.rebalance(sync, board.todos); // authority sweep when positions grow long
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
Reading order is a pure function of the materialized value, so every replica agrees without consulting the op log. `wrappedContainer` stores elements as `{ '~pos', value }` instead, keeping the payload a closed record a schema can validate; the choice is fixed when the container is created and never inferred from data, so peers of a synced container must agree on it. `posBetween(before, after)` is the fractional index underneath.
|
|
815
|
+
|
|
795
816
|
## Observability
|
|
796
817
|
|
|
797
818
|
An optional listener seam on the concurrency layer. `provideConcurrencyInstrumentation(listener)` receives events as transition scopes coordinate pending, suspense, and transaction windows; with no listener the taps are no-ops. `perfCustomTracks()` is a ready listener that writes each window to a Chrome DevTools Performance track, and the window hooks are span-shaped, so forwarding to [`@mmstack/telemetry-core`](https://www.npmjs.com/package/@mmstack/telemetry-core) is a direct mapping.
|
|
@@ -218,10 +218,10 @@ class MmActivity {
|
|
|
218
218
|
else
|
|
219
219
|
this.view.detach();
|
|
220
220
|
}
|
|
221
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
222
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
221
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MmActivity, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
222
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: MmActivity, isStandalone: true, selector: "[mmActivity]", inputs: { visible: { classPropertyName: "visible", publicName: "mmActivity", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
223
223
|
}
|
|
224
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
224
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MmActivity, decorators: [{
|
|
225
225
|
type: Directive,
|
|
226
226
|
args: [{
|
|
227
227
|
selector: '[mmActivity]',
|
|
@@ -1129,10 +1129,10 @@ class SuspenseBoundaryBase {
|
|
|
1129
1129
|
pending = this.scope.pending;
|
|
1130
1130
|
suspended = computed(() => this.scope.suspended(this.type()), /* @ts-ignore */
|
|
1131
1131
|
...(ngDevMode ? [{ debugName: "suspended" }] : /* istanbul ignore next */ []));
|
|
1132
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
1133
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
1132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: SuspenseBoundaryBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1133
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: SuspenseBoundaryBase, isStandalone: true, inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
1134
1134
|
}
|
|
1135
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
1135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: SuspenseBoundaryBase, decorators: [{
|
|
1136
1136
|
type: Directive
|
|
1137
1137
|
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
|
|
1138
1138
|
const SUSPENSE_TEMPLATE = `
|
|
@@ -1160,10 +1160,10 @@ const SUSPENSE_HOST = {
|
|
|
1160
1160
|
* `provideTransitionScope()`. The common case.
|
|
1161
1161
|
*/
|
|
1162
1162
|
class SuspenseBoundary extends SuspenseBoundaryBase {
|
|
1163
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
1164
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
1163
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: SuspenseBoundary, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1164
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: SuspenseBoundary, isStandalone: true, selector: "mm-suspense", host: { properties: { "attr.aria-busy": "pending() ? true : null" } }, providers: [provideTransitionScope()], usesInheritance: true, ngImport: i0, template: "\n @if (suspended()) {\n <ng-content select=\"[placeholder]\"><span>Loading\u2026</span></ng-content>\n } @else {\n @if (pending()) {\n <ng-content select=\"[busy]\" />\n }\n <ng-content />\n }\n", isInline: true, styles: [":host{display:contents}\n"] });
|
|
1165
1165
|
}
|
|
1166
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
1166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: SuspenseBoundary, decorators: [{
|
|
1167
1167
|
type: Component,
|
|
1168
1168
|
args: [{ selector: 'mm-suspense', template: SUSPENSE_TEMPLATE, host: SUSPENSE_HOST, providers: [provideTransitionScope()], styles: [":host{display:contents}\n"] }]
|
|
1169
1169
|
}] });
|
|
@@ -1174,10 +1174,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
1174
1174
|
* ancestor.
|
|
1175
1175
|
*/
|
|
1176
1176
|
class UnscopedSuspenseBoundary extends SuspenseBoundaryBase {
|
|
1177
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
1178
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
1177
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: UnscopedSuspenseBoundary, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1178
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: UnscopedSuspenseBoundary, isStandalone: true, selector: "mm-unscoped-suspense", host: { properties: { "attr.aria-busy": "pending() ? true : null" } }, usesInheritance: true, ngImport: i0, template: "\n @if (suspended()) {\n <ng-content select=\"[placeholder]\"><span>Loading\u2026</span></ng-content>\n } @else {\n @if (pending()) {\n <ng-content select=\"[busy]\" />\n }\n <ng-content />\n }\n", isInline: true, styles: [":host{display:contents}\n"] });
|
|
1179
1179
|
}
|
|
1180
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
1180
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: UnscopedSuspenseBoundary, decorators: [{
|
|
1181
1181
|
type: Component,
|
|
1182
1182
|
args: [{ selector: 'mm-unscoped-suspense', template: SUSPENSE_TEMPLATE, host: SUSPENSE_HOST, styles: [":host{display:contents}\n"] }]
|
|
1183
1183
|
}] });
|
|
@@ -1440,10 +1440,10 @@ class MmTransition {
|
|
|
1440
1440
|
node.style.display = hidden ? 'none' : '';
|
|
1441
1441
|
}
|
|
1442
1442
|
}
|
|
1443
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
1444
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
1443
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MmTransition, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1444
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: MmTransition, isStandalone: true, selector: "[mmTransition]", inputs: { value: { classPropertyName: "value", publicName: "mmTransition", isSignal: true, isRequired: true, transformFunction: null }, immediate: { classPropertyName: "immediate", publicName: "mmTransitionImmediate", isSignal: true, isRequired: false, transformFunction: null }, viewTransition: { classPropertyName: "viewTransition", publicName: "mmTransitionViewTransition", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["mmTransition"], ngImport: i0 });
|
|
1445
1445
|
}
|
|
1446
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
1446
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MmTransition, decorators: [{
|
|
1447
1447
|
type: Directive,
|
|
1448
1448
|
args: [{
|
|
1449
1449
|
selector: '[mmTransition]',
|
|
@@ -1488,10 +1488,10 @@ class MmViewTransitionName {
|
|
|
1488
1488
|
el.style.removeProperty('view-transition-name');
|
|
1489
1489
|
});
|
|
1490
1490
|
}
|
|
1491
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
1492
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
1491
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MmViewTransitionName, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1492
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: MmViewTransitionName, isStandalone: true, selector: "[mmViewTransitionName]", inputs: { mmViewTransitionName: { classPropertyName: "mmViewTransitionName", publicName: "mmViewTransitionName", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
1493
1493
|
}
|
|
1494
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
1494
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MmViewTransitionName, decorators: [{
|
|
1495
1495
|
type: Directive,
|
|
1496
1496
|
args: [{ selector: '[mmViewTransitionName]' }]
|
|
1497
1497
|
}], ctorParameters: () => [], propDecorators: { mmViewTransitionName: [{ type: i0.Input, args: [{ isSignal: true, alias: "mmViewTransitionName", required: true }] }] } });
|
|
@@ -1890,14 +1890,19 @@ function isDerivation(sig) {
|
|
|
1890
1890
|
|
|
1891
1891
|
function keepPrevious(src, opt) {
|
|
1892
1892
|
const mutableSrc = isWritableSignal$2(src) && isMutable(src);
|
|
1893
|
+
const { fallback, ...signalOpt } = opt ?? {};
|
|
1893
1894
|
let cnt = 0;
|
|
1894
1895
|
const baseEqual = opt?.equal;
|
|
1895
1896
|
const equal = mutableSrc
|
|
1896
1897
|
? (a, b) => cnt > 0 ? false : baseEqual ? baseEqual(a, b) : Object.is(a, b)
|
|
1897
1898
|
: baseEqual;
|
|
1898
|
-
const persisted = linkedSignal({ ...(ngDevMode ? { debugName: "persisted" } : /* istanbul ignore next */ {}), ...
|
|
1899
|
+
const persisted = linkedSignal({ ...(ngDevMode ? { debugName: "persisted" } : /* istanbul ignore next */ {}), ...signalOpt,
|
|
1899
1900
|
source: () => src(),
|
|
1900
|
-
computation: (next, prev) =>
|
|
1901
|
+
computation: (next, prev) => {
|
|
1902
|
+
if (next !== undefined)
|
|
1903
|
+
return next;
|
|
1904
|
+
return prev !== undefined ? prev.value : fallback;
|
|
1905
|
+
},
|
|
1901
1906
|
equal });
|
|
1902
1907
|
if (isWritableSignal$2(src)) {
|
|
1903
1908
|
persisted.set = src.set;
|
|
@@ -5152,6 +5157,81 @@ function validateEnvelope(env) {
|
|
|
5152
5157
|
}
|
|
5153
5158
|
return null;
|
|
5154
5159
|
}
|
|
5160
|
+
function wireValueViolation(value) {
|
|
5161
|
+
return violationAt(value, false, []);
|
|
5162
|
+
}
|
|
5163
|
+
function violationAt(value, nested, ancestors) {
|
|
5164
|
+
if (value === undefined)
|
|
5165
|
+
return nested ? 'undefined-in-container' : null;
|
|
5166
|
+
if (value === null)
|
|
5167
|
+
return null;
|
|
5168
|
+
const t = typeof value;
|
|
5169
|
+
if (t === 'boolean' || t === 'string')
|
|
5170
|
+
return null;
|
|
5171
|
+
if (t === 'number') {
|
|
5172
|
+
if (!Number.isFinite(value))
|
|
5173
|
+
return 'non-finite-number';
|
|
5174
|
+
return Object.is(value, -0) ? 'negative-zero' : null;
|
|
5175
|
+
}
|
|
5176
|
+
if (t === 'bigint')
|
|
5177
|
+
return 'bigint';
|
|
5178
|
+
if (t === 'function')
|
|
5179
|
+
return 'function';
|
|
5180
|
+
if (t === 'symbol')
|
|
5181
|
+
return 'symbol';
|
|
5182
|
+
if (ancestors.includes(value))
|
|
5183
|
+
return 'cycle';
|
|
5184
|
+
ancestors.push(value);
|
|
5185
|
+
try {
|
|
5186
|
+
if (Array.isArray(value)) {
|
|
5187
|
+
for (let i = 0; i < value.length; i++) {
|
|
5188
|
+
if (!Object.hasOwn(value, i))
|
|
5189
|
+
return 'sparse-array';
|
|
5190
|
+
const reason = violationAt(value[i], true, ancestors);
|
|
5191
|
+
if (reason)
|
|
5192
|
+
return reason;
|
|
5193
|
+
}
|
|
5194
|
+
const keys = Object.keys(value);
|
|
5195
|
+
if (keys.length !== value.length)
|
|
5196
|
+
return 'array-named-property';
|
|
5197
|
+
for (const key of keys) {
|
|
5198
|
+
const idx = Number(key);
|
|
5199
|
+
if (String(idx) !== key ||
|
|
5200
|
+
!Number.isInteger(idx) ||
|
|
5201
|
+
idx < 0 ||
|
|
5202
|
+
idx >= value.length) {
|
|
5203
|
+
return 'array-named-property';
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
return null;
|
|
5207
|
+
}
|
|
5208
|
+
const proto = Object.getPrototypeOf(value);
|
|
5209
|
+
if (proto !== Object.prototype && proto !== null)
|
|
5210
|
+
return 'non-plain-object';
|
|
5211
|
+
for (const key of Object.keys(value)) {
|
|
5212
|
+
const reason = violationAt(value[key], true, ancestors);
|
|
5213
|
+
if (reason)
|
|
5214
|
+
return reason;
|
|
5215
|
+
}
|
|
5216
|
+
return null;
|
|
5217
|
+
}
|
|
5218
|
+
finally {
|
|
5219
|
+
ancestors.pop();
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
// dev-only: one warning per offending batch names the first violating op, then stops
|
|
5223
|
+
const lintWireValues = (ops) => {
|
|
5224
|
+
for (const op of ops) {
|
|
5225
|
+
const reason = (op.kind === 'set' ? wireValueViolation(op.next) : null) ??
|
|
5226
|
+
(op.kind !== 'clear' && Object.hasOwn(op, 'prev')
|
|
5227
|
+
? wireValueViolation(op.prev)
|
|
5228
|
+
: null);
|
|
5229
|
+
if (!reason)
|
|
5230
|
+
continue;
|
|
5231
|
+
console.warn(`[@mmstack/primitives] op value at "${op.path.join('.')}" will not survive a JSON transport (${reason}): peers materialize a different value than this emitter, and the room can diverge. Op values must round-trip JSON unchanged; encode rich leaves as strings.`);
|
|
5232
|
+
return;
|
|
5233
|
+
}
|
|
5234
|
+
};
|
|
5155
5235
|
const lww = (_ancestor, mine) => mine;
|
|
5156
5236
|
const mergeThree = (ancestor, mine, theirs) => merge3(ancestor, mine, theirs);
|
|
5157
5237
|
const preserve = (ancestor, mine, theirs) => ({
|
|
@@ -5906,6 +5986,8 @@ function opSync(source, opt) {
|
|
|
5906
5986
|
const emitLocal = (ops) => {
|
|
5907
5987
|
const frontier = scopeFrontier;
|
|
5908
5988
|
const stamped = conv.stamp(ops, { bump: bumping, frontier });
|
|
5989
|
+
if (isDevMode())
|
|
5990
|
+
lintWireValues(stamped);
|
|
5909
5991
|
const nextVersion = (versions.get(origin) ?? 0) + 1;
|
|
5910
5992
|
const env = {
|
|
5911
5993
|
proto: OP_PROTO_VERSION,
|
|
@@ -6177,11 +6259,14 @@ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v)
|
|
|
6177
6259
|
* deterministic on every replica.
|
|
6178
6260
|
*/
|
|
6179
6261
|
function orderedEntries(container) {
|
|
6262
|
+
return entriesOf(container, (element) => element);
|
|
6263
|
+
}
|
|
6264
|
+
function entriesOf(container, payloadOf) {
|
|
6180
6265
|
const entries = [];
|
|
6181
6266
|
for (const key of Object.keys(container)) {
|
|
6182
|
-
const
|
|
6183
|
-
const raw = isRecord(
|
|
6184
|
-
entries.push({ key, pos: typeof raw === 'string' ? raw : '', value });
|
|
6267
|
+
const element = container[key];
|
|
6268
|
+
const raw = isRecord(element) ? element[POS_SEGMENT] : undefined;
|
|
6269
|
+
entries.push({ key, pos: typeof raw === 'string' ? raw : '', value: payloadOf(element) });
|
|
6185
6270
|
}
|
|
6186
6271
|
entries.sort((a, b) => a.pos < b.pos ? -1 : a.pos > b.pos ? 1 : a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
6187
6272
|
return entries;
|
|
@@ -6201,12 +6286,18 @@ const neighborPositions = (entries, index) => {
|
|
|
6201
6286
|
* layer diffs on its own. Returns the assigned position. Re-inserting an existing key overwrites it.
|
|
6202
6287
|
*/
|
|
6203
6288
|
function insertElement(container, key, value, index) {
|
|
6289
|
+
return insertInto(container, key, value, index, inlineElement);
|
|
6290
|
+
}
|
|
6291
|
+
const inlineElement = (value, pos) => {
|
|
6204
6292
|
if (POS_SEGMENT in value)
|
|
6205
|
-
devError(`
|
|
6293
|
+
devError(`insert: '${POS_SEGMENT}' is managed, drop it from the value`);
|
|
6294
|
+
return { ...value, [POS_SEGMENT]: pos };
|
|
6295
|
+
};
|
|
6296
|
+
function insertInto(container, key, value, index, elementOf) {
|
|
6206
6297
|
const entries = orderedEntries(container()).filter((e) => e.key !== key);
|
|
6207
6298
|
const [before, after] = neighborPositions(entries, index ?? entries.length);
|
|
6208
6299
|
const pos = posBetween(before, after);
|
|
6209
|
-
container.update((c) => ({ ...c, [key]:
|
|
6300
|
+
container.update((c) => ({ ...c, [key]: elementOf(value, pos) }));
|
|
6210
6301
|
return pos;
|
|
6211
6302
|
}
|
|
6212
6303
|
/**
|
|
@@ -6273,6 +6364,23 @@ function evenPositions(n) {
|
|
|
6273
6364
|
}
|
|
6274
6365
|
return out;
|
|
6275
6366
|
}
|
|
6367
|
+
function keyedContainer(config = {}) {
|
|
6368
|
+
return helpersFor(config.key, inlineElement, (element) => element);
|
|
6369
|
+
}
|
|
6370
|
+
function wrappedContainer(config = {}) {
|
|
6371
|
+
return helpersFor(config.key, (value, pos) => ({ [POS_SEGMENT]: pos, value }), (element) => element.value);
|
|
6372
|
+
}
|
|
6373
|
+
function helpersFor(extract, elementOf, payloadOf) {
|
|
6374
|
+
return {
|
|
6375
|
+
entries: (container) => entriesOf(container, payloadOf),
|
|
6376
|
+
insert: (container, a, b, c) => extract
|
|
6377
|
+
? insertInto(container, extract(a), a, b, elementOf)
|
|
6378
|
+
: insertInto(container, a, b, c, elementOf),
|
|
6379
|
+
move: (container, key, index) => moveElement(container, key, index),
|
|
6380
|
+
remove: (container, key) => removeElement(container, key),
|
|
6381
|
+
rebalance: (sync, container) => rebalanceContainer(sync, container),
|
|
6382
|
+
};
|
|
6383
|
+
}
|
|
6276
6384
|
|
|
6277
6385
|
const PATH_SEP = '';
|
|
6278
6386
|
const OP_SEP = '';
|
|
@@ -7011,10 +7119,10 @@ class MessageBus {
|
|
|
7011
7119
|
post: (value) => this.channel.postMessage({ id, value }),
|
|
7012
7120
|
};
|
|
7013
7121
|
}
|
|
7014
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7015
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
7122
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MessageBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7123
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MessageBus, providedIn: 'root' });
|
|
7016
7124
|
}
|
|
7017
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7125
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MessageBus, decorators: [{
|
|
7018
7126
|
type: Injectable,
|
|
7019
7127
|
args: [{
|
|
7020
7128
|
providedIn: 'root',
|
|
@@ -7448,5 +7556,5 @@ function withHistory(sourceOrValue, opt) {
|
|
|
7448
7556
|
* Generated bundle index. Do not edit.
|
|
7449
7557
|
*/
|
|
7450
7558
|
|
|
7451
|
-
export { CONCURRENCY_INSTRUMENTATION, MmActivity, MmTransition, MmViewTransitionName, OP_PROTO_VERSION, PAUSABLE_OPTIONS, PERSISTED_STORE_OPTIONS, POS_SEGMENT, SuspenseBoundary, SuspenseBoundaryBase, UnscopedSuspenseBoundary, activeTransaction, applyOps, batteryStatus, bridgeScopeToPendingTasks, chunked, clipboard, combineWith, compareHlc, compareSiblings, compareTotal, createAttributedPending, createConvergingApply, createForwardingScope, createHlcClock, createStoreContext, createTransaction, createTransitionScope, debounce, debounced, defaultFold, deferredValue, derived, diffOps, distinct, elementSize, elementVisibility, extendStore, filter, filterWith, focusWithin, forkStore, geolocation, getTransitionScope, holdUntilReady, idle, indexArray, injectPaused, injectRegisterResource, injectStartTransaction, injectStartTransition, injectTransitionScope, insertElement, invertBatch, isConflicted, isDerivation, isLeaf, isMutable, isOpaque, isStore, isStored, keepPrevious, keyArray, keyedArray, latest, lww, map, mapArray, mapObject, mediaQuery, merge3, mergeThree, mousePosition, moveElement, mutable, mutableStore, nestedEffect, networkStatus, opLog, opSync, opaque, orderedEntries, orientation, pageVisibility, pairwise, pausableComputed, pausableEffect, pausableSignal, perfCustomTracks, persist, persistedStore, pipeable, piped, pointerDrag, policyStrategy, pooled, pooledArray, pooledKeys, pooledMap, pooledSet, posBetween, prefersDarkMode, prefersReducedMotion, preserve, projection, provideConcurrencyInstrumentation, provideForwardingTransitionScope, providePausableOptions, providePaused, providePersistedStoreOptions, provideTransitionScope, rebalanceContainer, rebaseOps, reconcile, registerResource, removeElement, resolvePause, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, storeHistory, stored, syncedFork, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, traced, until, use, validateEnvelope, windowSize, withHistory };
|
|
7559
|
+
export { CONCURRENCY_INSTRUMENTATION, MmActivity, MmTransition, MmViewTransitionName, OP_PROTO_VERSION, PAUSABLE_OPTIONS, PERSISTED_STORE_OPTIONS, POS_SEGMENT, SuspenseBoundary, SuspenseBoundaryBase, UnscopedSuspenseBoundary, activeTransaction, applyOps, batteryStatus, bridgeScopeToPendingTasks, chunked, clipboard, combineWith, compareHlc, compareSiblings, compareTotal, createAttributedPending, createConvergingApply, createForwardingScope, createHlcClock, createStoreContext, createTransaction, createTransitionScope, debounce, debounced, defaultFold, deferredValue, derived, diffOps, distinct, elementSize, elementVisibility, extendStore, filter, filterWith, focusWithin, forkStore, geolocation, getTransitionScope, holdUntilReady, idle, indexArray, injectPaused, injectRegisterResource, injectStartTransaction, injectStartTransition, injectTransitionScope, insertElement, invertBatch, isConflicted, isDerivation, isLeaf, isMutable, isOpaque, isStore, isStored, keepPrevious, keyArray, keyedArray, keyedContainer, latest, lww, map, mapArray, mapObject, mediaQuery, merge3, mergeThree, mousePosition, moveElement, mutable, mutableStore, nestedEffect, networkStatus, opLog, opSync, opaque, orderedEntries, orientation, pageVisibility, pairwise, pausableComputed, pausableEffect, pausableSignal, perfCustomTracks, persist, persistedStore, pipeable, piped, pointerDrag, policyStrategy, pooled, pooledArray, pooledKeys, pooledMap, pooledSet, posBetween, prefersDarkMode, prefersReducedMotion, preserve, projection, provideConcurrencyInstrumentation, provideForwardingTransitionScope, providePausableOptions, providePaused, providePersistedStoreOptions, provideTransitionScope, rebalanceContainer, rebaseOps, reconcile, registerResource, removeElement, resolvePause, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, storeHistory, stored, syncedFork, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, traced, until, use, validateEnvelope, windowSize, withHistory, wrappedContainer };
|
|
7452
7560
|
//# sourceMappingURL=mmstack-primitives.mjs.map
|