You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@react-router/node

Package Overview
Dependencies
Maintainers
1
Versions
684
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@react-router/node - npm Package Compare versions

Comparing version
0.0.0-experimental-4996fbe2b
to
0.0.0-experimental-49eef6a01
+20
dist/crypto.mjs
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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 };
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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 };
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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 };
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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
*/
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';
export {};
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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-experimental-49eef6a01
*
* 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();
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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 };
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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 };
/**
* @react-router/node v0.0.0-experimental-49eef6a01
*
* 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 };
+1
-1
/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -21,17 +21,17 @@ * Copyright (c) Remix Software Inc.

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]; }
});
}
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);
}
});
}
n["default"] = e;
return Object.freeze(n);
}

@@ -94,2 +94,3 @@

}
// Remove expired session data.

@@ -96,0 +97,0 @@ if (expires) await node_fs.promises.unlink(file);

/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @react-router/node v0.0.0-experimental-4996fbe2b
* @react-router/node v0.0.0-experimental-49eef6a01
*

@@ -27,17 +27,17 @@ * Copyright (c) Remix Software Inc.

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]; }
});
}
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);
}
});
}
n["default"] = e;
return Object.freeze(n);
}

@@ -47,2 +47,7 @@

/**
* Chooses the path of the file to be uploaded. If a string is not
* returned the file will not be written.
*/
let defaultFilePathResolver = ({

@@ -125,2 +130,3 @@ filename

}
// TODO: remove this typecast once TS fixed File class regression

@@ -131,2 +137,3 @@ // https://github.com/microsoft/TypeScript/issues/52166

}
// TODO: remove this `Omit` usage once TS fixed File class regression

@@ -137,2 +144,3 @@ // https://github.com/microsoft/TypeScript/issues/52166

webkitRelativePath = "";
// TODO: remove this property once TS fixed File class regression

@@ -139,0 +147,0 @@ // https://github.com/microsoft/TypeScript/issues/52166

{
"name": "@react-router/node",
"version": "0.0.0-experimental-4996fbe2b",
"version": "0.0.0-experimental-49eef6a01",
"description": "Node.js platform abstractions for React Router",

@@ -16,4 +16,18 @@ "bugs": {

"typings": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./install": {
"types": "./dist/install.d.ts",
"import": "./dist/install.mjs",
"require": "./dist/install.js"
},
"./package.json": "./package.json"
},
"sideEffects": [
"./install.js"
"./dist/install.js",
"./dist/install.mjs"
],

@@ -31,7 +45,7 @@ "dependencies": {

"typescript": "^5.1.6",
"react-router": "0.0.0-experimental-4996fbe2b"
"react-router": "0.0.0-experimental-49eef6a01"
},
"peerDependencies": {
"typescript": "^5.1.0",
"react-router": "0.0.0-experimental-4996fbe2b"
"react-router": "0.0.0-experimental-49eef6a01"
},

@@ -38,0 +52,0 @@ "peerDependenciesMeta": {

export {};
/* eslint-disable */
"use strict";
var globals = require("./dist/globals.js");
Object.defineProperty(exports, "__esModule", { value: true });
globals.installGlobals();