@remix-run/node 1.4.3 → 1.5.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  /// <reference types="node" />
2
- import type { UploadHandler } from "../formData";
2
+ import type { UploadHandler } from "@remix-run/server-runtime";
3
3
  export declare type FileUploadHandlerFilterArgs = {
4
4
  filename: string;
5
- encoding: string;
6
- mimetype: string;
5
+ contentType: string;
6
+ name: string;
7
7
  };
8
8
  export declare type FileUploadHandlerPathResolverArgs = {
9
9
  filename: string;
10
- encoding: string;
11
- mimetype: string;
10
+ contentType: string;
11
+ name: string;
12
12
  };
13
13
  /**
14
14
  * Chooses the path of the file to be uploaded. If a string is not
@@ -34,7 +34,7 @@ export declare type FileUploadHandlerOptions = {
34
34
  * The maximum upload size allowed. If the size is exceeded an error will be thrown.
35
35
  * Defaults to 3000000B (3MB).
36
36
  */
37
- maxFileSize?: number;
37
+ maxPartSize?: number;
38
38
  /**
39
39
  *
40
40
  * @param filename
@@ -43,18 +43,23 @@ export declare type FileUploadHandlerOptions = {
43
43
  */
44
44
  filter?(args: FileUploadHandlerFilterArgs): boolean | Promise<boolean>;
45
45
  };
