diff --git a/.changeset/stupid-frogs-draw.md b/.changeset/stupid-frogs-draw.md new file mode 100644 index 0000000..d4bd223 --- /dev/null +++ b/.changeset/stupid-frogs-draw.md @@ -0,0 +1,45 @@ +--- +"@mcansh/http-helmet": patch +--- + +adds support for remix middleware + +```ts +import { + securityHeaders, + NONCE, + getCSPNonce, + setCSPNonce, +} from "@mcansh/http-helmet/remix-middleware"; +import { createRouter } from "remix/fetch-router"; +import { asyncContext } from "@remix-run/async-context-middleware"; + +let nonce = createNonce(); +setCSPNonce(nonce); + +let router = createRouter({ + middleware: [ + asyncContext(), + securityHeaders({ + "Content-Security-Policy": { + "default-src": ["'self'"], + "script-src": ["'self'", NONCE(nonce)], + }, + }), + ], +}); +``` + +// then in your controller you can get the nonce from the context and use it in your templates to add a nonce to your script tags + +```ts +export const home = { + handler() { + let nonce = getCSPNonce(); + + return render( + ``, + ); + }, +} satisfies BuildAction; +``` diff --git a/packages/http-helmet/README.md b/packages/http-helmet/README.md index bed3e51..33ad64a 100644 --- a/packages/http-helmet/README.md +++ b/packages/http-helmet/README.md @@ -11,15 +11,14 @@ npm i @mcansh/http-helmet ## Usage -basic example using [`@mjackson/node-fetch-server`](https://github.com/mjackson/remix-the-web/tree/main/packages/node-fetch-server) +basic example using [`@remix-run/node-fetch-server`](https://github.com/remix-run/remix/tree/main/packages/node-fetch-server) ```js import * as http from "node:http"; -import { createRequestListener } from "@mjackson/node-fetch-server"; +import { createRequestListener } from "@remix-run/node-fetch-server"; +import { html } from "@remix-run/html-template"; import { createNonce, createSecureHeaders } from "@mcansh/http-helmet"; -let html = String.raw; - let handler = (request) => { let nonce = createNonce(); let headers = createSecureHeaders({ diff --git a/packages/http-helmet/package.json b/packages/http-helmet/package.json index d78bc02..2766204 100644 --- a/packages/http-helmet/package.json +++ b/packages/http-helmet/package.json @@ -39,6 +39,10 @@ "import": "./dist/react.js", "require": "./dist/react.cjs" }, + "./remix-middleware": { + "import": "./dist/remix-middleware.js", + "require": "./dist/remix-middleware.cjs" + }, "./package.json": "./package.json" }, "files": [ @@ -51,10 +55,18 @@ "type-fest": "^4.41.0" }, "peerDependencies": { + "@remix-run/fetch-router": ">=0.18.0", + "@remix-run/async-context-middleware": ">=0.2.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "peerDependenciesMeta": { + "@remix-run/fetch-router": { + "optional": true + }, + "@remix-run/async-context-middleware": { + "optional": true + }, "react": { "optional": true }, @@ -64,6 +76,8 @@ }, "devDependencies": { "@arethetypeswrong/core": "catalog:", + "@remix-run/async-context-middleware": "^0.2.0", + "@remix-run/fetch-router": "^0.18.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", @@ -77,6 +91,7 @@ "publint": "catalog:", "react": "^19.2.0", "react-dom": "^19.2.0", + "remix": "3.0.0-alpha.4", "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" diff --git a/packages/http-helmet/src/remix.ts b/packages/http-helmet/src/remix.ts new file mode 100644 index 0000000..c16cddc --- /dev/null +++ b/packages/http-helmet/src/remix.ts @@ -0,0 +1,54 @@ +import { getContext } from "@remix-run/async-context-middleware"; +import type { Middleware } from "@remix-run/fetch-router"; +import { createContextKey } from "@remix-run/fetch-router"; +import type { CreateSecureHeaders } from "./index.ts"; +import { createSecureHeaders, mergeHeaders } from "./index.ts"; + +export { + createNonce, + HASH, + mergeHeaders, + NONCE, + NONE, + REPORT_SAMPLE, + SELF, + STRICT_DYNAMIC, + UNSAFE_EVAL, + UNSAFE_HASHES, + UNSAFE_INLINE, + WASM_UNSAFE_EVAL, +} from "./utils"; + +let NONCE_KEY = createContextKey(); + +export function getCSPNonce(): string { + return getContext().get(NONCE_KEY); +} + +export function setCSPNonce(nonce: string): void { + getContext().set(NONCE_KEY, nonce); +} + +type Context = Parameters[0]; + +export type SecurityHeadersOptions = CreateSecureHeaders & { + skip?: (context: Context) => boolean | Promise; +}; + +export function securityHeaders({ + skip, + ...options +}: SecurityHeadersOptions): Middleware { + return async (context, next) => { + let response = await next(); + if (skip?.(context)) return response; + + let secureHeaders = createSecureHeaders(options); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: mergeHeaders(response.headers, secureHeaders), + }); + }; +} diff --git a/packages/http-helmet/src/utils.ts b/packages/http-helmet/src/utils.ts index a82e4b7..7a06a3f 100644 --- a/packages/http-helmet/src/utils.ts +++ b/packages/http-helmet/src/utils.ts @@ -37,7 +37,9 @@ function isObject(value: unknown) { return value !== null && typeof value === "object"; } -export function mergeHeaders(...sources: HeadersInit[]): Headers { +export function mergeHeaders( + ...sources: [HeadersInit, ...Array] +): Headers { let result = new Headers(); for (let source of sources) { diff --git a/packages/http-helmet/tsdown.config.ts b/packages/http-helmet/tsdown.config.ts index 0479692..ff3971c 100755 --- a/packages/http-helmet/tsdown.config.ts +++ b/packages/http-helmet/tsdown.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ entry: { index: "./src/index.ts", react: "./src/react.tsx", + "remix-middleware": "./src/remix.ts", }, dts: true, format: ["cjs", "esm"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dc8356..d067260 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -279,6 +279,12 @@ importers: '@arethetypeswrong/core': specifier: 'catalog:' version: 0.18.2 + '@remix-run/async-context-middleware': + specifier: ^0.2.0 + version: 0.2.0 + '@remix-run/fetch-router': + specifier: ^0.18.0 + version: 0.18.0 '@testing-library/dom': specifier: ^10.4.0 version: 10.4.1 @@ -318,6 +324,9 @@ importers: react-dom: specifier: ^19.2.0 version: 19.2.0(react@19.2.0) + remix: + specifier: 3.0.0-alpha.4 + version: 3.0.0-alpha.4 tsdown: specifier: 'catalog:' version: 0.12.9(@arethetypeswrong/core@0.18.2)(publint@0.3.15)(typescript@5.9.3) @@ -1700,6 +1709,63 @@ packages: peerDependencies: react-router: 7.9.5 + '@remix-run/async-context-middleware@0.2.0': + resolution: {integrity: sha512-ebnPM2OFM/BO3GSoRKs2i5+lgY/fIunzpL8/RhuEuP9a04cQYXMbEzbdae+qBHTgtsVdPrqeGr/lwlF5FNnQZA==} + + '@remix-run/auth-middleware@0.1.0': + resolution: {integrity: sha512-QZlI4MyU8VTVI6WB4T4MWWqtbYrIUiiBG3mgdJUXjEYMBBoxVAkT/6rT3YErmhX+SVARP2zC46qzujrbYcQLCQ==} + + '@remix-run/auth@0.1.0': + resolution: {integrity: sha512-TFsK40jymT7iZyGWOjoqlFFmILdDXy3jYBoI1mohjdqrp8SVWbm7htiSbdd+j8DGBUEbxMTvKcaVG0bPA1M8VA==} + + '@remix-run/component@0.6.0': + resolution: {integrity: sha512-hmIUdPnBtONeOH8bbLuOVGSgFD/MAUNvpH0Xwgo27GByBIjQhOm25Tt9R04HQslDhHAzwNVnWeQkr3j9eyXYhQ==} + + '@remix-run/compression-middleware@0.1.4': + resolution: {integrity: sha512-vE+d4a04Tq9fO5KBmNtMD5GKJEt6U++B1yufEdBMNe5ROBQkpPWSuKlhkRcZyoV8SPEI2HbkKMtQ8QXfGJR11g==} + + '@remix-run/cookie@0.5.1': + resolution: {integrity: sha512-gbeZfVd1AKRlFj3IJWcIcR6zqVGz2XGJhR+mcqYiWnYt6KM8oUGtc82dsc4qZnWWA1f0nM4/He9wrU4GjB0pag==} + + '@remix-run/cop-middleware@0.1.0': + resolution: {integrity: sha512-5rMNrBswTJJLPsu1nUfXubgVf7XBTRfPNfd/4QQHaXRdhXVWVxU9HbNJrOjd7iNbBWuZeaJxSdDepbQLnuBkTg==} + + '@remix-run/cors-middleware@0.1.0': + resolution: {integrity: sha512-x5nyxDhWEOnznqetDhtj5WYoHrKpbiZ8K6BCGQ2+DFjLYHoyWCayciUNrqOVSIWc5XdV6KlKHcg1TyQvW/cS4w==} + + '@remix-run/csrf-middleware@0.1.0': + resolution: {integrity: sha512-EZAV9UZrx3qdYu3ov9NgFVJchw6kOLYI2uRB+e3WBVW1phruHKc42EpdJIZ6fU0J9Of0Lfo4X4VTM7O1/u1cWw==} + + '@remix-run/data-schema@0.2.0': + resolution: {integrity: sha512-bXgdWcWZS6TkbVO9Piwn035iw0LczfSTFrCW4Vd1k57x/4j3eWZwfHBr3lXdw5whefacs3m3pVZ8RUiu8NtBeA==} + + '@remix-run/data-table-mysql@0.2.0': + resolution: {integrity: sha512-voTcYUjG12PWlrWIYSqoa+C3WK9Q6TVKPrqnvGKY9s6EVBil9e+glaP2JOM8BbzL0hGuKPifdbrDGfGpGitdQw==} + peerDependencies: + mysql2: ^3.15.3 + peerDependenciesMeta: + mysql2: + optional: true + + '@remix-run/data-table-postgres@0.2.0': + resolution: {integrity: sha512-03v+zpWL4lMJr34PZ76PWFpjIa4K6Uq3NLBqyvN4ojPpm6J7TGsFwPy6sInRm3HjoS4gPSCEmmQ7dUovWa58EA==} + peerDependencies: + pg: ^8.16.3 + peerDependenciesMeta: + pg: + optional: true + + '@remix-run/data-table-sqlite@0.2.0': + resolution: {integrity: sha512-TAQ0u8YnmO3WdnuZULPhc+fgZbKZZPutXiPLf8c9NDhXSC41FlcvfQHTKUxBPVhqdi9mhGiblHHNp6BsVu11dg==} + peerDependencies: + better-sqlite3: ^12.4.1 + peerDependenciesMeta: + better-sqlite3: + optional: true + + '@remix-run/data-table@0.2.0': + resolution: {integrity: sha512-6QbdlKER0F0rX3XkTHC7ZPBgu/dyGvvgrKRbraBD9iGHTE7AED1emo3/YWsDJUeFzy7INNJaUIpNm9jNcgL8Kg==} + '@remix-run/dev@2.17.2': resolution: {integrity: sha512-gfc4Hu2Sysr+GyU/F12d2uvEfQwUwWvsOjBtiemhdN1xGOn1+FyYzlLl/aJ7AL9oYko8sDqOPyJCiApWJJGCCw==} engines: {node: '>=18.0.0'} @@ -1741,6 +1807,51 @@ packages: typescript: optional: true + '@remix-run/fetch-proxy@0.7.1': + resolution: {integrity: sha512-rPLfOpAaCXtm1dLI45uIPKERNbXbrh0P9AJc1sliz8pWd/McaFYjdr5KzB4QrFSfPvEt/Wmy6F2521qB1kK0ug==} + + '@remix-run/fetch-router@0.18.0': + resolution: {integrity: sha512-9Z4JgLH9/jD8jiVvAY9LZR+VoZxPJOQ7pENTBJoSo91PZOkPXfCxQWhPhAwYCP8z+/0FV4ZWSg1DmPPoU2f0UQ==} + + '@remix-run/file-storage-s3@0.1.0': + resolution: {integrity: sha512-r80An7nSFidK/0xn9O9/HxfUcgxVpM4kprnTGr6pGhKdgbaTCEtA+U5ETYGfeedFxhDcT+7ue+4Fv/VxeIvFwQ==} + + '@remix-run/file-storage@0.13.3': + resolution: {integrity: sha512-HBDz9RRsFRvI6EoeasklxH/NleGy0QZBXBcA4gQBW8ueucop21TQI4wvGlhZmXcnJ3nP4RkhdF2Gff2/HD5eiA==} + + '@remix-run/form-data-middleware@0.2.0': + resolution: {integrity: sha512-oaBcYvyP/U2GrDdFyQUh2pNCshvKwPFBoUup1Bz3NHnMzym4OkZJgaBi4o7HIJC33QudnqI7Mp7TW1pRg0CvIA==} + + '@remix-run/form-data-parser@0.16.0': + resolution: {integrity: sha512-k4QCgCyPURpqe+9Rual1GBJ8Ab6ri82Clfh5ooxq03jKQ1TyQZT5xh+XP0R05+Bqg5bzAZl7r2BpmDEvfoRTWw==} + + '@remix-run/fs@0.4.2': + resolution: {integrity: sha512-z3W2L+iUwgZ7i0S379SYQ8veOe2Weqs+JajmyTCqSVzbmMUniH3qQ6SAYr3FjbrKtLLWHN3SpK4XtFv57VzbLA==} + + '@remix-run/headers@0.19.0': + resolution: {integrity: sha512-+62NbkXuXm9r/NdG6KfH9OCKofCWm8VjkrVPICiHKtRl8Gf2Vi6eFTN4mGgBlZRhd5mmEVRV4hTIn/JUSHDAOw==} + + '@remix-run/html-template@0.3.0': + resolution: {integrity: sha512-aAMx68udtIk0fmCpCXHYscVeCDsRVEmEgh4XvtusPr3vkHu3jn4gx5oAxgsPXPdDmmD/d75SYyI0m/F+aLz5iQ==} + + '@remix-run/lazy-file@5.0.2': + resolution: {integrity: sha512-52Bo5dTV+EDwrUMS3mjeR+Sly85aHeN3fnNTeaflqzlCMWJwr2pX+y6/3mTDtRdxmTWF1MGQAoeayzfPb4zZJg==} + + '@remix-run/logger-middleware@0.1.4': + resolution: {integrity: sha512-gFDyYn8o5ddjQoEbrc8CK6PPq3lzrOX6BCqlvM+QVTMJ5/2aHgfMbQdXnWtbeiWJJrpODpABrSNfjLkHjaK4og==} + + '@remix-run/method-override-middleware@0.1.5': + resolution: {integrity: sha512-HYQG6YR4l0rYMJ0MXth9SWfeSCwnqHIrWgMQpZHKS/MT7roX7EAfknrl9vzjQWsVfkwl6RrSD30SrLPONj3tsg==} + + '@remix-run/mime@0.4.0': + resolution: {integrity: sha512-O6TcTL6CtuX82Q8BHqAere5O+0hYcrzSgY9whsDOBuqbW753Rczprs2jYw3qCDSo0kLxykW4ys3qgZcdgZ+chw==} + + '@remix-run/multipart-parser@0.15.0': + resolution: {integrity: sha512-/Ugo6k2bN7gh7Ybyhe7R/NrkD075fHrEfVf17P+NNC9rlWHBQCOSyJ4V8n4wtoG8umeImagU/AuHuUuTwzvHww==} + + '@remix-run/node-fetch-server@0.13.0': + resolution: {integrity: sha512-1EsNo0ZpgXu/90AWoRZf/oE3RVTUS80tiTUpt+hv5pjtAkw7icN4WskDwz/KdAw5ARbJLMhZBrO1NqThmy/McA==} + '@remix-run/node-fetch-server@0.9.0': resolution: {integrity: sha512-SoLMv7dbH+njWzXnOY6fI08dFMI5+/dQ+vY3n8RnnbdG7MdJEgiP28Xj/xWlnRnED/aB6SFw56Zop+LbmaaKqA==} @@ -1764,6 +1875,12 @@ packages: typescript: optional: true + '@remix-run/response@0.3.2': + resolution: {integrity: sha512-GkFqVq5E7Do6rMKTBjgoNyJlrsLrqYg+TDlCDrXoZ3v8O2RlSI14+bCF8lGQHy15DWX2pizVj6R0e6NmjcuLuA==} + + '@remix-run/route-pattern@0.20.0': + resolution: {integrity: sha512-TEdJ5eFn40St26oyaRYGI1FWeXDvlEzkbvollM8Xit0qIuDFyFG03Okvvfc1s8KgR9sYULDPjxJIzk6xvIRR9A==} + '@remix-run/router@1.23.0': resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} @@ -1782,6 +1899,29 @@ packages: typescript: optional: true + '@remix-run/session-middleware@0.2.0': + resolution: {integrity: sha512-kRAr0inELyXeOnUhlaM/eKIe9x1RZsOGbglb7Keb1+Lf6KxWxNMwv1ymmHBfae2ossjQTN/QUaABwQgX/uk3DQ==} + + '@remix-run/session-storage-memcache@0.1.0': + resolution: {integrity: sha512-k853rpHncdTJUwdk0hqd+gZ2OONZLNdOUJBKdJB+MehxrVv1TtacDnA+Xs3kh+IVwUrsTmBhED+GHSUocMATUg==} + + '@remix-run/session-storage-redis@0.1.0': + resolution: {integrity: sha512-MovUS1E98wDHP8zsESJGm3ySB7iiOhd+3usxyXXM2sbF9gIe6r1bdAXXirGIoC8AEq1v8IiFE5u5ipo7PX0UHQ==} + peerDependencies: + redis: ^5.10.0 + peerDependenciesMeta: + redis: + optional: true + + '@remix-run/session@0.4.1': + resolution: {integrity: sha512-Bm6aKYgutb/raHZ3laloz8g/Qu7f3CeK3o4gUVDMxtEiAdWCzJamwHoTpGOc5+g1Kuy7z85v4M6nGrF06MFDSg==} + + '@remix-run/static-middleware@0.4.5': + resolution: {integrity: sha512-jxEbrQMDWcUgmv/2NSv4pahvaW3I4jxqZUMDQ7/VfkcgeJKLFk15vgp4BJ5vLDKKMvwVcgM0Ccbw4Y3Ev2zemA==} + + '@remix-run/tar-parser@0.7.0': + resolution: {integrity: sha512-PW8JxEUzaGcnqxC5hBI8L9lK/Qz3oad6IGKZ+NExI3L7urVJUux+yCBrsme79DMBgS6hL+lgd/5LPFA5fSwF9A==} + '@remix-run/web-blob@3.1.0': resolution: {integrity: sha512-owGzFLbqPH9PlKb8KvpNJ0NO74HWE2euAn61eEiyCXX/oteoVzTVSN8mpLgDjaxBf2btj5/nUllSUgpyd6IH6g==} @@ -2022,6 +2162,9 @@ packages: '@speed-highlight/core@1.2.8': resolution: {integrity: sha512-IGytNtnUnPIobIbOq5Y6LIlqiHNX+vnToQIS7lj6L5819C+rA8TXRDkkG8vePsiBOGcoW9R6i+dp2YBUKdB09Q==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tailwindcss/node@4.1.17': resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} @@ -2163,6 +2306,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/dom-navigation@1.0.7': + resolution: {integrity: sha512-Di4W+i2faYquHUnyWUg3bBQp5pTNvjDDA7mIYfD/1WlLgan6sKkeVjGbdL78K0CuNEk5Pfc/c0rfelwkz10mnQ==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -2708,6 +2854,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + aws4fetch@1.0.20: + resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} + axe-core@4.11.0: resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} engines: {node: '>=4'} @@ -3721,11 +3870,12 @@ packages: glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -5281,6 +5431,9 @@ packages: remark-rehype@10.1.0: resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==} + remix@3.0.0-alpha.4: + resolution: {integrity: sha512-fvYHPm8QbrbL09wmmddrnknyntB+fl4bLmpECpK32cOoJW3pMoMvESBB1qRAQe+yWgTexVjp5G3BGOmmtYOX0A==} + require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} @@ -5684,6 +5837,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} @@ -7565,6 +7719,66 @@ snapshots: - supports-color - typescript + '@remix-run/async-context-middleware@0.2.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + + '@remix-run/auth-middleware@0.1.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/session': 0.4.1 + + '@remix-run/auth@0.1.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/session': 0.4.1 + + '@remix-run/component@0.6.0': + dependencies: + '@types/dom-navigation': 1.0.7 + + '@remix-run/compression-middleware@0.1.4': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/mime': 0.4.0 + '@remix-run/response': 0.3.2 + + '@remix-run/cookie@0.5.1': + dependencies: + '@remix-run/headers': 0.19.0 + + '@remix-run/cop-middleware@0.1.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + + '@remix-run/cors-middleware@0.1.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/headers': 0.19.0 + + '@remix-run/csrf-middleware@0.1.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/session': 0.4.1 + + '@remix-run/data-schema@0.2.0': + dependencies: + '@standard-schema/spec': 1.1.0 + + '@remix-run/data-table-mysql@0.2.0': + dependencies: + '@remix-run/data-table': 0.2.0 + + '@remix-run/data-table-postgres@0.2.0': + dependencies: + '@remix-run/data-table': 0.2.0 + + '@remix-run/data-table-sqlite@0.2.0': + dependencies: + '@remix-run/data-table': 0.2.0 + + '@remix-run/data-table@0.2.0': {} + '@remix-run/dev@2.17.2(@remix-run/react@2.17.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3))(@remix-run/serve@2.17.2(typescript@5.9.3))(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(typescript@5.9.3)(vite@6.3.6(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.1))(wrangler@4.45.0)(yaml@2.8.1)': dependencies: '@babel/core': 7.28.5 @@ -7766,6 +7980,62 @@ snapshots: optionalDependencies: typescript: 5.9.3 + '@remix-run/fetch-proxy@0.7.1': + dependencies: + '@remix-run/headers': 0.19.0 + + '@remix-run/fetch-router@0.18.0': + dependencies: + '@remix-run/route-pattern': 0.20.0 + + '@remix-run/file-storage-s3@0.1.0': + dependencies: + '@remix-run/file-storage': 0.13.3 + aws4fetch: 1.0.20 + + '@remix-run/file-storage@0.13.3': + dependencies: + '@remix-run/fs': 0.4.2 + '@remix-run/lazy-file': 5.0.2 + + '@remix-run/form-data-middleware@0.2.0': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/form-data-parser': 0.16.0 + + '@remix-run/form-data-parser@0.16.0': + dependencies: + '@remix-run/multipart-parser': 0.15.0 + + '@remix-run/fs@0.4.2': + dependencies: + '@remix-run/lazy-file': 5.0.2 + '@remix-run/mime': 0.4.0 + + '@remix-run/headers@0.19.0': {} + + '@remix-run/html-template@0.3.0': {} + + '@remix-run/lazy-file@5.0.2': + dependencies: + '@remix-run/mime': 0.4.0 + + '@remix-run/logger-middleware@0.1.4': + dependencies: + '@remix-run/fetch-router': 0.18.0 + + '@remix-run/method-override-middleware@0.1.5': + dependencies: + '@remix-run/fetch-router': 0.18.0 + + '@remix-run/mime@0.4.0': {} + + '@remix-run/multipart-parser@0.15.0': + dependencies: + '@remix-run/headers': 0.19.0 + + '@remix-run/node-fetch-server@0.13.0': {} + '@remix-run/node-fetch-server@0.9.0': {} '@remix-run/node@2.17.2(typescript@5.9.3)': @@ -7792,6 +8062,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 + '@remix-run/response@0.3.2': + dependencies: + '@remix-run/headers': 0.19.0 + '@remix-run/html-template': 0.3.0 + '@remix-run/mime': 0.4.0 + + '@remix-run/route-pattern@0.20.0': {} + '@remix-run/router@1.23.0': {} '@remix-run/serve@2.17.2(typescript@5.9.3)': @@ -7820,6 +8098,32 @@ snapshots: optionalDependencies: typescript: 5.9.3 + '@remix-run/session-middleware@0.2.0': + dependencies: + '@remix-run/cookie': 0.5.1 + '@remix-run/fetch-router': 0.18.0 + '@remix-run/session': 0.4.1 + + '@remix-run/session-storage-memcache@0.1.0': + dependencies: + '@remix-run/session': 0.4.1 + + '@remix-run/session-storage-redis@0.1.0': + dependencies: + '@remix-run/session': 0.4.1 + + '@remix-run/session@0.4.1': {} + + '@remix-run/static-middleware@0.4.5': + dependencies: + '@remix-run/fetch-router': 0.18.0 + '@remix-run/fs': 0.4.2 + '@remix-run/html-template': 0.3.0 + '@remix-run/mime': 0.4.0 + '@remix-run/response': 0.3.2 + + '@remix-run/tar-parser@0.7.0': {} + '@remix-run/web-blob@3.1.0': dependencies: '@remix-run/web-stream': 1.1.0 @@ -7988,6 +8292,8 @@ snapshots: '@speed-highlight/core@1.2.8': {} + '@standard-schema/spec@1.1.0': {} + '@tailwindcss/node@4.1.17': dependencies: '@jridgewell/remapping': 2.3.5 @@ -8122,6 +8428,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/dom-navigation@1.0.7': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -8749,6 +9057,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + aws4fetch@1.0.20: {} + axe-core@4.11.0: {} axobject-query@4.1.0: {} @@ -11813,6 +12123,51 @@ snapshots: mdast-util-to-hast: 12.3.0 unified: 10.1.2 + remix@3.0.0-alpha.4: + dependencies: + '@remix-run/async-context-middleware': 0.2.0 + '@remix-run/auth': 0.1.0 + '@remix-run/auth-middleware': 0.1.0 + '@remix-run/component': 0.6.0 + '@remix-run/compression-middleware': 0.1.4 + '@remix-run/cookie': 0.5.1 + '@remix-run/cop-middleware': 0.1.0 + '@remix-run/cors-middleware': 0.1.0 + '@remix-run/csrf-middleware': 0.1.0 + '@remix-run/data-schema': 0.2.0 + '@remix-run/data-table': 0.2.0 + '@remix-run/data-table-mysql': 0.2.0 + '@remix-run/data-table-postgres': 0.2.0 + '@remix-run/data-table-sqlite': 0.2.0 + '@remix-run/fetch-proxy': 0.7.1 + '@remix-run/fetch-router': 0.18.0 + '@remix-run/file-storage': 0.13.3 + '@remix-run/file-storage-s3': 0.1.0 + '@remix-run/form-data-middleware': 0.2.0 + '@remix-run/form-data-parser': 0.16.0 + '@remix-run/fs': 0.4.2 + '@remix-run/headers': 0.19.0 + '@remix-run/html-template': 0.3.0 + '@remix-run/lazy-file': 5.0.2 + '@remix-run/logger-middleware': 0.1.4 + '@remix-run/method-override-middleware': 0.1.5 + '@remix-run/mime': 0.4.0 + '@remix-run/multipart-parser': 0.15.0 + '@remix-run/node-fetch-server': 0.13.0 + '@remix-run/response': 0.3.2 + '@remix-run/route-pattern': 0.20.0 + '@remix-run/session': 0.4.1 + '@remix-run/session-middleware': 0.2.0 + '@remix-run/session-storage-memcache': 0.1.0 + '@remix-run/session-storage-redis': 0.1.0 + '@remix-run/static-middleware': 0.4.5 + '@remix-run/tar-parser': 0.7.0 + transitivePeerDependencies: + - better-sqlite3 + - mysql2 + - pg + - redis + require-like@0.1.2: {} requireindex@1.2.0: {}