@sveltejs/kit 2.60.1 → 2.61.1

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.
Files changed (45) hide show
  1. package/package.json +8 -9
  2. package/src/core/postbuild/analyse.js +1 -3
  3. package/src/core/sync/create_manifest_data/conflict.js +72 -0
  4. package/src/core/sync/create_manifest_data/index.js +1 -65
  5. package/src/core/sync/write_non_ambient.js +2 -2
  6. package/src/core/sync/write_types/index.js +1 -1
  7. package/src/exports/public.d.ts +23 -30
  8. package/src/exports/vite/build/build_server.js +36 -13
  9. package/src/exports/vite/dev/index.js +4 -2
  10. package/src/exports/vite/index.js +18 -16
  11. package/src/runtime/app/server/index.js +1 -2
  12. package/src/runtime/app/server/remote/form.js +10 -0
  13. package/src/runtime/app/server/remote/query.js +100 -36
  14. package/src/runtime/client/client.js +13 -8
  15. package/src/runtime/client/remote-functions/cache.svelte.js +157 -0
  16. package/src/runtime/client/remote-functions/form.svelte.js +235 -196
  17. package/src/runtime/client/remote-functions/index.js +2 -2
  18. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -2
  19. package/src/runtime/client/remote-functions/query/cache.js +4 -0
  20. package/src/runtime/client/remote-functions/query/index.js +48 -0
  21. package/src/runtime/client/remote-functions/query/instance.svelte.js +249 -0
  22. package/src/runtime/client/remote-functions/query/proxy.js +156 -0
  23. package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
  24. package/src/runtime/client/remote-functions/query-live/cache.js +4 -0
  25. package/src/runtime/client/remote-functions/query-live/index.js +31 -0
  26. package/src/runtime/client/remote-functions/{query-live.svelte.js → query-live/instance.svelte.js} +61 -310
  27. package/src/runtime/client/remote-functions/query-live/iterator.js +91 -0
  28. package/src/runtime/client/remote-functions/query-live/proxy.js +144 -0
  29. package/src/runtime/client/remote-functions/shared.svelte.js +53 -6
  30. package/src/runtime/client/utils.js +1 -1
  31. package/src/runtime/form-utils.js +7 -16
  32. package/src/runtime/server/index.js +2 -3
  33. package/src/runtime/server/page/actions.js +2 -9
  34. package/src/runtime/server/page/csp.js +3 -4
  35. package/src/runtime/server/page/render.js +13 -14
  36. package/src/runtime/server/respond.js +60 -36
  37. package/src/runtime/server/utils.js +23 -3
  38. package/src/types/global-private.d.ts +5 -0
  39. package/src/types/internal.d.ts +29 -6
  40. package/src/utils/routing.js +3 -1
  41. package/src/utils/shared-iterator.js +213 -0
  42. package/src/version.js +1 -1
  43. package/types/index.d.ts +27 -31
  44. package/types/index.d.ts.map +1 -1
  45. package/src/runtime/client/remote-functions/query.svelte.js +0 -512
@@ -1,143 +1,18 @@
1
- /** @import { RemoteLiveQuery, RemoteLiveQueryFunction } from '@sveltejs/kit' */
2
- /** @import { PromiseWithResolvers } from '../../../utils/promise.js' */
3
- import { app_dir, base } from '$app/paths/internal/client';
4
- import { app, live_query_map } from '../client.js';
5
- import {
6
- get_remote_request_headers,
7
- handle_side_channel_response,
8
- is_in_effect,
9
- QUERY_FUNCTION_ID,
10
- QUERY_RESOURCE_KEY
11
- } from './shared.svelte.js';
1
+ /** @import { PromiseWithResolvers } from '../../../../utils/promise.js' */
2
+ import { app } from '../../client.js';
12
3
  import * as devalue from 'devalue';
13
4
  import { HttpError, Redirect } from '@sveltejs/kit/internal';
14
- import { DEV } from 'esm-env';
15
- import { noop, once } from '../../../utils/functions.js';
16
- import { with_resolvers } from '../../../utils/promise.js';
5
+ import { noop, once } from '../../../../utils/functions.js';
6
+ import { with_resolvers } from '../../../../utils/promise.js';
7
+ import { SharedIterator } from '../../../../utils/shared-iterator.js';
17
8
  import { tick } from 'svelte';
