@react-router/node
Advanced tools
| import { RequestListener } from 'node:http'; | ||
| import { ServerBuild, UNSAFE_MiddlewareEnabled, RouterContextProvider, AppLoadContext, SessionData, SessionIdStorageStrategy, SessionStorage } from 'react-router'; | ||
| import { ClientAddress } from '@mjackson/node-fetch-server'; | ||
| import { Readable, Writable } from 'node:stream'; | ||
| type MaybePromise<T> = T | Promise<T>; | ||
| interface RequestListenerOptions { | ||
| build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>); | ||
| getLoadContext?: (request: Request, client: ClientAddress) => UNSAFE_MiddlewareEnabled extends true ? MaybePromise<RouterContextProvider> : MaybePromise<AppLoadContext>; | ||
| mode?: string; | ||
| } | ||
| /** | ||
| * Creates a request listener that handles requests using Node's built-in HTTP server. | ||
| * | ||
| * @param options Options for creating a request listener. | ||
| * @returns A request listener that can be used with `http.createServer`. | ||
| */ | ||
| declare function createRequestListener(options: RequestListenerOptions): RequestListener; | ||
| interface FileSessionStorageOptions { | ||
| /** | ||
| * The Cookie used to store the session id on the client, or options used | ||
| * to automatically create one. | ||
| */ | ||
| cookie?: SessionIdStorageStrategy["cookie"]; | ||
| /** | ||
| * The directory to use to store session files. | ||
| */ | ||
| dir: string; | ||
| } | ||
| /** | ||
| * Creates a SessionStorage that stores session data on a filesystem. | ||
| * | ||
| * The advantage of using this instead of cookie session storage is that | ||
| * files may contain much more data than cookies. | ||
| * | ||
| * @see https://api.reactrouter.com/v7/functions/_react-router_node.createFileSessionStorage | ||
| */ | ||
| declare function createFileSessionStorage<Data = SessionData, FlashData = Data>({ cookie, dir, }: FileSessionStorageOptions): SessionStorage<Data, FlashData>; | ||
| declare function writeReadableStreamToWritable(stream: ReadableStream, writable: Writable): Promise<void>; | ||
| declare function writeAsyncIterableToWritable(iterable: AsyncIterable<Uint8Array>, writable: Writable): Promise<void>; | ||
| declare function readableStreamToString(stream: ReadableStream<Uint8Array>, encoding?: BufferEncoding): Promise<string>; | ||
| declare const createReadableStreamFromReadable: (source: Readable & { | ||
| readableHighWaterMark?: number; | ||
| }) => ReadableStream<Uint8Array>; | ||
| export { type RequestListenerOptions, createFileSessionStorage, createReadableStreamFromReadable, createRequestListener, readableStreamToString, writeAsyncIterableToWritable, writeReadableStreamToWritable }; |
+322
-8
@@ -1,3 +0,324 @@ | ||
| # `@remix-run/node` | ||
| # `@react-router/node` | ||
| ## 7.13.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.13.1` | ||
| ## 7.13.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.13.0` | ||
| ## 7.12.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.12.0` | ||
| ## 7.11.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.11.0` | ||
| ## 7.10.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.10.1` | ||
| ## 7.10.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.10.0` | ||
| ## 7.9.6 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.9.6` | ||
| ## 7.9.5 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.9.5` | ||
| ## 7.9.4 | ||
| ### Patch Changes | ||
| - Validate format of incoming session ids in `createFileSessionStorage` ([#14426](https://github.com/remix-run/react-router/pull/14426)) | ||
| - Updated dependencies: | ||
| - `react-router@7.9.4` | ||
| ## 7.9.3 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.9.3` | ||
| ## 7.9.2 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.9.2` | ||
| ## 7.9.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.9.1` | ||
| ## 7.9.0 | ||
| ### Minor Changes | ||
| - Stabilize middleware and context APIs. ([#14215](https://github.com/remix-run/react-router/pull/14215)) | ||
| We have removed the `unstable_` prefix from the following APIs and they are now considered stable and ready for production use: | ||
| - [`RouterContextProvider`](https://reactrouter.com/api/utils/RouterContextProvider) | ||
| - [`createContext`](https://reactrouter.com/api/utils/createContext) | ||
| - `createBrowserRouter` [`getContext`](https://reactrouter.com/api/data-routers/createBrowserRouter#optsgetcontext) option | ||
| - `<HydratedRouter>` [`getContext`](https://reactrouter.com/api/framework-routers/HydratedRouter#getcontext) prop | ||
| Please see the [Middleware Docs](https://reactrouter.com/how-to/middleware), the [Middleware RFC](https://github.com/remix-run/remix/discussions/7642), and the [Client-side Context RFC](https://github.com/remix-run/react-router/discussions/9856) for more information. | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.9.0` | ||
| ## 7.8.2 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.8.2` | ||
| ## 7.8.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.8.1` | ||
| ## 7.8.0 | ||
| ### Patch Changes | ||
| - \[UNSTABLE] Change `getLoadContext` signature (`type GetLoadContextFunction`) when `future.unstable_middleware` is enabled so that it returns an `unstable_RouterContextProvider` instance instead of a `Map` used to contruct the instance internally ([#14097](https://github.com/remix-run/react-router/pull/14097)) | ||
| - This also removes the `type unstable_InitialContext` export | ||
| - ⚠️ This is a breaking change if you have adopted middleware and are using a custom server with a `getLoadContext` function | ||
| - Updated dependencies: | ||
| - `react-router@7.8.0` | ||
| ## 7.7.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.7.1` | ||
| ## 7.7.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.7.0` | ||
| ## 7.6.3 | ||
| ### Patch Changes | ||
| - Remove old "install" package exports ([#13762](https://github.com/remix-run/react-router/pull/13762)) | ||
| - Updated dependencies: | ||
| - `react-router@7.6.3` | ||
| ## 7.6.2 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.6.2` | ||
| ## 7.6.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.6.1` | ||
| ## 7.6.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.6.0` | ||
| ## 7.5.3 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.5.3` | ||
| ## 7.5.2 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.5.2` | ||
| ## 7.5.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.5.1` | ||
| ## 7.5.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.5.0` | ||
| ## 7.4.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.4.1` | ||
| ## 7.4.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.4.0` | ||
| ## 7.3.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.3.0` | ||
| ## 7.2.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.2.0` | ||
| ## 7.1.5 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.1.5` | ||
| ## 7.1.4 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.1.4` | ||
| ## 7.1.3 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.1.3` | ||
| ## 7.1.2 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.1.2` | ||
| ## 7.1.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.1.1` | ||
| ## 7.1.0 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.1.0` | ||
| ## 7.0.2 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.0.2` | ||
| ## 7.0.1 | ||
| ### Patch Changes | ||
| - Updated dependencies: | ||
| - `react-router@7.0.1` | ||
| ## 7.0.0 | ||
| ### Major Changes | ||
| - Remove single fetch future flag. ([#11522](https://github.com/remix-run/react-router/pull/11522)) | ||
| - For Remix consumers migrating to React Router, the `crypto` global from the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) is now required when using cookie and session APIs. This means that the following APIs are provided from `react-router` rather than platform-specific packages: ([#11837](https://github.com/remix-run/react-router/pull/11837)) | ||
| - `createCookie` | ||
| - `createCookieSessionStorage` | ||
| - `createMemorySessionStorage` | ||
| - `createSessionStorage` | ||
| For consumers running older versions of Node, the `installGlobals` function from `@remix-run/node` has been updated to define `globalThis.crypto`, using [Node's `require('node:crypto').webcrypto` implementation.](https://nodejs.org/api/webcrypto.html) | ||
| Since platform-specific packages no longer need to implement this API, the following low-level APIs have been removed: | ||
| - `createCookieFactory` | ||
| - `createSessionStorageFactory` | ||
| - `createCookieSessionStorageFactory` | ||
| - `createMemorySessionStorageFactory` | ||
| - update minimum node version to 18 ([#11690](https://github.com/remix-run/react-router/pull/11690)) | ||
| - Add `exports` field to all packages ([#11675](https://github.com/remix-run/react-router/pull/11675)) | ||
| - node package no longer re-exports from react-router ([#11702](https://github.com/remix-run/react-router/pull/11702)) | ||
| - Drop support for Node 18, update minimum Node vestion to 20 ([#12171](https://github.com/remix-run/react-router/pull/12171)) | ||
| - Remove `installGlobals()` as this should no longer be necessary | ||
| ### Patch Changes | ||
| - Add createRequestListener to @react-router/node ([#12319](https://github.com/remix-run/react-router/pull/12319)) | ||
| - Remove unstable upload handler. ([#12015](https://github.com/remix-run/react-router/pull/12015)) | ||
| - Remove unneeded dependency on @web3-storage/multipart-parser ([#12274](https://github.com/remix-run/react-router/pull/12274)) | ||
| - Updated dependencies: | ||
| - `react-router@7.0.0` | ||
| ## 2.9.0 | ||
@@ -139,3 +460,2 @@ | ||
| - Removed/adjusted types to prefer `unknown` over `any` and to align with underlying React Router types ([#7319](https://github.com/remix-run/remix/pull/7319), [#7354](https://github.com/remix-run/remix/pull/7354)): | ||
| - Renamed the `useMatches()` return type from `RouteMatch` to `UIMatch` | ||
@@ -152,7 +472,5 @@ - Renamed `LoaderArgs`/`ActionArgs` to `LoaderFunctionArgs`/`ActionFunctionArgs` | ||
| - The route `meta` API now defaults to the new "V2 Meta" API ([#6958](https://github.com/remix-run/remix/pull/6958)) | ||
| - Please refer to the ([docs](https://remix.run/docs/en/2.0.0/route/meta) and [Preparing for V2](https://remix.run/docs/en/2.0.0/start/v2#route-meta) guide for more information. | ||
| - For preparation of using Node's built in fetch implementation, installing the fetch globals is now a responsibility of the app server ([#7009](https://github.com/remix-run/remix/pull/7009)) | ||
| - If you are using `remix-serve`, nothing is required | ||
@@ -168,3 +486,2 @@ - If you are using your own app server, you will need to install the globals yourself | ||
| - `source-map-support` is now a responsibility of the app server ([#7009](https://github.com/remix-run/remix/pull/7009)) | ||
| - If you are using `remix-serve`, nothing is required | ||
@@ -303,3 +620,2 @@ - If you are using your own app server, you will need to install [`source-map-support`](https://www.npmjs.com/package/source-map-support) yourself. | ||
| - We have made a few changes to the API for route module `meta` functions when using the `future.v2_meta` flag. **These changes are _only_ breaking for users who have opted in.** ([#5746](https://github.com/remix-run/remix/pull/5746)) | ||
| - `V2_HtmlMetaDescriptor` has been renamed to `V2_MetaDescriptor` | ||
@@ -397,3 +713,2 @@ - The `meta` function's arguments have been simplified | ||
| Informational Resources: | ||
| - <https://gist.github.com/jacob-ebey/9bde9546c1aafaa6bc8c242054b1be26> | ||
@@ -403,3 +718,2 @@ - <https://github.com/remix-run/remix/blob/main/decisions/0004-streaming-apis.md> | ||
| Documentation Resources (better docs specific to Remix are in the works): | ||
| - <https://reactrouter.com/en/main/utils/defer> | ||
@@ -406,0 +720,0 @@ - <https://reactrouter.com/en/main/components/await> |
+48
-5
@@ -1,5 +0,48 @@ | ||
| export { installGlobals } from "./globals"; | ||
| export { createFileSessionStorage } from "./sessions/fileStorage"; | ||
| export { createFileUploadHandler as unstable_createFileUploadHandler, NodeOnDiskFile, } from "./upload/fileUploadHandler"; | ||
| export { createCookie, createCookieSessionStorage, createMemorySessionStorage, createSessionStorage, } from "./implementations"; | ||
| export { createReadableStreamFromReadable, readableStreamToString, writeAsyncIterableToWritable, writeReadableStreamToWritable, } from "./stream"; | ||
| import { RequestListener } from 'node:http'; | ||
| import { ServerBuild, UNSAFE_MiddlewareEnabled, RouterContextProvider, AppLoadContext, SessionData, SessionIdStorageStrategy, SessionStorage } from 'react-router'; | ||
| import { ClientAddress } from '@mjackson/node-fetch-server'; | ||
| import { Readable, Writable } from 'node:stream'; | ||
| type MaybePromise<T> = T | Promise<T>; | ||
| interface RequestListenerOptions { | ||
| build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>); | ||
| getLoadContext?: (request: Request, client: ClientAddress) => UNSAFE_MiddlewareEnabled extends true ? MaybePromise<RouterContextProvider> : MaybePromise<AppLoadContext>; | ||
| mode?: string; | ||
| } | ||
| /** | ||
| * Creates a request listener that handles requests using Node's built-in HTTP server. | ||
| * | ||
| * @param options Options for creating a request listener. | ||
| * @returns A request listener that can be used with `http.createServer`. | ||
| */ | ||
| declare function createRequestListener(options: RequestListenerOptions): RequestListener; | ||
| interface FileSessionStorageOptions { | ||
| /** | ||
| * The Cookie used to store the session id on the client, or options used | ||
| * to automatically create one. | ||
| */ | ||
| cookie?: SessionIdStorageStrategy["cookie"]; | ||
| /** | ||
| * The directory to use to store session files. | ||
| */ | ||
| dir: string; | ||
| } | ||
| /** | ||
| * Creates a SessionStorage that stores session data on a filesystem. | ||
| * | ||
| * The advantage of using this instead of cookie session storage is that | ||
| * files may contain much more data than cookies. | ||
| * | ||
| * @see https://api.reactrouter.com/v7/functions/_react-router_node.createFileSessionStorage | ||
| */ | ||
| declare function createFileSessionStorage<Data = SessionData, FlashData = Data>({ cookie, dir, }: FileSessionStorageOptions): SessionStorage<Data, FlashData>; | ||
| declare function writeReadableStreamToWritable(stream: ReadableStream, writable: Writable): Promise<void>; | ||
| declare function writeAsyncIterableToWritable(iterable: AsyncIterable<Uint8Array>, writable: Writable): Promise<void>; | ||
| declare function readableStreamToString(stream: ReadableStream<Uint8Array>, encoding?: BufferEncoding): Promise<string>; | ||
| declare const createReadableStreamFromReadable: (source: Readable & { | ||
| readableHighWaterMark?: number; | ||
| }) => ReadableStream<Uint8Array>; | ||
| export { type RequestListenerOptions, createFileSessionStorage, createReadableStreamFromReadable, createRequestListener, readableStreamToString, writeAsyncIterableToWritable, writeReadableStreamToWritable }; |
+266
-21
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * @react-router/node v0.0.0-nightly-18bac808f-20260323 | ||
| * | ||
@@ -11,25 +11,270 @@ * Copyright (c) Remix Software Inc. | ||
| */ | ||
| 'use strict'; | ||
| "use strict"; | ||
| var __create = Object.create; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __getProtoOf = Object.getPrototypeOf; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| // If the importer is in node compatibility mode or this is not an ESM | ||
| // file that has been converted to a CommonJS file using a Babel- | ||
| // compatible transform (i.e. "__esModule" has not been set), then set | ||
| // "default" to the CommonJS "module.exports" for node compatibility. | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| // index.ts | ||
| var index_exports = {}; | ||
| __export(index_exports, { | ||
| createFileSessionStorage: () => createFileSessionStorage, | ||
| createReadableStreamFromReadable: () => createReadableStreamFromReadable, | ||
| createRequestListener: () => createRequestListener, | ||
| readableStreamToString: () => readableStreamToString, | ||
| writeAsyncIterableToWritable: () => writeAsyncIterableToWritable, | ||
| writeReadableStreamToWritable: () => writeReadableStreamToWritable | ||
| }); | ||
| module.exports = __toCommonJS(index_exports); | ||
| var globals = require('./globals.js'); | ||
| var fileStorage = require('./sessions/fileStorage.js'); | ||
| var fileUploadHandler = require('./upload/fileUploadHandler.js'); | ||
| var implementations = require('./implementations.js'); | ||
| var stream = require('./stream.js'); | ||
| // server.ts | ||
| var import_react_router = require("react-router"); | ||
| var import_node_fetch_server = require("@mjackson/node-fetch-server"); | ||
| function createRequestListener(options) { | ||
| let handleRequest = (0, import_react_router.createRequestHandler)(options.build, options.mode); | ||
| return (0, import_node_fetch_server.createRequestListener)(async (request, client) => { | ||
| let loadContext = await options.getLoadContext?.(request, client); | ||
| return handleRequest(request, loadContext); | ||
| }); | ||
| } | ||
| // sessions/fileStorage.ts | ||
| var import_node_fs = require("fs"); | ||
| var path = __toESM(require("path")); | ||
| var import_react_router2 = require("react-router"); | ||
| function createFileSessionStorage({ | ||
| cookie, | ||
| dir | ||
| }) { | ||
| return (0, import_react_router2.createSessionStorage)({ | ||
| cookie, | ||
| async createData(data, expires) { | ||
| let content = JSON.stringify({ data, expires }); | ||
| while (true) { | ||
| let randomBytes = crypto.getRandomValues(new Uint8Array(8)); | ||
| let id = Buffer.from(randomBytes).toString("hex"); | ||
| try { | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| throw new Error("Error generating session"); | ||
| } | ||
| await import_node_fs.promises.mkdir(path.dirname(file), { recursive: true }); | ||
| await import_node_fs.promises.writeFile(file, content, { encoding: "utf-8", flag: "wx" }); | ||
| return id; | ||
| } catch (error) { | ||
| if (error.code !== "EEXIST") throw error; | ||
| } | ||
| } | ||
| }, | ||
| async readData(id) { | ||
| try { | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| return null; | ||
| } | ||
| let content = JSON.parse(await import_node_fs.promises.readFile(file, "utf-8")); | ||
| let data = content.data; | ||
| let expires = typeof content.expires === "string" ? new Date(content.expires) : null; | ||
| if (!expires || expires > /* @__PURE__ */ new Date()) { | ||
| return data; | ||
| } | ||
| if (expires) await import_node_fs.promises.unlink(file); | ||
| return null; | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| return null; | ||
| } | ||
| }, | ||
| async updateData(id, data, expires) { | ||
| let content = JSON.stringify({ data, expires }); | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| return; | ||
| } | ||
| await import_node_fs.promises.mkdir(path.dirname(file), { recursive: true }); | ||
| await import_node_fs.promises.writeFile(file, content, "utf-8"); | ||
| }, | ||
| async deleteData(id) { | ||
| if (!id) { | ||
| return; | ||
| } | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| return; | ||
| } | ||
| try { | ||
| await import_node_fs.promises.unlink(file); | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function getFile(dir, id) { | ||
| if (!/^[0-9a-f]{16}$/i.test(id)) { | ||
| return null; | ||
| } | ||
| return path.join(dir, id.slice(0, 4), id.slice(4)); | ||
| } | ||
| exports.installGlobals = globals.installGlobals; | ||
| exports.createFileSessionStorage = fileStorage.createFileSessionStorage; | ||
| exports.NodeOnDiskFile = fileUploadHandler.NodeOnDiskFile; | ||
| exports.unstable_createFileUploadHandler = fileUploadHandler.createFileUploadHandler; | ||
| exports.createCookie = implementations.createCookie; | ||
| exports.createCookieSessionStorage = implementations.createCookieSessionStorage; | ||
| exports.createMemorySessionStorage = implementations.createMemorySessionStorage; | ||
| exports.createSessionStorage = implementations.createSessionStorage; | ||
| exports.createReadableStreamFromReadable = stream.createReadableStreamFromReadable; | ||
| exports.readableStreamToString = stream.readableStreamToString; | ||
| exports.writeAsyncIterableToWritable = stream.writeAsyncIterableToWritable; | ||
| exports.writeReadableStreamToWritable = stream.writeReadableStreamToWritable; | ||
| // stream.ts | ||
| var import_node_stream = require("stream"); | ||
| async function writeReadableStreamToWritable(stream, writable) { | ||
| let reader = stream.getReader(); | ||
| let flushable = writable; | ||
| try { | ||
| while (true) { | ||
| let { done, value } = await reader.read(); | ||
| if (done) { | ||
| writable.end(); | ||
| break; | ||
| } | ||
| writable.write(value); | ||
| if (typeof flushable.flush === "function") { | ||
| flushable.flush(); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function writeAsyncIterableToWritable(iterable, writable) { | ||
| try { | ||
| for await (let chunk of iterable) { | ||
| writable.write(chunk); | ||
| } | ||
| writable.end(); | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function readableStreamToString(stream, encoding) { | ||
| let reader = stream.getReader(); | ||
| let chunks = []; | ||
| while (true) { | ||
| let { done, value } = await reader.read(); | ||
| if (done) { | ||
| break; | ||
| } | ||
| if (value) { | ||
| chunks.push(value); | ||
| } | ||
| } | ||
| return Buffer.concat(chunks).toString(encoding); | ||
| } | ||
| var createReadableStreamFromReadable = (source) => { | ||
| let pump = new StreamPump(source); | ||
| let stream = new ReadableStream(pump, pump); | ||
| return stream; | ||
| }; | ||
| var StreamPump = class { | ||
| highWaterMark; | ||
| accumulatedSize; | ||
| stream; | ||
| controller; | ||
| constructor(stream) { | ||
| this.highWaterMark = stream.readableHighWaterMark || new import_node_stream.Stream.Readable().readableHighWaterMark; | ||
| this.accumulatedSize = 0; | ||
| this.stream = stream; | ||
| this.enqueue = this.enqueue.bind(this); | ||
| this.error = this.error.bind(this); | ||
| this.close = this.close.bind(this); | ||
| } | ||
| size(chunk) { | ||
| return chunk?.byteLength || 0; | ||
| } | ||
| start(controller) { | ||
| this.controller = controller; | ||
| this.stream.on("data", this.enqueue); | ||
| this.stream.once("error", this.error); | ||
| this.stream.once("end", this.close); | ||
| this.stream.once("close", this.close); | ||
| } | ||
| pull() { | ||
| this.resume(); | ||
| } | ||
| cancel(reason) { | ||
| if (this.stream.destroy) { | ||
| this.stream.destroy(reason); | ||
| } | ||
| this.stream.off("data", this.enqueue); | ||
| this.stream.off("error", this.error); | ||
| this.stream.off("end", this.close); | ||
| this.stream.off("close", this.close); | ||
| } | ||
| enqueue(chunk) { | ||
| if (this.controller) { | ||
| try { | ||
| let bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk); | ||
| let available = (this.controller.desiredSize || 0) - bytes.byteLength; | ||
| this.controller.enqueue(bytes); | ||
| if (available <= 0) { | ||
| this.pause(); | ||
| } | ||
| } catch (error) { | ||
| this.controller.error( | ||
| new Error( | ||
| "Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object" | ||
| ) | ||
| ); | ||
| this.cancel(); | ||
| } | ||
| } | ||
| } | ||
| pause() { | ||
| if (this.stream.pause) { | ||
| this.stream.pause(); | ||
| } | ||
| } | ||
| resume() { | ||
| if (this.stream.readable && this.stream.resume) { | ||
| this.stream.resume(); | ||
| } | ||
| } | ||
| close() { | ||
| if (this.controller) { | ||
| this.controller.close(); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| error(error) { | ||
| if (this.controller) { | ||
| this.controller.error(error); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| }; | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| createFileSessionStorage, | ||
| createReadableStreamFromReadable, | ||
| createRequestListener, | ||
| readableStreamToString, | ||
| writeAsyncIterableToWritable, | ||
| writeReadableStreamToWritable | ||
| }); |
+229
-6
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * @react-router/node v0.0.0-nightly-18bac808f-20260323 | ||
| * | ||
@@ -11,6 +11,229 @@ * Copyright (c) Remix Software Inc. | ||
| */ | ||
| export { installGlobals } from './globals.mjs'; | ||
| export { createFileSessionStorage } from './sessions/fileStorage.mjs'; | ||
| export { NodeOnDiskFile, createFileUploadHandler as unstable_createFileUploadHandler } from './upload/fileUploadHandler.mjs'; | ||
| export { createCookie, createCookieSessionStorage, createMemorySessionStorage, createSessionStorage } from './implementations.mjs'; | ||
| export { createReadableStreamFromReadable, readableStreamToString, writeAsyncIterableToWritable, writeReadableStreamToWritable } from './stream.mjs'; | ||
| // server.ts | ||
| import { createRequestHandler } from "react-router"; | ||
| import { createRequestListener as createRequestListener_ } from "@mjackson/node-fetch-server"; | ||
| function createRequestListener(options) { | ||
| let handleRequest = createRequestHandler(options.build, options.mode); | ||
| return createRequestListener_(async (request, client) => { | ||
| let loadContext = await options.getLoadContext?.(request, client); | ||
| return handleRequest(request, loadContext); | ||
| }); | ||
| } | ||
| // sessions/fileStorage.ts | ||
| import { promises as fsp } from "fs"; | ||
| import * as path from "path"; | ||
| import { createSessionStorage } from "react-router"; | ||
| function createFileSessionStorage({ | ||
| cookie, | ||
| dir | ||
| }) { | ||
| return createSessionStorage({ | ||
| cookie, | ||
| async createData(data, expires) { | ||
| let content = JSON.stringify({ data, expires }); | ||
| while (true) { | ||
| let randomBytes = crypto.getRandomValues(new Uint8Array(8)); | ||
| let id = Buffer.from(randomBytes).toString("hex"); | ||
| try { | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| throw new Error("Error generating session"); | ||
| } | ||
| await fsp.mkdir(path.dirname(file), { recursive: true }); | ||
| await fsp.writeFile(file, content, { encoding: "utf-8", flag: "wx" }); | ||
| return id; | ||
| } catch (error) { | ||
| if (error.code !== "EEXIST") throw error; | ||
| } | ||
| } | ||
| }, | ||
| async readData(id) { | ||
| try { | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| return null; | ||
| } | ||
| let content = JSON.parse(await fsp.readFile(file, "utf-8")); | ||
| let data = content.data; | ||
| let expires = typeof content.expires === "string" ? new Date(content.expires) : null; | ||
| if (!expires || expires > /* @__PURE__ */ new Date()) { | ||
| return data; | ||
| } | ||
| if (expires) await fsp.unlink(file); | ||
| return null; | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| return null; | ||
| } | ||
| }, | ||
| async updateData(id, data, expires) { | ||
| let content = JSON.stringify({ data, expires }); | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| return; | ||
| } | ||
| await fsp.mkdir(path.dirname(file), { recursive: true }); | ||
| await fsp.writeFile(file, content, "utf-8"); | ||
| }, | ||
| async deleteData(id) { | ||
| if (!id) { | ||
| return; | ||
| } | ||
| let file = getFile(dir, id); | ||
| if (!file) { | ||
| return; | ||
| } | ||
| try { | ||
| await fsp.unlink(file); | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function getFile(dir, id) { | ||
| if (!/^[0-9a-f]{16}$/i.test(id)) { | ||
| return null; | ||
| } | ||
| return path.join(dir, id.slice(0, 4), id.slice(4)); | ||
| } | ||
| // stream.ts | ||
| import { Stream } from "stream"; | ||
| async function writeReadableStreamToWritable(stream, writable) { | ||
| let reader = stream.getReader(); | ||
| let flushable = writable; | ||
| try { | ||
| while (true) { | ||
| let { done, value } = await reader.read(); | ||
| if (done) { | ||
| writable.end(); | ||
| break; | ||
| } | ||
| writable.write(value); | ||
| if (typeof flushable.flush === "function") { | ||
| flushable.flush(); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function writeAsyncIterableToWritable(iterable, writable) { | ||
| try { | ||
| for await (let chunk of iterable) { | ||
| writable.write(chunk); | ||
| } | ||
| writable.end(); | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function readableStreamToString(stream, encoding) { | ||
| let reader = stream.getReader(); | ||
| let chunks = []; | ||
| while (true) { | ||
| let { done, value } = await reader.read(); | ||
| if (done) { | ||
| break; | ||
| } | ||
| if (value) { | ||
| chunks.push(value); | ||
| } | ||
| } | ||
| return Buffer.concat(chunks).toString(encoding); | ||
| } | ||
| var createReadableStreamFromReadable = (source) => { | ||
| let pump = new StreamPump(source); | ||
| let stream = new ReadableStream(pump, pump); | ||
| return stream; | ||
| }; | ||
| var StreamPump = class { | ||
| highWaterMark; | ||
| accumulatedSize; | ||
| stream; | ||
| controller; | ||
| constructor(stream) { | ||
| this.highWaterMark = stream.readableHighWaterMark || new Stream.Readable().readableHighWaterMark; | ||
| this.accumulatedSize = 0; | ||
| this.stream = stream; | ||
| this.enqueue = this.enqueue.bind(this); | ||
| this.error = this.error.bind(this); | ||
| this.close = this.close.bind(this); | ||
| } | ||
| size(chunk) { | ||
| return chunk?.byteLength || 0; | ||
| } | ||
| start(controller) { | ||
| this.controller = controller; | ||
| this.stream.on("data", this.enqueue); | ||
| this.stream.once("error", this.error); | ||
| this.stream.once("end", this.close); | ||
| this.stream.once("close", this.close); | ||
| } | ||
| pull() { | ||
| this.resume(); | ||
| } | ||
| cancel(reason) { | ||
| if (this.stream.destroy) { | ||
| this.stream.destroy(reason); | ||
| } | ||
| this.stream.off("data", this.enqueue); | ||
| this.stream.off("error", this.error); | ||
| this.stream.off("end", this.close); | ||
| this.stream.off("close", this.close); | ||
| } | ||
| enqueue(chunk) { | ||
| if (this.controller) { | ||
| try { | ||
| let bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk); | ||
| let available = (this.controller.desiredSize || 0) - bytes.byteLength; | ||
| this.controller.enqueue(bytes); | ||
| if (available <= 0) { | ||
| this.pause(); | ||
| } | ||
| } catch (error) { | ||
| this.controller.error( | ||
| new Error( | ||
| "Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object" | ||
| ) | ||
| ); | ||
| this.cancel(); | ||
| } | ||
| } | ||
| } | ||
| pause() { | ||
| if (this.stream.pause) { | ||
| this.stream.pause(); | ||
| } | ||
| } | ||
| resume() { | ||
| if (this.stream.readable && this.stream.resume) { | ||
| this.stream.resume(); | ||
| } | ||
| } | ||
| close() { | ||
| if (this.controller) { | ||
| this.controller.close(); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| error(error) { | ||
| if (this.controller) { | ||
| this.controller.error(error); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| }; | ||
| export { | ||
| createFileSessionStorage, | ||
| createReadableStreamFromReadable, | ||
| createRequestListener, | ||
| readableStreamToString, | ||
| writeAsyncIterableToWritable, | ||
| writeReadableStreamToWritable | ||
| }; |
+39
-29
| { | ||
| "name": "@react-router/node", | ||
| "version": "0.0.0-nightly-183fdb88c-20240730", | ||
| "version": "0.0.0-nightly-18bac808f-20260323", | ||
| "description": "Node.js platform abstractions for React Router", | ||
@@ -14,37 +14,49 @@ "bugs": { | ||
| "license": "MIT", | ||
| "types": "dist/index.d.ts", | ||
| "main": "dist/index.js", | ||
| "typings": "dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.mjs", | ||
| "require": "./dist/index.js" | ||
| "node": { | ||
| "types": "./dist/index.d.ts", | ||
| "module-sync": "./dist/index.mjs", | ||
| "default": "./dist/index.js" | ||
| }, | ||
| "import": { | ||
| "types": "./dist/index.d.mts", | ||
| "default": "./dist/index.mjs" | ||
| }, | ||
| "default": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| } | ||
| }, | ||
| "./install": { | ||
| "types": "./dist/install.d.ts", | ||
| "import": "./dist/install.mjs", | ||
| "require": "./dist/install.js" | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "sideEffects": [ | ||
| "./dist/install.js", | ||
| "./dist/install.mjs" | ||
| ], | ||
| "wireit": { | ||
| "build": { | ||
| "command": "tsup", | ||
| "files": [ | ||
| "../../pnpm-workspace.yaml", | ||
| "sessions/**", | ||
| "*.ts", | ||
| "tsconfig.json", | ||
| "package.json" | ||
| ], | ||
| "output": [ | ||
| "dist/**" | ||
| ] | ||
| } | ||
| }, | ||
| "dependencies": { | ||
| "@web3-storage/multipart-parser": "^1.0.0", | ||
| "cookie-signature": "^1.1.0", | ||
| "source-map-support": "^0.5.21", | ||
| "stream-slice": "^0.1.2", | ||
| "undici": "^6.19.2" | ||
| "@mjackson/node-fetch-server": "^0.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/cookie-signature": "^1.0.3", | ||
| "@types/source-map-support": "^0.5.4", | ||
| "typescript": "^5.1.6", | ||
| "react-router": "0.0.0-nightly-183fdb88c-20240730" | ||
| "tsup": "^8.3.0", | ||
| "typescript": "^5.4.5", | ||
| "wireit": "0.14.9", | ||
| "react-router": "0.0.0-nightly-18bac808f-20260323" | ||
| }, | ||
| "peerDependencies": { | ||
| "typescript": "^5.1.0", | ||
| "react-router": "0.0.0-nightly-183fdb88c-20240730" | ||
| "react-router": "0.0.0-nightly-18bac808f-20260323" | ||
| }, | ||
@@ -57,9 +69,6 @@ "peerDependenciesMeta": { | ||
| "engines": { | ||
| "node": ">=18.0.0" | ||
| "node": ">=20.0.0" | ||
| }, | ||
| "files": [ | ||
| "dist/", | ||
| "globals.d.ts", | ||
| "install.d.ts", | ||
| "install.js", | ||
| "CHANGELOG.md", | ||
@@ -70,4 +79,5 @@ "LICENSE.md", | ||
| "scripts": { | ||
| "tsc": "tsc" | ||
| "build": "wireit", | ||
| "typecheck": "tsc" | ||
| } | ||
| } |
+2
-10
@@ -1,13 +0,5 @@ | ||
| # Welcome to Remix! | ||
| Node.js platform abstractions for React Router | ||
| [Remix](https://remix.run) is a web framework that helps you build better websites with React. | ||
| To get started, open a new shell and run: | ||
| ```sh | ||
| npx create-remix@latest | ||
| npm install @react-router/node | ||
| ``` | ||
| Then follow the prompts you see in your terminal. | ||
| For more information about Remix, [visit remix.run](https://remix.run)! |
| import type { SignFunction, UnsignFunction } from "react-router"; | ||
| export declare const sign: SignFunction; | ||
| export declare const unsign: UnsignFunction; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var cookieSignature = require('cookie-signature'); | ||
| function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
| var cookieSignature__default = /*#__PURE__*/_interopDefaultLegacy(cookieSignature); | ||
| const sign = async (value, secret) => { | ||
| return cookieSignature__default["default"].sign(value, secret); | ||
| }; | ||
| const unsign = async (signed, secret) => { | ||
| return cookieSignature__default["default"].unsign(signed, secret); | ||
| }; | ||
| exports.sign = sign; | ||
| exports.unsign = unsign; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import cookieSignature from 'cookie-signature'; | ||
| const sign = async (value, secret) => { | ||
| return cookieSignature.sign(value, secret); | ||
| }; | ||
| const unsign = async (signed, secret) => { | ||
| return cookieSignature.unsign(signed, secret); | ||
| }; | ||
| export { sign, unsign }; |
| declare global { | ||
| namespace NodeJS { | ||
| interface ProcessEnv { | ||
| NODE_ENV: "development" | "production" | "test"; | ||
| } | ||
| interface Global { | ||
| File: typeof File; | ||
| Headers: typeof Headers; | ||
| Request: typeof Request; | ||
| Response: typeof Response; | ||
| fetch: typeof fetch; | ||
| FormData: typeof FormData; | ||
| ReadableStream: typeof ReadableStream; | ||
| WritableStream: typeof WritableStream; | ||
| } | ||
| } | ||
| interface RequestInit { | ||
| duplex?: "half"; | ||
| } | ||
| } | ||
| export declare function installGlobals(): void; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var undici = require('undici'); | ||
| function installGlobals() { | ||
| global.File = undici.File; | ||
| // @ts-ignore - this shows as an error in VSCode but is not an error via TSC so we can't use `ts-expect-error` | ||
| global.Headers = undici.Headers; | ||
| // @ts-expect-error - overriding globals | ||
| global.Request = undici.Request; | ||
| // @ts-expect-error - overriding globals | ||
| global.Response = undici.Response; | ||
| // @ts-expect-error - overriding globals | ||
| global.fetch = undici.fetch; | ||
| // @ts-expect-error - overriding globals | ||
| global.FormData = undici.FormData; | ||
| } | ||
| exports.installGlobals = installGlobals; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import { File, Headers, Request, Response, fetch, FormData } from 'undici'; | ||
| function installGlobals() { | ||
| global.File = File; | ||
| // @ts-ignore - this shows as an error in VSCode but is not an error via TSC so we can't use `ts-expect-error` | ||
| global.Headers = Headers; | ||
| // @ts-expect-error - overriding globals | ||
| global.Request = Request; | ||
| // @ts-expect-error - overriding globals | ||
| global.Response = Response; | ||
| // @ts-expect-error - overriding globals | ||
| global.fetch = fetch; | ||
| // @ts-expect-error - overriding globals | ||
| global.FormData = FormData; | ||
| } | ||
| export { installGlobals }; |
| export declare const createCookie: import("react-router").CreateCookieFunction; | ||
| export declare const createCookieSessionStorage: import("react-router").CreateCookieSessionStorageFunction; | ||
| export declare const createSessionStorage: import("react-router").CreateSessionStorageFunction; | ||
| export declare const createMemorySessionStorage: import("react-router").CreateMemorySessionStorageFunction; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var reactRouter = require('react-router'); | ||
| var crypto = require('./crypto.js'); | ||
| const createCookie = reactRouter.createCookieFactory({ | ||
| sign: crypto.sign, | ||
| unsign: crypto.unsign | ||
| }); | ||
| const createCookieSessionStorage = reactRouter.createCookieSessionStorageFactory(createCookie); | ||
| const createSessionStorage = reactRouter.createSessionStorageFactory(createCookie); | ||
| const createMemorySessionStorage = reactRouter.createMemorySessionStorageFactory(createSessionStorage); | ||
| exports.createCookie = createCookie; | ||
| exports.createCookieSessionStorage = createCookieSessionStorage; | ||
| exports.createMemorySessionStorage = createMemorySessionStorage; | ||
| exports.createSessionStorage = createSessionStorage; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import { createCookieFactory, createCookieSessionStorageFactory, createSessionStorageFactory, createMemorySessionStorageFactory } from 'react-router'; | ||
| import { sign, unsign } from './crypto.mjs'; | ||
| const createCookie = createCookieFactory({ | ||
| sign, | ||
| unsign | ||
| }); | ||
| const createCookieSessionStorage = createCookieSessionStorageFactory(createCookie); | ||
| const createSessionStorage = createSessionStorageFactory(createCookie); | ||
| const createMemorySessionStorage = createMemorySessionStorageFactory(createSessionStorage); | ||
| export { createCookie, createCookieSessionStorage, createMemorySessionStorage, createSessionStorage }; |
| export {}; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| var globals = require('./globals.js'); | ||
| globals.installGlobals(); |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import { installGlobals } from './globals.mjs'; | ||
| installGlobals(); |
| import type { SessionStorage, SessionIdStorageStrategy, SessionData } from "react-router"; | ||
| interface FileSessionStorageOptions { | ||
| /** | ||
| * The Cookie used to store the session id on the client, or options used | ||
| * to automatically create one. | ||
| */ | ||
| cookie?: SessionIdStorageStrategy["cookie"]; | ||
| /** | ||
| * The directory to use to store session files. | ||
| */ | ||
| dir: string; | ||
| } | ||
| /** | ||
| * Creates a SessionStorage that stores session data on a filesystem. | ||
| * | ||
| * The advantage of using this instead of cookie session storage is that | ||
| * files may contain much more data than cookies. | ||
| * | ||
| * @see https://remix.run/utils/sessions#createfilesessionstorage-node | ||
| */ | ||
| export declare function createFileSessionStorage<Data = SessionData, FlashData = Data>({ cookie, dir, }: FileSessionStorageOptions): SessionStorage<Data, FlashData>; | ||
| export declare function getFile(dir: string, id: string): string; | ||
| export {}; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var crypto = require('node:crypto'); | ||
| var node_fs = require('node:fs'); | ||
| var path = require('node:path'); | ||
| var implementations = require('../implementations.js'); | ||
| function _interopNamespace(e) { | ||
| if (e && e.__esModule) return e; | ||
| var n = Object.create(null); | ||
| if (e) { | ||
| Object.keys(e).forEach(function (k) { | ||
| if (k !== 'default') { | ||
| var d = Object.getOwnPropertyDescriptor(e, k); | ||
| Object.defineProperty(n, k, d.get ? d : { | ||
| enumerable: true, | ||
| get: function () { return e[k]; } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| n["default"] = e; | ||
| return Object.freeze(n); | ||
| } | ||
| var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto); | ||
| var path__namespace = /*#__PURE__*/_interopNamespace(path); | ||
| /** | ||
| * Creates a SessionStorage that stores session data on a filesystem. | ||
| * | ||
| * The advantage of using this instead of cookie session storage is that | ||
| * files may contain much more data than cookies. | ||
| * | ||
| * @see https://remix.run/utils/sessions#createfilesessionstorage-node | ||
| */ | ||
| function createFileSessionStorage({ | ||
| cookie, | ||
| dir | ||
| }) { | ||
| return implementations.createSessionStorage({ | ||
| cookie, | ||
| async createData(data, expires) { | ||
| let content = JSON.stringify({ | ||
| data, | ||
| expires | ||
| }); | ||
| while (true) { | ||
| // TODO: Once Node v19 is supported we should use the globally provided | ||
| // Web Crypto API's crypto.getRandomValues() function here instead. | ||
| let randomBytes = crypto__namespace.webcrypto.getRandomValues(new Uint8Array(8)); | ||
| // This storage manages an id space of 2^64 ids, which is far greater | ||
| // than the maximum number of files allowed on an NTFS or ext4 volume | ||
| // (2^32). However, the larger id space should help to avoid collisions | ||
| // with existing ids when creating new sessions, which speeds things up. | ||
| let id = Buffer.from(randomBytes).toString("hex"); | ||
| try { | ||
| let file = getFile(dir, id); | ||
| await node_fs.promises.mkdir(path__namespace.dirname(file), { | ||
| recursive: true | ||
| }); | ||
| await node_fs.promises.writeFile(file, content, { | ||
| encoding: "utf-8", | ||
| flag: "wx" | ||
| }); | ||
| return id; | ||
| } catch (error) { | ||
| if (error.code !== "EEXIST") throw error; | ||
| } | ||
| } | ||
| }, | ||
| async readData(id) { | ||
| try { | ||
| let file = getFile(dir, id); | ||
| let content = JSON.parse(await node_fs.promises.readFile(file, "utf-8")); | ||
| let data = content.data; | ||
| let expires = typeof content.expires === "string" ? new Date(content.expires) : null; | ||
| if (!expires || expires > new Date()) { | ||
| return data; | ||
| } | ||
| // Remove expired session data. | ||
| if (expires) await node_fs.promises.unlink(file); | ||
| return null; | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| return null; | ||
| } | ||
| }, | ||
| async updateData(id, data, expires) { | ||
| let content = JSON.stringify({ | ||
| data, | ||
| expires | ||
| }); | ||
| let file = getFile(dir, id); | ||
| await node_fs.promises.mkdir(path__namespace.dirname(file), { | ||
| recursive: true | ||
| }); | ||
| await node_fs.promises.writeFile(file, content, "utf-8"); | ||
| }, | ||
| async deleteData(id) { | ||
| // Return early if the id is empty, otherwise we'll end up trying to | ||
| // unlink the dir, which will cause the EPERM error. | ||
| if (!id) { | ||
| return; | ||
| } | ||
| try { | ||
| await node_fs.promises.unlink(getFile(dir, id)); | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function getFile(dir, id) { | ||
| // Divide the session id up into a directory (first 2 bytes) and filename | ||
| // (remaining 6 bytes) to reduce the chance of having very large directories, | ||
| // which should speed up file access. This is a maximum of 2^16 directories, | ||
| // each with 2^48 files. | ||
| return path__namespace.join(dir, id.slice(0, 4), id.slice(4)); | ||
| } | ||
| exports.createFileSessionStorage = createFileSessionStorage; | ||
| exports.getFile = getFile; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import * as crypto from 'node:crypto'; | ||
| import { promises } from 'node:fs'; | ||
| import * as path from 'node:path'; | ||
| import { createSessionStorage } from '../implementations.mjs'; | ||
| /** | ||
| * Creates a SessionStorage that stores session data on a filesystem. | ||
| * | ||
| * The advantage of using this instead of cookie session storage is that | ||
| * files may contain much more data than cookies. | ||
| * | ||
| * @see https://remix.run/utils/sessions#createfilesessionstorage-node | ||
| */ | ||
| function createFileSessionStorage({ | ||
| cookie, | ||
| dir | ||
| }) { | ||
| return createSessionStorage({ | ||
| cookie, | ||
| async createData(data, expires) { | ||
| let content = JSON.stringify({ | ||
| data, | ||
| expires | ||
| }); | ||
| while (true) { | ||
| // TODO: Once Node v19 is supported we should use the globally provided | ||
| // Web Crypto API's crypto.getRandomValues() function here instead. | ||
| let randomBytes = crypto.webcrypto.getRandomValues(new Uint8Array(8)); | ||
| // This storage manages an id space of 2^64 ids, which is far greater | ||
| // than the maximum number of files allowed on an NTFS or ext4 volume | ||
| // (2^32). However, the larger id space should help to avoid collisions | ||
| // with existing ids when creating new sessions, which speeds things up. | ||
| let id = Buffer.from(randomBytes).toString("hex"); | ||
| try { | ||
| let file = getFile(dir, id); | ||
| await promises.mkdir(path.dirname(file), { | ||
| recursive: true | ||
| }); | ||
| await promises.writeFile(file, content, { | ||
| encoding: "utf-8", | ||
| flag: "wx" | ||
| }); | ||
| return id; | ||
| } catch (error) { | ||
| if (error.code !== "EEXIST") throw error; | ||
| } | ||
| } | ||
| }, | ||
| async readData(id) { | ||
| try { | ||
| let file = getFile(dir, id); | ||
| let content = JSON.parse(await promises.readFile(file, "utf-8")); | ||
| let data = content.data; | ||
| let expires = typeof content.expires === "string" ? new Date(content.expires) : null; | ||
| if (!expires || expires > new Date()) { | ||
| return data; | ||
| } | ||
| // Remove expired session data. | ||
| if (expires) await promises.unlink(file); | ||
| return null; | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| return null; | ||
| } | ||
| }, | ||
| async updateData(id, data, expires) { | ||
| let content = JSON.stringify({ | ||
| data, | ||
| expires | ||
| }); | ||
| let file = getFile(dir, id); | ||
| await promises.mkdir(path.dirname(file), { | ||
| recursive: true | ||
| }); | ||
| await promises.writeFile(file, content, "utf-8"); | ||
| }, | ||
| async deleteData(id) { | ||
| // Return early if the id is empty, otherwise we'll end up trying to | ||
| // unlink the dir, which will cause the EPERM error. | ||
| if (!id) { | ||
| return; | ||
| } | ||
| try { | ||
| await promises.unlink(getFile(dir, id)); | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| function getFile(dir, id) { | ||
| // Divide the session id up into a directory (first 2 bytes) and filename | ||
| // (remaining 6 bytes) to reduce the chance of having very large directories, | ||
| // which should speed up file access. This is a maximum of 2^16 directories, | ||
| // each with 2^48 files. | ||
| return path.join(dir, id.slice(0, 4), id.slice(4)); | ||
| } | ||
| export { createFileSessionStorage, getFile }; |
| /// <reference types="node" /> | ||
| /// <reference types="node" /> | ||
| import type { Readable, Writable } from "node:stream"; | ||
| export declare function writeReadableStreamToWritable(stream: ReadableStream, writable: Writable): Promise<void>; | ||
| export declare function writeAsyncIterableToWritable(iterable: AsyncIterable<Uint8Array>, writable: Writable): Promise<void>; | ||
| export declare function readableStreamToString(stream: ReadableStream<Uint8Array>, encoding?: BufferEncoding): Promise<string>; | ||
| export declare const createReadableStreamFromReadable: (source: Readable & { | ||
| readableHighWaterMark?: number; | ||
| }) => ReadableStream<Uint8Array>; |
-146
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var node_stream = require('node:stream'); | ||
| async function writeReadableStreamToWritable(stream, writable) { | ||
| let reader = stream.getReader(); | ||
| let flushable = writable; | ||
| try { | ||
| while (true) { | ||
| let { | ||
| done, | ||
| value | ||
| } = await reader.read(); | ||
| if (done) { | ||
| writable.end(); | ||
| break; | ||
| } | ||
| writable.write(value); | ||
| if (typeof flushable.flush === "function") { | ||
| flushable.flush(); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function writeAsyncIterableToWritable(iterable, writable) { | ||
| try { | ||
| for await (let chunk of iterable) { | ||
| writable.write(chunk); | ||
| } | ||
| writable.end(); | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function readableStreamToString(stream, encoding) { | ||
| let reader = stream.getReader(); | ||
| let chunks = []; | ||
| while (true) { | ||
| let { | ||
| done, | ||
| value | ||
| } = await reader.read(); | ||
| if (done) { | ||
| break; | ||
| } | ||
| if (value) { | ||
| chunks.push(value); | ||
| } | ||
| } | ||
| return Buffer.concat(chunks).toString(encoding); | ||
| } | ||
| const createReadableStreamFromReadable = source => { | ||
| let pump = new StreamPump(source); | ||
| let stream = new ReadableStream(pump, pump); | ||
| return stream; | ||
| }; | ||
| class StreamPump { | ||
| constructor(stream) { | ||
| this.highWaterMark = stream.readableHighWaterMark || new node_stream.Stream.Readable().readableHighWaterMark; | ||
| this.accumalatedSize = 0; | ||
| this.stream = stream; | ||
| this.enqueue = this.enqueue.bind(this); | ||
| this.error = this.error.bind(this); | ||
| this.close = this.close.bind(this); | ||
| } | ||
| size(chunk) { | ||
| return (chunk === null || chunk === void 0 ? void 0 : chunk.byteLength) || 0; | ||
| } | ||
| start(controller) { | ||
| this.controller = controller; | ||
| this.stream.on("data", this.enqueue); | ||
| this.stream.once("error", this.error); | ||
| this.stream.once("end", this.close); | ||
| this.stream.once("close", this.close); | ||
| } | ||
| pull() { | ||
| this.resume(); | ||
| } | ||
| cancel(reason) { | ||
| if (this.stream.destroy) { | ||
| this.stream.destroy(reason); | ||
| } | ||
| this.stream.off("data", this.enqueue); | ||
| this.stream.off("error", this.error); | ||
| this.stream.off("end", this.close); | ||
| this.stream.off("close", this.close); | ||
| } | ||
| enqueue(chunk) { | ||
| if (this.controller) { | ||
| try { | ||
| let bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk); | ||
| let available = (this.controller.desiredSize || 0) - bytes.byteLength; | ||
| this.controller.enqueue(bytes); | ||
| if (available <= 0) { | ||
| this.pause(); | ||
| } | ||
| } catch (error) { | ||
| this.controller.error(new Error("Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object")); | ||
| this.cancel(); | ||
| } | ||
| } | ||
| } | ||
| pause() { | ||
| if (this.stream.pause) { | ||
| this.stream.pause(); | ||
| } | ||
| } | ||
| resume() { | ||
| if (this.stream.readable && this.stream.resume) { | ||
| this.stream.resume(); | ||
| } | ||
| } | ||
| close() { | ||
| if (this.controller) { | ||
| this.controller.close(); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| error(error) { | ||
| if (this.controller) { | ||
| this.controller.error(error); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| } | ||
| exports.createReadableStreamFromReadable = createReadableStreamFromReadable; | ||
| exports.readableStreamToString = readableStreamToString; | ||
| exports.writeAsyncIterableToWritable = writeAsyncIterableToWritable; | ||
| exports.writeReadableStreamToWritable = writeReadableStreamToWritable; |
-139
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import { Stream } from 'node:stream'; | ||
| async function writeReadableStreamToWritable(stream, writable) { | ||
| let reader = stream.getReader(); | ||
| let flushable = writable; | ||
| try { | ||
| while (true) { | ||
| let { | ||
| done, | ||
| value | ||
| } = await reader.read(); | ||
| if (done) { | ||
| writable.end(); | ||
| break; | ||
| } | ||
| writable.write(value); | ||
| if (typeof flushable.flush === "function") { | ||
| flushable.flush(); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function writeAsyncIterableToWritable(iterable, writable) { | ||
| try { | ||
| for await (let chunk of iterable) { | ||
| writable.write(chunk); | ||
| } | ||
| writable.end(); | ||
| } catch (error) { | ||
| writable.destroy(error); | ||
| throw error; | ||
| } | ||
| } | ||
| async function readableStreamToString(stream, encoding) { | ||
| let reader = stream.getReader(); | ||
| let chunks = []; | ||
| while (true) { | ||
| let { | ||
| done, | ||
| value | ||
| } = await reader.read(); | ||
| if (done) { | ||
| break; | ||
| } | ||
| if (value) { | ||
| chunks.push(value); | ||
| } | ||
| } | ||
| return Buffer.concat(chunks).toString(encoding); | ||
| } | ||
| const createReadableStreamFromReadable = source => { | ||
| let pump = new StreamPump(source); | ||
| let stream = new ReadableStream(pump, pump); | ||
| return stream; | ||
| }; | ||
| class StreamPump { | ||
| constructor(stream) { | ||
| this.highWaterMark = stream.readableHighWaterMark || new Stream.Readable().readableHighWaterMark; | ||
| this.accumalatedSize = 0; | ||
| this.stream = stream; | ||
| this.enqueue = this.enqueue.bind(this); | ||
| this.error = this.error.bind(this); | ||
| this.close = this.close.bind(this); | ||
| } | ||
| size(chunk) { | ||
| return (chunk === null || chunk === void 0 ? void 0 : chunk.byteLength) || 0; | ||
| } | ||
| start(controller) { | ||
| this.controller = controller; | ||
| this.stream.on("data", this.enqueue); | ||
| this.stream.once("error", this.error); | ||
| this.stream.once("end", this.close); | ||
| this.stream.once("close", this.close); | ||
| } | ||
| pull() { | ||
| this.resume(); | ||
| } | ||
| cancel(reason) { | ||
| if (this.stream.destroy) { | ||
| this.stream.destroy(reason); | ||
| } | ||
| this.stream.off("data", this.enqueue); | ||
| this.stream.off("error", this.error); | ||
| this.stream.off("end", this.close); | ||
| this.stream.off("close", this.close); | ||
| } | ||
| enqueue(chunk) { | ||
| if (this.controller) { | ||
| try { | ||
| let bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk); | ||
| let available = (this.controller.desiredSize || 0) - bytes.byteLength; | ||
| this.controller.enqueue(bytes); | ||
| if (available <= 0) { | ||
| this.pause(); | ||
| } | ||
| } catch (error) { | ||
| this.controller.error(new Error("Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object")); | ||
| this.cancel(); | ||
| } | ||
| } | ||
| } | ||
| pause() { | ||
| if (this.stream.pause) { | ||
| this.stream.pause(); | ||
| } | ||
| } | ||
| resume() { | ||
| if (this.stream.readable && this.stream.resume) { | ||
| this.stream.resume(); | ||
| } | ||
| } | ||
| close() { | ||
| if (this.controller) { | ||
| this.controller.close(); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| error(error) { | ||
| if (this.controller) { | ||
| this.controller.error(error); | ||
| delete this.controller; | ||
| } | ||
| } | ||
| } | ||
| export { createReadableStreamFromReadable, readableStreamToString, writeAsyncIterableToWritable, writeReadableStreamToWritable }; |
| /// <reference types="node" /> | ||
| import type { UploadHandler } from "react-router"; | ||
| export type FileUploadHandlerFilterArgs = { | ||
| filename: string; | ||
| contentType: string; | ||
| name: string; | ||
| }; | ||
| export type FileUploadHandlerPathResolverArgs = { | ||
| filename: string; | ||
| contentType: string; | ||
| name: string; | ||
| }; | ||
| /** | ||
| * Chooses the path of the file to be uploaded. If a string is not | ||
| * returned the file will not be written. | ||
| */ | ||
| export type FileUploadHandlerPathResolver = (args: FileUploadHandlerPathResolverArgs) => string | undefined; | ||
| export type FileUploadHandlerOptions = { | ||
| /** | ||
| * Avoid file conflicts by appending a count on the end of the filename | ||
| * if it already exists on disk. Defaults to `true`. | ||
| */ | ||
| avoidFileConflicts?: boolean; | ||
| /** | ||
| * The directory to write the upload. | ||
| */ | ||
| directory?: string | FileUploadHandlerPathResolver; | ||
| /** | ||
| * The name of the file in the directory. Can be a relative path, the directory | ||
| * structure will be created if it does not exist. | ||
| */ | ||
| file?: FileUploadHandlerPathResolver; | ||
| /** | ||
| * The maximum upload size allowed. If the size is exceeded an error will be thrown. | ||
| * Defaults to 3000000B (3MB). | ||
| */ | ||
| maxPartSize?: number; | ||
| /** | ||
| * | ||
| * @param filename | ||
| * @param contentType | ||
| * @param name | ||
| */ | ||
| filter?(args: FileUploadHandlerFilterArgs): boolean | Promise<boolean>; | ||
| }; | ||
| export declare function createFileUploadHandler({ directory, avoidFileConflicts, file, filter, maxPartSize, }?: FileUploadHandlerOptions): UploadHandler; | ||
| export declare class NodeOnDiskFile implements Omit<File, "constructor"> { | ||
| private filepath; | ||
| type: string; | ||
| private slicer?; | ||
| name: string; | ||
| lastModified: number; | ||
| webkitRelativePath: string; | ||
| prototype: File; | ||
| constructor(filepath: string, type: string, slicer?: { | ||
| start: number; | ||
| end: number; | ||
| } | undefined); | ||
| get size(): number; | ||
| slice(start?: number, end?: number, type?: string): Blob; | ||
| arrayBuffer(): Promise<ArrayBuffer>; | ||
| stream(): ReadableStream<any>; | ||
| stream(): NodeJS.ReadableStream; | ||
| text(): Promise<string>; | ||
| get [Symbol.toStringTag](): string; | ||
| remove(): Promise<void>; | ||
| getFilePath(): string; | ||
| } |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var crypto = require('node:crypto'); | ||
| var node_fs = require('node:fs'); | ||
| var promises = require('node:fs/promises'); | ||
| var node_os = require('node:os'); | ||
| var path = require('node:path'); | ||
| var node_stream = require('node:stream'); | ||
| var node_util = require('node:util'); | ||
| var reactRouter = require('react-router'); | ||
| var streamSlice = require('stream-slice'); | ||
| var stream = require('../stream.js'); | ||
| function _interopNamespace(e) { | ||
| if (e && e.__esModule) return e; | ||
| var n = Object.create(null); | ||
| if (e) { | ||
| Object.keys(e).forEach(function (k) { | ||
| if (k !== 'default') { | ||
| var d = Object.getOwnPropertyDescriptor(e, k); | ||
| Object.defineProperty(n, k, d.get ? d : { | ||
| enumerable: true, | ||
| get: function () { return e[k]; } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| n["default"] = e; | ||
| return Object.freeze(n); | ||
| } | ||
| var streamSlice__namespace = /*#__PURE__*/_interopNamespace(streamSlice); | ||
| /** | ||
| * Chooses the path of the file to be uploaded. If a string is not | ||
| * returned the file will not be written. | ||
| */ | ||
| let defaultFilePathResolver = ({ | ||
| filename | ||
| }) => { | ||
| let ext = filename ? path.extname(filename) : ""; | ||
| return "upload_" + crypto.randomBytes(4).readUInt32LE(0) + ext; | ||
| }; | ||
| async function uniqueFile(filepath) { | ||
| let ext = path.extname(filepath); | ||
| let uniqueFilepath = filepath; | ||
| for (let i = 1; await promises.stat(uniqueFilepath).then(() => true).catch(() => false); i++) { | ||
| uniqueFilepath = (ext ? filepath.slice(0, -ext.length) : filepath) + `-${new Date().getTime()}${ext}`; | ||
| } | ||
| return uniqueFilepath; | ||
| } | ||
| function createFileUploadHandler({ | ||
| directory = node_os.tmpdir(), | ||
| avoidFileConflicts = true, | ||
| file = defaultFilePathResolver, | ||
| filter, | ||
| maxPartSize = 3000000 | ||
| } = {}) { | ||
| return async ({ | ||
| name, | ||
| filename, | ||
| contentType, | ||
| data | ||
| }) => { | ||
| if (!filename || filter && !(await filter({ | ||
| name, | ||
| filename, | ||
| contentType | ||
| }))) { | ||
| return undefined; | ||
| } | ||
| let dir = typeof directory === "string" ? directory : directory({ | ||
| name, | ||
| filename, | ||
| contentType | ||
| }); | ||
| if (!dir) { | ||
| return undefined; | ||
| } | ||
| let filedir = path.resolve(dir); | ||
| let path$1 = typeof file === "string" ? file : file({ | ||
| name, | ||
| filename, | ||
| contentType | ||
| }); | ||
| if (!path$1) { | ||
| return undefined; | ||
| } | ||
| let filepath = path.resolve(filedir, path$1); | ||
| if (avoidFileConflicts) { | ||
| filepath = await uniqueFile(filepath); | ||
| } | ||
| await promises.mkdir(path.dirname(filepath), { | ||
| recursive: true | ||
| }).catch(() => {}); | ||
| let writeFileStream = node_fs.createWriteStream(filepath); | ||
| let size = 0; | ||
| let deleteFile = false; | ||
| try { | ||
| for await (let chunk of data) { | ||
| size += chunk.byteLength; | ||
| if (size > maxPartSize) { | ||
| deleteFile = true; | ||
| throw new reactRouter.MaxPartSizeExceededError(name, maxPartSize); | ||
| } | ||
| writeFileStream.write(chunk); | ||
| } | ||
| } finally { | ||
| writeFileStream.end(); | ||
| await node_util.promisify(node_stream.finished)(writeFileStream); | ||
| if (deleteFile) { | ||
| await promises.rm(filepath).catch(() => {}); | ||
| } | ||
| } | ||
| // TODO: remove this typecast once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| return new NodeOnDiskFile(filepath, contentType); | ||
| }; | ||
| } | ||
| // TODO: remove this `Omit` usage once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| class NodeOnDiskFile { | ||
| lastModified = 0; | ||
| webkitRelativePath = ""; | ||
| // TODO: remove this property once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| prototype = File.prototype; | ||
| constructor(filepath, type, slicer) { | ||
| this.filepath = filepath; | ||
| this.type = type; | ||
| this.slicer = slicer; | ||
| this.name = path.basename(filepath); | ||
| } | ||
| get size() { | ||
| let stats = node_fs.statSync(this.filepath); | ||
| if (this.slicer) { | ||
| let slice = this.slicer.end - this.slicer.start; | ||
| return slice < 0 ? 0 : slice > stats.size ? stats.size : slice; | ||
| } | ||
| return stats.size; | ||
| } | ||
| slice(start, end, type) { | ||
| var _this$slicer; | ||
| if (typeof start === "number" && start < 0) start = this.size + start; | ||
| if (typeof end === "number" && end < 0) end = this.size + end; | ||
| let startOffset = ((_this$slicer = this.slicer) === null || _this$slicer === void 0 ? void 0 : _this$slicer.start) || 0; | ||
| start = startOffset + (start || 0); | ||
| end = startOffset + (end || this.size); | ||
| return new NodeOnDiskFile(this.filepath, typeof type === "string" ? type : this.type, { | ||
| start, | ||
| end | ||
| } | ||
| // TODO: remove this typecast once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| ); | ||
| } | ||
| async arrayBuffer() { | ||
| let stream = node_fs.createReadStream(this.filepath); | ||
| if (this.slicer) { | ||
| stream = stream.pipe(streamSlice__namespace.slice(this.slicer.start, this.slicer.end)); | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| let buf = []; | ||
| stream.on("data", chunk => buf.push(chunk)); | ||
| stream.on("end", () => resolve(Buffer.concat(buf))); | ||
| stream.on("error", err => reject(err)); | ||
| }); | ||
| } | ||
| stream() { | ||
| let stream$1 = node_fs.createReadStream(this.filepath); | ||
| if (this.slicer) { | ||
| stream$1 = stream$1.pipe(streamSlice__namespace.slice(this.slicer.start, this.slicer.end)); | ||
| } | ||
| return stream.createReadableStreamFromReadable(stream$1); | ||
| } | ||
| async text() { | ||
| return stream.readableStreamToString(this.stream()); | ||
| } | ||
| get [Symbol.toStringTag]() { | ||
| return "File"; | ||
| } | ||
| remove() { | ||
| return promises.unlink(this.filepath); | ||
| } | ||
| getFilePath() { | ||
| return this.filepath; | ||
| } | ||
| } | ||
| exports.NodeOnDiskFile = NodeOnDiskFile; | ||
| exports.createFileUploadHandler = createFileUploadHandler; |
| /** | ||
| * @react-router/node v0.0.0-nightly-183fdb88c-20240730 | ||
| * | ||
| * Copyright (c) Remix Software Inc. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE.md file in the root directory of this source tree. | ||
| * | ||
| * @license MIT | ||
| */ | ||
| import { randomBytes } from 'node:crypto'; | ||
| import { createWriteStream, statSync, createReadStream } from 'node:fs'; | ||
| import { mkdir, rm, unlink, stat } from 'node:fs/promises'; | ||
| import { tmpdir } from 'node:os'; | ||
| import { resolve, dirname, basename, extname } from 'node:path'; | ||
| import { finished } from 'node:stream'; | ||
| import { promisify } from 'node:util'; | ||
| import { MaxPartSizeExceededError } from 'react-router'; | ||
| import * as streamSlice from 'stream-slice'; | ||
| import { createReadableStreamFromReadable, readableStreamToString } from '../stream.mjs'; | ||
| let defaultFilePathResolver = ({ | ||
| filename | ||
| }) => { | ||
| let ext = filename ? extname(filename) : ""; | ||
| return "upload_" + randomBytes(4).readUInt32LE(0) + ext; | ||
| }; | ||
| async function uniqueFile(filepath) { | ||
| let ext = extname(filepath); | ||
| let uniqueFilepath = filepath; | ||
| for (let i = 1; await stat(uniqueFilepath).then(() => true).catch(() => false); i++) { | ||
| uniqueFilepath = (ext ? filepath.slice(0, -ext.length) : filepath) + `-${new Date().getTime()}${ext}`; | ||
| } | ||
| return uniqueFilepath; | ||
| } | ||
| function createFileUploadHandler({ | ||
| directory = tmpdir(), | ||
| avoidFileConflicts = true, | ||
| file = defaultFilePathResolver, | ||
| filter, | ||
| maxPartSize = 3000000 | ||
| } = {}) { | ||
| return async ({ | ||
| name, | ||
| filename, | ||
| contentType, | ||
| data | ||
| }) => { | ||
| if (!filename || filter && !(await filter({ | ||
| name, | ||
| filename, | ||
| contentType | ||
| }))) { | ||
| return undefined; | ||
| } | ||
| let dir = typeof directory === "string" ? directory : directory({ | ||
| name, | ||
| filename, | ||
| contentType | ||
| }); | ||
| if (!dir) { | ||
| return undefined; | ||
| } | ||
| let filedir = resolve(dir); | ||
| let path = typeof file === "string" ? file : file({ | ||
| name, | ||
| filename, | ||
| contentType | ||
| }); | ||
| if (!path) { | ||
| return undefined; | ||
| } | ||
| let filepath = resolve(filedir, path); | ||
| if (avoidFileConflicts) { | ||
| filepath = await uniqueFile(filepath); | ||
| } | ||
| await mkdir(dirname(filepath), { | ||
| recursive: true | ||
| }).catch(() => {}); | ||
| let writeFileStream = createWriteStream(filepath); | ||
| let size = 0; | ||
| let deleteFile = false; | ||
| try { | ||
| for await (let chunk of data) { | ||
| size += chunk.byteLength; | ||
| if (size > maxPartSize) { | ||
| deleteFile = true; | ||
| throw new MaxPartSizeExceededError(name, maxPartSize); | ||
| } | ||
| writeFileStream.write(chunk); | ||
| } | ||
| } finally { | ||
| writeFileStream.end(); | ||
| await promisify(finished)(writeFileStream); | ||
| if (deleteFile) { | ||
| await rm(filepath).catch(() => {}); | ||
| } | ||
| } | ||
| // TODO: remove this typecast once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| return new NodeOnDiskFile(filepath, contentType); | ||
| }; | ||
| } | ||
| // TODO: remove this `Omit` usage once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| class NodeOnDiskFile { | ||
| lastModified = 0; | ||
| webkitRelativePath = ""; | ||
| // TODO: remove this property once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| prototype = File.prototype; | ||
| constructor(filepath, type, slicer) { | ||
| this.filepath = filepath; | ||
| this.type = type; | ||
| this.slicer = slicer; | ||
| this.name = basename(filepath); | ||
| } | ||
| get size() { | ||
| let stats = statSync(this.filepath); | ||
| if (this.slicer) { | ||
| let slice = this.slicer.end - this.slicer.start; | ||
| return slice < 0 ? 0 : slice > stats.size ? stats.size : slice; | ||
| } | ||
| return stats.size; | ||
| } | ||
| slice(start, end, type) { | ||
| var _this$slicer; | ||
| if (typeof start === "number" && start < 0) start = this.size + start; | ||
| if (typeof end === "number" && end < 0) end = this.size + end; | ||
| let startOffset = ((_this$slicer = this.slicer) === null || _this$slicer === void 0 ? void 0 : _this$slicer.start) || 0; | ||
| start = startOffset + (start || 0); | ||
| end = startOffset + (end || this.size); | ||
| return new NodeOnDiskFile(this.filepath, typeof type === "string" ? type : this.type, { | ||
| start, | ||
| end | ||
| } | ||
| // TODO: remove this typecast once TS fixed File class regression | ||
| // https://github.com/microsoft/TypeScript/issues/52166 | ||
| ); | ||
| } | ||
| async arrayBuffer() { | ||
| let stream = createReadStream(this.filepath); | ||
| if (this.slicer) { | ||
| stream = stream.pipe(streamSlice.slice(this.slicer.start, this.slicer.end)); | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| let buf = []; | ||
| stream.on("data", chunk => buf.push(chunk)); | ||
| stream.on("end", () => resolve(Buffer.concat(buf))); | ||
| stream.on("error", err => reject(err)); | ||
| }); | ||
| } | ||
| stream() { | ||
| let stream = createReadStream(this.filepath); | ||
| if (this.slicer) { | ||
| stream = stream.pipe(streamSlice.slice(this.slicer.start, this.slicer.end)); | ||
| } | ||
| return createReadableStreamFromReadable(stream); | ||
| } | ||
| async text() { | ||
| return readableStreamToString(this.stream()); | ||
| } | ||
| get [Symbol.toStringTag]() { | ||
| return "File"; | ||
| } | ||
| remove() { | ||
| return unlink(this.filepath); | ||
| } | ||
| getFilePath() { | ||
| return this.filepath; | ||
| } | ||
| } | ||
| export { NodeOnDiskFile, createFileUploadHandler }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
3
-57.14%5
-28.57%46655
-22.71%8
-71.43%554
-54.78%6
-57.14%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed