@kontsedal/olas-core 0.0.4 → 0.0.5

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/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_root = require("./root-CoafhkTg.cjs");
2
+ const require_root = require("./root-lBp7qziQ.cjs");
3
3
  //#region src/signals/readonly.ts
4
4
  /**
5
5
  * Project a Signal (or any object with a reactive `value` + `peek` + `subscribe`)
@@ -395,6 +395,7 @@ function throttled(source, ms, options) {
395
395
  return out;
396
396
  }
397
397
  //#endregion
398
+ exports._unregisterMutationById = require_root._unregisterMutationById;
398
399
  exports.batch = require_root.batch;
399
400
  exports.computed = require_root.computed;
400
401
  exports.createEmitter = require_root.createEmitter;
@@ -403,12 +404,14 @@ exports.debounced = debounced;
403
404
  exports.debouncedValidator = require_root.debouncedValidator;
404
405
  exports.defineController = require_root.defineController;
405
406
  exports.defineInfiniteQuery = defineInfiniteQuery;
407
+ exports.defineMutation = require_root.defineMutation;
406
408
  exports.defineQuery = defineQuery;
407
409
  exports.defineScope = defineScope;
408
410
  exports.effect = require_root.effect;
409
411
  exports.email = email;
410
412
  exports.isAbortError = require_root.isAbortError;
411
413
  exports.isStandardSchema = isStandardSchema;
414
+ exports.lookupRegisteredMutation = require_root.lookupRegisteredMutation;
412
415
  exports.lookupRegisteredQuery = require_root.lookupRegisteredQuery;
413
416
  exports.max = max;
414
417
  exports.maxLength = maxLength;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as FormErrors, A as DebugEvent, B as QuerySpec, C as SetDataEvent, D as MutationSpec, E as MutationConcurrency, F as AsyncStatus, G as UseOptions, H as RetryDelay, I as DehydratedEntry, J as FieldArrayItemErrors, K as DeepPartial, L as DehydratedState, M as InfiniteQuerySpec, N as InfiniteQuerySubscription, O as DebugBus, P as AsyncState, Q as Form, R as LocalCache, S as RegisteredQuery, T as Mutation, U as RetryPolicy, V as QuerySubscription, W as Snapshot, X as FieldArrayValidator, Y as FieldArrayOptions, Z as FieldArrayValue, _ as defineScope, a as CollectionFactoryResult, at as Validator, b as QueryClientPlugin, c as CtrlApi, ct as Signal, d as Field, dt as EmitterErrorReporter, et as FormOptions, f as LazyChild, ft as createEmitter, g as ScopeOptions, h as Scope, i as CollectionFactoryOptions, it as ItemInitial, j as InfiniteQuery, k as DebugCacheEntry, l as CtrlProps, lt as ErrorContext, m as RootOptions, n as Collection, nt as FormValidator, o as CollectionHomogeneousOptions, ot as Computed, p as Root, q as FieldArray, r as CollectionFactoryApi, rt as FormValue, s as ControllerDef, st as ReadSignal, t as AmbientDeps, tt as FormSchema, u as Ctx, ut as Emitter, v as GcEvent, w as lookupRegisteredQuery, x as QueryClientPluginApi, y as InvalidateEvent, z as Query } from "./types-Ijeun3qo.cjs";
1
+ import { $ as DeepPartial, A as Mutation, B as InfiniteQuerySubscription, C as QueryClientPluginApi, D as _unregisterMutationById, E as SetDataEvent, F as DebugBus, G as LocalCache, H as AsyncStatus, I as DebugCacheEntry, J as QuerySubscription, K as Query, L as DebugEvent, M as MutationDef, N as MutationSpec, O as lookupRegisteredMutation, P as defineMutation, Q as UseOptions, R as InfiniteQuery, S as QueryClientPlugin, T as RegisteredQuery, U as DehydratedEntry, V as AsyncState, W as DehydratedState, X as RetryPolicy, Y as RetryDelay, Z as Snapshot, _ as defineScope, _t as Emitter, a as CollectionFactoryResult, at as Form, b as MutationEnqueueEvent, c as CtrlApi, ct as FormSchema, d as Field, dt as ItemInitial, et as FieldArray, f as LazyChild, ft as Validator, g as ScopeOptions, gt as ErrorContext, h as Scope, ht as Signal, i as CollectionFactoryOptions, it as FieldArrayValue, j as MutationConcurrency, k as lookupRegisteredQuery, l as CtrlProps, lt as FormValidator, m as RootOptions, mt as ReadSignal, n as Collection, nt as FieldArrayOptions, o as CollectionHomogeneousOptions, ot as FormErrors, p as Root, pt as Computed, q as QuerySpec, r as CollectionFactoryApi, rt as FieldArrayValidator, s as ControllerDef, st as FormOptions, t as AmbientDeps, tt as FieldArrayItemErrors, u as Ctx, ut as FormValue, v as GcEvent, vt as EmitterErrorReporter, w as RegisteredMutation, x as MutationSettleEvent, y as InvalidateEvent, yt as createEmitter, z as InfiniteQuerySpec } from "./types-C4Vtkxbn.cjs";
2
2
 
3
3
  //#region src/signals/runtime.d.ts
4
4
  /**
@@ -245,5 +245,5 @@ declare function throttled<T>(source: ReadSignal<T>, ms: number, options?: {
245
245
  */
