UNPKG

@sveltejs/kit

Version:

SvelteKit is the fastest way to build Svelte apps

110 lines (90 loc) • 2.95 kB
/** @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; }