@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.
Files changed (29) hide show
  1. package/dist/@trpc/server/index.d.ts +1 -5
  2. package/dist/@trpc/server/index.d.ts.map +1 -1
  3. package/dist/adapters/node-http/content-type/form-data/index.js +2 -2
  4. package/dist/adapters/node-http/content-type/form-data/index.mjs +1 -1
  5. package/dist/bundle-analysis.json +65 -123
  6. package/dist/unstable-core-do-not-import/index.d.ts +1 -1
  7. package/dist/unstable-core-do-not-import/index.d.ts.map +1 -1
  8. package/dist/unstable-core-do-not-import/initTRPC.d.ts +15 -7
  9. package/dist/unstable-core-do-not-import/initTRPC.d.ts.map +1 -1
  10. package/dist/unstable-core-do-not-import/router.d.ts +25 -18
  11. package/dist/unstable-core-do-not-import/router.d.ts.map +1 -1
  12. package/dist/unstable-core-do-not-import/router.js +36 -19
  13. package/dist/unstable-core-do-not-import/router.mjs +37 -20
  14. package/dist/unstable-core-do-not-import/rpc/codes.d.ts +1 -2
  15. package/dist/unstable-core-do-not-import/rpc/codes.d.ts.map +1 -1
  16. package/dist/unstable-core-do-not-import/types.d.ts +1 -0
  17. package/dist/unstable-core-do-not-import/types.d.ts.map +1 -1
  18. package/package.json +2 -2
  19. package/src/@trpc/server/index.ts +1 -5
  20. package/src/unstable-core-do-not-import/index.ts +0 -2
  21. package/src/unstable-core-do-not-import/router.ts +84 -54
  22. package/src/unstable-core-do-not-import/rpc/codes.ts +1 -1
  23. package/src/unstable-core-do-not-import/types.ts +1 -0
  24. package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/index.js +0 -203
  25. package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/index.mjs +0 -201
  26. package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/search.js +0 -167
  27. package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/search.mjs +0 -163
  28. package/dist/node_modules/.pnpm/@web3-storage_multipart-parser@1.0.0/node_modules/@web3-storage/multipart-parser/esm/src/utils.js +0 -35
  29. 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
- /** @internal **/
16
- export type ProcedureRecord = Record<string, AnyProcedure>;
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 DecoratedProcedureRecord<
30
- TProcedures extends ProcedureRouterRecord,
31
- > = {
32
- [TKey in keyof TProcedures]: TProcedures[TKey] extends AnyRouter
33
- ? DecoratedProcedureRecord<TProcedures[TKey]['_def']['record']>
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 ProcedureRouterRecord,
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
- ) => DecoratedProcedureRecord<TRecord>;
47
+ ) => DecorateRouterRecord<TRecord>;
53
48
 
54
49
  export interface Router<
55
50
  TRoot extends AnyRootTypes,
56
- TRecord extends ProcedureRouterRecord,
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
- TRecord extends ProcedureRouterRecord,
75
- > = Router<TRoot, TRecord> & TRecord;
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
- TRouter extends AnyRouter,
86
+ TRoot extends AnyRootTypes,
87
+ TRecord extends RouterRecord,
92
88
  > = {
93
- [TKey in keyof TRouter['_def']['record']]: TRouter['_def']['record'][TKey] extends infer TRouterOrProcedure
94
- ? TRouterOrProcedure extends AnyRouter
95
- ? GetInferenceHelpers<TType, TRouterOrProcedure>
96
- : TRouterOrProcedure extends AnyProcedure
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<TRouterOrProcedure>
99
- : inferTransformedProcedureOutput<TRouter, TRouterOrProcedure>
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: AnyProcedure | AnyRouter,
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
- return function createRouterInner<
149
- TProcRouterRecord extends ProcedureRouterRecord,
150
- >(procedures: TProcRouterRecord): BuiltRouter<TRoot, TProcRouterRecord> {
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(procedures).filter((v) => reservedWords.includes(v)),
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 routerProcedures: ProcedureRecord = omitPrototype({});
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
- if (isRouter(procedureOrRouter)) {
167
- recursiveGetPaths(procedureOrRouter._def.procedures, `${newPath}.`);
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
- if (routerProcedures[newPath]) {
196
+ const newPath = [...path, key].join('.');
197
+
198
+ if (procedures[newPath]) {
172
199
  throw new Error(`Duplicate key: ${newPath}`);
173
200
  }
174
201
 
175
- routerProcedures[newPath] = procedureOrRouter;
202
+ procedures[newPath] = item;
203
+ aggregate[key] = item;
176
204
  }
205
+
206
+ return aggregate;
177
207
  }
178
- recursiveGetPaths(procedures);
208
+ const record = step(input);
179
209
 
180
210
  const _def: AnyRouter['_def'] = {
181
211
  _config: config,
182
212
  router: true,
183
- procedures: routerProcedures,
213
+ procedures,
184
214
  ...emptyRouter,
185
- record: procedures,
215
+ record,
186
216
  };
187
217
 
188
- const router: BuiltRouter<TRoot, TProcRouterRecord> = {
189
- ...procedures,
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
- return router;
209
- };
239
+ return createRouterInner;
210
240
  }
211
241
 
212
242
  function isProcedure(
213
- procedureOrRouter: AnyProcedure | AnyRouter,
243
+ procedureOrRouter: ValueOf<CreateRouterOptions>,
214
244
  ): procedureOrRouter is AnyProcedure {
215
- return !!procedureOrRouter._def.procedure;
245
+ return typeof procedureOrRouter === 'function';
216
246
  }
217
247
  /**
218
248
  * @internal
219
249
  */
220
250
  export function callProcedure(
221
- opts: ProcedureCallOptions & { procedures: ProcedureRouterRecord },
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 ProcedureRouterRecord>(
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 ProcedureRouterRecord = {},
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;
@@ -154,3 +154,4 @@ export const ERROR_SYMBOL = Symbol('TypeError');
154
154
  export type TypeError<TMessage extends string> = TMessage & {
155
155
  _: typeof ERROR_SYMBOL;
156
156
  };
157
+ export type ValueOf<TObj> = TObj[keyof TObj];
@@ -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 };