246
246
  declare function isAbortError(err: unknown): boolean;
247
247
  //#endregion
248
- export { type AmbientDeps, type AsyncState, type AsyncStatus, type Collection, type CollectionFactoryApi, type CollectionFactoryOptions, type CollectionFactoryResult, type CollectionHomogeneousOptions, type Computed, type ControllerDef, type CtrlApi, type CtrlProps, type Ctx, type DebugBus, type DebugCacheEntry, type DebugEvent, type DeepPartial, type DehydratedEntry, type DehydratedState, type Emitter, type EmitterErrorReporter, type ErrorContext, type Field, type FieldArray, type FieldArrayItemErrors, type FieldArrayOptions, type FieldArrayValidator, type FieldArrayValue, type Form, type FormErrors, type FormOptions, type FormSchema, type FormValidator, type FormValue, type GcEvent, type InfiniteQuery, type InfiniteQuerySpec, type InfiniteQuerySubscription, type InvalidateEvent, type ItemInitial, type LazyChild, type LocalCache, type Mutation, type MutationConcurrency, type MutationSpec, type Query, type QueryClientPlugin, type QueryClientPluginApi, type QuerySpec, type QuerySubscription, type ReadSignal, type RegisteredQuery, type RetryDelay, type RetryPolicy, type Root, type RootOptions, type Scope, type ScopeOptions, type Selection, type SetDataEvent, type Signal, type Snapshot, type StandardSchemaV1, type UseOptions, type Validator, batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineQuery, defineScope, effect, email, isAbortError, isStandardSchema, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, stableHash, throttled, untracked, validator };
248
+ export { type AmbientDeps, type AsyncState, type AsyncStatus, type Collection, type CollectionFactoryApi, type CollectionFactoryOptions, type CollectionFactoryResult, type CollectionHomogeneousOptions, type Computed, type ControllerDef, type CtrlApi, type CtrlProps, type Ctx, type DebugBus, type DebugCacheEntry, type DebugEvent, type DeepPartial, type DehydratedEntry, type DehydratedState, type Emitter, type EmitterErrorReporter, type ErrorContext, type Field, type FieldArray, type FieldArrayItemErrors, type FieldArrayOptions, type FieldArrayValidator, type FieldArrayValue, type Form, type FormErrors, type FormOptions, type FormSchema, type FormValidator, type FormValue, type GcEvent, type InfiniteQuery, type InfiniteQuerySpec, type InfiniteQuerySubscription, type InvalidateEvent, type ItemInitial, type LazyChild, type LocalCache, type Mutation, type MutationConcurrency, type MutationDef, type MutationEnqueueEvent, type MutationSettleEvent, type MutationSpec, type Query, type QueryClientPlugin, type QueryClientPluginApi, type QuerySpec, type QuerySubscription, type ReadSignal, type RegisteredMutation, type RegisteredQuery, type RetryDelay, type RetryPolicy, type Root, type RootOptions, type Scope, type ScopeOptions, type Selection, type SetDataEvent, type Signal, type Snapshot, type StandardSchemaV1, type UseOptions, type Validator, _unregisterMutationById, batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineMutation, defineQuery, defineScope, effect, email, isAbortError, isStandardSchema, lookupRegisteredMutation, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, stableHash, throttled, untracked, validator };
249
249
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as FormErrors, A as DebugEvent, B as QuerySpec, C as SetDataEvent, D as MutationSpec, E as MutationConcurrency, F as AsyncStatus, G as UseOptions, H as RetryDelay, I as DehydratedEntry, J as FieldArrayItemErrors, K as DeepPartial, L as DehydratedState, M as InfiniteQuerySpec, N as InfiniteQuerySubscription, O as DebugBus, P as AsyncState, Q as Form, R as LocalCache, S as RegisteredQuery, T as Mutation, U as RetryPolicy, V as QuerySubscription, W as Snapshot, X as FieldArrayValidator, Y as FieldArrayOptions, Z as FieldArrayValue, _ as defineScope, a as CollectionFactoryResult, at as Validator, b as QueryClientPlugin, c as CtrlApi, ct as Signal, d as Field, dt as EmitterErrorReporter, et as FormOptions, f as LazyChild, ft as createEmitter, g as ScopeOptions, h as Scope, i as CollectionFactoryOptions, it as ItemInitial, j as InfiniteQuery, k as DebugCacheEntry, l as CtrlProps, lt as ErrorContext, m as RootOptions, n as Collection, nt as FormValidator, o as CollectionHomogeneousOptions, ot as Computed, p as Root, q as FieldArray, r as CollectionFactoryApi, rt as FormValue, s as ControllerDef, st as ReadSignal, t as AmbientDeps, tt as FormSchema, u as Ctx, ut as Emitter, v as GcEvent, w as lookupRegisteredQuery, x as QueryClientPluginApi, y as InvalidateEvent, z as Query } from "./types-BCf2nB2N.mjs";
1
+ import { $ as DeepPartial, A as Mutation, B as InfiniteQuerySubscription, C as QueryClientPluginApi, D as _unregisterMutationById, E as SetDataEvent, F as DebugBus, G as LocalCache, H as AsyncStatus, I as DebugCacheEntry, J as QuerySubscription, K as Query, L as DebugEvent, M as MutationDef, N as MutationSpec, O as lookupRegisteredMutation, P as defineMutation, Q as UseOptions, R as InfiniteQuery, S as QueryClientPlugin, T as RegisteredQuery, U as DehydratedEntry, V as AsyncState, W as DehydratedState, X as RetryPolicy, Y as RetryDelay, Z as Snapshot, _ as defineScope, _t as Emitter, a as CollectionFactoryResult, at as Form, b as MutationEnqueueEvent, c as CtrlApi, ct as FormSchema, d as Field, dt as ItemInitial, et as FieldArray, f as LazyChild, ft as Validator, g as ScopeOptions, gt as ErrorContext, h as Scope, ht as Signal, i as CollectionFactoryOptions, it as FieldArrayValue, j as MutationConcurrency, k as lookupRegisteredQuery, l as CtrlProps, lt as FormValidator, m as RootOptions, mt as ReadSignal, n as Collection, nt as FieldArrayOptions, o as CollectionHomogeneousOptions, ot as FormErrors, p as Root, pt as Computed, q as QuerySpec, r as CollectionFactoryApi, rt as FieldArrayValidator, s as ControllerDef, st as FormOptions, t as AmbientDeps, tt as FieldArrayItemErrors, u as Ctx, ut as FormValue, v as GcEvent, vt as EmitterErrorReporter, w as RegisteredMutation, x as MutationSettleEvent, y as InvalidateEvent, yt as createEmitter, z as InfiniteQuerySpec } from "./types-BH1o6nYa.mjs";
2
2
 
