@trpc/server 11.0.0-next-beta.206 → 11.0.0-next-beta.208
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.
- package/dist/adapters/aws-lambda/index.js +14 -105
- package/dist/adapters/aws-lambda/index.mjs +1 -90
- package/dist/adapters/aws-lambda/utils.js +100 -0
- package/dist/adapters/aws-lambda/utils.mjs +93 -0
- package/dist/adapters/express.js +1 -7
- package/dist/adapters/express.mjs +1 -5
- package/dist/adapters/fastify/fastifyRequestHandler.js +81 -0
- package/dist/adapters/fastify/fastifyRequestHandler.mjs +79 -0
- package/dist/adapters/fastify/fastifyTRPCPlugin.js +51 -0
- package/dist/adapters/fastify/fastifyTRPCPlugin.mjs +49 -0
- package/dist/adapters/fastify/index.js +4 -128
- package/dist/adapters/fastify/index.mjs +2 -128
- package/dist/adapters/fetch/fetchRequestHandler.js +118 -0
- package/dist/adapters/fetch/fetchRequestHandler.mjs +116 -0
- package/dist/adapters/fetch/index.js +2 -115
- package/dist/adapters/fetch/index.mjs +1 -116
- package/dist/adapters/next.js +1 -6
- package/dist/adapters/next.mjs +1 -4
- package/dist/adapters/node-http/content-type/form-data/fileUploadHandler.js +161 -0
- package/dist/adapters/node-http/content-type/form-data/fileUploadHandler.mjs +157 -0
- package/dist/adapters/node-http/content-type/form-data/index.js +20 -646
- package/dist/adapters/node-http/content-type/form-data/index.mjs +9 -631
- package/dist/adapters/node-http/content-type/form-data/memoryUploadHandler.js +29 -0
- package/dist/adapters/node-http/content-type/form-data/memoryUploadHandler.mjs +27 -0
- package/dist/adapters/node-http/content-type/form-data/streamSlice.js +46 -0
- package/dist/adapters/node-http/content-type/form-data/streamSlice.mjs +44 -0
- package/dist/adapters/node-http/content-type/form-data/uploadHandler.js +30 -0
- package/dist/adapters/node-http/content-type/form-data/uploadHandler.mjs +26 -0
- package/dist/adapters/node-http/content-type/json/getPostBody.js +42 -0
- package/dist/adapters/node-http/content-type/json/getPostBody.mjs +40 -0
- package/dist/adapters/node-http/content-type/json/index.js +3 -42
- package/dist/adapters/node-http/content-type/json/index.mjs +2 -39
- package/dist/adapters/node-http/index.js +1 -7
- package/dist/adapters/node-http/index.mjs +1 -5
- package/dist/{contentType-72ed9df5.mjs → adapters/node-http/internals/contentType.mjs} +1 -1
- package/dist/{nodeHTTPRequestHandler-83441c73.js → adapters/node-http/nodeHTTPRequestHandler.js} +2 -2
- package/dist/{nodeHTTPRequestHandler-0223fac5.mjs → adapters/node-http/nodeHTTPRequestHandler.mjs} +2 -2
- package/dist/adapters/standalone.js +2 -12
- package/dist/adapters/standalone.mjs +1 -5
- package/dist/adapters/ws.js +0 -2
- package/dist/bundle-analysis.json +97 -97
- package/dist/http.js +1 -3
- package/dist/index.js +10 -12
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/index.js +203 -0
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/index.mjs +201 -0
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/search.js +167 -0
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/search.mjs +163 -0
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/utils.js +35 -0
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/utils.mjs +30 -0
- package/dist/observable.js +1 -3
- package/dist/rpc.js +1 -3
- package/dist/shared.js +2 -4
- package/package.json +4 -4
- package/dist/contentType-24c44bba.js +0 -5
- package/dist/nodeHTTPRequestHandler-aa0dce4e.js +0 -105
- /package/dist/{contentType-d9d22104.js → adapters/node-http/internals/contentType.js} +0 -0
|
@@ -1,120 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var fetchRequestHandler = require('./fetchRequestHandler.js');
|
|
4
4
|
|
|
5
|
-
var http = require('@trpc/core/http');
|
|
6
5
|
|
|
7
|
-
/**
|
|
8
|
-
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
|
|
9
|
-
*
|
|
10
|
-
* Do **not** import from `@trpc/core`
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* import type { AnyTRPCRouter } from '@trpc/server'
|
|
14
|
-
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
|
|
15
|
-
* ```
|
|
16
|
-
*/ // @trpc/server
|
|
17
|
-
const trimSlashes = (path)=>{
|
|
18
|
-
path = path.startsWith('/') ? path.slice(1) : path;
|
|
19
|
-
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
20
|
-
return path;
|
|
21
|
-
};
|
|
22
|
-
async function fetchRequestHandler(opts) {
|
|
23
|
-
const resHeaders = new Headers();
|
|
24
|
-
const createContext = async (innerOpts)=>{
|
|
25
|
-
return opts.createContext?.({
|
|
26
|
-
req: opts.req,
|
|
27
|
-
resHeaders,
|
|
28
|
-
...innerOpts
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
const url = new URL(opts.req.url);
|
|
32
|
-
const pathname = trimSlashes(url.pathname);
|
|
33
|
-
const endpoint = trimSlashes(opts.endpoint);
|
|
34
|
-
const path = trimSlashes(pathname.slice(endpoint.length));
|
|
35
|
-
const req = {
|
|
36
|
-
query: url.searchParams,
|
|
37
|
-
method: opts.req.method,
|
|
38
|
-
headers: Object.fromEntries(opts.req.headers),
|
|
39
|
-
body: opts.req.headers.get('content-type')?.startsWith('application/json') ? await opts.req.text() : ''
|
|
40
|
-
};
|
|
41
|
-
let resolve;
|
|
42
|
-
const promise = new Promise((r)=>resolve = r);
|
|
43
|
-
let status = 200;
|
|
44
|
-
let isStream = false;
|
|
45
|
-
let controller;
|
|
46
|
-
let encoder;
|
|
47
|
-
let formatter;
|
|
48
|
-
const unstable_onHead = (head, isStreaming)=>{
|
|
49
|
-
for (const [key, value] of Object.entries(head.headers ?? {})){
|
|
50
|
-
/* istanbul ignore if -- @preserve */ if (typeof value === 'undefined') {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (typeof value === 'string') {
|
|
54
|
-
resHeaders.set(key, value);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
for (const v of value){
|
|
58
|
-
resHeaders.append(key, v);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
status = head.status;
|
|
62
|
-
if (isStreaming) {
|
|
63
|
-
resHeaders.set('Transfer-Encoding', 'chunked');
|
|
64
|
-
resHeaders.append('Vary', 'trpc-batch-mode');
|
|
65
|
-
const stream = new ReadableStream({
|
|
66
|
-
start (c) {
|
|
67
|
-
controller = c;
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
const response = new Response(stream, {
|
|
71
|
-
status,
|
|
72
|
-
headers: resHeaders
|
|
73
|
-
});
|
|
74
|
-
resolve(response);
|
|
75
|
-
encoder = new TextEncoder();
|
|
76
|
-
formatter = http.getBatchStreamFormatter();
|
|
77
|
-
isStream = true;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
const unstable_onChunk = ([index, string])=>{
|
|
81
|
-
if (index === -1) {
|
|
82
|
-
// full response, no streaming
|
|
83
|
-
const response = new Response(string || null, {
|
|
84
|
-
status,
|
|
85
|
-
headers: resHeaders
|
|
86
|
-
});
|
|
87
|
-
resolve(response);
|
|
88
|
-
} else {
|
|
89
|
-
controller.enqueue(encoder.encode(formatter(index, string)));
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
http.resolveHTTPResponse({
|
|
93
|
-
req,
|
|
94
|
-
createContext,
|
|
95
|
-
path,
|
|
96
|
-
router: opts.router,
|
|
97
|
-
batching: opts.batching,
|
|
98
|
-
responseMeta: opts.responseMeta,
|
|
99
|
-
onError (o) {
|
|
100
|
-
opts?.onError?.({
|
|
101
|
-
...o,
|
|
102
|
-
req: opts.req
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
|
-
unstable_onHead,
|
|
106
|
-
unstable_onChunk
|
|
107
|
-
}).then(()=>{
|
|
108
|
-
if (isStream) {
|
|
109
|
-
controller.enqueue(encoder.encode(formatter.end()));
|
|
110
|
-
controller.close();
|
|
111
|
-
}
|
|
112
|
-
}).catch(()=>{
|
|
113
|
-
if (isStream) {
|
|
114
|
-
controller.close();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
return promise;
|
|
118
|
-
}
|
|
119
6
|
|
|
120
|
-
exports.fetchRequestHandler = fetchRequestHandler;
|
|
7
|
+
exports.fetchRequestHandler = fetchRequestHandler.fetchRequestHandler;
|
|
@@ -1,116 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
|
|
5
|
-
*
|
|
6
|
-
* Do **not** import from `@trpc/core`
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import type { AnyTRPCRouter } from '@trpc/server'
|
|
10
|
-
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
|
|
11
|
-
* ```
|
|
12
|
-
*/ // @trpc/server
|
|
13
|
-
const trimSlashes = (path)=>{
|
|
14
|
-
path = path.startsWith('/') ? path.slice(1) : path;
|
|
15
|
-
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
16
|
-
return path;
|
|
17
|
-
};
|
|
18
|
-
async function fetchRequestHandler(opts) {
|
|
19
|
-
const resHeaders = new Headers();
|
|
20
|
-
const createContext = async (innerOpts)=>{
|
|
21
|
-
return opts.createContext?.({
|
|
22
|
-
req: opts.req,
|
|
23
|
-
resHeaders,
|
|
24
|
-
...innerOpts
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
const url = new URL(opts.req.url);
|
|
28
|
-
const pathname = trimSlashes(url.pathname);
|
|
29
|
-
const endpoint = trimSlashes(opts.endpoint);
|
|
30
|
-
const path = trimSlashes(pathname.slice(endpoint.length));
|
|
31
|
-
const req = {
|
|
32
|
-
query: url.searchParams,
|
|
33
|
-
method: opts.req.method,
|
|
34
|
-
headers: Object.fromEntries(opts.req.headers),
|
|
35
|
-
body: opts.req.headers.get('content-type')?.startsWith('application/json') ? await opts.req.text() : ''
|
|
36
|
-
};
|
|
37
|
-
let resolve;
|
|
38
|
-
const promise = new Promise((r)=>resolve = r);
|
|
39
|
-
let status = 200;
|
|
40
|
-
let isStream = false;
|
|
41
|
-
let controller;
|
|
42
|
-
let encoder;
|
|
43
|
-
let formatter;
|
|
44
|
-
const unstable_onHead = (head, isStreaming)=>{
|
|
45
|
-
for (const [key, value] of Object.entries(head.headers ?? {})){
|
|
46
|
-
/* istanbul ignore if -- @preserve */ if (typeof value === 'undefined') {
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (typeof value === 'string') {
|
|
50
|
-
resHeaders.set(key, value);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
for (const v of value){
|
|
54
|
-
resHeaders.append(key, v);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
status = head.status;
|
|
58
|
-
if (isStreaming) {
|
|
59
|
-
resHeaders.set('Transfer-Encoding', 'chunked');
|
|
60
|
-
resHeaders.append('Vary', 'trpc-batch-mode');
|
|
61
|
-
const stream = new ReadableStream({
|
|
62
|
-
start (c) {
|
|
63
|
-
controller = c;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
const response = new Response(stream, {
|
|
67
|
-
status,
|
|
68
|
-
headers: resHeaders
|
|
69
|
-
});
|
|
70
|
-
resolve(response);
|
|
71
|
-
encoder = new TextEncoder();
|
|
72
|
-
formatter = getBatchStreamFormatter();
|
|
73
|
-
isStream = true;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const unstable_onChunk = ([index, string])=>{
|
|
77
|
-
if (index === -1) {
|
|
78
|
-
// full response, no streaming
|
|
79
|
-
const response = new Response(string || null, {
|
|
80
|
-
status,
|
|
81
|
-
headers: resHeaders
|
|
82
|
-
});
|
|
83
|
-
resolve(response);
|
|
84
|
-
} else {
|
|
85
|
-
controller.enqueue(encoder.encode(formatter(index, string)));
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
resolveHTTPResponse({
|
|
89
|
-
req,
|
|
90
|
-
createContext,
|
|
91
|
-
path,
|
|
92
|
-
router: opts.router,
|
|
93
|
-
batching: opts.batching,
|
|
94
|
-
responseMeta: opts.responseMeta,
|
|
95
|
-
onError (o) {
|
|
96
|
-
opts?.onError?.({
|
|
97
|
-
...o,
|
|
98
|
-
req: opts.req
|
|
99
|
-
});
|
|
100
|
-
},
|
|
101
|
-
unstable_onHead,
|
|
102
|
-
unstable_onChunk
|
|
103
|
-
}).then(()=>{
|
|
104
|
-
if (isStream) {
|
|
105
|
-
controller.enqueue(encoder.encode(formatter.end()));
|
|
106
|
-
controller.close();
|
|
107
|
-
}
|
|
108
|
-
}).catch(()=>{
|
|
109
|
-
if (isStream) {
|
|
110
|
-
controller.close();
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
return promise;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export { fetchRequestHandler };
|
|
1
|
+
export { fetchRequestHandler } from './fetchRequestHandler.mjs';
|
package/dist/adapters/next.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var core = require('@trpc/core');
|
|
6
|
-
var nodeHTTPRequestHandler = require('
|
|
7
|
-
require('@trpc/core/http');
|
|
8
|
-
require('./node-http/content-type/json/index.js');
|
|
9
|
-
require('../contentType-d9d22104.js');
|
|
4
|
+
var nodeHTTPRequestHandler = require('./node-http/nodeHTTPRequestHandler.js');
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
7
|
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
|
package/dist/adapters/next.mjs
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { getErrorShape, TRPCError } from '@trpc/core';
|
|
2
|
-
import {
|
|
3
|
-
import '@trpc/core/http';
|
|
4
|
-
import './node-http/content-type/json/index.mjs';
|
|
5
|
-
import '../contentType-72ed9df5.mjs';
|
|
2
|
+
import { nodeHTTPRequestHandler } from './node-http/nodeHTTPRequestHandler.mjs';
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
5
|
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var node_crypto = require('node:crypto');
|
|
4
|
+
var node_fs = require('node:fs');
|
|
5
|
+
var fs = require('node:fs/promises');
|
|
6
|
+
var node_os = require('node:os');
|
|
7
|
+
var node_path = require('node:path');
|
|
8
|
+
var node_stream = require('node:stream');
|
|
9
|
+
var node_util = require('node:util');
|
|
10
|
+
var streamSlice = require('./streamSlice.js');
|
|
11
|
+
var uploadHandler = require('./uploadHandler.js');
|
|
12
|
+
|
|
13
|
+
async function readableStreamToString(stream, encoding) {
|
|
14
|
+
const reader = stream.getReader();
|
|
15
|
+
const chunks = [];
|
|
16
|
+
async function read() {
|
|
17
|
+
const { done , value } = await reader.read();
|
|
18
|
+
if (done) {
|
|
19
|
+
return;
|
|
20
|
+
} else if (value) {
|
|
21
|
+
chunks.push(value);
|
|
22
|
+
}
|
|
23
|
+
await read();
|
|
24
|
+
}
|
|
25
|
+
await read();
|
|
26
|
+
return Buffer.concat(chunks).toString(encoding);
|
|
27
|
+
}
|
|
28
|
+
const defaultFilePathResolver = ({ filename , })=>{
|
|
29
|
+
const ext = filename ? node_path.extname(filename) : '';
|
|
30
|
+
return 'upload_' + node_crypto.randomBytes(4).readUInt32LE(0) + ext;
|
|
31
|
+
};
|
|
32
|
+
async function uniqueFile(filepath) {
|
|
33
|
+
const ext = node_path.extname(filepath);
|
|
34
|
+
let uniqueFilepath = filepath;
|
|
35
|
+
for(let i = 1; await fs.stat(uniqueFilepath).then(()=>true).catch(()=>false); i++){
|
|
36
|
+
uniqueFilepath = (ext ? filepath.slice(0, -ext.length) : filepath) + `-${new Date().getTime()}${ext}`;
|
|
37
|
+
}
|
|
38
|
+
return uniqueFilepath;
|
|
39
|
+
}
|
|
40
|
+
function createFileUploadHandler({ directory =node_os.tmpdir() , avoidFileConflicts =true , file =defaultFilePathResolver , filter , maxPartSize =3000000 } = {}) {
|
|
41
|
+
return async ({ name , filename , contentType , data })=>{
|
|
42
|
+
if (!filename || filter && !await filter({
|
|
43
|
+
name,
|
|
44
|
+
filename,
|
|
45
|
+
contentType
|
|
46
|
+
})) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const dir = typeof directory === 'string' ? directory : directory({
|
|
50
|
+
name,
|
|
51
|
+
filename,
|
|
52
|
+
contentType
|
|
53
|
+
});
|
|
54
|
+
if (!dir) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const filedir = node_path.resolve(dir);
|
|
58
|
+
const path = typeof file === 'string' ? file : file({
|
|
59
|
+
name,
|
|
60
|
+
filename,
|
|
61
|
+
contentType
|
|
62
|
+
});
|
|
63
|
+
if (!path) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
let filepath = node_path.resolve(filedir, path);
|
|
67
|
+
if (avoidFileConflicts) {
|
|
68
|
+
filepath = await uniqueFile(filepath);
|
|
69
|
+
}
|
|
70
|
+
await fs.mkdir(node_path.dirname(filepath), {
|
|
71
|
+
recursive: true
|
|
72
|
+
}).catch(()=>{});
|
|
73
|
+
const writeFileStream = node_fs.createWriteStream(filepath);
|
|
74
|
+
let size = 0;
|
|
75
|
+
let deleteFile = false;
|
|
76
|
+
try {
|
|
77
|
+
for await (const chunk of data){
|
|
78
|
+
size += chunk.byteLength;
|
|
79
|
+
if (size > maxPartSize) {
|
|
80
|
+
deleteFile = true;
|
|
81
|
+
throw new uploadHandler.MaxPartSizeExceededError(name, maxPartSize);
|
|
82
|
+
}
|
|
83
|
+
writeFileStream.write(chunk);
|
|
84
|
+
}
|
|
85
|
+
} finally{
|
|
86
|
+
writeFileStream.end();
|
|
87
|
+
await node_util.promisify(node_stream.finished)(writeFileStream);
|
|
88
|
+
if (deleteFile) {
|
|
89
|
+
await fs.rm(filepath).catch(()=>{});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return new NodeOnDiskFile(filepath, contentType);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
let _toStringTag = Symbol.toStringTag;
|
|
96
|
+
class NodeOnDiskFile {
|
|
97
|
+
get size() {
|
|
98
|
+
const stats = node_fs.statSync(this.filepath);
|
|
99
|
+
if (this.slicer) {
|
|
100
|
+
const slice = this.slicer.end - this.slicer.start;
|
|
101
|
+
return slice < 0 ? 0 : slice > stats.size ? stats.size : slice;
|
|
102
|
+
}
|
|
103
|
+
return stats.size;
|
|
104
|
+
}
|
|
105
|
+
slice(start, end, type) {
|
|
106
|
+
if (typeof start === 'number' && start < 0) start = this.size + start;
|
|
107
|
+
if (typeof end === 'number' && end < 0) end = this.size + end;
|
|
108
|
+
const startOffset = this.slicer?.start ?? 0;
|
|
109
|
+
start = startOffset + (start ?? 0);
|
|
110
|
+
end = startOffset + (end ?? this.size);
|
|
111
|
+
return new NodeOnDiskFile(this.filepath, typeof type === 'string' ? type : this.type, {
|
|
112
|
+
start,
|
|
113
|
+
end
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async arrayBuffer() {
|
|
117
|
+
let stream = node_fs.createReadStream(this.filepath);
|
|
118
|
+
if (this.slicer) {
|
|
119
|
+
stream = stream.pipe(streamSlice.streamSlice(this.slicer.start, this.slicer.end));
|
|
120
|
+
}
|
|
121
|
+
return new Promise((resolve, reject)=>{
|
|
122
|
+
const buf = [];
|
|
123
|
+
stream.on('data', (chunk)=>buf.push(chunk));
|
|
124
|
+
stream.on('end', ()=>{
|
|
125
|
+
resolve(Buffer.concat(buf));
|
|
126
|
+
});
|
|
127
|
+
stream.on('error', (err)=>{
|
|
128
|
+
reject(err);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
stream() {
|
|
133
|
+
let stream = node_fs.createReadStream(this.filepath);
|
|
134
|
+
if (this.slicer) {
|
|
135
|
+
stream = stream.pipe(streamSlice.streamSlice(this.slicer.start, this.slicer.end));
|
|
136
|
+
}
|
|
137
|
+
return node_stream.Readable.toWeb(stream);
|
|
138
|
+
}
|
|
139
|
+
async text() {
|
|
140
|
+
return readableStreamToString(this.stream());
|
|
141
|
+
}
|
|
142
|
+
remove() {
|
|
143
|
+
return fs.unlink(this.filepath);
|
|
144
|
+
}
|
|
145
|
+
getFilePath() {
|
|
146
|
+
return this.filepath;
|
|
147
|
+
}
|
|
148
|
+
constructor(filepath, type, slicer){
|
|
149
|
+
this.filepath = filepath;
|
|
150
|
+
this.type = type;
|
|
151
|
+
this.slicer = slicer;
|
|
152
|
+
this.lastModified = 0;
|
|
153
|
+
this.webkitRelativePath = '';
|
|
154
|
+
this[_toStringTag] = 'File';
|
|
155
|
+
this.name = node_path.basename(filepath);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
exports.NodeOnDiskFile = NodeOnDiskFile;
|
|
160
|
+
exports.createFileUploadHandler = createFileUploadHandler;
|
|
161
|
+
exports.readableStreamToString = readableStreamToString;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createWriteStream, statSync, createReadStream } from 'node:fs';
|
|
3
|
+
import { mkdir, rm, unlink, stat } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { resolve, dirname, basename, extname } from 'node:path';
|
|
6
|
+
import { finished, Readable } from 'node:stream';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
import { streamSlice } from './streamSlice.mjs';
|
|
9
|
+
import { MaxPartSizeExceededError } from './uploadHandler.mjs';
|
|
10
|
+
|
|
11
|
+
async function readableStreamToString(stream, encoding) {
|
|
12
|
+
const reader = stream.getReader();
|
|
13
|
+
const chunks = [];
|
|
14
|
+
async function read() {
|
|
15
|
+
const { done , value } = await reader.read();
|
|
16
|
+
if (done) {
|
|
17
|
+
return;
|
|
18
|
+
} else if (value) {
|
|
19
|
+
chunks.push(value);
|
|
20
|
+
}
|
|
21
|
+
await read();
|
|
22
|
+
}
|
|
23
|
+
await read();
|
|
24
|
+
return Buffer.concat(chunks).toString(encoding);
|
|
25
|
+
}
|
|
26
|
+
const defaultFilePathResolver = ({ filename , })=>{
|
|
27
|
+
const ext = filename ? extname(filename) : '';
|
|
28
|
+
return 'upload_' + randomBytes(4).readUInt32LE(0) + ext;
|
|
29
|
+
};
|
|
30
|
+
async function uniqueFile(filepath) {
|
|
31
|
+
const ext = extname(filepath);
|
|
32
|
+
let uniqueFilepath = filepath;
|
|
33
|
+
for(let i = 1; await stat(uniqueFilepath).then(()=>true).catch(()=>false); i++){
|
|
34
|
+
uniqueFilepath = (ext ? filepath.slice(0, -ext.length) : filepath) + `-${new Date().getTime()}${ext}`;
|
|
35
|
+
}
|
|
36
|
+
return uniqueFilepath;
|
|
37
|
+
}
|
|
38
|
+
function createFileUploadHandler({ directory =tmpdir() , avoidFileConflicts =true , file =defaultFilePathResolver , filter , maxPartSize =3000000 } = {}) {
|
|
39
|
+
return async ({ name , filename , contentType , data })=>{
|
|
40
|
+
if (!filename || filter && !await filter({
|
|
41
|
+
name,
|
|
42
|
+
filename,
|
|
43
|
+
contentType
|
|
44
|
+
})) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const dir = typeof directory === 'string' ? directory : directory({
|
|
48
|
+
name,
|
|
49
|
+
filename,
|
|
50
|
+
contentType
|
|
51
|
+
});
|
|
52
|
+
if (!dir) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const filedir = resolve(dir);
|
|
56
|
+
const path = typeof file === 'string' ? file : file({
|
|
57
|
+
name,
|
|
58
|
+
filename,
|
|
59
|
+
contentType
|
|
60
|
+
});
|
|
61
|
+
if (!path) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
let filepath = resolve(filedir, path);
|
|
65
|
+
if (avoidFileConflicts) {
|
|
66
|
+
filepath = await uniqueFile(filepath);
|
|
67
|
+
}
|
|
68
|
+
await mkdir(dirname(filepath), {
|
|
69
|
+
recursive: true
|
|
70
|
+
}).catch(()=>{});
|
|
71
|
+
const writeFileStream = createWriteStream(filepath);
|
|
72
|
+
let size = 0;
|
|
73
|
+
let deleteFile = false;
|
|
74
|
+
try {
|
|
75
|
+
for await (const chunk of data){
|
|
76
|
+
size += chunk.byteLength;
|
|
77
|
+
if (size > maxPartSize) {
|
|
78
|
+
deleteFile = true;
|
|
79
|
+
throw new MaxPartSizeExceededError(name, maxPartSize);
|
|
80
|
+
}
|
|
81
|
+
writeFileStream.write(chunk);
|
|
82
|
+
}
|
|
83
|
+
} finally{
|
|
84
|
+
writeFileStream.end();
|
|
85
|
+
await promisify(finished)(writeFileStream);
|
|
86
|
+
if (deleteFile) {
|
|
87
|
+
await rm(filepath).catch(()=>{});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return new NodeOnDiskFile(filepath, contentType);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
let _toStringTag = Symbol.toStringTag;
|
|
94
|
+
class NodeOnDiskFile {
|
|
95
|
+
get size() {
|
|
96
|
+
const stats = statSync(this.filepath);
|
|
97
|
+
if (this.slicer) {
|
|
98
|
+
const slice = this.slicer.end - this.slicer.start;
|
|
99
|
+
return slice < 0 ? 0 : slice > stats.size ? stats.size : slice;
|
|
100
|
+
}
|
|
101
|
+
return stats.size;
|
|
102
|
+
}
|
|
103
|
+
slice(start, end, type) {
|
|
104
|
+
if (typeof start === 'number' && start < 0) start = this.size + start;
|
|
105
|
+
if (typeof end === 'number' && end < 0) end = this.size + end;
|
|
106
|
+
const startOffset = this.slicer?.start ?? 0;
|
|
107
|
+
start = startOffset + (start ?? 0);
|
|
108
|
+
end = startOffset + (end ?? this.size);
|
|
109
|
+
return new NodeOnDiskFile(this.filepath, typeof type === 'string' ? type : this.type, {
|
|
110
|
+
start,
|
|
111
|
+
end
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async arrayBuffer() {
|
|
115
|
+
let stream = createReadStream(this.filepath);
|
|
116
|
+
if (this.slicer) {
|
|
117
|
+
stream = stream.pipe(streamSlice(this.slicer.start, this.slicer.end));
|
|
118
|
+
}
|
|
119
|
+
return new Promise((resolve, reject)=>{
|
|
120
|
+
const buf = [];
|
|
121
|
+
stream.on('data', (chunk)=>buf.push(chunk));
|
|
122
|
+
stream.on('end', ()=>{
|
|
123
|
+
resolve(Buffer.concat(buf));
|
|
124
|
+
});
|
|
125
|
+
stream.on('error', (err)=>{
|
|
126
|
+
reject(err);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
stream() {
|
|
131
|
+
let stream = createReadStream(this.filepath);
|
|
132
|
+
if (this.slicer) {
|
|
133
|
+
stream = stream.pipe(streamSlice(this.slicer.start, this.slicer.end));
|
|
134
|
+
}
|
|
135
|
+
return Readable.toWeb(stream);
|
|
136
|
+
}
|
|
137
|
+
async text() {
|
|
138
|
+
return readableStreamToString(this.stream());
|
|
139
|
+
}
|
|
140
|
+
remove() {
|
|
141
|
+
return unlink(this.filepath);
|
|
142
|
+
}
|
|
143
|
+
getFilePath() {
|
|
144
|
+
return this.filepath;
|
|
145
|
+
}
|
|
146
|
+
constructor(filepath, type, slicer){
|
|
147
|
+
this.filepath = filepath;
|
|
148
|
+
this.type = type;
|
|
149
|
+
this.slicer = slicer;
|
|
150
|
+
this.lastModified = 0;
|
|
151
|
+
this.webkitRelativePath = '';
|
|
152
|
+
this[_toStringTag] = 'File';
|
|
153
|
+
this.name = basename(filepath);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { NodeOnDiskFile, createFileUploadHandler, readableStreamToString };
|