@trpc/server 11.0.0-next-beta.242 → 11.0.0-next-beta.248
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/@trpc/server/index.d.ts +1 -5
- package/dist/@trpc/server/index.d.ts.map +1 -1
- package/dist/adapters/node-http/content-type/form-data/index.js +2 -2
- package/dist/adapters/node-http/content-type/form-data/index.mjs +1 -1
- package/dist/bundle-analysis.json +65 -123
- package/dist/unstable-core-do-not-import/index.d.ts +1 -1
- package/dist/unstable-core-do-not-import/index.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/initTRPC.d.ts +15 -7
- package/dist/unstable-core-do-not-import/initTRPC.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/router.d.ts +25 -18
- package/dist/unstable-core-do-not-import/router.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/router.js +36 -19
- package/dist/unstable-core-do-not-import/router.mjs +37 -20
- package/dist/unstable-core-do-not-import/rpc/codes.d.ts +1 -2
- package/dist/unstable-core-do-not-import/rpc/codes.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/types.d.ts +1 -0
- package/dist/unstable-core-do-not-import/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/@trpc/server/index.ts +1 -5
- package/src/unstable-core-do-not-import/index.ts +0 -2
- package/src/unstable-core-do-not-import/router.ts +84 -54
- package/src/unstable-core-do-not-import/rpc/codes.ts +1 -1
- package/src/unstable-core-do-not-import/types.ts +1 -0
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/index.js +0 -203
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/index.mjs +0 -201
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/search.js +0 -167
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/search.mjs +0 -163
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/utils.js +0 -35
- package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/utils.mjs +0 -30
|
@@ -9,14 +9,11 @@ import type {
|
|
|
9
9
|
import type { ProcedureCallOptions } from './procedureBuilder';
|
|
10
10
|
import type { AnyRootTypes, RootConfig } from './rootConfig';
|
|
11
11
|
import { defaultTransformer } from './transformer';
|
|
12
|
-
import type { MaybePromise } from './types';
|
|
12
|
+
import type { MaybePromise, ValueOf } from './types';
|
|
13
13
|
import { mergeWithoutOverrides, omitPrototype } from './utils';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export interface ProcedureRouterRecord {
|
|
19
|
-
[key: string]: AnyProcedure | AnyRouter;
|
|
15
|
+
export interface RouterRecord {
|
|
16
|
+
[key: string]: AnyProcedure | RouterRecord;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
type DecorateProcedure<TProcedure extends AnyProcedure> = (
|
|
@@ -26,13 +23,11 @@ type DecorateProcedure<TProcedure extends AnyProcedure> = (
|
|
|
26
23
|
/**
|
|
27
24
|
* @internal
|
|
28
25
|
*/
|
|
29
|
-
export type
|
|
30
|
-
|
|
31
|
-
>
|
|
32
|
-
|
|
33
|
-
?
|
|
34
|
-
: TProcedures[TKey] extends AnyProcedure
|
|
35
|
-
? DecorateProcedure<TProcedures[TKey]>
|
|
26
|
+
export type DecorateRouterRecord<TRecord extends RouterRecord> = {
|
|
27
|
+
[TKey in keyof TRecord]: TRecord[TKey] extends AnyProcedure
|
|
28
|
+
? DecorateProcedure<TRecord[TKey]>
|
|
29
|
+
: TRecord[TKey] extends RouterRecord
|
|
30
|
+
? DecorateRouterRecord<TRecord[TKey]>
|
|
36
31
|
: never;
|
|
37
32
|
};
|
|
38
33
|
|
|
@@ -41,7 +36,7 @@ export type DecoratedProcedureRecord<
|
|
|
41
36
|
*/
|
|
42
37
|
export type RouterCaller<
|
|
43
38
|
TRoot extends AnyRootTypes,
|
|
44
|
-
TRecord extends
|
|
39
|
+
TRecord extends RouterRecord,
|
|
45
40
|
> = (
|
|
46
41
|
/**
|
|
47
42
|
* @note
|
|
@@ -49,11 +44,11 @@ export type RouterCaller<
|
|
|
49
44
|
* e.g. wrapped in `React.cache` to avoid unnecessary computations
|
|
50
45
|
*/
|
|
51
46
|
ctx: TRoot['ctx'] | (() => MaybePromise<TRoot['ctx']>),
|
|
52
|
-
) =>
|
|
47
|
+
) => DecorateRouterRecord<TRecord>;
|
|
53
48
|
|
|
54
49
|
export interface Router<
|
|
55
50
|
TRoot extends AnyRootTypes,
|
|
56
|
-
TRecord extends
|
|
51
|
+
TRecord extends RouterRecord,
|
|
57
52
|
> {
|
|
58
53
|
_def: {
|
|
59
54
|
_config: RootConfig<TRoot>;
|
|
@@ -71,8 +66,8 @@ export interface Router<
|
|
|
71
66
|
|
|
72
67
|
export type BuiltRouter<
|
|
73
68
|
TRoot extends AnyRootTypes,
|
|
74
|
-
|
|
75
|
-
> = Router<TRoot,
|
|
69
|
+
TDef extends RouterRecord,
|
|
70
|
+
> = Router<TRoot, TDef> & TDef;
|
|
76
71
|
|
|
77
72
|
export type AnyRouter = Router<any, any>;
|
|
78
73
|
|
|
@@ -88,33 +83,36 @@ export type inferRouterMeta<TRouter extends AnyRouter> =
|
|
|
88
83
|
|
|
89
84
|
export type GetInferenceHelpers<
|
|
90
85
|
TType extends 'input' | 'output',
|
|
91
|
-
|
|
86
|
+
TRoot extends AnyRootTypes,
|
|
87
|
+
TRecord extends RouterRecord,
|
|
92
88
|
> = {
|
|
93
|
-
[TKey in keyof
|
|
94
|
-
?
|
|
95
|
-
? GetInferenceHelpers<TType,
|
|
96
|
-
:
|
|
89
|
+
[TKey in keyof TRecord]: TRecord[TKey] extends infer $Value
|
|
90
|
+
? $Value extends RouterRecord
|
|
91
|
+
? GetInferenceHelpers<TType, TRoot, $Value>
|
|
92
|
+
: $Value extends AnyProcedure
|
|
97
93
|
? TType extends 'input'
|
|
98
|
-
? inferProcedureInput
|
|
99
|
-
: inferTransformedProcedureOutput<
|
|
94
|
+
? inferProcedureInput<$Value>
|
|
95
|
+
: inferTransformedProcedureOutput<TRoot, $Value>
|
|
100
96
|
: never
|
|
101
97
|
: never;
|
|
102
98
|
};
|
|
103
99
|
|
|
104
100
|
export type inferRouterInputs<TRouter extends AnyRouter> = GetInferenceHelpers<
|
|
105
101
|
'input',
|
|
106
|
-
TRouter
|
|
102
|
+
TRouter['_def']['_config']['$types'],
|
|
103
|
+
TRouter['_def']['record']
|
|
107
104
|
>;
|
|
108
105
|
|
|
109
106
|
export type inferRouterOutputs<TRouter extends AnyRouter> = GetInferenceHelpers<
|
|
110
107
|
'output',
|
|
111
|
-
TRouter
|
|
108
|
+
TRouter['_def']['_config']['$types'],
|
|
109
|
+
TRouter['_def']['record']
|
|
112
110
|
>;
|
|
113
111
|
|
|
114
112
|
function isRouter(
|
|
115
|
-
procedureOrRouter:
|
|
113
|
+
procedureOrRouter: ValueOf<CreateRouterOptions>,
|
|
116
114
|
): procedureOrRouter is AnyRouter {
|
|
117
|
-
return 'router' in procedureOrRouter._def;
|
|
115
|
+
return procedureOrRouter._def && 'router' in procedureOrRouter._def;
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
const emptyRouter = {
|
|
@@ -139,17 +137,39 @@ const reservedWords = [
|
|
|
139
137
|
'then',
|
|
140
138
|
];
|
|
141
139
|
|
|
140
|
+
export type CreateRouterOptions = {
|
|
141
|
+
[key: string]: AnyProcedure | AnyRouter | CreateRouterOptions;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export type DecorateCreateRouterOptions<
|
|
145
|
+
TRouterOptions extends CreateRouterOptions,
|
|
146
|
+
> = {
|
|
147
|
+
[K in keyof TRouterOptions]: TRouterOptions[K] extends infer $Value
|
|
148
|
+
? $Value extends AnyProcedure
|
|
149
|
+
? $Value
|
|
150
|
+
: $Value extends Router<any, infer TRecord>
|
|
151
|
+
? TRecord
|
|
152
|
+
: $Value extends CreateRouterOptions
|
|
153
|
+
? DecorateCreateRouterOptions<$Value>
|
|
154
|
+
: never
|
|
155
|
+
: never;
|
|
156
|
+
};
|
|
157
|
+
|
|
142
158
|
/**
|
|
143
159
|
* @internal
|
|
144
160
|
*/
|
|
145
161
|
export function createRouterFactory<TRoot extends AnyRootTypes>(
|
|
146
162
|
config: RootConfig<TRoot>,
|
|
147
163
|
) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
function createRouterInner<TInput extends RouterRecord>(
|
|
165
|
+
input: TInput,
|
|
166
|
+
): BuiltRouter<TRoot, TInput>;
|
|
167
|
+
function createRouterInner<TInput extends CreateRouterOptions>(
|
|
168
|
+
input: TInput,
|
|
169
|
+
): BuiltRouter<TRoot, DecorateCreateRouterOptions<TInput>>;
|
|
170
|
+
function createRouterInner(input: RouterRecord | CreateRouterOptions) {
|
|
151
171
|
const reservedWordsUsed = new Set(
|
|
152
|
-
Object.keys(
|
|
172
|
+
Object.keys(input).filter((v) => reservedWords.includes(v)),
|
|
153
173
|
);
|
|
154
174
|
if (reservedWordsUsed.size > 0) {
|
|
155
175
|
throw new Error(
|
|
@@ -158,37 +178,47 @@ export function createRouterFactory<TRoot extends AnyRootTypes>(
|
|
|
158
178
|
);
|
|
159
179
|
}
|
|
160
180
|
|
|
161
|
-
const
|
|
162
|
-
function recursiveGetPaths(procedures: ProcedureRouterRecord, path = '') {
|
|
163
|
-
for (const [key, procedureOrRouter] of Object.entries(procedures ?? {})) {
|
|
164
|
-
const newPath = `${path}${key}`;
|
|
181
|
+
const procedures: Record<string, AnyProcedure> = omitPrototype({});
|
|
165
182
|
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
function step(from: CreateRouterOptions, path: string[] = []) {
|
|
184
|
+
const aggregate: RouterRecord = omitPrototype({});
|
|
185
|
+
for (const [key, item] of Object.entries(from ?? {})) {
|
|
186
|
+
if (isRouter(item)) {
|
|
187
|
+
aggregate[key] = step(item._def.record, [...path, key]);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (!isProcedure(item)) {
|
|
191
|
+
// RouterRecord
|
|
192
|
+
aggregate[key] = step(item, [...path, key]);
|
|
168
193
|
continue;
|
|
169
194
|
}
|
|
170
195
|
|
|
171
|
-
|
|
196
|
+
const newPath = [...path, key].join('.');
|
|
197
|
+
|
|
198
|
+
if (procedures[newPath]) {
|
|
172
199
|
throw new Error(`Duplicate key: ${newPath}`);
|
|
173
200
|
}
|
|
174
201
|
|
|
175
|
-
|
|
202
|
+
procedures[newPath] = item;
|
|
203
|
+
aggregate[key] = item;
|
|
176
204
|
}
|
|
205
|
+
|
|
206
|
+
return aggregate;
|
|
177
207
|
}
|
|
178
|
-
|
|
208
|
+
const record = step(input);
|
|
179
209
|
|
|
180
210
|
const _def: AnyRouter['_def'] = {
|
|
181
211
|
_config: config,
|
|
182
212
|
router: true,
|
|
183
|
-
procedures
|
|
213
|
+
procedures,
|
|
184
214
|
...emptyRouter,
|
|
185
|
-
record
|
|
215
|
+
record,
|
|
186
216
|
};
|
|
187
217
|
|
|
188
|
-
|
|
189
|
-
...
|
|
218
|
+
return {
|
|
219
|
+
...record,
|
|
190
220
|
_def,
|
|
191
|
-
createCaller(ctx) {
|
|
221
|
+
createCaller(ctx: TRoot['ctx']) {
|
|
192
222
|
const proxy = createRecursiveProxy(({ path, args }) => {
|
|
193
223
|
const fullPath = path.join('.');
|
|
194
224
|
const procedure = _def.procedures[fullPath] as AnyProcedure;
|
|
@@ -204,21 +234,21 @@ export function createRouterFactory<TRoot extends AnyRootTypes>(
|
|
|
204
234
|
return proxy as ReturnType<RouterCaller<any, any>>;
|
|
205
235
|
},
|
|
206
236
|
};
|
|
237
|
+
}
|
|
207
238
|
|
|
208
|
-
|
|
209
|
-
};
|
|
239
|
+
return createRouterInner;
|
|
210
240
|
}
|
|
211
241
|
|
|
212
242
|
function isProcedure(
|
|
213
|
-
procedureOrRouter:
|
|
243
|
+
procedureOrRouter: ValueOf<CreateRouterOptions>,
|
|
214
244
|
): procedureOrRouter is AnyProcedure {
|
|
215
|
-
return
|
|
245
|
+
return typeof procedureOrRouter === 'function';
|
|
216
246
|
}
|
|
217
247
|
/**
|
|
218
248
|
* @internal
|
|
219
249
|
*/
|
|
220
250
|
export function callProcedure(
|
|
221
|
-
opts: ProcedureCallOptions & { procedures:
|
|
251
|
+
opts: ProcedureCallOptions & { procedures: RouterRecord },
|
|
222
252
|
) {
|
|
223
253
|
const { type, path } = opts;
|
|
224
254
|
const proc = opts.procedures[path];
|
|
@@ -233,7 +263,7 @@ export function callProcedure(
|
|
|
233
263
|
}
|
|
234
264
|
|
|
235
265
|
export function createCallerFactory<TRoot extends AnyRootTypes>() {
|
|
236
|
-
return function createCallerInner<TRecord extends
|
|
266
|
+
return function createCallerInner<TRecord extends RouterRecord>(
|
|
237
267
|
router: Router<TRoot, TRecord>,
|
|
238
268
|
): RouterCaller<TRoot, TRecord> {
|
|
239
269
|
const _def = router._def;
|
|
@@ -274,7 +304,7 @@ type MergeRouters<
|
|
|
274
304
|
TRouters extends AnyRouter[],
|
|
275
305
|
TRoot extends AnyRootTypes = TRouters[0]['_def']['_config']['$types'],
|
|
276
306
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
277
|
-
TRecord extends
|
|
307
|
+
TRecord extends RouterRecord = {},
|
|
278
308
|
> = TRouters extends [
|
|
279
309
|
infer Head extends AnyRouter,
|
|
280
310
|
...infer Tail extends AnyRouter[],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ValueOf } from '../types';
|
|
1
2
|
import { invert } from '../utils';
|
|
2
3
|
|
|
3
4
|
// reference: https://www.jsonrpc.org/specification
|
|
@@ -39,6 +40,5 @@ export const TRPC_ERROR_CODES_BY_KEY = {
|
|
|
39
40
|
|
|
40
41
|
export const TRPC_ERROR_CODES_BY_NUMBER = invert(TRPC_ERROR_CODES_BY_KEY);
|
|
41
42
|
|
|
42
|
-
type ValueOf<TObj> = TObj[keyof TObj];
|
|
43
43
|
export type TRPC_ERROR_CODE_NUMBER = ValueOf<typeof TRPC_ERROR_CODES_BY_KEY>;
|
|
44
44
|
export type TRPC_ERROR_CODE_KEY = keyof typeof TRPC_ERROR_CODES_BY_KEY;
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var search = require('./search.js');
|
|
4
|
-
var utils = require('./utils.js');
|
|
5
|
-
|
|
6
|
-
const mergeArrays2 = Function.prototype.apply.bind(utils.mergeArrays, undefined);
|
|
7
|
-
const dash = utils.stringToArray('--');
|
|
8
|
-
const CRLF = utils.stringToArray('\r\n');
|
|
9
|
-
function parseContentDisposition(header) {
|
|
10
|
-
const parts = header.split(';').map(part => part.trim());
|
|
11
|
-
if (parts.shift() !== 'form-data') {
|
|
12
|
-
throw new Error('malformed content-disposition header: missing "form-data" in `' + JSON.stringify(parts) + '`');
|
|
13
|
-
}
|
|
14
|
-
const out = {};
|
|
15
|
-
for (const part of parts) {
|
|
16
|
-
const kv = part.split('=', 2);
|
|
17
|
-
if (kv.length !== 2) {
|
|
18
|
-
throw new Error('malformed content-disposition header: key-value pair not found - ' + part + ' in `' + header + '`');
|
|
19
|
-
}
|
|
20
|
-
const [name, value] = kv;
|
|
21
|
-
if (value[0] === '"' && value[value.length - 1] === '"') {
|
|
22
|
-
out[name] = value.slice(1, -1).replace(/\\"/g, '"');
|
|
23
|
-
} else if (value[0] !== '"' && value[value.length - 1] !== '"') {
|
|
24
|
-
out[name] = value;
|
|
25
|
-
} else if (value[0] === '"' && value[value.length - 1] !== '"' || value[0] !== '"' && value[value.length - 1] === '"') {
|
|
26
|
-
throw new Error('malformed content-disposition header: mismatched quotations in `' + header + '`');
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (!out.name) {
|
|
30
|
-
throw new Error('malformed content-disposition header: missing field name in `' + header + '`');
|
|
31
|
-
}
|
|
32
|
-
return out;
|
|
33
|
-
}
|
|
34
|
-
function parsePartHeaders(lines) {
|
|
35
|
-
const entries = [];
|
|
36
|
-
let disposition = false;
|
|
37
|
-
let line;
|
|
38
|
-
while (typeof (line = lines.shift()) !== 'undefined') {
|
|
39
|
-
const colon = line.indexOf(':');
|
|
40
|
-
if (colon === -1) {
|
|
41
|
-
throw new Error('malformed multipart-form header: missing colon');
|
|
42
|
-
}
|
|
43
|
-
const header = line.slice(0, colon).trim().toLowerCase();
|
|
44
|
-
const value = line.slice(colon + 1).trim();
|
|
45
|
-
switch (header) {
|
|
46
|
-
case 'content-disposition':
|
|
47
|
-
disposition = true;
|
|
48
|
-
entries.push(...Object.entries(parseContentDisposition(value)));
|
|
49
|
-
break;
|
|
50
|
-
case 'content-type':
|
|
51
|
-
entries.push([
|
|
52
|
-
'contentType',
|
|
53
|
-
value
|
|
54
|
-
]);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (!disposition) {
|
|
58
|
-
throw new Error('malformed multipart-form header: missing content-disposition');
|
|
59
|
-
}
|
|
60
|
-
return Object.fromEntries(entries);
|
|
61
|
-
}
|
|
62
|
-
async function readHeaderLines(it, needle) {
|
|
63
|
-
let firstChunk = true;
|
|
64
|
-
let lastTokenWasMatch = false;
|
|
65
|
-
const headerLines = [[]];
|
|
66
|
-
const crlfSearch = new search.StreamSearch(CRLF);
|
|
67
|
-
for (;;) {
|
|
68
|
-
const result = await it.next();
|
|
69
|
-
if (result.done) {
|
|
70
|
-
throw new Error('malformed multipart-form data: unexpected end of stream');
|
|
71
|
-
}
|
|
72
|
-
if (firstChunk && result.value !== search.MATCH && utils.arraysEqual(result.value.slice(0, 2), dash)) {
|
|
73
|
-
return [
|
|
74
|
-
undefined,
|
|
75
|
-
new Uint8Array()
|
|
76
|
-
];
|
|
77
|
-
}
|
|
78
|
-
let chunk;
|
|
79
|
-
if (result.value !== search.MATCH) {
|
|
80
|
-
chunk = result.value;
|
|
81
|
-
} else if (!lastTokenWasMatch) {
|
|
82
|
-
chunk = needle;
|
|
83
|
-
} else {
|
|
84
|
-
throw new Error('malformed multipart-form data: unexpected boundary');
|
|
85
|
-
}
|
|
86
|
-
if (!chunk.length) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (firstChunk) {
|
|
90
|
-
firstChunk = false;
|
|
91
|
-
}
|
|
92
|
-
const tokens = crlfSearch.feed(chunk);
|
|
93
|
-
for (const [i, token] of tokens.entries()) {
|
|
94
|
-
const isMatch = token === search.MATCH;
|
|
95
|
-
if (!isMatch && !token.length) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (lastTokenWasMatch && isMatch) {
|
|
99
|
-
tokens.push(crlfSearch.end());
|
|
100
|
-
return [
|
|
101
|
-
headerLines.filter(chunks => chunks.length).map(mergeArrays2).map(utils.arrayToString),
|
|
102
|
-
utils.mergeArrays(...tokens.slice(i + 1).map(token => token === search.MATCH ? CRLF : token))
|
|
103
|
-
];
|
|
104
|
-
}
|
|
105
|
-
if (lastTokenWasMatch = isMatch) {
|
|
106
|
-
headerLines.push([]);
|
|
107
|
-
} else {
|
|
108
|
-
headerLines[headerLines.length - 1].push(token);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
async function* streamMultipart(body, boundary) {
|
|
114
|
-
const needle = utils.mergeArrays(dash, utils.stringToArray(boundary));
|
|
115
|
-
const it = new search.ReadableStreamSearch(needle, body)[Symbol.asyncIterator]();
|
|
116
|
-
for (;;) {
|
|
117
|
-
const result = await it.next();
|
|
118
|
-
if (result.done) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (result.value === search.MATCH) {
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const crlfSearch = new search.StreamSearch(CRLF);
|
|
126
|
-
for (;;) {
|
|
127
|
-
const [headerLines, tail] = await readHeaderLines(it, needle);
|
|
128
|
-
if (!headerLines) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
async function nextToken() {
|
|
132
|
-
const result = await it.next();
|
|
133
|
-
if (result.done) {
|
|
134
|
-
throw new Error('malformed multipart-form data: unexpected end of stream');
|
|
135
|
-
}
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
let trailingCRLF = false;
|
|
139
|
-
function feedChunk(chunk) {
|
|
140
|
-
const chunks = [];
|
|
141
|
-
for (const token of crlfSearch.feed(chunk)) {
|
|
142
|
-
if (trailingCRLF) {
|
|
143
|
-
chunks.push(CRLF);
|
|
144
|
-
}
|
|
145
|
-
if (!(trailingCRLF = token === search.MATCH)) {
|
|
146
|
-
chunks.push(token);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return utils.mergeArrays(...chunks);
|
|
150
|
-
}
|
|
151
|
-
let done = false;
|
|
152
|
-
async function nextChunk() {
|
|
153
|
-
const result = await nextToken();
|
|
154
|
-
let chunk;
|
|
155
|
-
if (result.value !== search.MATCH) {
|
|
156
|
-
chunk = result.value;
|
|
157
|
-
} else if (!trailingCRLF) {
|
|
158
|
-
chunk = CRLF;
|
|
159
|
-
} else {
|
|
160
|
-
done = true;
|
|
161
|
-
return { value: crlfSearch.end() };
|
|
162
|
-
}
|
|
163
|
-
return { value: feedChunk(chunk) };
|
|
164
|
-
}
|
|
165
|
-
const bufferedChunks = [{ value: feedChunk(tail) }];
|
|
166
|
-
yield {
|
|
167
|
-
...parsePartHeaders(headerLines),
|
|
168
|
-
data: {
|
|
169
|
-
[Symbol.asyncIterator]() {
|
|
170
|
-
return this;
|
|
171
|
-
},
|
|
172
|
-
async next() {
|
|
173
|
-
for (;;) {
|
|
174
|
-
const result = bufferedChunks.shift();
|
|
175
|
-
if (!result) {
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
if (result.value.length > 0) {
|
|
179
|
-
return result;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
for (;;) {
|
|
183
|
-
if (done) {
|
|
184
|
-
return {
|
|
185
|
-
done,
|
|
186
|
-
value: undefined
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
const result = await nextChunk();
|
|
190
|
-
if (result.value.length > 0) {
|
|
191
|
-
return result;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
while (!done) {
|
|
198
|
-
bufferedChunks.push(await nextChunk());
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
exports.streamMultipart = streamMultipart;
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { ReadableStreamSearch, MATCH, StreamSearch } from './search.mjs';
|
|
2
|
-
import { mergeArrays, stringToArray, arraysEqual, arrayToString } from './utils.mjs';
|
|
3
|
-
|
|
4
|
-
const mergeArrays2 = Function.prototype.apply.bind(mergeArrays, undefined);
|
|
5
|
-
const dash = stringToArray('--');
|
|
6
|
-
const CRLF = stringToArray('\r\n');
|
|
7
|
-
function parseContentDisposition(header) {
|
|
8
|
-
const parts = header.split(';').map(part => part.trim());
|
|
9
|
-
if (parts.shift() !== 'form-data') {
|
|
10
|
-
throw new Error('malformed content-disposition header: missing "form-data" in `' + JSON.stringify(parts) + '`');
|
|
11
|
-
}
|
|
12
|
-
const out = {};
|
|
13
|
-
for (const part of parts) {
|
|
14
|
-
const kv = part.split('=', 2);
|
|
15
|
-
if (kv.length !== 2) {
|
|
16
|
-
throw new Error('malformed content-disposition header: key-value pair not found - ' + part + ' in `' + header + '`');
|
|
17
|
-
}
|
|
18
|
-
const [name, value] = kv;
|
|
19
|
-
if (value[0] === '"' && value[value.length - 1] === '"') {
|
|
20
|
-
out[name] = value.slice(1, -1).replace(/\\"/g, '"');
|
|
21
|
-
} else if (value[0] !== '"' && value[value.length - 1] !== '"') {
|
|
22
|
-
out[name] = value;
|
|
23
|
-
} else if (value[0] === '"' && value[value.length - 1] !== '"' || value[0] !== '"' && value[value.length - 1] === '"') {
|
|
24
|
-
throw new Error('malformed content-disposition header: mismatched quotations in `' + header + '`');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (!out.name) {
|
|
28
|
-
throw new Error('malformed content-disposition header: missing field name in `' + header + '`');
|
|
29
|
-
}
|
|
30
|
-
return out;
|
|
31
|
-
}
|
|
32
|
-
function parsePartHeaders(lines) {
|
|
33
|
-
const entries = [];
|
|
34
|
-
let disposition = false;
|
|
35
|
-
let line;
|
|
36
|
-
while (typeof (line = lines.shift()) !== 'undefined') {
|
|
37
|
-
const colon = line.indexOf(':');
|
|
38
|
-
if (colon === -1) {
|
|
39
|
-
throw new Error('malformed multipart-form header: missing colon');
|
|
40
|
-
}
|
|
41
|
-
const header = line.slice(0, colon).trim().toLowerCase();
|
|
42
|
-
const value = line.slice(colon + 1).trim();
|
|
43
|
-
switch (header) {
|
|
44
|
-
case 'content-disposition':
|
|
45
|
-
disposition = true;
|
|
46
|
-
entries.push(...Object.entries(parseContentDisposition(value)));
|
|
47
|
-
break;
|
|
48
|
-
case 'content-type':
|
|
49
|
-
entries.push([
|
|
50
|
-
'contentType',
|
|
51
|
-
value
|
|
52
|
-
]);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (!disposition) {
|
|
56
|
-
throw new Error('malformed multipart-form header: missing content-disposition');
|
|
57
|
-
}
|
|
58
|
-
return Object.fromEntries(entries);
|
|
59
|
-
}
|
|
60
|
-
async function readHeaderLines(it, needle) {
|
|
61
|
-
let firstChunk = true;
|
|
62
|
-
let lastTokenWasMatch = false;
|
|
63
|
-
const headerLines = [[]];
|
|
64
|
-
const crlfSearch = new StreamSearch(CRLF);
|
|
65
|
-
for (;;) {
|
|
66
|
-
const result = await it.next();
|
|
67
|
-
if (result.done) {
|
|
68
|
-
throw new Error('malformed multipart-form data: unexpected end of stream');
|
|
69
|
-
}
|
|
70
|
-
if (firstChunk && result.value !== MATCH && arraysEqual(result.value.slice(0, 2), dash)) {
|
|
71
|
-
return [
|
|
72
|
-
undefined,
|
|
73
|
-
new Uint8Array()
|
|
74
|
-
];
|
|
75
|
-
}
|
|
76
|
-
let chunk;
|
|
77
|
-
if (result.value !== MATCH) {
|
|
78
|
-
chunk = result.value;
|
|
79
|
-
} else if (!lastTokenWasMatch) {
|
|
80
|
-
chunk = needle;
|
|
81
|
-
} else {
|
|
82
|
-
throw new Error('malformed multipart-form data: unexpected boundary');
|
|
83
|
-
}
|
|
84
|
-
if (!chunk.length) {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (firstChunk) {
|
|
88
|
-
firstChunk = false;
|
|
89
|
-
}
|
|
90
|
-
const tokens = crlfSearch.feed(chunk);
|
|
91
|
-
for (const [i, token] of tokens.entries()) {
|
|
92
|
-
const isMatch = token === MATCH;
|
|
93
|
-
if (!isMatch && !token.length) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (lastTokenWasMatch && isMatch) {
|
|
97
|
-
tokens.push(crlfSearch.end());
|
|
98
|
-
return [
|
|
99
|
-
headerLines.filter(chunks => chunks.length).map(mergeArrays2).map(arrayToString),
|
|
100
|
-
mergeArrays(...tokens.slice(i + 1).map(token => token === MATCH ? CRLF : token))
|
|
101
|
-
];
|
|
102
|
-
}
|
|
103
|
-
if (lastTokenWasMatch = isMatch) {
|
|
104
|
-
headerLines.push([]);
|
|
105
|
-
} else {
|
|
106
|
-
headerLines[headerLines.length - 1].push(token);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
async function* streamMultipart(body, boundary) {
|
|
112
|
-
const needle = mergeArrays(dash, stringToArray(boundary));
|
|
113
|
-
const it = new ReadableStreamSearch(needle, body)[Symbol.asyncIterator]();
|
|
114
|
-
for (;;) {
|
|
115
|
-
const result = await it.next();
|
|
116
|
-
if (result.done) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (result.value === MATCH) {
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const crlfSearch = new StreamSearch(CRLF);
|
|
124
|
-
for (;;) {
|
|
125
|
-
const [headerLines, tail] = await readHeaderLines(it, needle);
|
|
126
|
-
if (!headerLines) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
async function nextToken() {
|
|
130
|
-
const result = await it.next();
|
|
131
|
-
if (result.done) {
|
|
132
|
-
throw new Error('malformed multipart-form data: unexpected end of stream');
|
|
133
|
-
}
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
let trailingCRLF = false;
|
|
137
|
-
function feedChunk(chunk) {
|
|
138
|
-
const chunks = [];
|
|
139
|
-
for (const token of crlfSearch.feed(chunk)) {
|
|
140
|
-
if (trailingCRLF) {
|
|
141
|
-
chunks.push(CRLF);
|
|
142
|
-
}
|
|
143
|
-
if (!(trailingCRLF = token === MATCH)) {
|
|
144
|
-
chunks.push(token);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return mergeArrays(...chunks);
|
|
148
|
-
}
|
|
149
|
-
let done = false;
|
|
150
|
-
async function nextChunk() {
|
|
151
|
-
const result = await nextToken();
|
|
152
|
-
let chunk;
|
|
153
|
-
if (result.value !== MATCH) {
|
|
154
|
-
chunk = result.value;
|
|
155
|
-
} else if (!trailingCRLF) {
|
|
156
|
-
chunk = CRLF;
|
|
157
|
-
} else {
|
|
158
|
-
done = true;
|
|
159
|
-
return { value: crlfSearch.end() };
|
|
160
|
-
}
|
|
161
|
-
return { value: feedChunk(chunk) };
|
|
162
|
-
}
|
|
163
|
-
const bufferedChunks = [{ value: feedChunk(tail) }];
|
|
164
|
-
yield {
|
|
165
|
-
...parsePartHeaders(headerLines),
|
|
166
|
-
data: {
|
|
167
|
-
[Symbol.asyncIterator]() {
|
|
168
|
-
return this;
|
|
169
|
-
},
|
|
170
|
-
async next() {
|
|
171
|
-
for (;;) {
|
|
172
|
-
const result = bufferedChunks.shift();
|
|
173
|
-
if (!result) {
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
if (result.value.length > 0) {
|
|
177
|
-
return result;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
for (;;) {
|
|
181
|
-
if (done) {
|
|
182
|
-
return {
|
|
183
|
-
done,
|
|
184
|
-
value: undefined
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
const result = await nextChunk();
|
|
188
|
-
if (result.value.length > 0) {
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
while (!done) {
|
|
196
|
-
bufferedChunks.push(await nextChunk());
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export { streamMultipart };
|