3
3
  //#region src/signals/runtime.d.ts
4
4
  /**
@@ -245,5 +245,5 @@ declare function throttled<T>(source: ReadSignal<T>, ms: number, options?: {
245
245
  */
246
246
  declare function isAbortError(err: unknown): boolean;
247
247
  //#endregion
248
- export { type AmbientDeps, type AsyncState, type AsyncStatus, type Collection, type CollectionFactoryApi, type CollectionFactoryOptions, type CollectionFactoryResult, type CollectionHomogeneousOptions, type Computed, type ControllerDef, type CtrlApi, type CtrlProps, type Ctx, type DebugBus, type DebugCacheEntry, type DebugEvent, type DeepPartial, type DehydratedEntry, type DehydratedState, type Emitter, type EmitterErrorReporter, type ErrorContext, type Field, type FieldArray, type FieldArrayItemErrors, type FieldArrayOptions, type FieldArrayValidator, type FieldArrayValue, type Form, type FormErrors, type FormOptions, type FormSchema, type FormValidator, type FormValue, type GcEvent, type InfiniteQuery, type InfiniteQuerySpec, type InfiniteQuerySubscription, type InvalidateEvent, type ItemInitial, type LazyChild, type LocalCache, type Mutation, type MutationConcurrency, type MutationSpec, type Query, type QueryClientPlugin, type QueryClientPluginApi, type QuerySpec, type QuerySubscription, type ReadSignal, type RegisteredQuery, type RetryDelay, type RetryPolicy, type Root, type RootOptions, type Scope, type ScopeOptions, type Selection, type SetDataEvent, type Signal, type Snapshot, type StandardSchemaV1, type UseOptions, type Validator, batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineQuery, defineScope, effect, email, isAbortError, isStandardSchema, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, stableHash, throttled, untracked, validator };
248
+ export { type AmbientDeps, type AsyncState, type AsyncStatus, type Collection, type CollectionFactoryApi, type CollectionFactoryOptions, type CollectionFactoryResult, type CollectionHomogeneousOptions, type Computed, type ControllerDef, type CtrlApi, type CtrlProps, type Ctx, type DebugBus, type DebugCacheEntry, type DebugEvent, type DeepPartial, type DehydratedEntry, type DehydratedState, type Emitter, type EmitterErrorReporter, type ErrorContext, type Field, type FieldArray, type FieldArrayItemErrors, type FieldArrayOptions, type FieldArrayValidator, type FieldArrayValue, type Form, type FormErrors, type FormOptions, type FormSchema, type FormValidator, type FormValue, type GcEvent, type InfiniteQuery, type InfiniteQuerySpec, type InfiniteQuerySubscription, type InvalidateEvent, type ItemInitial, type LazyChild, type LocalCache, type Mutation, type MutationConcurrency, type MutationDef, type MutationEnqueueEvent, type MutationSettleEvent, type MutationSpec, type Query, type QueryClientPlugin, type QueryClientPluginApi, type QuerySpec, type QuerySubscription, type ReadSignal, type RegisteredMutation, type RegisteredQuery, type RetryDelay, type RetryPolicy, type Root, type RootOptions, type Scope, type ScopeOptions, type Selection, type SetDataEvent, type Signal, type Snapshot, type StandardSchemaV1, type UseOptions, type Validator, _unregisterMutationById, batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineMutation, defineQuery, defineScope, effect, email, isAbortError, isStandardSchema, lookupRegisteredMutation, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, stableHash, throttled, untracked, validator };
249
249
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as lookupRegisteredQuery, c as isAbortError, d as effect, f as signal, i as createEmitter, l as batch, m as defineController, o as registerQueryById, p as untracked, r as debouncedValidator, s as stableHash, t as createRoot, u as computed } from "./root-BBSlzvJ2.mjs";
1
+ import { _ as defineController, a as createEmitter, c as lookupRegisteredQuery, d as isAbortError, f as batch, g as untracked, h as signal, i as debouncedValidator, l as registerQueryById, m as effect, o as _unregisterMutationById, p as computed, r as defineMutation, s as lookupRegisteredMutation, t as createRoot, u as stableHash } from "./root-DqWolle_.mjs";
2
2
  //#region src/signals/readonly.ts
