@sveltejs/kit
Version:
SvelteKit is the fastest way to build Svelte apps
110 lines (90 loc) • 2.95 kB
JavaScript
/** @import { RemoteCommand, RemoteQueryUpdate } from '$app/server' */
import { app_dir, base } from '#app/paths';
import { stringify_command_arg } from '../../shared.js';
import * as e from '../../../messages/client-errors.js';
import * as w from '../../../messages/client-warnings.js';
import {
get_remote_request_headers,
categorize_updates,
remote_request,
fail_unhandled_refreshes
} from './shared.svelte.js';
/**
* Client-version of the `command` function from `$app/server`.
* @param {string} id
* @returns {RemoteCommand<any, any>}
*/
export function command(id) {
/** @type {number} */
let pending_count = $state(0);
// Careful: This function MUST be synchronous (can't use the async keyword) because the return type has to be a promise with an updates() method.
// If we make it async, the return type will be a promise that resolves to a promise with an updates() method, which is not what we want.
/** @type {RemoteCommand<any, any>} */
const command_function = (arg) => {
let overrides = /** @type {Array<() => void> | null} */ (null);
/** @type {Set<string> | null} */
let refreshes = null;
/** @type {Error | undefined} */
let updates_error;
// Increment pending count when command starts
pending_count++;
// No one should call commands during rendering, but this is belt and braces.
// Do this here, after Svelte's reactivity context is gone.
const headers = {
'Content-Type': 'application/json',
...get_remote_request_headers()
};
const promise =
/** @type {Promise<any> & { updates: (...args: RemoteQueryUpdate[]) => Promise<any> }} */ (
(async () => {
try {
// Wait a tick to give room for the `updates` method to be called
await Promise.resolve();
if (updates_error) {
throw updates_error;
}
const response = await remote_request(
`${base}/${app_dir}/remote/${id}`,
{
method: 'POST',
body: JSON.stringify({
payload: await stringify_command_arg(arg),
refreshes: Array.from(refreshes ?? [])
}),
headers
},
refreshes
);
if (response.redirect) {
e.remote_command_redirect();
}
fail_unhandled_refreshes(refreshes);
return response._;
} finally {
overrides?.forEach((fn) => fn());
// Decrement pending count when command completes
pending_count--;
}
})()
);
let updates_called = false;
promise.updates = (...args) => {
if (updates_called) {
w.remote_updates_repeated({ invocation: 'command invocation' });
return promise;
}
updates_called = true;
try {
({ refreshes, overrides } = categorize_updates(args));
} catch (error) {
updates_error = /** @type {Error} */ (error);
}
return promise;
};
return promise;
};
Object.defineProperty(command_function, 'pending', {
get: () => pending_count
});
return command_function;
}