Created
January 9, 2024 18:57
-
-
Save LorisSigrist/20b9caf91c1deb2a60a66f70669c4437 to your computer and use it in GitHub Desktop.
A SvelteKit hook for enforcing rules on a route-level
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { dev } from "$app/environment"; | |
import { sequence } from "@sveltejs/kit/hooks"; | |
// A rule segment has the form ($ruleName) | |
const ruleSegmentRegex = /^\(\$[a-zA-Z_]+\)$/; | |
/** | |
* Checks if a segment is a rule segment. | |
* | |
* @param {string} segment | |
* @returns {segment is `($${string})`} | |
*/ | |
const isRuleSegment = (segment) => ruleSegmentRegex.test(segment); | |
/** | |
* Enforces a set of rules based on the route. | |
* | |
* You can apply a rule to a route by adding a segment to the route that looks like this: ($ruleName) | |
* This will cause the rule with the name "ruleName" to be applied when a request is made to that route. | |
* | |
* @param {Record<string, import("@sveltejs/kit").Handle>} rules | |
* @returns {import("@sveltejs/kit").Handle} | |
*/ | |
export function enforce(rules) { | |
const ruleNames = Object.keys(rules); | |
return async ({ event, resolve }) => { | |
// ignore 404s | |
if (!event.route.id) return resolve(event); | |
// Get all the rules that apply to this route | |
const applicableRules = event.route.id | |
.split('/') | |
.filter(isRuleSegment) | |
.map((segment) => segment.slice(2, -1)); | |
// If any of the rules that should apply to this route are not defined throw an error | |
// it's most likely a typo. Ee don't want to miss it & cause a security issue. | |
for (const rule of applicableRules) { | |
if (!ruleNames.includes(rule)) { | |
return new Response(dev ? `Enforcer Error: Rule "${rule}" in route ${event.route.id} is not defined. Did you make a typo?` : "Internal Server Error", { | |
status: 500, | |
}); | |
} | |
} | |
const ruleHandlers = applicableRules.map((rule) => rules[rule]); | |
return await sequence(...ruleHandlers)({ event, resolve }); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment