kit icon indicating copy to clipboard operation
kit copied to clipboard

Allowing regular expressions as arguments in the "depends" function for load functions

Open wvhulle opened this issue 1 year ago • 4 comments

Describe the problem

I have to use regular expressions in depends to invalidate certain load functions by calling the invalidate(urlWithPattern => condition(urlWithPattern)) function. Sometimes urls passed to depends have parameters and I don't want to specify the whole url, or cannot write the full pathname, because it's dynamic. However, I have to decode them each time from the string urlWithPattern which is automatically encoded when passing them to depends. Could there be an option to pass regular expressions to the depends function? It seems logical given that there are dynamic urls everywhere.

Describe the proposed solution

Allow instances of the class RegExp as arguments to depends instead of just strings.

Alternatives considered

Using decodeURI, see my hack below.

Importance

nice to have

Additional Information

My current hack is

export const smartInvalidate = async (absoluteOrRelativeUrl: string): Promise<void> => {
    const fullUrl = urlForSvelteFetch(absoluteOrRelativeUrl);
    await invalidate((loadUrlPattern: URL) => {
        const decoded = urlForSvelteFetch(
            decodeURI(loadUrlPattern.toString())
        );
        const regex = new RegExp(`${escapeUrl(decoded)}$`);
        const matches = fullUrl.match(regex);
        const condition = (matches && matches.length > 0) ?? false;
        return condition;
    });
}

wvhulle avatar Feb 24 '23 12:02 wvhulle

What would this give you that the current way of passing a function wouldn't give you? What you describe as a hack is a valid way of doing things to me (I think you can simplify this to just using .href for getting the string btw), and from looking at your function body it sounds like you need more capabilities than a simple regex either way. How would your current solution look like if you had the regex capability?

dummdidumm avatar Feb 24 '23 13:02 dummdidumm

It would save me the decodeURI call, but I can also just do it with this current function.

Do you mean I could change decodeURI(loadUrlPattern.toString()) to loadUrlPattern.href? I am afraid that that is undefined for me. I also have to decode the regex if it would be defined.

wvhulle avatar Mar 02 '23 16:03 wvhulle

Can you give a concrete example where the decodeURI/href things is necessary? Like "depends('x:y') followed by smartInvalidate('z')"

dummdidumm avatar Mar 02 '23 16:03 dummdidumm

For example, while parsing a form endpoint response, I do this

	if (!text) {
		throw Error(`Response was undefined.`);
	}
	try {
		response = deserialize(text) as ActionResult<ServerDataOutput>;
	} catch (e) {
		throw Error(`Problem deserializing ${text.slice(0, 100)}`);
	}

	// fetching.done(`Done fetching`);
	const result: ActionResult<ServerDataOutput> = response;
	if (result.type === 'success' || (!('type' in result) && fetchResponse.ok)) {
		await smartInvalidate(rURL);
		return { serverDataOutput: result.data ?? (result as unknown as ServerDataOutput) };
	} else if (result.type === 'redirect') {
		const newUrl = relativeURL(result.location);
		await goto(newUrl, { keepFocus: true, noScroll: true });
		await smartInvalidate(rURL);
		return {};
	} else if (result.type === 'failure') {
		await smartInvalidate(rURL);

		return {
			processorError: new Error(`Form failure with status ${result.status}.`),
			serverDataOutput: result.data as ServerDataOutput
		};
	} else {

I have declared dependencies in load functions using

export function regexToPathname(regex: RegExp, root: URL) {
	// Remove the starting and ending slashes.
	const regexString = regex.source;

	// Encode special characters in the regex to create a valid URL pathname.
	const isAbsolute = regex.source.startsWith('^\\/');
	if (isAbsolute) {
		return '/regex/' + encodeURIComponent(regexString);
	} else if (regex.source.startsWith('\\?')) {
		return (
			'/regex/' +
			encodeURIComponent(
				`${stringToRegex(decodeURIComponent(root.pathname.replace(/\/$/, '')))}${regexString}`
			)
		);
	} else {
		return (
			'/regex/' +
			encodeURIComponent(
				`${stringToRegex(decodeURIComponent(root.pathname.replace(/\/$/, '')))}\\/${regexString}`
			)
		);
	}
}

Then i use this with

import { regexToPathname } from '$lib/browser/http';

import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ depends, params, url }) => {
	depends(
		...[
			new RegExp(
				`^\\/${params.projectLabel}\\/${params.processIndex}\\/files/upload\\/[^/]+\\?\\/upload`
			),
			new RegExp(
				`[^/]+\\?\\/deleteFile`
			)
		].map((r) => regexToPathname(r, url))
	);
///
}

But this feels like an unnecessary hack. If regexes would be directly accepted (opposed to strings) this would not be necessary.

wvhulle avatar Jun 20 '23 19:06 wvhulle

strings passed to depends must be valid URL strings as its converted to an URL object. Passing regexes would make all this more complicated, and we would need to ship additional code in the client for everybody when only a fraction of people would use this. I'm therefore closing this issue - creating helper functions building upon depends and invalidate is the way to go.

For SvelteKit 2 we might in general rethink whether or not really makes sense for depends/invalidate to deal with URLs instead of just plain strings.

dummdidumm avatar Jun 30 '23 10:06 dummdidumm