3
3
  /**
4
4
  * Project a Signal (or any object with a reactive `value` + `peek` + `subscribe`)
@@ -394,6 +394,6 @@ function throttled(source, ms, options) {
394
394
  return out;
395
395
  }
396
396
  //#endregion
397
- export { batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineQuery, defineScope, effect, email, isAbortError, isStandardSchema, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, stableHash, throttled, untracked, validator };
397
+ export { _unregisterMutationById, batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineMutation, defineQuery, defineScope, effect, email, isAbortError, isStandardSchema, lookupRegisteredMutation, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, stableHash, throttled, untracked, validator };
398
398
 
399
399
  //# sourceMappingURL=index.mjs.map
@@ -978,6 +978,25 @@ function registerQueryById(queryId, query) {
978
978
  function lookupRegisteredQuery(queryId) {
979
979
  return queryRegistry.get(queryId);
980
980
  }
981
+ const mutationRegistry = /* @__PURE__ */ new Map();
982
+ /** Register a mutation by its `mutationId`. Internal — called from `defineMutation`. */
983
+ function registerMutationById(mutationId, entry) {
984
+ mutationRegistry.set(mutationId, entry);
985
+ }
986
+ /**
987
+ * Look up a registered mutation by id. Returns `undefined` when no
988
+ * mutation with that id has been defined — typical when a queue entry
989
+ * references a mutation whose module hasn't been imported (e.g. a
990
+ * code-split route boundary). The plugin should leave such entries in
991
+ * place and retry once the module loads.
992
+ */
993
+ function lookupRegisteredMutation(mutationId) {
994
+ return mutationRegistry.get(mutationId);
995
+ }
996
+ /** Test-only — drop a registered mutation. Not exported from the package. */
997
+ function _unregisterMutationById(mutationId) {
998
+ mutationRegistry.delete(mutationId);
999
+ }
981
1000
  //#endregion
