From 525233553b5ec7494ad3a03a381614c7894a1137 Mon Sep 17 00:00:00 2001 From: Andrew McGrath Date: Sun, 5 Dec 2021 21:22:20 -0500 Subject: [PATCH 1/8] Add functionality to override http methods (issue #1046) See: https://github.com/sveltejs/kit/issues/1046 --- .changeset/chilly-moose-provide.md | 6 + documentation/docs/01-routing.md | 39 ++++ documentation/docs/14-configuration.md | 19 ++ .../templates/default/src/hooks.ts | 5 - .../templates/default/svelte.config.js | 7 +- packages/kit/src/core/build/index.js | 6 + packages/kit/src/core/config/index.spec.js | 12 + packages/kit/src/core/config/options.js | 16 ++ packages/kit/src/core/config/test/index.js | 6 + packages/kit/src/core/dev/index.js | 1 + packages/kit/src/runtime/server/index.js | 26 +++ .../server/parse_body/read_only_form_data.js | 2 +- .../src/routes/method-override/_tests.js | 54 +++++ .../src/routes/method-override/fetch.json.js | 26 +++ .../src/routes/method-override/index.svelte | 52 +++++ .../kit/test/apps/basics/svelte.config.js | 3 + packages/kit/types/config.d.ts | 3 +- packages/kit/types/internal.d.ts | 10 + pnpm-lock.yaml | 220 +++++++++++++++++- 19 files changed, 502 insertions(+), 11 deletions(-) create mode 100644 .changeset/chilly-moose-provide.md create mode 100644 packages/kit/test/apps/basics/src/routes/method-override/_tests.js create mode 100644 packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js create mode 100644 packages/kit/test/apps/basics/src/routes/method-override/index.svelte diff --git a/.changeset/chilly-moose-provide.md b/.changeset/chilly-moose-provide.md new file mode 100644 index 000000000000..db4e0b7489d6 --- /dev/null +++ b/.changeset/chilly-moose-provide.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': patch +'create-svelte': patch +--- + +[feat] add functionality to override http methods (issue #1046) diff --git a/documentation/docs/01-routing.md b/documentation/docs/01-routing.md index 45967672be89..1a0a1d8c21ed 100644 --- a/documentation/docs/01-routing.md +++ b/documentation/docs/01-routing.md @@ -161,6 +161,45 @@ The `body` property of the request object will be provided in the case of POST r - Form data (with content-type `application/x-www-form-urlencoded` or `multipart/form-data`) will be parsed to a read-only version of the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object. - All other data will be provided as a `Uint8Array` +#### HTTP Method Overrides + +Given that the only valid `
` methods are GET and POST, functionality is provided to override this limitation and allow the use of other HTTP verbs. This is particularly helpful when ensuring your application works even when javascript fails or is disabled. + +- Disabled by default to prevent unintended behavior for those who don't need it +- For security purposes, the original method on the form must be `POST` and cannot be overridden with `GET` +- There are 2 different ways to specify a method override strategy + - `url_parameter`: Pass the key and desired method as a query string + - `form_data`: Pass as a field within the form where the field name is the key and the field value is the desired method +- `strategy: 'both'` will enable both methods (`url_parameter` takes precendence in the event that both strategies are used within the same form) + +```js +// svelte.config.js +export default { + kit: { + methodOverride: { + enabled: true, + key: '_method', + allowedMethods: ['PUT', 'PATCH', 'DELETE'], + strategy: 'both' + }, + } +}; +``` + +```html + + +
+``` + +OR + +```html +
+ +
+``` + ### Private modules A filename that has a segment with a leading underscore, such as `src/routes/foo/_Private.svelte` or `src/routes/bar/_utils/cool-util.js`, is hidden from the router, but can be imported by files that are not. diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index 2b4691b1057e..1a390f9d8ec3 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -29,6 +29,12 @@ const config = { host: null, hostHeader: null, hydrate: true, + methodOverride: { + enabled: false, + key: '_method', + allowedMethods: ['PUT', 'PATCH', 'DELETE'], + strategy: 'both' + }, package: { dir: 'package', emitTypes: true, @@ -124,6 +130,19 @@ export default { Whether to [hydrate](#ssr-and-javascript-hydrate) the server-rendered HTML with a client-side app. (It's rare that you would set this to `false` on an app-wide basis.) +### methodOverride + +See [HTTP Method Overrides](#routing-endpoints-http-method-overrides). An object containing zero or more of the following: + +- `enabled` — set to `true` to enable method overriding +- `key` — query parameter name/field name to use for passing the intended method value +- `allowedMethods` - array of HTTP methods that can be used when overriding the original request method +- `strategy` + + - `'both'` — (default) will look for the override key in both the list of query parameters and the form fields + - `'url_parameter'` — only allow overriding via a query parameter + - `form_data` — only allow overriding via a form field + ### package Options related to [creating a package](#packaging). diff --git a/packages/create-svelte/templates/default/src/hooks.ts b/packages/create-svelte/templates/default/src/hooks.ts index 54bad4ce5b8c..ce345fed108f 100644 --- a/packages/create-svelte/templates/default/src/hooks.ts +++ b/packages/create-svelte/templates/default/src/hooks.ts @@ -6,11 +6,6 @@ export const handle: Handle = async ({ request, resolve }) => { const cookies = cookie.parse(request.headers.cookie || ''); request.locals.userid = cookies.userid || uuid(); - // TODO https://github.com/sveltejs/kit/issues/1046 - if (request.query.has('_method')) { - request.method = request.query.get('_method').toUpperCase(); - } - const response = await resolve(request); if (!cookies.userid) { diff --git a/packages/create-svelte/templates/default/svelte.config.js b/packages/create-svelte/templates/default/svelte.config.js index aa85d10b3262..49ac291bd2fb 100644 --- a/packages/create-svelte/templates/default/svelte.config.js +++ b/packages/create-svelte/templates/default/svelte.config.js @@ -11,7 +11,12 @@ const config = { adapter: adapter(), // hydrate the
element in src/app.html - target: '#svelte' + target: '#svelte', + + // Override http methods in the Todo forms + methodOverride: { + enabled: true + } } }; diff --git a/packages/kit/src/core/build/index.js b/packages/kit/src/core/build/index.js index 666a2c42362c..cbdb8a691978 100644 --- a/packages/kit/src/core/build/index.js +++ b/packages/kit/src/core/build/index.js @@ -334,6 +334,12 @@ async function build_server( initiator: undefined, load_component, manifest, + methodOverride: { + enabled: ${config.kit.methodOverride.enabled}, + key: ${s(config.kit.methodOverride.key)}, + allowedMethods: [${config.kit.methodOverride.allowedMethods.map(method => s(method))}], + strategy: ${s(config.kit.methodOverride.strategy)} + }, paths: settings.paths, prerender: ${config.kit.prerender.enabled}, read: settings.read, diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index 2707b6133c2d..5f0ec532cbf7 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -32,6 +32,12 @@ test('fills in defaults', () => { host: null, hostHeader: null, hydrate: true, + methodOverride: { + enabled: false, + key: '_method', + allowedMethods: ['PUT', 'PATCH', 'DELETE'], + strategy: 'both' + }, package: { dir: 'package', emitTypes: true @@ -132,6 +138,12 @@ test('fills in partial blanks', () => { host: null, hostHeader: null, hydrate: true, + methodOverride: { + enabled: false, + key: '_method', + allowedMethods: ['PUT', 'PATCH', 'DELETE'], + strategy: 'both' + }, package: { dir: 'package', emitTypes: true diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index 1a76bb14e113..5d75def359c6 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -73,6 +73,22 @@ const options = object( hydrate: boolean(true), + methodOverride: object({ + enabled: boolean(false), + key: string('_method'), + allowedMethods: validate(['PUT', 'PATCH', 'DELETE'], (input, keypath) => { + if (!Array.isArray(input) || !input.every((method) => typeof method === 'string')) { + throw new Error(`${keypath} must be an array of strings`); + } + + return input; + }), + strategy: validate('both', (input, keypath) => { + if (['both', 'url_parameter', 'form_data'].includes(input)) return input; + throw new Error(`${keypath} must be either "both", "url_parameter" or "form_data"`); + }) + }), + package: object({ dir: string('package'), // excludes all .d.ts and filename starting with _ diff --git a/packages/kit/src/core/config/test/index.js b/packages/kit/src/core/config/test/index.js index 57d966c14909..00e8a90fac3b 100644 --- a/packages/kit/src/core/config/test/index.js +++ b/packages/kit/src/core/config/test/index.js @@ -37,6 +37,12 @@ async function testLoadDefaultConfig(path) { host: null, hostHeader: null, hydrate: true, + methodOverride: { + enabled: false, + key: '_method', + allowedMethods: ['PUT', 'PATCH', 'DELETE'], + strategy: 'both' + }, package: { dir: 'package', emitTypes: true diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js index 11e247c3b1fb..5ee4d8d4f5d8 100644 --- a/packages/kit/src/core/dev/index.js +++ b/packages/kit/src/core/dev/index.js @@ -406,6 +406,7 @@ async function create_plugin(config, dir, cwd, get_manifest) { }, hooks, hydrate: config.kit.hydrate, + methodOverride: config.kit.methodOverride, paths: { base: config.kit.paths.base, assets: config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 52d4f05d5ee3..a02c9b722338 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -7,6 +7,7 @@ import { lowercase_keys } from './utils.js'; import { hash } from '../hash.js'; import { get_single_valued_header } from '../../utils/http.js'; import { coalesce_to_error } from '../../utils/error.js'; +import { ReadOnlyFormData } from './parse_body/read_only_form_data.js'; /** @type {import('@sveltejs/kit/ssr').Respond} */ export async function respond(incoming, options, state = {}) { @@ -40,6 +41,31 @@ export async function respond(incoming, options, state = {}) { locals: {} }; + if (options.methodOverride.enabled && request.method.toUpperCase() === 'POST') { + const { strategy = '', key: method_key = '', allowedMethods = [] } = options.methodOverride; + let new_request_method; + + if ( + ['both', 'form_data'].includes(strategy) && + request.body instanceof ReadOnlyFormData && + request.body.has(method_key) + ) { + new_request_method = (request.body.get(method_key) || request.method).toUpperCase(); + } + + if (['both', 'url_parameter'].includes(strategy) && incoming.query.has(method_key)) { + new_request_method = (incoming.query.get(method_key) || request.method).toUpperCase(); + } + + if ( + new_request_method && + allowedMethods.includes(new_request_method) && + new_request_method !== 'GET' + ) { + request.method = new_request_method; + } + } + try { return await options.hooks.handle({ request, diff --git a/packages/kit/src/runtime/server/parse_body/read_only_form_data.js b/packages/kit/src/runtime/server/parse_body/read_only_form_data.js index 9f4aaeb965ac..4978ef455323 100644 --- a/packages/kit/src/runtime/server/parse_body/read_only_form_data.js +++ b/packages/kit/src/runtime/server/parse_body/read_only_form_data.js @@ -19,7 +19,7 @@ export function read_only_form_data() { }; } -class ReadOnlyFormData { +export class ReadOnlyFormData { /** @type {Map} */ #map; diff --git a/packages/kit/test/apps/basics/src/routes/method-override/_tests.js b/packages/kit/test/apps/basics/src/routes/method-override/_tests.js new file mode 100644 index 000000000000..0c341bad8988 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/method-override/_tests.js @@ -0,0 +1,54 @@ +import * as assert from 'uvu/assert'; + +/** @type {import('test').TestMaker} */ +export default function (test) { + test('http method is overridden via URL parameter', '/method-override', async ({ page }) => { + let val; + + // Check initial value + val = await page.textContent('h1'); + assert.equal('', val); + + await page.click('"PATCH"'); + val = await page.textContent('h1'); + assert.equal('PATCH', val); + + await page.click('"DELETE"'); + val = await page.textContent('h1'); + assert.equal('DELETE', val); + }); + + test('GET method is not overridden', '/method-override', async ({ page }) => { + await page.click('"No Override From GET"'); + const val = await page.textContent('h1'); + assert.equal('GET', val); + }); + + test('POST method is not overridden with GET', '/method-override', async ({ page }) => { + await page.click('"No Override To GET"'); + const val = await page.textContent('h1'); + assert.equal('POST', val); + }); + + test('http method is overridden via hidden input', '/method-override', async ({ page }) => { + await page.click('"PATCH Via Hidden Input"'); + const val = await page.textContent('h1'); + assert.equal('PATCH', val); + }); + + test('GET method is not overridden via hidden input', '/method-override', async ({ page }) => { + await page.click('"No Override From GET Via Hidden Input"'); + const val = await page.textContent('h1'); + assert.equal('GET', val); + }); + + test( + 'POST method is not overridden with GET via hidden input', + '/method-override', + async ({ page }) => { + await page.click('"No Override To GET Via Hidden Input"'); + const val = await page.textContent('h1'); + assert.equal('POST', val); + } + ); +} diff --git a/packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js b/packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js new file mode 100644 index 000000000000..f33471c06b09 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js @@ -0,0 +1,26 @@ +const buildResponse = (/** @type {string} */ method) => ({ + status: 303, + headers: { + location: `/method-override?method=${method}` + } +}); + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export const get = (request) => { + return buildResponse(request.method); +}; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export const post = (request) => { + return buildResponse(request.method); +}; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export const patch = (request) => { + return buildResponse(request.method); +}; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export const del = (request) => { + return buildResponse(request.method); +}; diff --git a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte b/packages/kit/test/apps/basics/src/routes/method-override/index.svelte new file mode 100644 index 000000000000..5222af23c681 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/method-override/index.svelte @@ -0,0 +1,52 @@ + + + + +

{method}

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
diff --git a/packages/kit/test/apps/basics/svelte.config.js b/packages/kit/test/apps/basics/svelte.config.js index 32c11b217047..b1b0837e47f0 100644 --- a/packages/kit/test/apps/basics/svelte.config.js +++ b/packages/kit/test/apps/basics/svelte.config.js @@ -12,6 +12,9 @@ const config = { // the reload confuses Playwright include: ['cookie', 'marked'] } + }, + methodOverride: { + enabled: true } } }; diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts index 5d2214d0eacb..bed46f5e299d 100644 --- a/packages/kit/types/config.d.ts +++ b/packages/kit/types/config.d.ts @@ -1,6 +1,6 @@ import { UserConfig as ViteConfig } from 'vite'; import { RecursiveRequired } from './helper'; -import { Logger, TrailingSlash } from './internal'; +import { Logger, MethodOverride, TrailingSlash } from './internal'; export interface AdapterUtils { log: Logger; @@ -65,6 +65,7 @@ export interface Config { host?: string; hostHeader?: string; hydrate?: boolean; + methodOverride?: MethodOverride; package?: { dir?: string; emitTypes?: boolean; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index bfc5b5ab0a05..b94ff9d1ca26 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -146,6 +146,7 @@ export interface SSRRenderOptions { hydrate: boolean; load_component(id: PageId): Promise; manifest: SSRManifest; + methodOverride: MethodOverride; paths: { base: string; assets: string; @@ -217,3 +218,12 @@ export interface NormalizedLoadOutput { } export type TrailingSlash = 'never' | 'always' | 'ignore'; + +export type MethodOverrideStrategy = 'both' | 'url_parameter' | 'form_data'; + +export interface MethodOverride { + enabled?: boolean; + key?: string; + allowedMethods?: string[]; + strategy?: MethodOverrideStrategy; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27be7f490524..076f3ee7a74f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: '@sveltejs/eslint-config': github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641_d9525e585486a10ae6317f3f61e9597f '@typescript-eslint/eslint-plugin': 5.5.0_15fb0f7dd5018b02e6608eb3a323af2f '@typescript-eslint/parser': 5.5.0_eslint@8.3.0+typescript@4.4.4 - action-deploy-docs: github.com/sveltejs/action-deploy-docs/773baf07ba9391eee581dcecfb672265e5fdbddb + action-deploy-docs: github.com/sveltejs/action-deploy-docs/a65fbf5a90f53c9d72fed4daaca59da50f074355 dotenv: 10.0.0 eslint: 8.3.0 eslint-plugin-import: 2.25.3_eslint@8.3.0 @@ -943,6 +943,10 @@ packages: '@types/node': 16.11.11 dev: true + /@types/unist/2.0.6: + resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} + dev: true + /@types/yauzl/2.9.2: resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==} requiresBuild: true @@ -1314,6 +1318,10 @@ packages: engines: {node: '>=6'} dev: true + /ccount/1.1.0: + resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1339,6 +1347,18 @@ packages: supports-color: 7.2.0 dev: true + /character-entities-legacy/1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + dev: true + + /character-entities/1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + dev: true + + /character-reference-invalid/1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + dev: true + /chardet/0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true @@ -2474,6 +2494,17 @@ packages: resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=} dev: true + /is-alphabetical/1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + dev: true + + /is-alphanumerical/1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + dev: true + /is-arrayish/0.2.1: resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} dev: true @@ -2523,6 +2554,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-decimal/1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + dev: true + /is-extglob/2.1.1: resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} engines: {node: '>=0.10.0'} @@ -2545,6 +2580,10 @@ packages: is-extglob: 2.1.1 dev: true + /is-hexadecimal/1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + dev: true + /is-module/1.0.0: resolution: {integrity: sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=} dev: true @@ -2775,6 +2814,10 @@ packages: resolution: {integrity: sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=} dev: true + /longest-streak/2.0.4: + resolution: {integrity: sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==} + dev: true + /lower-case/2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: @@ -2817,12 +2860,82 @@ packages: engines: {node: '>=8'} dev: true + /markdown-table/2.0.0: + resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} + dependencies: + repeat-string: 1.6.1 + dev: true + /marked/4.0.5: resolution: {integrity: sha512-eUToMA5d5lunnipkCN7zFD0RiunCF2Uo6bImEt/Qx8LZMW7oPXTw7R+f+M5V3eS7164HjEDPfW8/TrefuFhDfw==} engines: {node: '>= 12'} hasBin: true dev: true + /mdast-util-find-and-replace/1.1.1: + resolution: {integrity: sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==} + dependencies: + escape-string-regexp: 4.0.0 + unist-util-is: 4.1.0 + unist-util-visit-parents: 3.1.1 + dev: true + + /mdast-util-gfm-autolink-literal/0.1.3: + resolution: {integrity: sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==} + dependencies: + ccount: 1.1.0 + mdast-util-find-and-replace: 1.1.1 + micromark: 2.11.4 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm-strikethrough/0.2.3: + resolution: {integrity: sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==} + dependencies: + mdast-util-to-markdown: 0.6.5 + dev: true + + /mdast-util-gfm-table/0.1.6: + resolution: {integrity: sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==} + dependencies: + markdown-table: 2.0.0 + mdast-util-to-markdown: 0.6.5 + dev: true + + /mdast-util-gfm-task-list-item/0.1.6: + resolution: {integrity: sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==} + dependencies: + mdast-util-to-markdown: 0.6.5 + dev: true + + /mdast-util-gfm/0.1.2: + resolution: {integrity: sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==} + dependencies: + mdast-util-gfm-autolink-literal: 0.1.3 + mdast-util-gfm-strikethrough: 0.2.3 + mdast-util-gfm-table: 0.1.6 + mdast-util-gfm-task-list-item: 0.1.6 + mdast-util-to-markdown: 0.6.5 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-to-markdown/0.6.5: + resolution: {integrity: sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==} + dependencies: + '@types/unist': 2.0.6 + longest-streak: 2.0.4 + mdast-util-to-string: 2.0.0 + parse-entities: 2.0.0 + repeat-string: 1.6.1 + zwitch: 1.0.5 + dev: true + + /mdast-util-to-string/2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + dev: true + /meow/6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} @@ -2845,6 +2958,64 @@ packages: engines: {node: '>= 8'} dev: true + /micromark-extension-gfm-autolink-literal/0.5.7: + resolution: {integrity: sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==} + dependencies: + micromark: 2.11.4 + transitivePeerDependencies: + - supports-color + dev: true + + /micromark-extension-gfm-strikethrough/0.6.5: + resolution: {integrity: sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==} + dependencies: + micromark: 2.11.4 + transitivePeerDependencies: + - supports-color + dev: true + + /micromark-extension-gfm-table/0.4.3: + resolution: {integrity: sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==} + dependencies: + micromark: 2.11.4 + transitivePeerDependencies: + - supports-color + dev: true + + /micromark-extension-gfm-tagfilter/0.3.0: + resolution: {integrity: sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==} + dev: true + + /micromark-extension-gfm-task-list-item/0.3.3: + resolution: {integrity: sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==} + dependencies: + micromark: 2.11.4 + transitivePeerDependencies: + - supports-color + dev: true + + /micromark-extension-gfm/0.3.3: + resolution: {integrity: sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==} + dependencies: + micromark: 2.11.4 + micromark-extension-gfm-autolink-literal: 0.5.7 + micromark-extension-gfm-strikethrough: 0.6.5 + micromark-extension-gfm-table: 0.4.3 + micromark-extension-gfm-tagfilter: 0.3.0 + micromark-extension-gfm-task-list-item: 0.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /micromark/2.11.4: + resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + dependencies: + debug: 4.3.3 + parse-entities: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /micromatch/4.0.4: resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} engines: {node: '>=8.6'} @@ -3151,6 +3322,17 @@ packages: callsites: 3.1.0 dev: true + /parse-entities/2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + dev: true + /parse-json/5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -3456,6 +3638,20 @@ packages: engines: {node: '>=8'} dev: true + /remark-gfm/1.0.0: + resolution: {integrity: sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==} + dependencies: + mdast-util-gfm: 0.1.2 + micromark-extension-gfm: 0.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /repeat-string/1.6.1: + resolution: {integrity: sha1-jcrkcOHIirwtYA//Sndihtp15jc=} + engines: {node: '>=0.10'} + dev: true + /require-directory/2.1.1: resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} engines: {node: '>=0.10.0'} @@ -4163,6 +4359,17 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unist-util-is/4.1.0: + resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + dev: true + + /unist-util-visit-parents/3.1.1: + resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 4.1.0 + dev: true + /universal-user-agent/6.0.0: resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} dev: true @@ -4423,8 +4630,12 @@ packages: engines: {node: '>=10'} dev: true - github.com/sveltejs/action-deploy-docs/773baf07ba9391eee581dcecfb672265e5fdbddb: - resolution: {tarball: https://codeload.github.com/sveltejs/action-deploy-docs/tar.gz/773baf07ba9391eee581dcecfb672265e5fdbddb} + /zwitch/1.0.5: + resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} + dev: true + + github.com/sveltejs/action-deploy-docs/a65fbf5a90f53c9d72fed4daaca59da50f074355: + resolution: {tarball: https://codeload.github.com/sveltejs/action-deploy-docs/tar.gz/a65fbf5a90f53c9d72fed4daaca59da50f074355} name: action-deploy-docs version: 1.0.0 hasBin: true @@ -4432,6 +4643,9 @@ packages: '@actions/github': 4.0.0 '@polka/send': 1.0.0-next.15 httpie: 1.1.2 + remark-gfm: 1.0.0 + transitivePeerDependencies: + - supports-color dev: true github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641_d9525e585486a10ae6317f3f61e9597f: From d9cb88a94f1659bb16b5c6df5b1a27a0b1168aa0 Mon Sep 17 00:00:00 2001 From: Andrew McGrath Date: Mon, 13 Dec 2021 10:01:26 -0500 Subject: [PATCH 2/8] Removing changes to lockfile --- pnpm-lock.yaml | 220 +------------------------------------------------ 1 file changed, 3 insertions(+), 217 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 076f3ee7a74f..27be7f490524 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: '@sveltejs/eslint-config': github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641_d9525e585486a10ae6317f3f61e9597f '@typescript-eslint/eslint-plugin': 5.5.0_15fb0f7dd5018b02e6608eb3a323af2f '@typescript-eslint/parser': 5.5.0_eslint@8.3.0+typescript@4.4.4 - action-deploy-docs: github.com/sveltejs/action-deploy-docs/a65fbf5a90f53c9d72fed4daaca59da50f074355 + action-deploy-docs: github.com/sveltejs/action-deploy-docs/773baf07ba9391eee581dcecfb672265e5fdbddb dotenv: 10.0.0 eslint: 8.3.0 eslint-plugin-import: 2.25.3_eslint@8.3.0 @@ -943,10 +943,6 @@ packages: '@types/node': 16.11.11 dev: true - /@types/unist/2.0.6: - resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} - dev: true - /@types/yauzl/2.9.2: resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==} requiresBuild: true @@ -1318,10 +1314,6 @@ packages: engines: {node: '>=6'} dev: true - /ccount/1.1.0: - resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} - dev: true - /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1347,18 +1339,6 @@ packages: supports-color: 7.2.0 dev: true - /character-entities-legacy/1.1.4: - resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} - dev: true - - /character-entities/1.2.4: - resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} - dev: true - - /character-reference-invalid/1.1.4: - resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} - dev: true - /chardet/0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true @@ -2494,17 +2474,6 @@ packages: resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=} dev: true - /is-alphabetical/1.0.4: - resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} - dev: true - - /is-alphanumerical/1.0.4: - resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} - dependencies: - is-alphabetical: 1.0.4 - is-decimal: 1.0.4 - dev: true - /is-arrayish/0.2.1: resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} dev: true @@ -2554,10 +2523,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-decimal/1.0.4: - resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} - dev: true - /is-extglob/2.1.1: resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} engines: {node: '>=0.10.0'} @@ -2580,10 +2545,6 @@ packages: is-extglob: 2.1.1 dev: true - /is-hexadecimal/1.0.4: - resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} - dev: true - /is-module/1.0.0: resolution: {integrity: sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=} dev: true @@ -2814,10 +2775,6 @@ packages: resolution: {integrity: sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=} dev: true - /longest-streak/2.0.4: - resolution: {integrity: sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==} - dev: true - /lower-case/2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: @@ -2860,82 +2817,12 @@ packages: engines: {node: '>=8'} dev: true - /markdown-table/2.0.0: - resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} - dependencies: - repeat-string: 1.6.1 - dev: true - /marked/4.0.5: resolution: {integrity: sha512-eUToMA5d5lunnipkCN7zFD0RiunCF2Uo6bImEt/Qx8LZMW7oPXTw7R+f+M5V3eS7164HjEDPfW8/TrefuFhDfw==} engines: {node: '>= 12'} hasBin: true dev: true - /mdast-util-find-and-replace/1.1.1: - resolution: {integrity: sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==} - dependencies: - escape-string-regexp: 4.0.0 - unist-util-is: 4.1.0 - unist-util-visit-parents: 3.1.1 - dev: true - - /mdast-util-gfm-autolink-literal/0.1.3: - resolution: {integrity: sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==} - dependencies: - ccount: 1.1.0 - mdast-util-find-and-replace: 1.1.1 - micromark: 2.11.4 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-gfm-strikethrough/0.2.3: - resolution: {integrity: sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==} - dependencies: - mdast-util-to-markdown: 0.6.5 - dev: true - - /mdast-util-gfm-table/0.1.6: - resolution: {integrity: sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==} - dependencies: - markdown-table: 2.0.0 - mdast-util-to-markdown: 0.6.5 - dev: true - - /mdast-util-gfm-task-list-item/0.1.6: - resolution: {integrity: sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==} - dependencies: - mdast-util-to-markdown: 0.6.5 - dev: true - - /mdast-util-gfm/0.1.2: - resolution: {integrity: sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==} - dependencies: - mdast-util-gfm-autolink-literal: 0.1.3 - mdast-util-gfm-strikethrough: 0.2.3 - mdast-util-gfm-table: 0.1.6 - mdast-util-gfm-task-list-item: 0.1.6 - mdast-util-to-markdown: 0.6.5 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-to-markdown/0.6.5: - resolution: {integrity: sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==} - dependencies: - '@types/unist': 2.0.6 - longest-streak: 2.0.4 - mdast-util-to-string: 2.0.0 - parse-entities: 2.0.0 - repeat-string: 1.6.1 - zwitch: 1.0.5 - dev: true - - /mdast-util-to-string/2.0.0: - resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} - dev: true - /meow/6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} @@ -2958,64 +2845,6 @@ packages: engines: {node: '>= 8'} dev: true - /micromark-extension-gfm-autolink-literal/0.5.7: - resolution: {integrity: sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==} - dependencies: - micromark: 2.11.4 - transitivePeerDependencies: - - supports-color - dev: true - - /micromark-extension-gfm-strikethrough/0.6.5: - resolution: {integrity: sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==} - dependencies: - micromark: 2.11.4 - transitivePeerDependencies: - - supports-color - dev: true - - /micromark-extension-gfm-table/0.4.3: - resolution: {integrity: sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==} - dependencies: - micromark: 2.11.4 - transitivePeerDependencies: - - supports-color - dev: true - - /micromark-extension-gfm-tagfilter/0.3.0: - resolution: {integrity: sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==} - dev: true - - /micromark-extension-gfm-task-list-item/0.3.3: - resolution: {integrity: sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==} - dependencies: - micromark: 2.11.4 - transitivePeerDependencies: - - supports-color - dev: true - - /micromark-extension-gfm/0.3.3: - resolution: {integrity: sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==} - dependencies: - micromark: 2.11.4 - micromark-extension-gfm-autolink-literal: 0.5.7 - micromark-extension-gfm-strikethrough: 0.6.5 - micromark-extension-gfm-table: 0.4.3 - micromark-extension-gfm-tagfilter: 0.3.0 - micromark-extension-gfm-task-list-item: 0.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /micromark/2.11.4: - resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} - dependencies: - debug: 4.3.3 - parse-entities: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /micromatch/4.0.4: resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} engines: {node: '>=8.6'} @@ -3322,17 +3151,6 @@ packages: callsites: 3.1.0 dev: true - /parse-entities/2.0.0: - resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - dependencies: - character-entities: 1.2.4 - character-entities-legacy: 1.1.4 - character-reference-invalid: 1.1.4 - is-alphanumerical: 1.0.4 - is-decimal: 1.0.4 - is-hexadecimal: 1.0.4 - dev: true - /parse-json/5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -3638,20 +3456,6 @@ packages: engines: {node: '>=8'} dev: true - /remark-gfm/1.0.0: - resolution: {integrity: sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==} - dependencies: - mdast-util-gfm: 0.1.2 - micromark-extension-gfm: 0.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /repeat-string/1.6.1: - resolution: {integrity: sha1-jcrkcOHIirwtYA//Sndihtp15jc=} - engines: {node: '>=0.10'} - dev: true - /require-directory/2.1.1: resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} engines: {node: '>=0.10.0'} @@ -4359,17 +4163,6 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /unist-util-is/4.1.0: - resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} - dev: true - - /unist-util-visit-parents/3.1.1: - resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 4.1.0 - dev: true - /universal-user-agent/6.0.0: resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} dev: true @@ -4630,12 +4423,8 @@ packages: engines: {node: '>=10'} dev: true - /zwitch/1.0.5: - resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} - dev: true - - github.com/sveltejs/action-deploy-docs/a65fbf5a90f53c9d72fed4daaca59da50f074355: - resolution: {tarball: https://codeload.github.com/sveltejs/action-deploy-docs/tar.gz/a65fbf5a90f53c9d72fed4daaca59da50f074355} + github.com/sveltejs/action-deploy-docs/773baf07ba9391eee581dcecfb672265e5fdbddb: + resolution: {tarball: https://codeload.github.com/sveltejs/action-deploy-docs/tar.gz/773baf07ba9391eee581dcecfb672265e5fdbddb} name: action-deploy-docs version: 1.0.0 hasBin: true @@ -4643,9 +4432,6 @@ packages: '@actions/github': 4.0.0 '@polka/send': 1.0.0-next.15 httpie: 1.1.2 - remark-gfm: 1.0.0 - transitivePeerDependencies: - - supports-color dev: true github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641_d9525e585486a10ae6317f3f61e9597f: From 8b4b713b64bc4f29293baf0df2381e87367c2fe2 Mon Sep 17 00:00:00 2001 From: Andrew McGrath Date: Mon, 20 Dec 2021 10:46:22 -0500 Subject: [PATCH 3/8] Validate allowed_methods does not contain GET, rephrase docs --- documentation/docs/01-routing.md | 12 +++++----- documentation/docs/14-configuration.md | 2 +- .../default/src/routes/todos/index.svelte | 6 ++--- packages/kit/src/core/config/options.js | 4 ++++ packages/kit/src/runtime/server/index.js | 22 ++++++++++++------- .../src/routes/method-override/_tests.js | 16 -------------- .../src/routes/method-override/index.svelte | 10 --------- 7 files changed, 29 insertions(+), 43 deletions(-) diff --git a/documentation/docs/01-routing.md b/documentation/docs/01-routing.md index 1a0a1d8c21ed..39cef50e1c25 100644 --- a/documentation/docs/01-routing.md +++ b/documentation/docs/01-routing.md @@ -163,13 +163,13 @@ The `body` property of the request object will be provided in the case of POST r #### HTTP Method Overrides -Given that the only valid `
` methods are GET and POST, functionality is provided to override this limitation and allow the use of other HTTP verbs. This is particularly helpful when ensuring your application works even when javascript fails or is disabled. +In contrast to `fetch` the only valid methods for a `` are GET and POST. Svelte allows you to override the `` method to workaround this limitation if you need to. By using fetch and `` interchangeably with the same endpoint, your application can continue to work when JavaScript fails or is disabled. - Disabled by default to prevent unintended behavior for those who don't need it -- For security purposes, the original method on the form must be `POST` and cannot be overridden with `GET` -- There are 2 different ways to specify a method override strategy +- The original method on the form must be `POST` and cannot be overridden with `GET` +- There are 2 different strategies available for overriding form methods - `url_parameter`: Pass the key and desired method as a query string - - `form_data`: Pass as a field within the form where the field name is the key and the field value is the desired method + - `form_data`: Pass as a hidden field within the form where the field name is the key and the field value is the desired method - `strategy: 'both'` will enable both methods (`url_parameter` takes precendence in the event that both strategies are used within the same form) ```js @@ -187,7 +187,8 @@ export default { ``` ```html - + +
``` @@ -195,6 +196,7 @@ export default { OR ```html +
diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index 1a390f9d8ec3..19a0973415f9 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -141,7 +141,7 @@ See [HTTP Method Overrides](#routing-endpoints-http-method-overrides). An object - `'both'` — (default) will look for the override key in both the list of query parameters and the form fields - `'url_parameter'` — only allow overriding via a query parameter - - `form_data` — only allow overriding via a form field + - `'form_data'` — only allow overriding via a form field ### package diff --git a/packages/create-svelte/templates/default/src/routes/todos/index.svelte b/packages/create-svelte/templates/default/src/routes/todos/index.svelte index f4db69b1c1e8..15048569bfc7 100644 --- a/packages/create-svelte/templates/default/src/routes/todos/index.svelte +++ b/packages/create-svelte/templates/default/src/routes/todos/index.svelte @@ -77,7 +77,7 @@ animate:flip={{ duration: 200 }} >
{ @@ -92,7 +92,7 @@ (todo.pending_delete = true), diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index 5d75def359c6..73046fccb6bf 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -81,6 +81,10 @@ const options = object( throw new Error(`${keypath} must be an array of strings`); } + if (input.map((i) => i.toUpperCase()).includes('GET')) { + throw new Error(`${keypath} cannot contain "GET"`); + } + return input; }), strategy: validate('both', (input, keypath) => { diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index a02c9b722338..40866bc50fcb 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -50,19 +50,25 @@ export async function respond(incoming, options, state = {}) { request.body instanceof ReadOnlyFormData && request.body.has(method_key) ) { - new_request_method = (request.body.get(method_key) || request.method).toUpperCase(); + new_request_method = /** @type {string} */ (request.body.get(method_key)).toUpperCase(); } if (['both', 'url_parameter'].includes(strategy) && incoming.query.has(method_key)) { - new_request_method = (incoming.query.get(method_key) || request.method).toUpperCase(); + new_request_method = /** @type {string} */ (incoming.query.get(method_key)).toUpperCase(); } - if ( - new_request_method && - allowedMethods.includes(new_request_method) && - new_request_method !== 'GET' - ) { - request.method = new_request_method; + if (new_request_method) { + if (allowedMethods.includes(new_request_method)) { + request.method = new_request_method; + } else { + return { + status: 400, + headers: {}, + body: `Form method override provided ("${new_request_method}") is not included in list of allowed methods ("${allowedMethods.join( + '", "' + )}")` + }; + } } } diff --git a/packages/kit/test/apps/basics/src/routes/method-override/_tests.js b/packages/kit/test/apps/basics/src/routes/method-override/_tests.js index 0c341bad8988..3065ceb5c361 100644 --- a/packages/kit/test/apps/basics/src/routes/method-override/_tests.js +++ b/packages/kit/test/apps/basics/src/routes/method-override/_tests.js @@ -24,12 +24,6 @@ export default function (test) { assert.equal('GET', val); }); - test('POST method is not overridden with GET', '/method-override', async ({ page }) => { - await page.click('"No Override To GET"'); - const val = await page.textContent('h1'); - assert.equal('POST', val); - }); - test('http method is overridden via hidden input', '/method-override', async ({ page }) => { await page.click('"PATCH Via Hidden Input"'); const val = await page.textContent('h1'); @@ -41,14 +35,4 @@ export default function (test) { const val = await page.textContent('h1'); assert.equal('GET', val); }); - - test( - 'POST method is not overridden with GET via hidden input', - '/method-override', - async ({ page }) => { - await page.click('"No Override To GET Via Hidden Input"'); - const val = await page.textContent('h1'); - assert.equal('POST', val); - } - ); } diff --git a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte b/packages/kit/test/apps/basics/src/routes/method-override/index.svelte index 5222af23c681..59b26984e0fb 100644 --- a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/method-override/index.svelte @@ -31,11 +31,6 @@
-
- - -
-
@@ -45,8 +40,3 @@
- -
- - -
From 2c357aece4d2a8323f2fb62371a74abc619206f3 Mon Sep 17 00:00:00 2001 From: Andrew McGrath Date: Mon, 10 Jan 2022 19:43:20 -0500 Subject: [PATCH 4/8] Remove hidden field strategy and enabled config, rename other config values --- documentation/docs/01-routing.md | 23 ++-------- documentation/docs/14-configuration.md | 16 ++----- .../templates/default/svelte.config.js | 2 +- packages/kit/src/core/config/index.spec.js | 12 ++--- packages/kit/src/core/config/options.js | 9 +--- packages/kit/src/core/config/test/index.js | 6 +-- packages/kit/src/runtime/server/index.js | 44 +++++++------------ .../src/routes/method-override/index.svelte | 12 ++--- .../kit/test/apps/basics/svelte.config.js | 2 +- packages/kit/test/apps/basics/test/test.js | 20 +++++---- packages/kit/types/config.d.ts | 7 ++- packages/kit/types/internal.d.ts | 9 +--- 12 files changed, 57 insertions(+), 105 deletions(-) diff --git a/documentation/docs/01-routing.md b/documentation/docs/01-routing.md index 6006333157f1..c4bd34ce42ec 100644 --- a/documentation/docs/01-routing.md +++ b/documentation/docs/01-routing.md @@ -163,43 +163,26 @@ The `body` property of the request object will be provided in the case of POST r In contrast to `fetch` the only valid methods for a `
` are GET and POST. Svelte allows you to override the `` method to workaround this limitation if you need to. By using fetch and `` interchangeably with the same endpoint, your application can continue to work when JavaScript fails or is disabled. -- Disabled by default to prevent unintended behavior for those who don't need it -- The original method on the form must be `POST` and cannot be overridden with `GET` -- There are 2 different strategies available for overriding form methods - - `url_parameter`: Pass the key and desired method as a query string - - `form_data`: Pass as a hidden field within the form where the field name is the key and the field value is the desired method -- `strategy: 'both'` will enable both methods (`url_parameter` takes precendence in the event that both strategies are used within the same form) +Specify the override method via a query string value, with the key being what's defined in the [`parameter`](#configuration-methodoverride) config option. The original method on the form must be `POST` and cannot be overridden with `GET`. Define which methods can override the `POST` using the [`allowed`](#configuration-methodoverride) config option. Default is an empty array ```js // svelte.config.js export default { kit: { methodOverride: { - enabled: true, - key: '_method', - allowedMethods: ['PUT', 'PATCH', 'DELETE'], - strategy: 'both' + parameter: '_method', + allowed: ['PUT', 'PATCH', 'DELETE'], }, } }; ``` ```html -
``` -OR - -```html - -
- -
-``` - ### Private modules A filename that has a segment with a leading underscore, such as `src/routes/foo/_Private.svelte` or `src/routes/bar/_utils/cool-util.js`, is hidden from the router, but can be imported by files that are not. diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index 874a249362be..1c7666a31481 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -33,10 +33,8 @@ const config = { host: null, hydrate: true, methodOverride: { - enabled: false, - key: '_method', - allowedMethods: ['PUT', 'PATCH', 'DELETE'], - strategy: 'both' + parameter: '_method', + allowed: [] }, package: { dir: 'package', @@ -144,14 +142,8 @@ Whether to [hydrate](#ssr-and-javascript-hydrate) the server-rendered HTML with See [HTTP Method Overrides](#routing-endpoints-http-method-overrides). An object containing zero or more of the following: -- `enabled` — set to `true` to enable method overriding -- `key` — query parameter name/field name to use for passing the intended method value -- `allowedMethods` - array of HTTP methods that can be used when overriding the original request method -- `strategy` - - - `'both'` — (default) will look for the override key in both the list of query parameters and the form fields - - `'url_parameter'` — only allow overriding via a query parameter - - `'form_data'` — only allow overriding via a form field +- `parameter` — query parameter name to use for passing the intended method value +- `allowed` - array of HTTP methods that can be used when overriding the original request method ### package diff --git a/packages/create-svelte/templates/default/svelte.config.js b/packages/create-svelte/templates/default/svelte.config.js index 49ac291bd2fb..3921efa5f072 100644 --- a/packages/create-svelte/templates/default/svelte.config.js +++ b/packages/create-svelte/templates/default/svelte.config.js @@ -15,7 +15,7 @@ const config = { // Override http methods in the Todo forms methodOverride: { - enabled: true + allowed: ['PATCH', 'DELETE'] } } }; diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index be8c092b5f77..f127150333b4 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -36,10 +36,8 @@ test('fills in defaults', () => { host: null, hydrate: true, methodOverride: { - enabled: false, - key: '_method', - allowedMethods: ['PUT', 'PATCH', 'DELETE'], - strategy: 'both' + parameter: '_method', + allowed: [] }, package: { dir: 'package', @@ -149,10 +147,8 @@ test('fills in partial blanks', () => { host: null, hydrate: true, methodOverride: { - enabled: false, - key: '_method', - allowedMethods: ['PUT', 'PATCH', 'DELETE'], - strategy: 'both' + parameter: '_method', + allowed: [] }, package: { dir: 'package', diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index 3c8fb3cc89d9..2082ae5f056a 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -76,9 +76,8 @@ const options = object( hydrate: boolean(true), methodOverride: object({ - enabled: boolean(false), - key: string('_method'), - allowedMethods: validate(['PUT', 'PATCH', 'DELETE'], (input, keypath) => { + parameter: string('_method'), + allowed: validate([], (input, keypath) => { if (!Array.isArray(input) || !input.every((method) => typeof method === 'string')) { throw new Error(`${keypath} must be an array of strings`); } @@ -88,10 +87,6 @@ const options = object( } return input; - }), - strategy: validate('both', (input, keypath) => { - if (['both', 'url_parameter', 'form_data'].includes(input)) return input; - throw new Error(`${keypath} must be either "both", "url_parameter" or "form_data"`); }) }), diff --git a/packages/kit/src/core/config/test/index.js b/packages/kit/src/core/config/test/index.js index b755e045fe6a..4308bf15eb92 100644 --- a/packages/kit/src/core/config/test/index.js +++ b/packages/kit/src/core/config/test/index.js @@ -38,10 +38,8 @@ test('load default config (esm)', async () => { host: null, hydrate: true, methodOverride: { - enabled: false, - key: '_method', - allowedMethods: ['PUT', 'PATCH', 'DELETE'], - strategy: 'both' + parameter: '_method', + allowed: [] }, package: { dir: 'package', diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 04919838b3e2..3991af5aa89d 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -7,7 +7,6 @@ import { lowercase_keys } from './utils.js'; import { hash } from '../hash.js'; import { get_single_valued_header } from '../../utils/http.js'; import { coalesce_to_error } from '../../utils/error.js'; -import { ReadOnlyFormData } from './parse_body/read_only_form_data.js'; /** @type {import('@sveltejs/kit/ssr').Respond} */ export async function respond(incoming, options, state = {}) { @@ -44,36 +43,25 @@ export async function respond(incoming, options, state = {}) { locals: {} }; - if (options.method_override.enabled && request.method.toUpperCase() === 'POST') { - const { strategy = '', key: method_key = '', allowedMethods = [] } = options.method_override; - let new_request_method; + const { parameter, allowed } = options.method_override; - if ( - ['both', 'form_data'].includes(strategy) && - request.body instanceof ReadOnlyFormData && - request.body.has(method_key) - ) { - new_request_method = /** @type {string} */ (request.body.get(method_key)).toUpperCase(); - } + if (request.method.toUpperCase() === 'POST' && incoming.url.searchParams.has(parameter)) { + const new_request_method = /** @type {string} */ ( + incoming.url.searchParams.get(parameter) + ).toUpperCase(); - if (['both', 'url_parameter'].includes(strategy) && incoming.url.searchParams.has(method_key)) { - new_request_method = /** @type {string} */ ( - incoming.url.searchParams.get(method_key) - ).toUpperCase(); - } + if (allowed.includes(new_request_method)) { + request.method = new_request_method; + } else { + const body = new_request_method === 'GET' ? + 'A POST request cannot be overridden with GET' : + `Form method override provided "${new_request_method}" is not included in list of allowed methods ("${allowed.join('", "')}")`; - if (new_request_method) { - if (allowedMethods.includes(new_request_method)) { - request.method = new_request_method; - } else { - return { - status: 400, - headers: {}, - body: `Form method override provided ("${new_request_method}") is not included in list of allowed methods ("${allowedMethods.join( - '", "' - )}")` - }; - } + return { + status: 400, + headers: {}, + body + }; } } diff --git a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte b/packages/kit/test/apps/basics/src/routes/method-override/index.svelte index 0b61af239918..f88ae1417ef9 100644 --- a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/method-override/index.svelte @@ -31,12 +31,12 @@ -
- - + + +
-
- - + + +
diff --git a/packages/kit/test/apps/basics/svelte.config.js b/packages/kit/test/apps/basics/svelte.config.js index 146226175265..d5b673464b77 100644 --- a/packages/kit/test/apps/basics/svelte.config.js +++ b/packages/kit/test/apps/basics/svelte.config.js @@ -13,7 +13,7 @@ const config = { } }, methodOverride: { - enabled: true + allowed: ['PUT', 'PATCH', 'DELETE'] } } }; diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 5ec91a6cac79..1eb7c8dd2779 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -944,26 +944,28 @@ test.describe.parallel('Method overrides', () => { test('GET method is not overridden', async ({ page }) => { await page.goto('/method-override'); - await page.click('"No Override From GET"'); + const val = await page.textContent('h1'); expect('GET').toBe(val); }); - test('http method is overridden via hidden input', async ({ page }) => { + test('400 response when trying to override POST with GET', async ({ page }) => { await page.goto('/method-override'); + await page.click('"No Override To GET"'); - await page.click('"PATCH Via Hidden Input"'); - const val = await page.textContent('h1'); - expect('PATCH').toBe(val); + expect(await page.innerHTML('pre')).toBe( + 'A POST request cannot be overridden with GET' + ); }); - test('GET method is not overridden via hidden input', async ({ page }) => { + test('400 response when override method not in allowed methods', async ({ page }) => { await page.goto('/method-override'); + await page.click('"No Override To CONNECT"'); - await page.click('"No Override From GET Via Hidden Input"'); - const val = await page.textContent('h1'); - expect('GET').toBe(val); + expect(await page.innerHTML('pre')).toBe( + 'Form method override provided "CONNECT" is not included in list of allowed methods ("PUT", "PATCH", "DELETE")' + ); }); }); diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts index d8e5a670d210..e77eefdc0b29 100644 --- a/packages/kit/types/config.d.ts +++ b/packages/kit/types/config.d.ts @@ -1,6 +1,6 @@ import { UserConfig as ViteConfig } from 'vite'; import { RecursiveRequired } from './helper'; -import { HttpMethod, Logger, MethodOverride, RouteSegment, TrailingSlash } from './internal'; +import { HttpMethod, Logger, RouteSegment, TrailingSlash } from './internal'; export interface RouteDefinition { type: 'page' | 'endpoint'; @@ -131,7 +131,10 @@ export interface Config { }; host?: string; hydrate?: boolean; - methodOverride?: MethodOverride; + methodOverride?: { + parameter?: string; + allowed?: string[]; + }; package?: { dir?: string; emitTypes?: boolean; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 43cadc556d9b..fa7fa1f53b57 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -231,12 +231,7 @@ export type NormalizedLoadOutput = Either< >; export type TrailingSlash = 'never' | 'always' | 'ignore'; - -export type MethodOverrideStrategy = 'both' | 'url_parameter' | 'form_data'; - export interface MethodOverride { - enabled?: boolean; - key?: string; - allowedMethods?: string[]; - strategy?: MethodOverrideStrategy; + parameter: string; + allowed: string[]; } From 63d9dcb58b343a509e8aaa701c26e271f651d568 Mon Sep 17 00:00:00 2001 From: Andrew McGrath Date: Mon, 10 Jan 2022 19:45:22 -0500 Subject: [PATCH 5/8] lint/format --- packages/kit/src/runtime/server/index.js | 9 ++++++--- packages/kit/test/apps/basics/test/test.js | 4 +--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 3991af5aa89d..75dd1ef0fcd9 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -53,9 +53,12 @@ export async function respond(incoming, options, state = {}) { if (allowed.includes(new_request_method)) { request.method = new_request_method; } else { - const body = new_request_method === 'GET' ? - 'A POST request cannot be overridden with GET' : - `Form method override provided "${new_request_method}" is not included in list of allowed methods ("${allowed.join('", "')}")`; + const body = + new_request_method === 'GET' + ? 'A POST request cannot be overridden with GET' + : `Form method override provided "${new_request_method}" is not included in list of allowed methods ("${allowed.join( + '", "' + )}")`; return { status: 400, diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 1eb7c8dd2779..8f1dc1d68569 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -954,9 +954,7 @@ test.describe.parallel('Method overrides', () => { await page.goto('/method-override'); await page.click('"No Override To GET"'); - expect(await page.innerHTML('pre')).toBe( - 'A POST request cannot be overridden with GET' - ); + expect(await page.innerHTML('pre')).toBe('A POST request cannot be overridden with GET'); }); test('400 response when override method not in allowed methods', async ({ page }) => { From ea1f75e07fb5e6ba93d48f1a7cba2020df078aa0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 10 Jan 2022 22:42:16 -0500 Subject: [PATCH 6/8] tweak error messages --- packages/kit/src/runtime/server/index.js | 37 ++++++++++------------ packages/kit/test/apps/basics/test/test.js | 6 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 75dd1ef0fcd9..62733e1140eb 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -44,27 +44,24 @@ export async function respond(incoming, options, state = {}) { }; const { parameter, allowed } = options.method_override; - - if (request.method.toUpperCase() === 'POST' && incoming.url.searchParams.has(parameter)) { - const new_request_method = /** @type {string} */ ( - incoming.url.searchParams.get(parameter) - ).toUpperCase(); - - if (allowed.includes(new_request_method)) { - request.method = new_request_method; + const method_override = incoming.url.searchParams.get(parameter)?.toUpperCase(); + + if (method_override) { + if (request.method.toUpperCase() === 'POST') { + if (allowed.includes(method_override)) { + request.method = method_override; + } else { + const verb = allowed.length === 0 ? 'enabled' : 'allowed'; + const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`; + + return { + status: 400, + headers: {}, + body + }; + } } else { - const body = - new_request_method === 'GET' - ? 'A POST request cannot be overridden with GET' - : `Form method override provided "${new_request_method}" is not included in list of allowed methods ("${allowed.join( - '", "' - )}")`; - - return { - status: 400, - headers: {}, - body - }; + throw new Error(`${parameter}=${method_override} is only allowed with POST requests`); } } diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 8f1dc1d68569..93f9854c49cb 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -954,7 +954,9 @@ test.describe.parallel('Method overrides', () => { await page.goto('/method-override'); await page.click('"No Override To GET"'); - expect(await page.innerHTML('pre')).toBe('A POST request cannot be overridden with GET'); + expect(await page.innerHTML('pre')).toBe( + '_method=GET is not allowed. See https://kit.svelte.dev/docs#configuration-methodoverride' + ); }); test('400 response when override method not in allowed methods', async ({ page }) => { @@ -962,7 +964,7 @@ test.describe.parallel('Method overrides', () => { await page.click('"No Override To CONNECT"'); expect(await page.innerHTML('pre')).toBe( - 'Form method override provided "CONNECT" is not included in list of allowed methods ("PUT", "PATCH", "DELETE")' + '_method=CONNECT is not allowed. See https://kit.svelte.dev/docs#configuration-methodoverride' ); }); }); From 6ae9e7a1b4a1e3ab751dc229d7f73a64a3ecaf63 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 10 Jan 2022 22:50:31 -0500 Subject: [PATCH 7/8] slim docs down --- documentation/docs/01-routing.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/documentation/docs/01-routing.md b/documentation/docs/01-routing.md index c4bd34ce42ec..56dfd9517bd0 100644 --- a/documentation/docs/01-routing.md +++ b/documentation/docs/01-routing.md @@ -161,28 +161,27 @@ The `body` property of the request object will be provided in the case of POST r #### HTTP Method Overrides -In contrast to `fetch` the only valid methods for a `
` are GET and POST. Svelte allows you to override the `` method to workaround this limitation if you need to. By using fetch and `` interchangeably with the same endpoint, your application can continue to work when JavaScript fails or is disabled. - -Specify the override method via a query string value, with the key being what's defined in the [`parameter`](#configuration-methodoverride) config option. The original method on the form must be `POST` and cannot be overridden with `GET`. Define which methods can override the `POST` using the [`allowed`](#configuration-methodoverride) config option. Default is an empty array +HTML `` elements only support `GET` and `POST` methods natively. You can allow other methods, like `PUT` and `DELETE`, by specifying them in your [configuration](#configuration-methodoverride) and adding a `_method=VERB` parameter (you can configure the name) to the form's `action`: ```js // svelte.config.js export default { kit: { methodOverride: { - parameter: '_method', - allowed: ['PUT', 'PATCH', 'DELETE'], - }, + allowed: ['PUT', 'PATCH', 'DELETE'] + } } }; ``` ```html - +
``` +> Using native `
` behaviour ensures your app continues to work when JavaScript fails or is disabled. + ### Private modules A filename that has a segment with a leading underscore, such as `src/routes/foo/_Private.svelte` or `src/routes/bar/_utils/cool-util.js`, is hidden from the router, but can be imported by files that are not. From e3cdab5a4cf2af78bbbb2fd8d07082bb586ef9dd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 10 Jan 2022 23:03:49 -0500 Subject: [PATCH 8/8] tweak changeset --- .changeset/chilly-moose-provide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/chilly-moose-provide.md b/.changeset/chilly-moose-provide.md index db4e0b7489d6..e1cb7889966f 100644 --- a/.changeset/chilly-moose-provide.md +++ b/.changeset/chilly-moose-provide.md @@ -3,4 +3,4 @@ 'create-svelte': patch --- -[feat] add functionality to override http methods (issue #1046) +Add methodOverride option for submitting PUT/PATCH/DELETE/etc with elements