@mmstack/primitives 20.13.3 → 20.14.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.
@@ -2046,7 +2046,9 @@ function effectiveKeys(arr, getKey, report) {
2046
2046
  * when the list is reordered.
2047
2047
  *
2048
2048
  * @param source A `Signal<T[]>` or a function returning `T[]`.
2049
- * @param mapFn The mapping function. Receives the item and its index as a Signal.
2049
+ * @param mapFn The mapping function. Receives the item, its index as a Signal, and the
2050
+ * entry's key (the effective ordinal key when `duplicateKeys` is enabled, otherwise
2051
+ * the raw key) — stable for the lifetime of the mapped entry.
2050
2052
  * @param options Optional configuration:
2051
2053
  * - `onDestroy`: A callback invoked when a mapped item is removed from the array.
2052
2054
  * - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
@@ -2123,7 +2125,7 @@ function keyArray(source, mapFn, options = {}) {
2123
2125
  items[j] = item;
2124
2126
  const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
2125
2127
  newIndexes[j] = indexSignal;
2126
- newMapped[j] = mapFn(item, indexSignal);
2128
+ newMapped[j] = mapFn(item, indexSignal, newKeys ? newKeys[j] : getKey(item));
2127
2129
  }
2128
2130
  }
2129
2131
  else {
@@ -2176,7 +2178,7 @@ function keyArray(source, mapFn, options = {}) {
2176
2178
  else {
2177
2179
  const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
2178
2180
  newIndexes[j] = indexSignal;
2179
- newMapped[j] = mapFn(newItems[j], indexSignal);
2181
+ newMapped[j] = mapFn(newItems[j], indexSignal, kNew(j));
2180
2182
  }
2181
2183
  }
2182
2184
  items.length = newLen;
@@ -6870,6 +6872,9 @@ function stored(fallback, { key, store: providedStore, serialize = JSON.stringif
6870
6872
  writable.key = keySig;
6871
6873
  return writable;
6872
6874
  }
6875
+ function isStored(value) {
6876
+ return 'clear' in value && typeof value.clear === 'function' && 'key' in value;
6877
+ }
6873
6878
 
6874
6879
  /** Op-mode sync for a writable store: hello exchange, then live envelopes. */
6875
6880
  function storeTabSync(sig, opt, bus, injector) {
@@ -7107,6 +7112,122 @@ function tabSync(sig, opt) {
7107
7112
  return sig;
7108
7113
  }
7109
7114
 
7115
+ /**
7116
+ * Wraps any member of the writable-signal family so that every synchronous write
7117
+ * first records an ambient "cause" (via the caller-provided `capture` function)
7118
+ * and then delegates to the original write, preserving the wrapped signal's exact
7119
+ * write semantics. The recorded cause is exposed through a plain {@link Traced.causedBy}
7120
+ * read, letting a downstream consumer attribute a change to whatever caused it —
7121
+ * without any dependency on a particular telemetry system.
7122
+ *
7123
+ * Capture is **synchronous-only** and last-writer-wins: the cause is whatever
7124
+ * `capture()` returns during the `set`/`update`/`mutate`/`inline` call. A write
7125
+ * performed after an `await` (once the ambient cause has been cleared) records
7126
+ * `undefined`; a write while `capture()` returns `undefined` clears the cause.
7127
+ * `causedBy()` is a plain read, not a signal — reading it never subscribes, and
7128
+ * a cause-only change (a write with a different cause but no dependent-visible
7129
+ * value change) never invalidates a consumer.
7130
+ *
7131
+ * The traced twin behaves like the original except for `causedBy`: it carries the
7132
+ * wrapped signal's surface — `asReadonly`, the `mutate`/`inline` methods of a
7133
+ * mutable, and the `from` of a derived — and the underlying write semantics survive
7134
+ * (a traced `derived` still writes through to its source; a traced `toWritable` still
7135
+ * runs its custom `set`). Reads through the twin stay tracked — consumers subscribe
7136
+ * exactly as they would to the source.
7137
+ *
7138
+ * @typeParam S - The wrapped writable-signal type.
7139
+ * @typeParam C - The type of the captured cause.
7140
+ *
7141
+ * @param sig - The writable signal to trace. Any family member is accepted
7142
+ * (`signal`, `mutable`, `derived`, `toWritable`, `stored`, ...).
7143
+ * @param capture - A function invoked synchronously at the start of every write.
7144
+ * Its return value becomes the current cause; returning `undefined`
7145
+ * clears it.
7146
+ * @param opt - Optional configuration.
7147
+ * @param opt.pure - If `true` (the default), the returned twin is a **new** signal
7148
+ * that reads through to `sig`; `sig` itself is left untouched, so
7149
+ * tracing a shared signal never makes the original writable or
7150
+ * alters its behaviour.
7151
+ *
7152
+ * CAUTION: with `pure: false` the write methods are patched
7153
+ * directly onto the `sig` object you passed in — every other
7154
+ * holder of that signal now records causes on write. Only use it
7155
+ * with a signal you created and own exclusively.
7156
+ *
7157
+ * @returns A {@link Traced} twin of `sig` exposing `causedBy()`.
7158
+ *
7159
+ * @example
7160
+ * // A refetch attributing itself to whatever wrote the query.
7161
+ * let activeCause: string | undefined;
7162
+ * const query = traced(signal(''), () => activeCause);
7163
+ *
7164
+ * activeCause = 'user-typed';
7165
+ * query.set('hello');
7166
+ * query.causedBy(); // 'user-typed'
7167
+ *
7168
+ * @example
7169
+ * // Tracing a shared, read-only-exposed signal without making it writable.
7170
+ * const shared = signal(0);
7171
+ * const twin = traced(shared, () => currentInteraction());
7172
+ * twin === shared; // false — the original is untouched
7173
+ */
7174
+ function traced(sig, capture, opt) {
7175
+ const twin = (opt?.pure === false ? sig : facadeOf(sig));
7176
+ let cause;
7177
+ const setOriginal = sig.set.bind(sig);
7178
+ twin.set = (value) => {
7179
+ cause = capture();
7180
+ setOriginal(value);
7181
+ };
7182
+ const updateOriginal = sig.update.bind(sig);
7183
+ twin.update = (updater) => {
7184
+ cause = capture();
7185
+ updateOriginal(updater);
7186
+ };
7187
+ if (isMutable(sig)) {
7188
+ const source = sig;
7189
+ const mutableTwin = twin;
7190
+ const mutateOriginal = source.mutate.bind(source);
7191
+ mutableTwin.mutate = (updater) => {
7192
+ cause = capture();
7193
+ mutateOriginal(updater);
7194
+ };
7195
+ const inlineOriginal = source.inline.bind(source);
7196
+ mutableTwin.inline = (updater) => {
7197
+ cause = capture();
7198
+ inlineOriginal(updater);
7199
+ };
7200
+ }
7201
+ if (isStored(sig)) {
7202
+ const source = sig;
7203
+ const storedTwin = twin;
7204
+ const clearOriginal = source.clear.bind(source);
7205
+ storedTwin.clear = () => {
7206
+ cause = capture();
7207
+ clearOriginal();
7208
+ };
7209
+ }
7210
+ twin.causedBy = () => cause;
7211
+ return twin;
7212
+ }
7213
+ /**
7214
+ * Builds a read-through facade over `sig`: a fresh signal whose reads track `sig`,
7215
+ * carrying `sig`'s non-write surface (`asReadonly`, and `from` on a derived signal)
7216
+ * by explicit delegation. The write methods are attached by {@link traced}. The
7217
+ * facade's `equal` always reports "changed", so it forwards every notification `sig`
7218
+ * emits — including the same-reference `mutate`/`inline` force-notifies that a default
7219
+ * `computed` would swallow (see `mutable`'s docs). The change decision is thereby
7220
+ * delegated entirely to `sig`.
7221
+ */
7222
+ function facadeOf(sig) {
7223
+ const facade = computed(sig, { equal: () => false });
7224
+ facade.asReadonly = sig.asReadonly.bind(sig);
7225
+ if (isDerivation(sig)) {
7226
+ facade.from = sig.from;
7227
+ }
7228
+ return facade;
7229
+ }
7230
+
7110
7231
  function until(sourceSignal, predicate, options = {}) {
7111
7232
  return new Promise((resolve, reject) => {
7112
7233
  let effectRef;
@@ -7304,5 +7425,5 @@ function withHistory(sourceOrValue, opt) {
7304
7425
  * Generated bundle index. Do not edit.
7305
7426
  */
7306
7427
 
7307
- 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, 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, 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, until, use, validateEnvelope, windowSize, withHistory };
7428
+ 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, 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 };
7308
7429
  //# sourceMappingURL=mmstack-primitives.mjs.map