18
- import { create_remote_key, stringify_remote_arg, unfriendly_hydratable } from '../../shared.js';
19
- import { read_ndjson } from '../ndjson.js';
20
-
21
- /**
22
- * @template T
23
- * @typedef {{
24
- * count: number;
25
- * resource: LiveQuery<T>;
26
- * cleanup: () => void;
27
- * }} RemoteLiveQueryCacheEntry
28
- */
29
-
30
- /**
31
- * @param {string} id
32
- * @returns {RemoteLiveQueryFunction<any, any>}
33
- */
34
- export function query_live(id) {
35
- if (DEV) {
36
- // If this reruns as part of HMR, refresh the query
37
- const entries = live_query_map.get(id);
38
-
39
- if (entries) {
40
- for (const entry of entries.values()) {
41
- void entry.resource.reconnect();
42
- }
43
- }
44
- }
45
-
46
- /** @type {RemoteLiveQueryFunction<any, any>} */
47
- const wrapper = (arg) => /** @type {RemoteLiveQuery<any>} */ (new LiveQueryProxy(id, arg));
48
-
49
- Object.defineProperty(wrapper, QUERY_FUNCTION_ID, { value: id });
50
-
51
- return wrapper;
52
- }
53
-
54
- /**
55
- * @param {Response} response
56
- * @returns {Promise<ReadableStreamDefaultReader<Uint8Array>>}
57
- */
58
- async function get_stream_reader(response) {
59
- const content_type = response.headers.get('content-type') ?? '';
60
-
61
- if (response.ok && content_type.includes('application/json')) {
62
- // we can end up here if we e.g. redirect in `handle`
63
- const result = await response.json();
64
- await handle_side_channel_response(result);
65
- throw new HttpError(500, 'Invalid query.live response');
66
- }
67
-
68
- if (!response.ok) {
69
- const result = await response.json().catch(() => ({
70
- type: 'error',
71
- status: response.status,
72
- error: response.statusText
73
- }));
74
-
75
- throw new HttpError(result.status ?? response.status ?? 500, result.error);
76
- }
77
-
78
- if (!response.body) {
79
- throw new Error('Expected query.live response body to be a ReadableStream');
80
- }
81
-
82
- return response.body.getReader();
83
- }
84
-
85
- /**
86
- * Yields deserialized results from a ReadableStream of newline-delimited JSON
87
- * @param {ReadableStreamDefaultReader<Uint8Array>} reader
88
- */
89
- async function* read_live_ndjson(reader) {
90
- for await (const node of read_ndjson(reader)) {
91
- if (node.type === 'result') {
92
- yield devalue.parse(node.result, app.decoders);
93
- continue;
94
- }
95
-
96
- await handle_side_channel_response(node);
97
- throw new HttpError(500, 'Invalid query.live response');
98
- }
99
- }
100
-
101
- /**
102
- * @template T
103
- * @param {string} id
104
- * @param {string} payload
105
- * @param {AbortController} [controller]
106
- * @param {() => void} [on_connect]
107
- * @returns {AsyncGenerator<T>}
108
- */
109
- export async function* create_live_iterator(
110
- id,
111
- payload,
112
- controller = new AbortController(),
113
- on_connect = noop
114
- ) {
115
- const url = `${base}/${app_dir}/remote/${id}${payload ? `?payload=${payload}` : ''}`;
116
- /** @type {ReadableStreamDefaultReader<Uint8Array> | null} */
117
- let reader = null;
118
-
119
- try {
120
- const response = await fetch(url, {
121
- headers: get_remote_request_headers(),
122
- signal: controller.signal
123
- });
124
- reader = await get_stream_reader(response);
125
-
126
- on_connect();
127
-
128
- yield* read_live_ndjson(reader);
129
- } finally {
130
- try {
131
- await reader?.cancel();
132
- } catch {
133
- // already closed
134
- }
135
- }
136
- }
9
+ import { unfriendly_hydratable } from '../../../shared.js';
10
+ import { create_live_iterator } from './iterator.js';
137
11
 
138
12
  /**
139
13
  * @template T
140
14
  * @implements {Promise<T>}
15
+ * @implements {AsyncIterable<T>}
141
16
  */