982
1001
  //#region src/query/client.ts
983
1002
  const DEFAULT_GC_TIME = 5 * 6e4;
@@ -1342,6 +1361,26 @@ var QueryClient = class {
1342
1361
  this.callPlugin(() => cb.call(plugin, event));
1343
1362
  }
1344
1363
  }
1364
+ /**
1365
+ * Fan out a `MutationEnqueueEvent` to every installed plugin. Called from
1366
+ * `MutationImpl.executeRun` when `spec.persist === true`. Plugins use this
1367
+ * to write the run to durable storage; the queue replays on reload. SPEC §13.3.
1368
+ */
1369
+ emitMutationEnqueue(event) {
1370
+ if (this.plugins.length === 0) return;
1371
+ for (const plugin of this.plugins) if (plugin.onMutationEnqueue) {
1372
+ const cb = plugin.onMutationEnqueue;
1373
+ this.callPlugin(() => cb.call(plugin, event));
1374
+ }
1375
+ }
1376
+ /** Fan out a `MutationSettleEvent` to every installed plugin. SPEC §13.3. */
1377
+ emitMutationSettle(event) {
1378
+ if (this.plugins.length === 0) return;
1379
+ for (const plugin of this.plugins) if (plugin.onMutationSettle) {
1380
+ const cb = plugin.onMutationSettle;
1381
+ this.callPlugin(() => cb.call(plugin, event));
1382
+ }
1383
+ }
1345
1384
  /** Resolve `queryId → live entry-map keys`. Empty array when unknown. */
