@sveltejs/kit 2.57.1 → 2.59.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/package.json +2 -2
- package/src/exports/internal/remote-functions.js +1 -1
- package/src/exports/public.d.ts +107 -16
- package/src/runtime/app/paths/client.js +7 -0
- package/src/runtime/app/paths/server.js +7 -0
- package/src/runtime/app/server/remote/command.js +7 -6
- package/src/runtime/app/server/remote/form.js +2 -1
- package/src/runtime/app/server/remote/prerender.js +3 -4
- package/src/runtime/app/server/remote/query.js +327 -113
- package/src/runtime/app/server/remote/requested.js +127 -32
- package/src/runtime/app/server/remote/shared.js +89 -20
- package/src/runtime/client/client.js +92 -62
- package/src/runtime/client/ndjson.js +42 -0
- package/src/runtime/client/remote-functions/command.svelte.js +8 -3
- package/src/runtime/client/remote-functions/form.svelte.js +24 -6
- package/src/runtime/client/remote-functions/index.js +3 -1
- package/src/runtime/client/remote-functions/query-batch.svelte.js +105 -0
- package/src/runtime/client/remote-functions/query-live.svelte.js +636 -0
- package/src/runtime/client/remote-functions/query.svelte.js +48 -148
- package/src/runtime/client/remote-functions/shared.svelte.js +76 -23
- package/src/runtime/form-utils.js +33 -12
- package/src/runtime/server/page/index.js +26 -16
- package/src/runtime/server/page/load_data.js +4 -2
- package/src/runtime/server/page/render.js +21 -18
- package/src/runtime/server/remote.js +117 -9
- package/src/runtime/server/respond.js +11 -8
- package/src/runtime/server/utils.js +10 -0
- package/src/runtime/shared.js +3 -3
- package/src/runtime/utils.js +0 -1
- package/src/types/internal.d.ts +54 -14
- package/src/utils/page_nodes.js +1 -0
- package/src/utils/url.js +3 -3
- package/src/version.js +1 -1
- package/types/index.d.ts +182 -30
- package/types/index.d.ts.map +8 -4
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
/** @import { RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { RemoteInternals, MaybePromise, RequestState, RemoteQueryBatchInternals, RemoteQueryInternals } from 'types' */
|
|
1
|
+
/** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
|
|
2
|
+
/** @import { RemoteInternals, MaybePromise, RequestState, RemoteQueryLiveInternals, RemoteQueryBatchInternals, RemoteQueryInternals, RemoteLiveQueryUserFunctionReturnType } from 'types' */
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
4
|
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
5
5
|
import { create_remote_key, stringify, stringify_remote_arg } from '../../../shared.js';
|
|
6
6
|
import { prerendering } from '__sveltekit/environment';
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
create_validator,
|
|
9
|
+
get_cache,
|
|
10
|
+
get_response,
|
|
11
|
+
run_remote_function,
|
|
12
|
+
run_remote_generator
|
|
13
|
+
} from './shared.js';
|
|
9
14
|
import { handle_error_and_jsonify } from '../../../server/utils.js';
|
|
10
15
|
import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
|
|
16
|
+
import { noop } from '../../../../utils/functions.js';
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
19
|
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -43,7 +49,7 @@ import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
|
|
|
43
49
|
* @overload
|
|
44
50
|
* @param {Schema} schema
|
|
45
51
|
* @param {(arg: StandardSchemaV1.InferOutput<Schema>) => MaybePromise<Output>} fn
|
|
46
|
-
* @returns {RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output
|
|
52
|
+
* @returns {RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output, StandardSchemaV1.InferOutput<Schema>>}
|
|
47
53
|
* @since 2.27
|
|
48
54
|
*/
|
|
49
55
|
/**
|
|
@@ -63,7 +69,19 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
63
69
|
const validate = create_validator(validate_or_fn, maybe_fn);
|
|
64
70
|
|
|
65
71
|
/** @type {RemoteQueryInternals} */
|
|
66
|
-
const __ = {
|
|
72
|
+
const __ = {
|
|
73
|
+
type: 'query',
|
|
74
|
+
id: '',
|
|
75
|
+
name: '',
|
|
76
|
+
validate,
|
|
77
|
+
bind(payload, validated_arg) {
|
|
78
|
+
const { event, state } = get_request_store();
|
|
79
|
+
|
|
80
|
+
return create_query_resource(__, payload, state, () =>
|
|
81
|
+
run_remote_function(event, state, false, () => validated_arg, fn)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
67
85
|
|
|
68
86
|
/** @type {RemoteQueryFunction<Input, Output> & { __: RemoteQueryInternals }} */
|
|
69
87
|
const wrapper = (arg) => {
|
|
@@ -74,11 +92,10 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
const { event, state } = get_request_store();
|
|
77
|
-
|
|
78
|
-
const is_validated = is_validated_argument(__, state, arg);
|
|
95
|
+
const payload = stringify_remote_arg(arg, state.transport);
|
|
79
96
|
|
|
80
|
-
return create_query_resource(__,
|
|
81
|
-
run_remote_function(event, state, false, () =>
|
|
97
|
+
return create_query_resource(__, payload, state, () =>
|
|
98
|
+
run_remote_function(event, state, false, () => validate(arg), fn)
|
|
82
99
|
);
|
|
83
100
|
};
|
|
84
101
|
|
|
@@ -88,30 +105,107 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
88
105
|
}
|
|
89
106
|
|
|
90
107
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
108
|
+
* Creates a live remote query. When called from the browser, the function will be invoked on the server via a streaming `fetch` call.
|
|
109
|
+
*
|
|
110
|
+
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query.live) for full documentation.
|
|
111
|
+
*
|
|
112
|
+
* @template Output
|
|
113
|
+
* @overload
|
|
114
|
+
* @param {(arg: void) => RemoteLiveQueryUserFunctionReturnType<Output>} fn
|
|
115
|
+
* @returns {RemoteLiveQueryFunction<void, Output>}
|
|
94
116
|
*/
|
|
95
|
-
function is_validated_argument(__, state, arg) {
|
|
96
|
-
return state.remote.validated?.get(__.id)?.has(arg) ?? false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
117
|
/**
|
|
100
|
-
* @
|
|
101
|
-
* @
|
|
102
|
-
* @
|
|
118
|
+
* @template Input
|
|
119
|
+
* @template Output
|
|
120
|
+
* @overload
|
|
121
|
+
* @param {'unchecked'} validate
|
|
122
|
+
* @param {(arg: Input) => RemoteLiveQueryUserFunctionReturnType<Output>} fn
|
|
123
|
+
* @returns {RemoteLiveQueryFunction<Input, Output>}
|
|
124
|
+
*/
|
|
125
|
+
/**
|
|
126
|
+
* @template {StandardSchemaV1} Schema
|
|
127
|
+
* @template Output
|
|
128
|
+
* @overload
|
|
129
|
+
* @param {Schema} schema
|
|
130
|
+
* @param {(arg: StandardSchemaV1.InferOutput<Schema>) => RemoteLiveQueryUserFunctionReturnType<Output>} fn
|
|
131
|
+
* @returns {RemoteLiveQueryFunction<StandardSchemaV1.InferInput<Schema>, Output, StandardSchemaV1.InferOutput<Schema>>}
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* @template Input
|
|
135
|
+
* @template Output
|
|
136
|
+
* @param {any} validate_or_fn
|
|
137
|
+
* @param {(args: Input) => RemoteLiveQueryUserFunctionReturnType<Output>} [maybe_fn]
|
|
138
|
+
* @returns {RemoteLiveQueryFunction<Input, Output>}
|
|
103
139
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
140
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
141
|
+
function live(validate_or_fn, maybe_fn) {
|
|
142
|
+
/** @type {(arg: Input) => RemoteLiveQueryUserFunctionReturnType<Output>} */
|
|
143
|
+
const fn = maybe_fn ?? validate_or_fn;
|
|
107
144
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
145
|
+
/** @type {(arg?: any) => MaybePromise<Input>} */
|
|
146
|
+
const validate = create_validator(validate_or_fn, maybe_fn);
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {any} event
|
|
150
|
+
* @param {any} state
|
|
151
|
+
* @param {any} get_input
|
|
152
|
+
*/
|
|
153
|
+
const run = (event, state, get_input) =>
|
|
154
|
+
run_remote_generator(event, state, false, get_input, fn, __.name);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @param {any} generator
|
|
158
|
+
* @returns {Promise<any>}
|
|
159
|
+
*/
|
|
160
|
+
const first_value = async (generator) => {
|
|
161
|
+
try {
|
|
162
|
+
const { value, done } = await generator.next();
|
|
163
|
+
|
|
164
|
+
if (done) {
|
|
165
|
+
throw new Error(`query.live '${__.name}' did not yield a value`);
|
|
166
|
+
}
|
|
112
167
|
|
|
113
|
-
|
|
114
|
-
|
|
168
|
+
return value;
|
|
169
|
+
} finally {
|
|
170
|
+
await generator.return(undefined);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/** @type {RemoteQueryLiveInternals} */
|
|
175
|
+
const __ = {
|
|
176
|
+
type: 'query_live',
|
|
177
|
+
id: '',
|
|
178
|
+
name: '',
|
|
179
|
+
run: (event, state, arg) => run(event, state, () => validate(arg)),
|
|
180
|
+
validate,
|
|
181
|
+
bind(payload, validated_arg) {
|
|
182
|
+
const { event, state } = get_request_store();
|
|
183
|
+
|
|
184
|
+
return create_live_query_resource(__, payload, state, () =>
|
|
185
|
+
first_value(run(event, state, () => validated_arg))
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/** @type {RemoteLiveQueryFunction<Input, Output> & { __: RemoteQueryLiveInternals }} */
|
|
191
|
+
const wrapper = (arg) => {
|
|
192
|
+
if (prerendering) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Cannot call query.live '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const { event, state } = get_request_store();
|
|
199
|
+
const payload = stringify_remote_arg(arg, state.transport);
|
|
200
|
+
|
|
201
|
+
return create_live_query_resource(__, payload, state, () =>
|
|
202
|
+
first_value(run(event, state, () => validate(arg)))
|
|
203
|
+
);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
Object.defineProperty(wrapper, '__', { value: __ });
|
|
207
|
+
|
|
208
|
+
return wrapper;
|
|
115
209
|
}
|
|
116
210
|
|
|
117
211
|
/**
|
|
@@ -137,7 +231,7 @@ export function mark_argument_validated(__, state, arg) {
|
|
|
137
231
|
* @overload
|
|
138
232
|
* @param {Schema} schema
|
|
139
233
|
* @param {(args: StandardSchemaV1.InferOutput<Schema>[]) => MaybePromise<(arg: StandardSchemaV1.InferOutput<Schema>, idx: number) => Output>} fn
|
|
140
|
-
* @returns {RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output
|
|
234
|
+
* @returns {RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output, StandardSchemaV1.InferOutput<Schema>>}
|
|
141
235
|
* @since 2.35
|
|
142
236
|
*/
|
|
143
237
|
/**
|
|
@@ -156,11 +250,81 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
156
250
|
/** @type {(arg?: any) => MaybePromise<Input>} */
|
|
157
251
|
const validate = create_validator(validate_or_fn, maybe_fn);
|
|
158
252
|
|
|
253
|
+
/** @type {Map<string, { get_validated: () => MaybePromise<any>, resolvers: Array<{resolve: (value: any) => void, reject: (error: any) => void}> }>} */
|
|
254
|
+
let batching = new Map();
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Enqueues a single call into the current batch (creating one if necessary)
|
|
258
|
+
* and returns a promise that resolves with the result for this entry.
|
|
259
|
+
*
|
|
260
|
+
* @param {string} payload — the stringified raw argument (cache key)
|
|
261
|
+
* @param {() => MaybePromise<any>} get_validated — produces the validated argument for this entry
|
|
262
|
+
* @returns {Promise<any>}
|
|
263
|
+
*/
|
|
264
|
+
const enqueue = (payload, get_validated) => {
|
|
265
|
+
const { event, state } = get_request_store();
|
|
266
|
+
|
|
267
|
+
return new Promise((resolve, reject) => {
|
|
268
|
+
const entry = batching.get(payload);
|
|
269
|
+
|
|
270
|
+
if (entry) {
|
|
271
|
+
entry.resolvers.push({ resolve, reject });
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
batching.set(payload, {
|
|
276
|
+
get_validated,
|
|
277
|
+
resolvers: [{ resolve, reject }]
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (batching.size > 1) return;
|
|
281
|
+
|
|
282
|
+
setTimeout(async () => {
|
|
283
|
+
const batched = batching;
|
|
284
|
+
batching = new Map();
|
|
285
|
+
const entries = Array.from(batched.values());
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
return await run_remote_function(
|
|
289
|
+
event,
|
|
290
|
+
state,
|
|
291
|
+
false,
|
|
292
|
+
async () => Promise.all(entries.map((entry) => entry.get_validated())),
|
|
293
|
+
async (input) => {
|
|
294
|
+
const get_result = await fn(input);
|
|
295
|
+
|
|
296
|
+
for (let i = 0; i < entries.length; i++) {
|
|
297
|
+
try {
|
|
298
|
+
const result = get_result(input[i], i);
|
|
299
|
+
|
|
300
|
+
for (const resolver of entries[i].resolvers) {
|
|
301
|
+
resolver.resolve(result);
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
for (const resolver of entries[i].resolvers) {
|
|
305
|
+
resolver.reject(error);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
for (const entry of batched.values()) {
|
|
313
|
+
for (const resolver of entry.resolvers) {
|
|
314
|
+
resolver.reject(error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}, 0);
|
|
319
|
+
});
|
|
320
|
+
};
|
|
321
|
+
|
|
159
322
|
/** @type {RemoteQueryBatchInternals} */
|
|
160
323
|
const __ = {
|
|
161
324
|
type: 'query_batch',
|
|
162
325
|
id: '',
|
|
163
326
|
name: '',
|
|
327
|
+
validate,
|
|
164
328
|
run: async (args, options) => {
|
|
165
329
|
const { event, state } = get_request_store();
|
|
166
330
|
|
|
@@ -191,12 +355,14 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
191
355
|
);
|
|
192
356
|
}
|
|
193
357
|
);
|
|
358
|
+
},
|
|
359
|
+
bind(payload, validated_arg) {
|
|
360
|
+
const { state } = get_request_store();
|
|
361
|
+
|
|
362
|
+
return create_query_resource(__, payload, state, () => enqueue(payload, () => validated_arg));
|
|
194
363
|
}
|
|
195
364
|
};
|
|
196
365
|
|
|
197
|
-
/** @type {Map<string, { arg: any, resolvers: Array<{resolve: (value: any) => void, reject: (error: any) => void}> }>} */
|
|
198
|
-
let batching = new Map();
|
|
199
|
-
|
|
200
366
|
/** @type {RemoteQueryFunction<Input, Output> & { __: RemoteQueryBatchInternals }} */
|
|
201
367
|
const wrapper = (arg) => {
|
|
202
368
|
if (prerendering) {
|
|
@@ -205,67 +371,14 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
205
371
|
);
|
|
206
372
|
}
|
|
207
373
|
|
|
208
|
-
const {
|
|
374
|
+
const { state } = get_request_store();
|
|
375
|
+
const payload = stringify_remote_arg(arg, state.transport);
|
|
209
376
|
|
|
210
|
-
return create_query_resource(__,
|
|
377
|
+
return create_query_resource(__, payload, state, () =>
|
|
211
378
|
// Collect all the calls to the same query in the same macrotask,
|
|
212
379
|
// then execute them as one backend request.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const entry = batching.get(key);
|
|
216
|
-
|
|
217
|
-
if (entry) {
|
|
218
|
-
entry.resolvers.push({ resolve, reject });
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
batching.set(key, {
|
|
223
|
-
arg,
|
|
224
|
-
resolvers: [{ resolve, reject }]
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
if (batching.size > 1) return;
|
|
228
|
-
|
|
229
|
-
setTimeout(async () => {
|
|
230
|
-
const batched = batching;
|
|
231
|
-
batching = new Map();
|
|
232
|
-
const entries = Array.from(batched.values());
|
|
233
|
-
const args = entries.map((entry) => entry.arg);
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
return await run_remote_function(
|
|
237
|
-
event,
|
|
238
|
-
state,
|
|
239
|
-
false,
|
|
240
|
-
async () => Promise.all(args.map(validate)),
|
|
241
|
-
async (input) => {
|
|
242
|
-
const get_result = await fn(input);
|
|
243
|
-
|
|
244
|
-
for (let i = 0; i < entries.length; i++) {
|
|
245
|
-
try {
|
|
246
|
-
const result = get_result(input[i], i);
|
|
247
|
-
|
|
248
|
-
for (const resolver of entries[i].resolvers) {
|
|
249
|
-
resolver.resolve(result);
|
|
250
|
-
}
|
|
251
|
-
} catch (error) {
|
|
252
|
-
for (const resolver of entries[i].resolvers) {
|
|
253
|
-
resolver.reject(error);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
|
-
} catch (error) {
|
|
260
|
-
for (const entry of batched.values()) {
|
|
261
|
-
for (const resolver of entry.resolvers) {
|
|
262
|
-
resolver.reject(error);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}, 0);
|
|
267
|
-
});
|
|
268
|
-
});
|
|
380
|
+
enqueue(payload, () => validate(arg))
|
|
381
|
+
);
|
|
269
382
|
};
|
|
270
383
|
|
|
271
384
|
Object.defineProperty(wrapper, '__', { value: __ });
|
|
@@ -275,17 +388,24 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
275
388
|
|
|
276
389
|
/**
|
|
277
390
|
* @param {RemoteInternals} __
|
|
278
|
-
* @param {
|
|
391
|
+
* @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
|
|
279
392
|
* @param {RequestState} state
|
|
280
393
|
* @param {() => Promise<any>} fn
|
|
281
394
|
* @returns {RemoteQuery<any>}
|
|
282
395
|
*/
|
|
283
|
-
function create_query_resource(__,
|
|
396
|
+
function create_query_resource(__, payload, state, fn) {
|
|
284
397
|
/** @type {Promise<any> | null} */
|
|
285
398
|
let promise = null;
|
|
286
399
|
|
|
287
400
|
const get_promise = () => {
|
|
288
|
-
return (promise ??= get_response(__,
|
|
401
|
+
return (promise ??= get_response(__, payload, state, fn));
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const populate_hydratable = () => {
|
|
405
|
+
// accessing data properties needs to kick off the work
|
|
406
|
+
// so that it gets seeded in the hydration cache
|
|
407
|
+
// and becomes available on the client
|
|
408
|
+
void (__.id && state.is_in_render && get_promise());
|
|
289
409
|
};
|
|
290
410
|
|
|
291
411
|
return {
|
|
@@ -293,17 +413,29 @@ function create_query_resource(__, arg, state, fn) {
|
|
|
293
413
|
catch(onrejected) {
|
|
294
414
|
return get_promise().catch(onrejected);
|
|
295
415
|
},
|
|
296
|
-
current
|
|
297
|
-
|
|
416
|
+
get current() {
|
|
417
|
+
populate_hydratable();
|
|
418
|
+
return undefined;
|
|
419
|
+
},
|
|
420
|
+
get error() {
|
|
421
|
+
populate_hydratable();
|
|
422
|
+
return undefined;
|
|
423
|
+
},
|
|
298
424
|
/** @type {Promise<any>['finally']} */
|
|
299
425
|
finally(onfinally) {
|
|
300
426
|
return get_promise().finally(onfinally);
|
|
301
427
|
},
|
|
302
|
-
loading
|
|
303
|
-
|
|
428
|
+
get loading() {
|
|
429
|
+
populate_hydratable();
|
|
430
|
+
return true;
|
|
431
|
+
},
|
|
432
|
+
get ready() {
|
|
433
|
+
populate_hydratable();
|
|
434
|
+
return false;
|
|
435
|
+
},
|
|
304
436
|
refresh() {
|
|
305
|
-
const refresh_context = get_refresh_context(__, 'refresh',
|
|
306
|
-
const is_immediate_refresh = !refresh_context.cache[refresh_context.
|
|
437
|
+
const refresh_context = get_refresh_context(__, 'refresh', payload);
|
|
438
|
+
const is_immediate_refresh = !refresh_context.cache[refresh_context.payload];
|
|
307
439
|
const value = is_immediate_refresh ? get_promise() : fn();
|
|
308
440
|
return update_refresh_value(refresh_context, value, is_immediate_refresh);
|
|
309
441
|
},
|
|
@@ -316,11 +448,11 @@ function create_query_resource(__, arg, state, fn) {
|
|
|
316
448
|
'On the server, .run() can only be called in universal `load` functions. Anywhere else, just await the query directly'
|
|
317
449
|
);
|
|
318
450
|
}
|
|
319
|
-
return get_response(__,
|
|
451
|
+
return get_response(__, payload, state, fn);
|
|
320
452
|
},
|
|
321
453
|
/** @param {any} value */
|
|
322
454
|
set(value) {
|
|
323
|
-
return update_refresh_value(get_refresh_context(__, 'set',
|
|
455
|
+
return update_refresh_value(get_refresh_context(__, 'set', payload), value);
|
|
324
456
|
},
|
|
325
457
|
/** @type {Promise<any>['then']} */
|
|
326
458
|
then(onfulfilled, onrejected) {
|
|
@@ -335,16 +467,94 @@ function create_query_resource(__, arg, state, fn) {
|
|
|
335
467
|
};
|
|
336
468
|
}
|
|
337
469
|
|
|
470
|
+
/**
|
|
471
|
+
* @param {RemoteQueryLiveInternals} __
|
|
472
|
+
* @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
|
|
473
|
+
* @param {RequestState} state
|
|
474
|
+
* @param {() => Promise<any>} get_first_value
|
|
475
|
+
* @returns {RemoteLiveQuery<any>}
|
|
476
|
+
*/
|
|
477
|
+
function create_live_query_resource(__, payload, state, get_first_value) {
|
|
478
|
+
/** @type {Promise<any> | null} */
|
|
479
|
+
let promise = null;
|
|
480
|
+
|
|
481
|
+
const get_promise = () => {
|
|
482
|
+
return (promise ??= get_response(__, payload, state, get_first_value));
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const populate_hydratable = () => {
|
|
486
|
+
void (__.id && state.is_in_render && get_promise());
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
/** @type {Promise<any>['catch']} */
|
|
491
|
+
catch(onrejected) {
|
|
492
|
+
return get_promise().catch(onrejected);
|
|
493
|
+
},
|
|
494
|
+
get current() {
|
|
495
|
+
populate_hydratable();
|
|
496
|
+
return undefined;
|
|
497
|
+
},
|
|
498
|
+
get error() {
|
|
499
|
+
populate_hydratable();
|
|
500
|
+
return undefined;
|
|
501
|
+
},
|
|
502
|
+
/** @type {Promise<any>['finally']} */
|
|
503
|
+
finally(onfinally) {
|
|
504
|
+
return get_promise().finally(onfinally);
|
|
505
|
+
},
|
|
506
|
+
get done() {
|
|
507
|
+
populate_hydratable();
|
|
508
|
+
return false;
|
|
509
|
+
},
|
|
510
|
+
get loading() {
|
|
511
|
+
populate_hydratable();
|
|
512
|
+
return true;
|
|
513
|
+
},
|
|
514
|
+
get ready() {
|
|
515
|
+
populate_hydratable();
|
|
516
|
+
return false;
|
|
517
|
+
},
|
|
518
|
+
get connected() {
|
|
519
|
+
populate_hydratable();
|
|
520
|
+
return false;
|
|
521
|
+
},
|
|
522
|
+
reconnect() {
|
|
523
|
+
const reconnects = state.remote.reconnects;
|
|
524
|
+
|
|
525
|
+
if (!reconnects) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Cannot call reconnect on query.live '${__.name}' because it is not executed in the context of a command/form remote function`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
reconnects.set(create_remote_key(__.id, payload), get_promise());
|
|
532
|
+
return Promise.resolve();
|
|
533
|
+
},
|
|
534
|
+
run() {
|
|
535
|
+
throw new Error('Cannot call .run() on a live query on the server');
|
|
536
|
+
},
|
|
537
|
+
/** @type {Promise<any>['then']} */
|
|
538
|
+
then(onfulfilled, onrejected) {
|
|
539
|
+
return get_promise().then(onfulfilled, onrejected);
|
|
540
|
+
},
|
|
541
|
+
get [Symbol.toStringTag]() {
|
|
542
|
+
return 'LiveQueryResource';
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
338
547
|
// Add batch as a property to the query function
|
|
339
548
|
Object.defineProperty(query, 'batch', { value: batch, enumerable: true });
|
|
549
|
+
Object.defineProperty(query, 'live', { value: live, enumerable: true });
|
|
340
550
|
|
|
341
551
|
/**
|
|
342
552
|
* @param {RemoteInternals} __
|
|
343
553
|
* @param {'set' | 'refresh'} action
|
|
344
|
-
* @param {
|
|
345
|
-
* @returns {{ __: RemoteInternals; state: any; refreshes:
|
|
554
|
+
* @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
|
|
555
|
+
* @returns {{ __: RemoteInternals; state: any; refreshes: Map<string, Promise<any>>; cache: Record<string, { serialize: boolean; data: any }>; refreshes_key: string; payload: string }}
|
|
346
556
|
*/
|
|
347
|
-
function get_refresh_context(__, action,
|
|
557
|
+
function get_refresh_context(__, action, payload) {
|
|
348
558
|
const { state } = get_request_store();
|
|
349
559
|
const { refreshes } = state.remote;
|
|
350
560
|
|
|
@@ -356,32 +566,36 @@ function get_refresh_context(__, action, arg) {
|
|
|
356
566
|
}
|
|
357
567
|
|
|
358
568
|
const cache = get_cache(__, state);
|
|
359
|
-
const
|
|
360
|
-
const refreshes_key = create_remote_key(__.id, cache_key);
|
|
569
|
+
const refreshes_key = create_remote_key(__.id, payload);
|
|
361
570
|
|
|
362
|
-
return { __, state, refreshes, refreshes_key, cache,
|
|
571
|
+
return { __, state, refreshes, refreshes_key, cache, payload };
|
|
363
572
|
}
|
|
364
573
|
|
|
365
574
|
/**
|
|
366
|
-
* @param {{ __: RemoteInternals; refreshes:
|
|
575
|
+
* @param {{ __: RemoteInternals; refreshes: Map<string, Promise<any>>; cache: Record<string, { serialize: boolean; data: any }>; refreshes_key: string; payload: string }} context
|
|
367
576
|
* @param {any} value
|
|
368
577
|
* @param {boolean} [is_immediate_refresh=false]
|
|
369
578
|
* @returns {Promise<void>}
|
|
370
579
|
*/
|
|
371
580
|
function update_refresh_value(
|
|
372
|
-
{ __, refreshes, refreshes_key, cache,
|
|
581
|
+
{ __, refreshes, refreshes_key, cache, payload },
|
|
373
582
|
value,
|
|
374
583
|
is_immediate_refresh = false
|
|
375
584
|
) {
|
|
376
585
|
const promise = Promise.resolve(value);
|
|
377
586
|
|
|
378
587
|
if (!is_immediate_refresh) {
|
|
379
|
-
cache[
|
|
588
|
+
cache[payload] = { serialize: true, data: promise };
|
|
380
589
|
}
|
|
381
590
|
|
|
382
591
|
if (__.id) {
|
|
383
|
-
refreshes
|
|
592
|
+
refreshes.set(refreshes_key, promise);
|
|
384
593
|
}
|
|
385
594
|
|
|
386
|
-
|
|
595
|
+
promise.catch(noop);
|
|
596
|
+
|
|
597
|
+
// we return an immediately-resolving promise so that the `refresh()` signature is consistent,
|
|
598
|
+
// but it doesn't delay anything if awaited inside a command. this way, people aren't
|
|
599
|
+
// penalised if they do `await q1.refresh(); await q2.refresh()`
|
|
600
|
+
return Promise.resolve();
|
|
387
601
|
}
|