142
17
  export class LiveQuery {
143
18
  #id;
@@ -170,6 +45,15 @@ export class LiveQuery {
170
45
  #interrupt = null;
171
46
  #attempt = 0;
172
47
 
48
+ /**
49
+ * Fan-out for `for await` consumers attached to this LiveQuery's shared
50
+ * stream. New subscribers see the most-recently-emitted value (if any) as
51
+ * their first yield. Subsequent yields fire whenever `set()` is called.
52
+ *
53
+ * @type {SharedIterator<T>}
54
+ */
55
+ #fan_out = new SharedIterator();
56
+
173
57
  /** @type {Promise<T>['then']} */
174
58
  // @ts-expect-error TS doesn't understand that the promise returns something
175
59
  #then = $derived.by(() => {
@@ -257,6 +141,7 @@ export class LiveQuery {
257
141
  }
258
142
 
259
143
  this.#done = true;
144
+ this.#fan_out.done();
260
145
  } catch (error) {
261
146
  if (controller.signal.aborted) break;
262
147
 
@@ -272,7 +157,6 @@ export class LiveQuery {
272
157
  if (!this.#ready) {
273
158
  // If we haven't successfully connected and received a value yet, surface the error
274
159
  this.fail(error);
275
- this.#done = true;
276
160
  on_connect_failed(error);
277
161
  break;
278
162
  }
@@ -280,7 +164,6 @@ export class LiveQuery {
280
164
  if (error instanceof HttpError) {
281
165
  // Server intentionally sent an error. Surface it and stop.
282
166
  this.fail(error);
283
- this.#done = true;
284
167
  break;
285
168
  }
286
169
 
@@ -349,9 +232,38 @@ export class LiveQuery {
349
232
  window.removeEventListener('pageshow', this.#on_pageshow);
350
233
  }
351
234
 
235
+ this.#fan_out.done();
236
+
352
237
  void this.#interrupt?.();
353
238
  }
354
239
 
240
+ /**
241
+ * Iterate the stream of values yielded by this live query. Multiple
242
+ * iterators share the underlying connection; the most-recently emitted value
243
+ * (if any) is yielded first to each new iterator, mirroring the semantics
244
+ * of awaiting the query directly.
245
+ *
246
+ * Backpressure note: if values arrive faster than the consumer drains, only
247
+ * the latest pending value is kept. This matches the reactive `.current`
248
+ * semantics — live streams are not event logs.
249
+ *
250
+ * @returns {AsyncGenerator<T, void, void>}
251
+ */
252
+ [Symbol.asyncIterator]() {
253
+ this.#start();
254
+
255
+ // Seed the new iterator with the current value (if any) so the first
256
+ // `.next()` resolves synchronously — mirroring `await liveQuery()`
257
+ // semantics. If the query has hard-failed, `#fan_out` is already closed
258
+ // with the terminal error and `subscribe()` returns an iterator whose
259
+ // first `.next()` rejects.
260
+ return this.#fan_out.subscribe(
261
+ this.#ready && this.#error === undefined
262
+ ? { initial_value: { value: /** @type {T} */ (this.#raw) } }
263
+ : undefined
264
+ );
265
+ }
266
+
355
267
  get then() {
356
268
  this.#start();
357
269
  return this.#then;
@@ -419,6 +331,10 @@ export class LiveQuery {
419
331
  promise.catch(noop);
420
332
  this.#done = false;
421
333
  this.#attempt = 0;
334
+ // The previous fan-out may have been closed by `done()`/`fail()`. Future
335
+ // `for await` consumers need a fresh, open fan-out attached to the new
336
+ // `#main` lifetime.
337
+ this.#fan_out = new SharedIterator();
422
338
  this.#main({ on_connect, on_connect_failed }).catch(noop);
423
339
  await promise;
424
340
  }
@@ -437,12 +353,20 @@ export class LiveQuery {
437
353
  } else {
438
354
  this.#promise = Promise.resolve();
439
355
  }
356
+
357
+ this.#fan_out.push(value);
440
358
  }
441
359
 
442
360
  /** @param {unknown} error */
443
361
  fail(error) {
444
362
  this.#loading = false;
445
363
  this.#error = error;
364
+ // `fail` is terminal — once a live query has hard-failed, the only way to start
365
+ // streaming again is via `reconnect()`. Mark it done and abort any in-flight
366
+ // request so that callers from outside the main loop (e.g. `apply_reconnections`)
367
+ // don't leave the loop spinning.
368
+ this.#done = true;
369
+ void this.#interrupt?.();
446
370
 
447
371
  if (this.#reject_first) {
448
372
  this.#reject_first(error);
@@ -453,184 +377,11 @@ export class LiveQuery {
453
377
  promise.catch(noop);
454
378
  this.#promise = promise;
455
379
  }
456
- }
457
-
458
- get [Symbol.toStringTag]() {
459
- return 'LiveQuery';
460
- }
461
- }
462
-
463
- /**
464
- * @template T
465
- * @implements {Promise<T>}
466
- */
467
- class LiveQueryProxy {
468
- #key;
469
- #id;
470
- #payload;
471
- #active = true;
472
- #tracking = is_in_effect();
473
-
474
- /**
475
- * @param {string} id
476
- * @param {any} arg
477
- */
478
- constructor(id, arg) {
479
- this.#id = id;
480
- this.#payload = stringify_remote_arg(arg, app.hooks.transport);
481
- this.#key = create_remote_key(id, this.#payload);
482
- Object.defineProperty(this, QUERY_RESOURCE_KEY, { value: this.#key });
483
-
484
- if (!this.#tracking) {
485
- this.#active = false;
486
- return;
487
- }
488
380
 
489
- const entry = this.#get_or_create_cache_entry();
490
-
491
- $effect.pre(() => () => {
492
- const die = this.#release(entry);
493
- void tick().then(die);
494
- });
495
- }
496
-
497
- /** @returns {RemoteLiveQueryCacheEntry<T>} */
498
- #get_or_create_cache_entry() {
499
- let query_instances = live_query_map.get(this.#id);
500
-
501
- if (!query_instances) {
502
- query_instances = new Map();
503
- live_query_map.set(this.#id, query_instances);
504
- }
505
-
506
- let this_instance = query_instances.get(this.#payload);
507
-
508
- if (!this_instance) {
509
- const c = (this_instance = {
510
- count: 0,
511
- resource: /** @type {LiveQuery<T>} */ (/** @type {unknown} */ (null)),
512
- cleanup: /** @type {() => void} */ (/** @type {unknown} */ (null))
513
- });
514
-
515
- c.cleanup = $effect.root(() => {
516
- c.resource = new LiveQuery(this.#id, this.#key, this.#payload);
517
- });
518
-
519
- query_instances.set(this.#payload, this_instance);
520
- }
521
-
522
- this_instance.count += 1;
523
-
524
- return this_instance;
525
- }
526
-
527
- /**
528
- * @param {RemoteLiveQueryCacheEntry<T>} entry
529
- * @param {boolean} [deactivate]
530
- */
531
- #release(entry, deactivate = true) {
532
- this.#active &&= !deactivate;
533
- entry.count -= 1;
534
-
535
- return () => {
536
- const query_instances = live_query_map.get(this.#id);
537
- const this_instance = query_instances?.get(this.#payload);
538
-
539
- if (this_instance?.count === 0) {
540
- this_instance.resource.destroy();
541
- this_instance.cleanup();
542
- query_instances?.delete(this.#payload);
543
- }
544
-
545
- if (query_instances?.size === 0) {
546
- live_query_map.delete(this.#id);
547
- }
548
- };
549
- }
550
-
551
- #get_cached_query() {
552
- if (!this.#tracking) {
553
- throw new Error(
554
- 'This live query was not created in a reactive context and is limited to calling `.run` and `.reconnect`.'
555
- );
556
- }
557
-
558
- if (!this.#active) {
559
- throw new Error(
560
- 'This query instance is no longer active and can no longer be used for reactive state access. ' +
561
- 'This typically means you created the query in a tracking context and stashed it somewhere outside of a tracking context.'
562
- );
563
- }
564
-
565
- const cached = live_query_map.get(this.#id)?.get(this.#payload);
566
-
567
- if (!cached) {
568
- throw new Error(
569
- 'No cached query found. This should be impossible. Please file a bug report.'
570
- );
571
- }
572
-
573
- return cached.resource;
574
- }
575
-
576
- #safe_get_cached_query() {
577
- return live_query_map.get(this.#id)?.get(this.#payload)?.resource;
578
- }
579
-
580
- get current() {
581
- return this.#safe_get_cached_query()?.current;
582
- }
583
-
584
- get error() {
585
- return this.#safe_get_cached_query()?.error;
586
- }
587
-
588
- get loading() {
589
- return this.#safe_get_cached_query()?.loading ?? false;
590
- }
591
-
592
- get ready() {
593
- return this.#safe_get_cached_query()?.ready ?? false;
594
- }
595
-
596
- get connected() {
597
- return this.#safe_get_cached_query()?.connected ?? false;
598
- }
599
-
600
- get done() {
601
- return this.#safe_get_cached_query()?.done ?? false;
602
- }
603
-
604
- run() {
605
- if (is_in_effect()) {
606
- throw new Error(
607
- 'On the client, .run() can only be called outside render, e.g. in universal `load` functions and event handlers. In render, await the query directly'
608
- );
609
- }
610
-
611
- return create_live_iterator(this.#id, this.#payload);
612
- }
613
-
614
- reconnect() {
615
- return this.#safe_get_cached_query()?.reconnect() ?? Promise.resolve();
616
- }
617
-
618
- get then() {
619
- const cached = this.#get_cached_query();
620
- return cached.then.bind(cached);
621
- }
622
-
623
- get catch() {
624
- const cached = this.#get_cached_query();
625
- return cached.catch.bind(cached);
626
- }
627
-
628
- get finally() {
629
- const cached = this.#get_cached_query();
630
- return cached.finally.bind(cached);
381
+ this.#fan_out.fail(error);
631
382
  }
632
383
 
633
384
  get [Symbol.toStringTag]() {
634
- return 'LiveQueryProxy';
385
+ return 'LiveQuery';
635
386
  }
636
387
  }
@@ -0,0 +1,91 @@
1
+ import { app_dir, base } from '$app/paths/internal/client';
2
+ import { app } from '../../client.js';
3
+ import { get_remote_request_headers, handle_side_channel_response } from '../shared.svelte.js';
4
+ import * as devalue from 'devalue';
5
+ import { HttpError } from '@sveltejs/kit/internal';
6
+ import { noop } from '../../../../utils/functions.js';
7
+ import { read_ndjson } from '../../ndjson.js';
8
+
9
+ /**
10
+ * @param {Response} response
11
+ * @returns {Promise<ReadableStreamDefaultReader<Uint8Array>>}
12
+ */
13
+ async function get_stream_reader(response) {
14
+ const content_type = response.headers.get('content-type') ?? '';
15
+
16
+ if (response.ok && content_type.includes('application/json')) {
17
+ // we can end up here if we e.g. redirect in `handle`
18
+ const result = await response.json();
19
+ await handle_side_channel_response(result);
20
+ throw new HttpError(500, 'Invalid query.live response');
21
+ }
22
+
23
+ if (!response.ok) {
24
+ const result = await response.json().catch(() => ({
25
+ type: 'error',
26
+ status: response.status,
27
+ error: response.statusText
28
+ }));
29
+
30
+ throw new HttpError(result.status ?? response.status ?? 500, result.error);
31
+ }
32
+
33
+ if (!response.body) {
34
+ throw new Error('Expected query.live response body to be a ReadableStream');
35
+ }
36
+
37
+ return response.body.getReader();
38
+ }
39
+
40
+ /**
41
+ * Yields deserialized results from a ReadableStream of newline-delimited JSON
42
+ * @param {ReadableStreamDefaultReader<Uint8Array>} reader
43
+ */
44
+ async function* read_live_ndjson(reader) {
45
+ for await (const node of read_ndjson(reader)) {
46
+ if (node.type === 'result') {
47
+ yield devalue.parse(node.result, app.decoders);
48
+ continue;
49
+ }
50
+
51
+ await handle_side_channel_response(node);
52
+ throw new HttpError(500, 'Invalid query.live response');
53
+ }
54
+ }
55
+
56
+ /**
57
+ * @template T
58
+ * @param {string} id
59
+ * @param {string} payload
60
+ * @param {AbortController} [controller]
61
+ * @param {() => void} [on_connect]
62
+ * @returns {AsyncGenerator<T>}
63
+ */
64
+ export async function* create_live_iterator(
65
+ id,
66
+ payload,
67
+ controller = new AbortController(),
68
+ on_connect = noop
69
+ ) {
70
+ const url = `${base}/${app_dir}/remote/${id}${payload ? `?payload=${payload}` : ''}`;
71
+ /** @type {ReadableStreamDefaultReader<Uint8Array> | null} */
72
+ let reader = null;
73
+
74
+ try {
75
+ const response = await fetch(url, {
76
+ headers: get_remote_request_headers(),
77
+ signal: controller.signal
78
+ });
79
+ reader = await get_stream_reader(response);
80
+
81
+ on_connect();
82
+
83
+ yield* read_live_ndjson(reader);
84
+ } finally {
85
+ try {
86
+ await reader?.cancel();
87
+ } catch {
88
+ // already closed
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,144 @@
1
+ import { app, live_query_map } from '../../client.js';
2
+ import { pin_in_effect, pin_while_resolving, QUERY_RESOURCE_KEY } from '../shared.svelte.js';
3
+ import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
4
+ import { LiveQuery } from './instance.svelte.js';
5
+ import { cache } from './cache.js';
6
+
7
+ /**
8
+ * @template T
9
+ * @implements {Promise<T>}
10
+ * @implements {AsyncIterable<T>}
11
+ */
12
+ export class LiveQueryProxy {
13
+ #key;
14
+ #id;
15
+ #payload;
16
+
17
+ /**
18
+ * @param {string} id
19
+ * @param {any} arg
20
+ */
21
+ constructor(id, arg) {
22
+ this.#id = id;
23
+ this.#payload = stringify_remote_arg(arg, app.hooks.transport);
24
+ this.#key = create_remote_key(id, this.#payload);
25
+ Object.defineProperty(this, QUERY_RESOURCE_KEY, { value: this.#key });
26
+
27
+ // Capture key/payload in locals so the create_resource closure doesn't capture
28
+ // `this` (the LiveQueryProxy), which would prevent the FinalizationRegistry from
29
+ // observing the proxy as unreachable and so leak the first proxy for a given key.
30
+ const key = this.#key;
31
+ const payload = this.#payload;
32
+ const entry = cache.ensure_entry(this.#id, payload, () => new LiveQuery(id, key, payload));
33
+
34
+ cache.ref(this, entry, this.#id, payload);
35
+ }
36
+
37
+ #get_cache_entry() {
38
+ const cached = live_query_map.get(this.#id)?.get(this.#payload);
39
+
40
+ if (!cached) {
41
+ throw new Error(
42
+ 'No cached query found. This should be impossible. Please file a bug report.'
43
+ );
44
+ }
45
+
46
+ return cached;
47
+ }
48
+
49
+ #get_cached_query() {
50
+ return this.#get_cache_entry().resource;
51
+ }
52
+
53
+ get current() {
54
+ return this.#get_cached_query().current;
55
+ }
56
+
57
+ get error() {
58
+ return this.#get_cached_query().error;
59
+ }
60
+
61
+ get loading() {
62
+ return this.#get_cached_query().loading;
63
+ }
64
+
65
+ get ready() {
66
+ return this.#get_cached_query().ready;
67
+ }
68
+
69
+ get connected() {
70
+ return this.#get_cached_query().connected;
71
+ }
72
+
73
+ get done() {
74
+ return this.#get_cached_query().done;
75
+ }
76
+
77
+ /**
78
+ * @deprecated Use `for await (const value of liveQuery())` instead.
79
+ * @returns {AsyncGenerator<T>}
80
+ */
81
+ run() {
82
+ throw new Error(
83
+ '`.run()` has been removed from live queries. Use `for await (const value of liveQuery())` instead.'
84
+ );
85
+ }
86
+
87
+ reconnect() {
88
+ return this.#get_cached_query().reconnect();
89
+ }
90
+
91
+ /**
92
+ * @returns {AsyncGenerator<T, void, void>}
93
+ */
94
+ async *[Symbol.asyncIterator]() {
95
+ const entry = this.#get_cache_entry();
96
+ const release = cache.manual_ref(entry, this.#id, this.#payload);
97
+
98
+ try {
99
+ yield* entry.resource;
100
+ } finally {
101
+ release();
102
+ }
103
+ }
104
+
105
+ get then() {
106
+ pin_in_effect(live_query_map, cache, this.#id, this.#payload);
107
+ const cached = this.#get_cached_query();
108
+ return pin_while_resolving(
109
+ live_query_map,
110
+ cache,
111
+ this.#id,
112
+ this.#payload,
113
+ cached.then.bind(cached)
114
+ );
115
+ }
116
+
117
+ get catch() {
118
+ pin_in_effect(live_query_map, cache, this.#id, this.#payload);
119
+ const cached = this.#get_cached_query();
120
+ return pin_while_resolving(
121
+ live_query_map,
122
+ cache,
123
+ this.#id,
124
+ this.#payload,
125
+ cached.catch.bind(cached)
126
+ );
127
+ }
128
+
129
+ get finally() {
130
+ pin_in_effect(live_query_map, cache, this.#id, this.#payload);
131
+ const cached = this.#get_cached_query();
132
+ return pin_while_resolving(
133
+ live_query_map,
134
+ cache,
135
+ this.#id,
136
+ this.#payload,
137
+ cached.finally.bind(cached)
138
+ );
139
+ }
140
+
141
+ get [Symbol.toStringTag]() {
142
+ return 'LiveQueryProxy';
143
+ }
144
+ }