@mmstack/resource 22.1.4 → 22.1.6
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 +50 -5
- package/fesm2022/mmstack-resource.mjs +118 -109
- package/fesm2022/mmstack-resource.mjs.map +1 -1
- package/package.json +1 -1
- package/types/mmstack-resource.d.ts +61 -12
package/README.md
CHANGED
|
@@ -279,6 +279,22 @@ mutationResource(
|
|
|
279
279
|
|
|
280
280
|
The `TCTX` returned from `onMutate` flows into `onError` / `onSuccess` / `onSettled`. The optional `initialCtx` second arg to `.mutate(value, initialCtx)` flows into `onMutate` as its second argument.
|
|
281
281
|
|
|
282
|
+
### Awaiting a mutation (`mutateAsync`)
|
|
283
|
+
|
|
284
|
+
`.mutate()` is fire-and-forget. When you need to `await` the outcome — a form submit handler, an async validator — use `.mutateAsync()`, which returns a `Promise` that resolves with the result or rejects with the error. The lifecycle hooks still run exactly as with `.mutate()`.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
try {
|
|
288
|
+
const saved = await createPost.mutateAsync(post);
|
|
289
|
+
router.navigate(['/posts', saved.id]);
|
|
290
|
+
} catch (e) {
|
|
291
|
+
if (e instanceof MutationCancelledError) return; // never ran — see below
|
|
292
|
+
toast.error('Failed to save');
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
If the mutation never completes — superseded by a newer one (latest-wins), dropped from the queue (`clearQueue()` / a `key` change), abandoned on `destroy()`, or its `request()` returned `undefined` — the promise rejects with a `MutationCancelledError` whose `.type` (`MutationCancellationReason`) tells you which. Awaiters that don't expect cancellation should rethrow it. (Plain `.mutate()` has no promise, so it never produces an unhandled rejection.)
|
|
297
|
+
|
|
282
298
|
### Queuing
|
|
283
299
|
|
|
284
300
|
By default, calling `.mutate()` while another mutation is in flight starts immediately — concurrent mutations run in parallel. With `queue: true`, mutations are serialized:
|
|
@@ -316,13 +332,42 @@ mutationResource(request, { triggerOnSameRequest: true });
|
|
|
316
332
|
|
|
317
333
|
A mutation also honours the `register` option — but it registers the **mutation ref itself** into the transition scope (its internal query is never registered), so a `<mm-suspense>`/transition reacts to the mutation's own `pending` state. See [transitions & Suspense](#transitions--suspense).
|
|
318
334
|
|
|
335
|
+
### File uploads & progress (`multipart/form-data`)
|
|
336
|
+
|
|
337
|
+
There's no special upload API — return a `FormData` body and `HttpClient` sets the `multipart/form-data` boundary for you. Opt into upload progress with `reportProgress: true` and read the `progress` signal (an `HttpProgressEvent`).
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
const upload = mutationResource<UploadResult, UploadResult, FormData>((form) => ({
|
|
341
|
+
url: '/api/upload',
|
|
342
|
+
method: 'POST',
|
|
343
|
+
body: form,
|
|
344
|
+
reportProgress: true, // opt in to progress events
|
|
345
|
+
}));
|
|
346
|
+
|
|
347
|
+
// trigger it:
|
|
348
|
+
const form = new FormData();
|
|
349
|
+
form.append('file', file);
|
|
350
|
+
upload.mutate(form);
|
|
351
|
+
|
|
352
|
+
// derive a percentage in a computed / template:
|
|
353
|
+
readonly pct = computed(() => {
|
|
354
|
+
const p = upload.progress();
|
|
355
|
+
return p?.total ? Math.round((p.loaded / p.total) * 100) : null;
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
`FormData`, `File`, and `Blob` bodies are hashed structurally for dedup/cache keys (a `File` by name + type + size + lastModified), so distinct files never collide. To re-upload the **same** file while one is already in flight, pair it with `triggerOnSameRequest: true`.
|
|
360
|
+
|
|
319
361
|
### Return shape (`MutationResourceRef<T, TMutation>`)
|
|
320
362
|
|
|
321
|
-
| Member | Type
|
|
322
|
-
| --------------------------------------------- |
|
|
323
|
-
| `mutate` | `(value, ctx?) => void`
|
|
324
|
-
| `
|
|
325
|
-
| `
|
|
363
|
+
| Member | Type | Notes |
|
|
364
|
+
| --------------------------------------------- | ------------------------------------------ | ------------------------------------------------------ |
|
|
365
|
+
| `mutate` | `(value, ctx?) => void` | Trigger the mutation (fire-and-forget). |
|
|
366
|
+
| `mutateAsync` | `(value, ctx?) => Promise<TResult>` | Trigger and `await` the result; rejects with the error or a `MutationCancelledError`. |
|
|
367
|
+
| `current` | `Signal<TMutation \| null>` | The value currently being mutated (or `null` if idle). |
|
|
368
|
+
| `progress` | `Signal<HttpProgressEvent \| undefined>` | Upload/download progress when `reportProgress: true`. |
|
|
369
|
+
| `status` / `error` / `isLoading` / `disabled` | as in `QueryResourceRef` | – |
|
|
370
|
+
| `headers` / `statusCode` | as in `QueryResourceRef` | Response metadata, when available. |
|
|
326
371
|
|
|
327
372
|
(Mutations deliberately don't expose `value`, `hasValue`, `set`, `update`, or `prefetch` — those don't make sense for one-off writes.)
|
|
328
373
|
|
|
@@ -1853,10 +1853,10 @@ function retryOnError(res, opt, onError) {
|
|
|
1853
1853
|
class ResourceSensors {
|
|
1854
1854
|
networkStatus = sensor('networkStatus');
|
|
1855
1855
|
pageVisibility = sensor('pageVisibility');
|
|
1856
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
1857
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.
|
|
1856
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ResourceSensors, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1857
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ResourceSensors, providedIn: 'root' });
|
|
1858
1858
|
}
|
|
1859
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
1859
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ResourceSensors, decorators: [{
|
|
1860
1860
|
type: Injectable,
|
|
1861
1861
|
args: [{
|
|
1862
1862
|
providedIn: 'root',
|
|
@@ -2377,6 +2377,23 @@ function manualQueryResource(request, options) {
|
|
|
2377
2377
|
}
|
|
2378
2378
|
|
|
2379
2379
|
const NULL_VALUE = Symbol('@mmstack/resource:null');
|
|
2380
|
+
/**
|
|
2381
|
+
* Rejection reason for a {@link MutationResourceRef.mutateAsync} promise whose
|
|
2382
|
+
* mutation never completed. The {@link MutationCancelledError.type} discriminant
|
|
2383
|
+
* carries the cause ({@link MutationCancellationReason}); the message is a
|
|
2384
|
+
* human-readable elaboration of it.
|
|
2385
|
+
*
|
|
2386
|
+
* Only `mutateAsync` promises reject with this; plain `mutate()` calls have no
|
|
2387
|
+
* promise and so produce no (potentially unhandled) rejection.
|
|
2388
|
+
*/
|
|
2389
|
+
class MutationCancelledError extends Error {
|
|
2390
|
+
type;
|
|
2391
|
+
constructor(type, message) {
|
|
2392
|
+
super(message);
|
|
2393
|
+
this.type = type;
|
|
2394
|
+
this.name = 'MutationCancelledError';
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2380
2397
|
const MUTATION_RESOURCE_OPTIONS = new InjectionToken('@mmstack/resource:mutation-resource-options', { factory: () => ({}) });
|
|
2381
2398
|
/**
|
|
2382
2399
|
* Layer 2 (mutation): default options for every `mutationResource`, inheriting + overriding the
|
|
@@ -2390,55 +2407,6 @@ function injectMutationResourceOptions(injector) {
|
|
|
2390
2407
|
? injector.get(MUTATION_RESOURCE_OPTIONS)
|
|
2391
2408
|
: inject(MUTATION_RESOURCE_OPTIONS);
|
|
2392
2409
|
}
|
|
2393
|
-
/**
|
|
2394
|
-
* Creates a resource for performing mutations (e.g., POST, PUT, PATCH, DELETE requests).
|
|
2395
|
-
* Unlike `queryResource`, `mutationResource` is designed for one-off operations that change data.
|
|
2396
|
-
* It does *not* cache responses and does not provide a `value` signal. Instead, it focuses on
|
|
2397
|
-
* managing the mutation lifecycle (pending, error, success) and provides callbacks for handling
|
|
2398
|
-
* these states.
|
|
2399
|
-
*
|
|
2400
|
-
* @param request A function that returns the base `HttpResourceRequest` to be made. This function is called reactively. The parameter is the mutation value provided by the `mutate` method.
|
|
2401
|
-
* @param options Configuration options for the mutation resource. This includes callbacks
|
|
2402
|
-
* for `onMutate`, `onError`, `onSuccess`, and `onSettled`.
|
|
2403
|
-
* @typeParam TResult - The type of the expected result from the mutation.
|
|
2404
|
-
* @typeParam TRaw - The raw response type from the HTTP request (defaults to TResult).
|
|
2405
|
-
* @typeParam TMutation - The type of the mutation value (the request body).
|
|
2406
|
-
* @typeParam TICTX - The type of the initial context value passed to `onMutate`.
|
|
2407
|
-
* @typeParam TCTX - The type of the context value returned by `onMutate`.
|
|
2408
|
-
* @typeParam TMethod - The HTTP method to be used for the mutation (defaults to `HttpResourceRequest['method']`).
|
|
2409
|
-
* @returns A `MutationResourceRef` instance, which provides methods for triggering the mutation
|
|
2410
|
-
* and observing its status.
|
|
2411
|
-
*
|
|
2412
|
-
* @example
|
|
2413
|
-
* ```ts
|
|
2414
|
-
* // Basic PATCH mutation
|
|
2415
|
-
* const updateUser = mutationResource<User, User, Partial<User>>(
|
|
2416
|
-
* (body) => ({ url: `/api/users/${userId()}`, method: 'PATCH', body }),
|
|
2417
|
-
* {
|
|
2418
|
-
* onSuccess: (saved) => toast.success(`Updated ${saved.name}`),
|
|
2419
|
-
* onError: (err) => toast.error(err),
|
|
2420
|
-
* },
|
|
2421
|
-
* );
|
|
2422
|
-
*
|
|
2423
|
-
* updateUser.mutate({ name: 'Alice' });
|
|
2424
|
-
* ```
|
|
2425
|
-
*
|
|
2426
|
-
* @example
|
|
2427
|
-
* ```ts
|
|
2428
|
-
* // Optimistic update with rollback via the `ctx` returned from `onMutate`
|
|
2429
|
-
* const updateUser = mutationResource<User, User, Partial<User>, { prev: User | null }>(
|
|
2430
|
-
* (body) => ({ url: `/api/users/${userId()}`, method: 'PATCH', body }),
|
|
2431
|
-
* {
|
|
2432
|
-
* onMutate: (patch) => {
|
|
2433
|
-
* const prev = current();
|
|
2434
|
-
* current.update((u) => (u ? { ...u, ...patch } : u));
|
|
2435
|
-
* return { prev };
|
|
2436
|
-
* },
|
|
2437
|
-
* onError: (_err, { prev }) => current.set(prev),
|
|
2438
|
-
* },
|
|
2439
|
-
* );
|
|
2440
|
-
* ```
|
|
2441
|
-
*/
|
|
2442
2410
|
function mutationResource(request, options0 = {}) {
|
|
2443
2411
|
// Two-layer option injection: per-call > provideMutationResourceOptions > provideResourceOptions.
|
|
2444
2412
|
const globalOpts = injectResourceOptions(options0.injector);
|
|
@@ -2455,11 +2423,6 @@ function mutationResource(request, options0 = {}) {
|
|
|
2455
2423
|
const { onMutate, onError, onSuccess, onSettled, equal, register, equalRequest, invalidates, ...rest } = options;
|
|
2456
2424
|
const cache = invalidates ? injectQueryCache(options.injector) : undefined;
|
|
2457
2425
|
const requestEqual = equalRequest ?? createEqualRequest(equal);
|
|
2458
|
-
// A mutation is an imperative command, so `triggerOnSameRequest` means "fire on EVERY mutate(),
|
|
2459
|
-
// even with an identical body". By default we dedup an identical value/request while one is in
|
|
2460
|
-
// flight (double-click protection); when this is set, both the `next` and `req` dedup are bypassed
|
|
2461
|
-
// so a repeat click isn't silently swallowed mid-flight. (Otherwise it'd be dropped until `next`
|
|
2462
|
-
// resets to NULL on settle — the "every other click" symptom.)
|
|
2463
2426
|
const triggerOnSame = options.triggerOnSameRequest ?? false;
|
|
2464
2427
|
const eq = equal ?? Object.is;
|
|
2465
2428
|
const next = signal(NULL_VALUE, { ...(ngDevMode ? { debugName: "next" } : /* istanbul ignore next */ {}), equal: (a, b) => {
|
|
@@ -2471,26 +2434,17 @@ function mutationResource(request, options0 = {}) {
|
|
|
2471
2434
|
return false;
|
|
2472
2435
|
return eq(a, b);
|
|
2473
2436
|
} });
|
|
2474
|
-
const
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
next.set(value);
|
|
2486
|
-
}
|
|
2487
|
-
catch (mutationErr) {
|
|
2488
|
-
ctx = undefined;
|
|
2489
|
-
next.set(NULL_VALUE);
|
|
2490
|
-
if (isDevMode())
|
|
2491
|
-
console.error('[@mmstack/resource]: error thrown in onMutate hook, mutation was not applied', mutationErr);
|
|
2492
|
-
}
|
|
2493
|
-
}, { ...(ngDevMode ? { debugName: "queueRef" } : /* istanbul ignore next */ {}), injector: options.injector });
|
|
2437
|
+
const queueEnabled = !!options.queue;
|
|
2438
|
+
const queueKeyFn = typeof options.queue === 'object' ? options.queue.key : undefined;
|
|
2439
|
+
const queue = linkedSignal({ ...(ngDevMode ? { debugName: "queue" } : /* istanbul ignore next */ {}), source: () => queueKeyFn?.(),
|
|
2440
|
+
computation: (_key, prev) => {
|
|
2441
|
+
// On a queue key change the previous pending entries are dropped — reject any
|
|
2442
|
+
// mutateAsync promises waiting on them so awaiters don't hang.
|
|
2443
|
+
if (prev)
|
|
2444
|
+
for (const [, , deferred] of untracked(prev.value))
|
|
2445
|
+
deferred?.reject(new MutationCancelledError('queue-key-changed', 'mutation dropped: queue key changed before it ran'));
|
|
2446
|
+
return signal([]);
|
|
2447
|
+
} });
|
|
2494
2448
|
const req = computed(() => {
|
|
2495
2449
|
const nr = next();
|
|
2496
2450
|
if (nr === NULL_VALUE)
|
|
@@ -2505,6 +2459,57 @@ function mutationResource(request, options0 = {}) {
|
|
|
2505
2459
|
return false;
|
|
2506
2460
|
return requestEqual(a, b);
|
|
2507
2461
|
} });
|
|
2462
|
+
let ctx = undefined;
|
|
2463
|
+
let currentDeferred;
|
|
2464
|
+
const begin = (value, ictx, deferred) => {
|
|
2465
|
+
let nextCtx;
|
|
2466
|
+
try {
|
|
2467
|
+
nextCtx = onMutate?.(value, ictx);
|
|
2468
|
+
}
|
|
2469
|
+
catch (mutationErr) {
|
|
2470
|
+
// match legacy mutate(): the throw aborts the mutation and resets state
|
|
2471
|
+
ctx = undefined;
|
|
2472
|
+
next.set(NULL_VALUE);
|
|
2473
|
+
deferred?.reject(mutationErr);
|
|
2474
|
+
if (isDevMode())
|
|
2475
|
+
console.error('[@mmstack/resource]: error thrown in onMutate hook, mutation was not applied', mutationErr);
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
ctx = nextCtx;
|
|
2479
|
+
currentDeferred = deferred;
|
|
2480
|
+
next.set(value);
|
|
2481
|
+
if (deferred && untracked(req) === undefined) {
|
|
2482
|
+
ctx = undefined;
|
|
2483
|
+
currentDeferred = undefined;
|
|
2484
|
+
next.set(NULL_VALUE);
|
|
2485
|
+
deferred.reject(new MutationCancelledError('no-request', 'mutation not sent: request() returned undefined'));
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
const supersedeInFlight = () => {
|
|
2489
|
+
if (untracked(next) === NULL_VALUE)
|
|
2490
|
+
return;
|
|
2491
|
+
if (isDevMode())
|
|
2492
|
+
console.warn('[@mmstack/resource]: mutate() called while another mutation was in flight — the previous mutation was superseded (latest-wins) and its onSettled was invoked. Use `queue: true` for sequential mutations.');
|
|
2493
|
+
try {
|
|
2494
|
+
onSettled?.(ctx);
|
|
2495
|
+
}
|
|
2496
|
+
catch (settleErr) {
|
|
2497
|
+
if (isDevMode())
|
|
2498
|
+
console.error('[@mmstack/resource]: error thrown in onSettled hook for a superseded mutation', settleErr);
|
|
2499
|
+
}
|
|
2500
|
+
currentDeferred?.reject(new MutationCancelledError('superseded', 'mutation superseded by a newer mutation (latest-wins)'));
|
|
2501
|
+
currentDeferred = undefined;
|
|
2502
|
+
ctx = undefined;
|
|
2503
|
+
};
|
|
2504
|
+
const queueRef = effect(() => {
|
|
2505
|
+
const q = queue(); // subscribe to swaps (key change / clearQueue)
|
|
2506
|
+
const nextInQueue = q().at(0); // subscribe to contents
|
|
2507
|
+
if (nextInQueue === undefined || next() !== NULL_VALUE)
|
|
2508
|
+
return;
|
|
2509
|
+
q.update((arr) => arr.slice(1));
|
|
2510
|
+
const [value, ictx, deferred] = nextInQueue;
|
|
2511
|
+
begin(value, ictx, deferred);
|
|
2512
|
+
}, { ...(ngDevMode ? { debugName: "queueRef" } : /* istanbul ignore next */ {}), injector: options.injector });
|
|
2508
2513
|
const lastValue = linkedSignal({ ...(ngDevMode ? { debugName: "lastValue" } : /* istanbul ignore next */ {}), source: next,
|
|
2509
2514
|
computation: (next, prev) => {
|
|
2510
2515
|
if (next === NULL_VALUE && !!prev)
|
|
@@ -2559,8 +2564,12 @@ function mutationResource(request, options0 = {}) {
|
|
|
2559
2564
|
return NULL_VALUE;
|
|
2560
2565
|
}), filter((v) => v !== NULL_VALUE), takeUntilDestroyed(destroyRef))
|
|
2561
2566
|
.subscribe((result) => {
|
|
2562
|
-
|
|
2567
|
+
const deferred = currentDeferred;
|
|
2568
|
+
currentDeferred = undefined;
|
|
2569
|
+
if (result.status === 'error') {
|
|
2563
2570
|
onError?.(result.error, ctx);
|
|
2571
|
+
deferred?.reject(result.error);
|
|
2572
|
+
}
|
|
2564
2573
|
else {
|
|
2565
2574
|
onSuccess?.(result.value, ctx);
|
|
2566
2575
|
if (cache && invalidates) {
|
|
@@ -2568,61 +2577,61 @@ function mutationResource(request, options0 = {}) {
|
|
|
2568
2577
|
const prefixes = typeof invalidates === 'function'
|
|
2569
2578
|
? invalidates(result.value, (mutation === NULL_VALUE ? undefined : mutation))
|
|
2570
2579
|
: invalidates;
|
|
2571
|
-
// auto-keys are `${method}:${url}:...` — a `GET:`-prefixed url prefix hits
|
|
2572
|
-
// the url with any params/subpaths and every varyHeaders variant
|
|
2573
2580
|
for (const prefix of prefixes)
|
|
2574
2581
|
cache.invalidatePrefix(`GET:${prefix}`);
|
|
2575
2582
|
}
|
|
2583
|
+
deferred?.resolve(result.value);
|
|
2576
2584
|
}
|
|
2577
2585
|
onSettled?.(ctx);
|
|
2578
2586
|
ctx = undefined;
|
|
2579
2587
|
next.set(NULL_VALUE);
|
|
2580
2588
|
});
|
|
2581
|
-
const shouldQueue = options.queue ?? false;
|
|
2582
2589
|
const ref = {
|
|
2583
2590
|
...resource,
|
|
2584
2591
|
destroy: () => {
|
|
2585
2592
|
// queue first — a late queue flush must not poke an already-destroyed resource
|
|
2586
2593
|
queueRef.destroy();
|
|
2587
2594
|
statusSub.unsubscribe();
|
|
2595
|
+
// reject any outstanding mutateAsync promises so awaiters don't hang
|
|
2596
|
+
const cancelled = new MutationCancelledError('destroyed', 'mutation abandoned: resource destroyed');
|
|
2597
|
+
currentDeferred?.reject(cancelled);
|
|
2598
|
+
currentDeferred = undefined;
|
|
2599
|
+
for (const [, , deferred] of untracked(queue)())
|
|
2600
|
+
deferred?.reject(cancelled);
|
|
2588
2601
|
resource.destroy();
|
|
2589
2602
|
},
|
|
2590
2603
|
mutate: (value, ictx) => {
|
|
2591
|
-
if (
|
|
2592
|
-
|
|
2604
|
+
if (queueEnabled) {
|
|
2605
|
+
queue().update((arr) => [...arr, [value, ictx, undefined]]);
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
supersedeInFlight();
|
|
2609
|
+
begin(value, ictx, undefined);
|
|
2610
|
+
},
|
|
2611
|
+
mutateAsync: (value, ictx) => {
|
|
2612
|
+
const deferred = Promise.withResolvers();
|
|
2613
|
+
if (queueEnabled) {
|
|
2614
|
+
queue().update((arr) => [...arr, [value, ictx, deferred]]);
|
|
2593
2615
|
}
|
|
2594
2616
|
else {
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
// settle its context NOW so optimistic state can be rolled back/cleaned up
|
|
2598
|
-
if (untracked(next) !== NULL_VALUE) {
|
|
2599
|
-
if (isDevMode())
|
|
2600
|
-
console.warn('[@mmstack/resource]: mutate() called while another mutation was in flight — the previous mutation was superseded (latest-wins) and its onSettled was invoked. Use `queue: true` for sequential mutations.');
|
|
2601
|
-
try {
|
|
2602
|
-
onSettled?.(ctx);
|
|
2603
|
-
}
|
|
2604
|
-
catch (settleErr) {
|
|
2605
|
-
if (isDevMode())
|
|
2606
|
-
console.error('[@mmstack/resource]: error thrown in onSettled hook for a superseded mutation', settleErr);
|
|
2607
|
-
}
|
|
2608
|
-
ctx = undefined;
|
|
2609
|
-
}
|
|
2610
|
-
try {
|
|
2611
|
-
ctx = onMutate?.(value, ictx);
|
|
2612
|
-
next.set(value);
|
|
2613
|
-
}
|
|
2614
|
-
catch (mutationErr) {
|
|
2615
|
-
ctx = undefined;
|
|
2616
|
-
next.set(NULL_VALUE);
|
|
2617
|
-
if (isDevMode())
|
|
2618
|
-
console.error('[@mmstack/resource]: error thrown in onMutate hook, mutation was not applied', mutationErr);
|
|
2619
|
-
}
|
|
2617
|
+
supersedeInFlight();
|
|
2618
|
+
begin(value, ictx, deferred);
|
|
2620
2619
|
}
|
|
2620
|
+
return deferred.promise;
|
|
2621
2621
|
},
|
|
2622
2622
|
current: computed(() => {
|
|
2623
2623
|
const nv = next();
|
|
2624
2624
|
return nv === NULL_VALUE ? null : nv;
|
|
2625
2625
|
}),
|
|
2626
|
+
clearQueue: () => {
|
|
2627
|
+
if (!queueEnabled)
|
|
2628
|
+
return;
|
|
2629
|
+
const dropped = untracked(queue)();
|
|
2630
|
+
queue.set(signal([]));
|
|
2631
|
+
// reject mutateAsync promises whose entries we just dropped
|
|
2632
|
+
for (const [, , deferred] of dropped)
|
|
2633
|
+
deferred?.reject(new MutationCancelledError('queue-cleared', 'mutation dropped: queue cleared before it ran'));
|
|
2634
|
+
},
|
|
2626
2635
|
// redeclare disabled with last value so that it is not affected by the resource's internal disablement logic
|
|
2627
2636
|
disabled: computed(() => cb.isOpen() || lastValueRequest() === undefined),
|
|
2628
2637
|
};
|
|
@@ -2634,5 +2643,5 @@ function mutationResource(request, options0 = {}) {
|
|
|
2634
2643
|
* Generated bundle index. Do not edit.
|
|
2635
2644
|
*/
|
|
2636
2645
|
|
|
2637
|
-
export { Cache, PAUSED, applyResourceRegistration, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, hashRequest, infiniteQueryResource, injectQueryCache, injectResourceOptions, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideMutationResourceOptions, provideQueryCache, provideQueryResourceOptions, provideResourceOptions, provideTypedResourceOptions, queryResource };
|
|
2646
|
+
export { Cache, MutationCancelledError, PAUSED, applyResourceRegistration, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, hashRequest, infiniteQueryResource, injectQueryCache, injectResourceOptions, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideMutationResourceOptions, provideQueryCache, provideQueryResourceOptions, provideResourceOptions, provideTypedResourceOptions, queryResource };
|
|
2638
2647
|
//# sourceMappingURL=mmstack-resource.mjs.map
|