1346
1385
  subscribedKeysFor(queryId) {
1347
1386
  const query = lookupRegisteredQuery(queryId);
@@ -2784,12 +2823,52 @@ function arraysEqual(a, b) {
2784
2823
  }
2785
2824
  //#endregion
2786
2825
  //#region src/query/mutation.ts
2826
+ /**
2827
+ * Register a persistable mutation at module scope. Returns the spec
2828
+ * unchanged (with a `__olas: 'mutation'` brand) so consumers can pass it
2829
+ * to `ctx.mutation(...)`, optionally spreading per-controller hooks on
2830
+ * top:
2831
+ *
2832
+ * ```ts
2833
+ * // module-scope
2834
+ * export const createOrder = defineMutation({
2835
+ * mutationId: 'order/create',
2836
+ * mutate: async (vars: OrderInput, { signal }) => api.createOrder(vars, { signal }),
2837
+ * })
2838
+ *
2839
+ * // controller
2840
+ * const m = ctx.mutation({
2841
+ * ...createOrder,
2842
+ * onSuccess: () => toast('Order placed'),
2843
+ * })
2844
+ * ```
2845
+ *
2846
+ * The `mutate` function MUST NOT close over controller-instance state — on
2847
+ * replay there is no controller. Module-level dependencies (a shared `api`
2848
+ * client, etc.) are fine.
2849
+ */
2850
+ function defineMutation(spec) {
2851
+ if (typeof spec.mutationId !== "string" || spec.mutationId.length === 0) throw new Error("[olas] defineMutation requires a non-empty `mutationId`.");
2852
+ const persistSpec = {
2853
+ ...spec,
2854
+ persist: spec.persist ?? true
2855
+ };
2856
+ registerMutationById(spec.mutationId, {
2857
+ mutationId: spec.mutationId,
2858
+ mutate: spec.mutate
2859
+ });
2860
+ return Object.assign(persistSpec, {
2861
+ __olas: "mutation",
2862
+ mutationId: spec.mutationId
2863
+ });
2864
+ }
2787
2865
  var MutationImpl = class {
2788
2866
  spec;
2789
2867
  onError;
2790
2868
  controllerPath;
2791
2869
  inflightCounter;
2792
2870
  devtools;
2871
+ lifecycle;
2793
2872
  data = signal$1(void 0);
2794
2873
  error = signal$1(void 0);
2795
2874
  isPending = signal$1(false);
@@ -2798,12 +2877,21 @@ var MutationImpl = class {
2798
2877
  serialQueue = [];
2799
2878
  serialActive = false;
2800
2879
  disposed = false;
2801
- constructor(spec, onError, controllerPath, inflightCounter, devtools) {
2880
+ constructor(spec, onError, controllerPath, inflightCounter, devtools, lifecycle) {
2802
2881
  this.spec = spec;
2803
2882
  this.onError = onError;
2804
2883
  this.controllerPath = controllerPath;
2805
2884
  this.inflightCounter = inflightCounter;
2806
2885
  this.devtools = devtools;
2886
+ this.lifecycle = lifecycle;
2887
+ }
2888
+ /**
2889
+ * True iff this mutation should emit persistable-lifecycle events.
2890
+ * Validated at construction time (in `createMutation`) so any malformed
2891
+ * `persist: true`-without-`mutationId` config surfaces early.
2892
+ */
2893
+ get isPersistable() {
2894
+ return this.spec.persist === true && this.lifecycle !== void 0;
2807
2895
  }
2808
2896
  emit(event) {}
2809
2897
  run = ((vars = void 0) => {
@@ -2867,10 +2955,30 @@ var MutationImpl = class {
2867
2955
  this.isPending.set(true);
2868
2956
  this.lastVariables.set(vars);
2869
2957
  });
2958
+ const runId = this.isPersistable ? makeRunId() : "";
2959
+ const mutationId = this.spec.mutationId;
2960
+ if (this.isPersistable && mutationId !== void 0) try {
2961
+ this.lifecycle?.emitEnqueue({
2962
+ mutationId,
2963
+ runId,
2964
+ variables: vars,
2965
+ attempt: 0
2966
+ });
2967
+ } catch (err) {
2968
+ dispatchError(this.onError, err, {
2969
+ kind: "plugin",
2970
+ controllerPath: this.controllerPath
2971
+ });
2972
+ }
2870
2973
  try {
2871
2974
  const result = await raceAbort(this.runWithRetry(vars, abort.signal), abort.signal);
2872
2975
  if (abort.signal.aborted || this.disposed) {
2873
2976
  snapshot?.rollback();
2977
+ if (this.isPersistable && mutationId !== void 0) this.safeEmitSettle({
2978
+ mutationId,
2979
+ runId,
2980
+ outcome: "cancelled"
2981
+ });
2874
2982
  throw new DOMException("Superseded", "AbortError");
2875
2983
  }
2876
2984
  batch$1(() => {
@@ -2880,16 +2988,32 @@ var MutationImpl = class {
2880
2988
  this.safeCall(() => this.spec.onSuccess?.(result, vars), "mutation");
2881
2989
  snapshot?.finalize();
2882
2990
  this.safeCall(() => this.spec.onSettled?.(result, void 0, vars), "mutation");
2991
+ if (this.isPersistable && mutationId !== void 0) this.safeEmitSettle({
2992
+ mutationId,
2993
+ runId,
2994
+ outcome: "success"
2995
+ });
2883
2996
  return result;
2884
2997
  } catch (err) {
2885
2998
  if (isAbortError(err) || abort.signal.aborted) {
2886
2999
  snapshot?.rollback();
3000
+ if (this.isPersistable && mutationId !== void 0) this.safeEmitSettle({
3001
+ mutationId,
3002
+ runId,
3003
+ outcome: "cancelled"
3004
+ });
2887
3005
  throw err;
2888
3006
  }
2889
3007
  this.error.set(err);
2890
3008
  this.safeCall(() => this.spec.onError?.(err, vars, snapshot), "mutation");
2891
3009
  snapshot?.rollback();
2892
3010
  this.safeCall(() => this.spec.onSettled?.(void 0, err, vars), "mutation");
3011
+ if (this.isPersistable && mutationId !== void 0) this.safeEmitSettle({
3012
+ mutationId,
3013
+ runId,
3014
+ outcome: "error",
3015
+ error: err
3016
+ });
2893
3017
  throw err;
2894
3018
  } finally {
2895
3019
  this.inflight.delete(handle);
@@ -2897,6 +3021,16 @@ var MutationImpl = class {
2897
3021
  if (this.inflight.size === 0) this.isPending.set(false);
2898
3022
  }
2899
3023
  }
3024
+ safeEmitSettle(event) {
3025
+ try {
3026
+ this.lifecycle?.emitSettle(event);
3027
+ } catch (err) {
3028
+ dispatchError(this.onError, err, {
3029
+ kind: "plugin",
3030
+ controllerPath: this.controllerPath
3031
+ });
3032
+ }
3033
+ }
2900
3034
  wrapSnapshot(raw) {
2901
3035
  let consumed = false;
2902
3036
  return {
@@ -2960,8 +3094,24 @@ var MutationImpl = class {
2960
3094
  this.serialQueue.length = 0;
2961
3095
  }
2962
3096
  };
2963
- function createMutation(spec, onError, controllerPath, inflightCounter, devtools) {
2964
- return new MutationImpl(spec, onError, controllerPath, inflightCounter, devtools);
3097
+ function createMutation(spec, onError, controllerPath, inflightCounter, devtools, lifecycle) {
3098
+ if (spec.persist === true) {
3099
+ if (typeof spec.mutationId !== "string" || spec.mutationId.length === 0) throw new Error("[olas] ctx.mutation({ persist: true, ... }) requires a non-empty `mutationId`.");
3100
+ }
3101
+ return new MutationImpl(spec, onError, controllerPath, inflightCounter, devtools, lifecycle);
3102
+ }
3103
+ /**
3104
+ * Generate a unique-enough run id for the persistable-mutation lifecycle.
3105
+ * Uses `crypto.randomUUID` where available (Node 19+, modern browsers),
3106
+ * with a timestamp+random fallback for older runtimes. Collisions only
3107
+ * affect dedup at the plugin layer, not correctness, so the fallback's
3108
+ * weakness is acceptable.
3109
+ */
3110
+ function makeRunId() {
3111
+ const g = globalThis;
3112
+ if (typeof g.crypto?.randomUUID === "function") return g.crypto.randomUUID();
3113
+ const rand = Math.random().toString(36).slice(2, 12);
3114
+ return `${Date.now().toString(36)}-${rand}`;
2965
3115
  }
2966
3116
  /**
2967
3117
  * Race a promise against an AbortSignal. If the signal fires before the
@@ -3497,7 +3647,11 @@ var ControllerInstance = class ControllerInstance {
3497
3647
  return handle.subscription;
3498
3648
  },
3499
3649
  mutation(spec) {
3500
- const m = createMutation(spec, self.rootShared.onError, self.path, self.rootShared.queryClient.mutationsInflight$, self.rootShared.devtools);
3650
+ const queryClient = self.rootShared.queryClient;
3651
+ const m = createMutation(spec, self.rootShared.onError, self.path, queryClient.mutationsInflight$, self.rootShared.devtools, spec.persist === true ? {
3652
+ emitEnqueue: (ev) => queryClient.emitMutationEnqueue(ev),
3653
+ emitSettle: (ev) => queryClient.emitMutationSettle(ev)
3654
+ } : void 0);
3501
3655
  self.entries.push({
3502
3656
  kind: "cleanup",
3503
3657
  dispose: () => m.dispose()
@@ -4099,6 +4253,6 @@ function createRoot(def, options) {
4099
4253
  return createRootWithProps(def, void 0, options);
4100
4254
  }
4101
4255
  //#endregion
4102
- export { lookupRegisteredQuery as a, isAbortError as c, effect$1 as d, signal$1 as f, createEmitter as i, batch$1 as l, defineController as m, createRootWithProps as n, registerQueryById as o, untracked$1 as p, debouncedValidator as r, stableHash as s, createRoot as t, computed$1 as u };
4256
+ export { defineController as _, createEmitter as a, lookupRegisteredQuery as c, isAbortError as d, batch$1 as f, untracked$1 as g, signal$1 as h, debouncedValidator as i, registerQueryById as l, effect$1 as m, createRootWithProps as n, _unregisterMutationById as o, computed$1 as p, defineMutation as r, lookupRegisteredMutation as s, createRoot as t, stableHash as u };
4103
4257
 
4104
- //# sourceMappingURL=root-BBSlzvJ2.mjs.map
4258
+ //# sourceMappingURL=root-DqWolle_.mjs.map