46
- export declare function createFileUploadHandler({ directory, avoidFileConflicts, file, filter, maxFileSize, }: FileUploadHandlerOptions): UploadHandler;
46
+ export declare function createFileUploadHandler({ directory, avoidFileConflicts, file, filter, maxPartSize, }?: FileUploadHandlerOptions): UploadHandler;
47
47
  export declare class NodeOnDiskFile implements File {
48
48
  private filepath;
49
- size: number;
50
49
  type: string;
50
+ private slicer?;
51
51
  name: string;
52
52
  lastModified: number;
53
53
  webkitRelativePath: string;
54
- constructor(filepath: string, size: number, type: string);
54
+ constructor(filepath: string, type: string, slicer?: {
55
+ start: number;
56
+ end: number;
57
+ } | undefined);
58
+ get size(): number;
59
+ slice(start?: number, end?: number, type?: string): Blob;
55
60
  arrayBuffer(): Promise<ArrayBuffer>;
56
- slice(start?: any, end?: any, contentType?: any): Blob;
57
61
  stream(): ReadableStream<any>;
58
62
  stream(): NodeJS.ReadableStream;
59
63
  text(): Promise<string>;
64
+ get [Symbol.toStringTag](): string;
60
65
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/node v1.4.3
2
+ * @remix-run/node v1.5.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -17,7 +17,31 @@ var fs = require('fs');
17
17
  var promises = require('fs/promises');
18
18
  var os = require('os');
19
19
  var path = require('path');
20
- var meter = require('./meter.js');
20
+ var stream = require('stream');
21
+ var util = require('util');
22
+ var serverRuntime = require('@remix-run/server-runtime');
23
+ var streamSlice = require('stream-slice');
24
+ var stream$1 = require('../stream.js');
25
+
26
+ function _interopNamespace(e) {
27
+ if (e && e.__esModule) return e;
28
+ var n = Object.create(null);
29
+ if (e) {
30
+ Object.keys(e).forEach(function (k) {
31
+ if (k !== 'default') {
32
+ var d = Object.getOwnPropertyDescriptor(e, k);
33
+ Object.defineProperty(n, k, d.get ? d : {
34
+ enumerable: true,
35
+ get: function () { return e[k]; }
36
+ });
37
+ }
38
+ });
39
+ }
40
+ n["default"] = e;
41
+ return Object.freeze(n);
42
+ }
43
+
44
+ var streamSlice__namespace = /*#__PURE__*/_interopNamespace(streamSlice);
21
45
 
22
46
  let defaultFilePathResolver = ({
23
47
  filename
@@ -42,45 +66,41 @@ function createFileUploadHandler({
42
66
  avoidFileConflicts = true,
43
67
  file = defaultFilePathResolver,
44
68
  filter,
45
- maxFileSize = 3000000
46
- }) {
69
+ maxPartSize = 3000000
70
+ } = {}) {
47
71
  return async ({
48
72
  name,
49
- stream,
50
73
  filename,
51
- encoding,
52
- mimetype
74
+ contentType,
75
+ data
53
76
  }) => {
54
- if (filter && !(await filter({
77
+ if (!filename || filter && !(await filter({
78
+ name,
55
79
  filename,
56
- encoding,
57
- mimetype
80
+ contentType
58
81
  }))) {
59
- stream.resume();
60
- return;
82
+ return undefined;
61
83
  }
62
84
 
63
85
  let dir = typeof directory === "string" ? directory : directory({
86
+ name,
64
87
  filename,
65
- encoding,
66
- mimetype
88
+ contentType
67
89
  });
68
90
 
69
91
  if (!dir) {
70
- stream.resume();
71
- return;
92
+ return undefined;
72
93
  }
73
94
 
74
95
  let filedir = path.resolve(dir);
75
96
  let path$1 = typeof file === "string" ? file : file({
97
+ name,
76
98
  filename,
77
- encoding,
78
- mimetype
99
+ contentType
79
100
  });
80
101
 
81
102
  if (!path$1) {
82
- stream.resume();
83
- return;
103
+ return undefined;
84
104
  }
85
105
 
86
106
  let filepath = path.resolve(filedir, path$1);
@@ -92,47 +112,76 @@ function createFileUploadHandler({
92
112
  await promises.mkdir(path.dirname(filepath), {
93
113
  recursive: true
94
114
  }).catch(() => {});
95
- let meter$1 = new meter.Meter(name, maxFileSize);
96
- await new Promise((resolve, reject) => {
97
- let writeFileStream = fs.createWriteStream(filepath);
98
- let aborted = false;
99
-
100
- async function abort(error) {
101
- if (aborted) return;
102
- aborted = true;
103
- stream.unpipe();
104
- meter$1.unpipe();
105
- stream.removeAllListeners();
106
- meter$1.removeAllListeners();
107
- writeFileStream.removeAllListeners();
108
- await promises.rm(filepath, {
109
- force: true
110
- }).catch(() => {});
111
- reject(error);
115
+ let writeFileStream = fs.createWriteStream(filepath);
116
+ let size = 0;
117
+ let deleteFile = false;
118
+
119
+ try {
120
+ for await (let chunk of data) {
121
+ size += chunk.byteLength;
122
+
123
+ if (size > maxPartSize) {
124
+ deleteFile = true;
125
+ throw new serverRuntime.MaxPartSizeExceededError(name, maxPartSize);
126
+ }
127
+
128
+ writeFileStream.write(chunk);
112
129
  }
130
+ } finally {
131
+ writeFileStream.end();
132
+ await util.promisify(stream.finished)(writeFileStream);
113
133
 
114
- stream.on("error", abort);
115
- meter$1.on("error", abort);
116
- writeFileStream.on("error", abort);
117
- writeFileStream.on("finish", resolve);
118
- stream.pipe(meter$1).pipe(writeFileStream);
119
- });
120
- return new NodeOnDiskFile(filepath, meter$1.bytes, mimetype);
134
+ if (deleteFile) {
135
+ await promises.rm(filepath).catch(() => {});
136
+ }
137
+ }
138
+
139
+ return new NodeOnDiskFile(filepath, contentType);
121
140
  };
122
141
  }
123
142
  class NodeOnDiskFile {
124
143
  lastModified = 0;
125
144
  webkitRelativePath = "";
126
145
 
127
- constructor(filepath, size, type) {
146
+ constructor(filepath, type, slicer) {
128
147
  this.filepath = filepath;
129
- this.size = size;
130
148
  this.type = type;
149
+ this.slicer = slicer;
131
150
  this.name = path.basename(filepath);
132
151
  }
133
152
 
153
+ get size() {
154
+ let stats = fs.statSync(this.filepath);
155
+
156
+ if (this.slicer) {
157
+ let slice = this.slicer.end - this.slicer.start;
158
+ return slice < 0 ? 0 : slice > stats.size ? stats.size : slice;
159
+ }
160
+
161
+ return stats.size;
162
+ }
163
+
164
+ slice(start, end, type) {
165
+ var _this$slicer;
166
+
167
+ if (typeof start === "number" && start < 0) start = this.size + start;
168
+ if (typeof end === "number" && end < 0) end = this.size + end;
169
+ let startOffset = ((_this$slicer = this.slicer) === null || _this$slicer === void 0 ? void 0 : _this$slicer.start) || 0;
170
+ start = startOffset + (start || 0);
171
+ end = startOffset + (end || this.size);
172
+ return new NodeOnDiskFile(this.filepath, typeof type === "string" ? type : this.type, {
173
+ start,
174
+ end
175
+ });
176
+ }
177
+
134
178
  async arrayBuffer() {
135
179
  let stream = fs.createReadStream(this.filepath);
180
+
181
+ if (this.slicer) {
182
+ stream = stream.pipe(streamSlice__namespace.slice(this.slicer.start, this.slicer.end));
183
+ }
184
+
136
185
  return new Promise((resolve, reject) => {
137
186
  let buf = [];
138
187
  stream.on("data", chunk => buf.push(chunk));
@@ -141,16 +190,22 @@ class NodeOnDiskFile {
141
190
  });
142
191
  }
143
192
 
144
- slice(start, end, contentType) {
145
- throw new Error("Method not implemented.");
193
+ stream() {
194
+ let stream = fs.createReadStream(this.filepath);
195
+
196
+ if (this.slicer) {
197
+ stream = stream.pipe(streamSlice__namespace.slice(this.slicer.start, this.slicer.end));
198
+ }
199
+
200
+ return stream$1.createReadableStreamFromReadable(stream);
146
201
  }
147
202
 
148
- stream() {
149
- return fs.createReadStream(this.filepath);
203
+ async text() {
204
+ return stream$1.readableStreamToString(this.stream());
150
205
  }
151
206
 
152
- text() {
153
- return promises.readFile(this.filepath, "utf-8");
207
+ get [Symbol.toStringTag]() {
208
+ return "File";
154
209
  }
155
210
 
156
211
  }
package/formData.d.ts DELETED
@@ -1,27 +0,0 @@
1
- /// <reference types="node" />
2
- import type { Readable } from "stream";
3
- export declare type UploadHandlerArgs = {
4
- name: string;
5
- stream: Readable;
6
- filename: string;
7
- encoding: string;
8
- mimetype: string;
9
- };
10
- export declare type UploadHandler = (args: UploadHandlerArgs) => Promise<string | File | undefined>;
11
- export declare function isFile(blob: Blob): blob is File;
12
- declare class NodeFormData implements FormData {
13
- private _fields;
14
- constructor(form?: any);
15
- append(name: string, value: string | Blob, fileName?: string): void;
16
- delete(name: string): void;
17
- get(name: string): FormDataEntryValue | null;
18
- getAll(name: string): FormDataEntryValue[];
19
- has(name: string): boolean;
20
- set(name: string, value: string | Blob, fileName?: string): void;
21
- forEach(callbackfn: (value: FormDataEntryValue, key: string, parent: FormData) => void, thisArg?: any): void;
22
- entries(): IterableIterator<[string, FormDataEntryValue]>;
23
- keys(): IterableIterator<string>;
24
- values(): IterableIterator<FormDataEntryValue>;
25
- [Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
26
- }
27
- export { NodeFormData as FormData };
package/formData.js DELETED
@@ -1,108 +0,0 @@
1
- /**
2
- * @remix-run/node v1.4.3
3
- *
4
- * Copyright (c) Remix Software Inc.
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE.md file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- 'use strict';
12
-
13
- Object.defineProperty(exports, '__esModule', { value: true });
14
-
15
- function isBlob(value) {
16
- return typeof value === "object" && (typeof value.arrayBuffer === "function" || typeof value.size === "number" || typeof value.slice === "function" || typeof value.stream === "function" || typeof value.text === "function" || typeof value.type === "string");
17
- }
18
-
19
- function isFile(blob) {
20
- let file = blob;
21
- return typeof file.name === "string";
22
- }
23
-
24
- class NodeFormData {
25
- constructor(form) {
26
- if (typeof form !== "undefined") {
27
- throw new Error("Form data on the server is not supported.");
28
- }
29
-
30
- this._fields = {};
31
- }
32
-
33
- append(name, value, fileName) {
34
- if (typeof value !== "string" && !isBlob(value)) {
35
- throw new Error("formData.append can only accept a string or Blob");
36
- }
37
-
38
- this._fields[name] = this._fields[name] || [];
39
-
40
- if (typeof value === "string" || isFile(value)) {
41
- this._fields[name].push(value);
42
- } else {
43
- this._fields[name].push(new File([value], fileName || "unknown"));
44
- }
45
- }
46
-
47
- delete(name) {
48
- delete this._fields[name];
49
- }
50
-
51
- get(name) {
52
- let arr = this._fields[name];
53
- return (arr === null || arr === void 0 ? void 0 : arr.slice(-1)[0]) ?? null;
54
- }
55
-
56
- getAll(name) {
57
- let arr = this._fields[name];
58
- return arr || [];
59
- }
60
-
61
- has(name) {
62
- return name in this._fields;
63
- }
64
-
65
- set(name, value, fileName) {
66
- if (typeof value !== "string" && !isBlob(value)) {
67
- throw new Error("formData.set can only accept a string or Blob");
68
- }
69
-
70
- if (typeof value === "string" || isFile(value)) {
71
- this._fields[name] = [value];
72
- } else {
73
- this._fields[name] = [new File([value], fileName || "unknown")];
74
- }
75
- }
76
-
77
- forEach(callbackfn, thisArg) {
78
- Object.entries(this._fields).forEach(([name, values]) => {
79
- values.forEach(value => callbackfn(value, name, thisArg), thisArg);
80
- });
81
- }
82
-
83
- entries() {
84
- return Object.entries(this._fields).reduce((entries, [name, values]) => {
85
- values.forEach(value => entries.push([name, value]));
86
- return entries;
87
- }, []).values();
88
- }
89
-
90
- keys() {
91
- return Object.keys(this._fields).values();
92
- }
93
-
94
- values() {
95
- return Object.entries(this._fields).reduce((results, [name, values]) => {
96
- values.forEach(value => results.push(value));
97
- return results;
98
- }, []).values();
99
- }
100
-
101
- *[Symbol.iterator]() {
102
- yield* this.entries();
103
- }
104
-
105
- }
106
-
107
- exports.FormData = NodeFormData;
108
- exports.isFile = isFile;
@@ -1,11 +0,0 @@
1
- /// <reference types="node" />
2
- import { Readable } from "stream";
3
- import type { UploadHandler } from "./formData";
4
- import { FormData as NodeFormData } from "./formData";
5
- /**
6
- * Allows you to handle multipart forms (file uploads) for your app.
7
- *
8
- * @see https://remix.run/api/remix#parsemultipartformdata-node
9
- */
10
- export declare function parseMultipartFormData(request: Request, uploadHandler: UploadHandler): Promise<FormData>;
11
- export declare function internalParseFormData(contentType: string, body: string | Buffer | Readable, abortController?: AbortController, uploadHandler?: UploadHandler): Promise<NodeFormData>;
@@ -1,114 +0,0 @@
1
- /**
2
- * @remix-run/node v1.4.3
3
- *
4
- * Copyright (c) Remix Software Inc.
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE.md file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- 'use strict';
12
-
13
- Object.defineProperty(exports, '__esModule', { value: true });
14
-
15
- var stream = require('stream');
16
- var Busboy = require('busboy');
17
- var formData = require('./formData.js');
18
-
19
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
-
21
- var Busboy__default = /*#__PURE__*/_interopDefaultLegacy(Busboy);
22
-
23
- /**
24
- * Allows you to handle multipart forms (file uploads) for your app.
25
- *
26
- * @see https://remix.run/api/remix#parsemultipartformdata-node
27
- */
28
-
29
- function parseMultipartFormData(request, uploadHandler) {
30
- return request.formData(uploadHandler);
31
- }
32
- async function internalParseFormData(contentType, body, abortController, uploadHandler) {
33
- let formData$1 = new formData.FormData();
34
- let fileWorkQueue = [];
35
- let stream$1;
36
-
37
- if (typeof body === "string" || Buffer.isBuffer(body)) {
38
- stream$1 = stream.Readable.from(body);
39
- } else {
40
- stream$1 = body;
41
- }
42
-
43
- await new Promise(async (resolve, reject) => {
44
- try {
45
- let busboy = new Busboy__default["default"]({
46
- highWaterMark: 2 * 1024 * 1024,
47
- headers: {
48
- "content-type": contentType
49
- }
50
- });
51
- let aborted = false;
52
-
53
- function abort(error) {
54
- if (aborted) return;
55
- aborted = true;
56
- stream$1.unpipe();
57
- stream$1.removeAllListeners();
58
- busboy.removeAllListeners();
59
- abortController === null || abortController === void 0 ? void 0 : abortController.abort();
60
- reject(error || new Error("failed to parse form data"));
61
- }
62
-
63
- busboy.on("field", (name, value) => {
64
- formData$1.append(name, value);
65
- });
66
- busboy.on("file", (name, filestream, filename, encoding, mimetype) => {
67
- if (uploadHandler) {
68
- fileWorkQueue.push((async () => {
69
- try {
70
- let value = await uploadHandler({
71
- name,
72
- stream: filestream,
73
- filename,
74
- encoding,
75
- mimetype
76
- });
77
-
78
- if (typeof value !== "undefined") {
79
- formData$1.append(name, value);
80
- }
81
- } catch (error) {
82
- // Emit error to busboy to bail early if possible
83
- busboy.emit("error", error); // It's possible that the handler is doing stuff and fails
84
- // *after* busboy has finished. Rethrow the error for surfacing
85
- // in the Promise.all(fileWorkQueue) below.
86
-
87
- throw error;
88
- } finally {
89
- filestream.resume();
90
- }
91
- })());
92
- } else {
93
- filestream.resume();
94
- }
95
-
96
- if (!uploadHandler) {
97
- console.warn(`Tried to parse multipart file upload for field "${name}" but no uploadHandler was provided.` + " Read more here: https://remix.run/api/remix#parseMultipartFormData-node");
98
- }
99
- });
100
- stream$1.on("error", abort);
101
- stream$1.on("aborted", abort);
102
- busboy.on("error", abort);
103
- busboy.on("finish", resolve);
104
- stream$1.pipe(busboy);
105
- } catch (err) {
106
- reject(err);
107
- }
108
- });
109
- await Promise.all(fileWorkQueue);
110
- return formData$1;
111
- }
112
-
113
- exports.internalParseFormData = internalParseFormData;
114
- exports.parseMultipartFormData = parseMultipartFormData;
@@ -1,21 +0,0 @@
1
- import type { UploadHandler } from "../formData";
2
- export declare type MemoryUploadHandlerFilterArgs = {
3
- filename: string;
4
- encoding: string;
5
- mimetype: string;
6
- };
7
- export declare type MemoryUploadHandlerOptions = {
8
- /**
9
- * The maximum upload size allowed. If the size is exceeded an error will be thrown.
10
- * Defaults to 3000000B (3MB).
11
- */
12
- maxFileSize?: number;
13
- /**
14
- *
15
- * @param filename
16
- * @param mimetype
17
- * @param encoding
18
- */
19
- filter?(args: MemoryUploadHandlerFilterArgs): boolean | Promise<boolean>;
20
- };
21
- export declare function createMemoryUploadHandler({ filter, maxFileSize, }: MemoryUploadHandlerOptions): UploadHandler;
@@ -1,80 +0,0 @@
1
- /**
2
- * @remix-run/node v1.4.3
3
- *
4
- * Copyright (c) Remix Software Inc.
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE.md file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- 'use strict';
12
-
13
- Object.defineProperty(exports, '__esModule', { value: true });
14
-
15
- var stream = require('stream');
16
- var file = require('@web-std/file');
17
- var meter = require('./meter.js');
18
-
19
- function createMemoryUploadHandler({
20
- filter,
21
- maxFileSize = 3000000
22
- }) {
23
- return async ({
24
- name,
25
- stream,
26
- filename,
27
- encoding,
28
- mimetype
29
- }) => {
30
- if (filter && !(await filter({
31
- filename,
32
- encoding,
33
- mimetype
34
- }))) {
35
- stream.resume();
36
- return;
37
- }
38
-
39
- let bufferStream = new BufferStream();
40
- await new Promise((resolve, reject) => {
41
- let meter$1 = new meter.Meter(name, maxFileSize);
42
- let aborted = false;
43
-
44
- async function abort(error) {
45
- if (aborted) return;
46
- aborted = true;
47
- stream.unpipe();
48
- meter$1.unpipe();
49
- stream.removeAllListeners();
50
- meter$1.removeAllListeners();
51
- bufferStream.removeAllListeners();
52
- reject(error);
53
- }
54
-
55
- stream.on("error", abort);
56
- meter$1.on("error", abort);
57
- bufferStream.on("error", abort);
58
- bufferStream.on("finish", resolve);
59
- stream.pipe(meter$1).pipe(bufferStream);
60
- });
61
- return new file.File(bufferStream.data, filename, {
62
- type: mimetype
63
- });
64
- };
65
- }
66
-
67
- class BufferStream extends stream.Transform {
68
- constructor() {
69
- super();
70
- this.data = [];
71
- }
72
-
73
- _transform(chunk, _, callback) {
74
- this.data.push(chunk);
75
- callback();
76
- }
77
-
78
- }
79
-
80
- exports.createMemoryUploadHandler = createMemoryUploadHandler;
package/upload/meter.d.ts DELETED
@@ -1,15 +0,0 @@
1
- /// <reference types="node" />
2
- import type { TransformCallback } from "stream";
3
- import { Transform } from "stream";
4
- export declare class Meter extends Transform {
5
- field: string;
6
- maxBytes: number | undefined;
7
- bytes: number;
8
- constructor(field: string, maxBytes: number | undefined);
9
- _transform(chunk: any, _: BufferEncoding, callback: TransformCallback): void;
10
- }
11
- export declare class MeterError extends Error {
12
- field: string;
13
- maxBytes: number;
14
- constructor(field: string, maxBytes: number);
15
- }