@bytecodealliance/preview2-shim 0.17.8 → 0.18.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,11 +1,11 @@
1
- import { streams, poll } from './io.js';
1
+ import { streams, poll } from "./io.js";
2
2
 
3
3
  const { InputStream, OutputStream } = streams;
4
4
  const { Pollable } = poll;
5
5
 
6
- const symbolDispose = Symbol.dispose || Symbol.for('dispose');
6
+ const symbolDispose = Symbol.dispose || Symbol.for("dispose");
7
7
  const utf8Decoder = new TextDecoder();
8
- const forbiddenHeaders = new Set(['connection', 'keep-alive', 'host']);
8
+ const forbiddenHeaders = new Set(["connection", "keep-alive", "host"]);
9
9
  const DEFAULT_HTTP_TIMEOUT_NS = 600_000_000_000n;
10
10
 
11
11
  // RFC 9110 compliant header validation
@@ -13,133 +13,142 @@ const TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
13
13
  const FIELD_VALUE_RE = /^[\t\x20-\x7E\x80-\xFF]*$/;
14
14
 
15
15
  function validateHeaderName(name) {
16
- if (!TOKEN_RE.test(name)) {
17
- throw { tag: 'invalid-syntax' };
18
- }
16
+ if (!TOKEN_RE.test(name)) {
17
+ throw { tag: "invalid-syntax" };
18
+ }
19
19
  }
20
20
 
21
21
  function validateHeaderValue(value) {
22
- const str = typeof value === 'string' ? value : utf8Decoder.decode(value);
23
- if (!FIELD_VALUE_RE.test(str)) {
24
- throw { tag: 'invalid-syntax' };
25
- }
22
+ const str = typeof value === "string" ? value : utf8Decoder.decode(value);
23
+ if (!FIELD_VALUE_RE.test(str)) {
24
+ throw { tag: "invalid-syntax" };
25
+ }
26
26
  }
27
27
 
28
28
  class Fields {
29
- #immutable = false;
30
- /** @type {[string, Uint8Array][]} */ #entries = [];
31
- /** @type {Map<string, [string, Uint8Array][]>} */ #table = new Map();
32
-
33
- static fromList(entries) {
34
- const fields = new Fields();
35
- for (const [key, value] of entries) {
36
- fields.append(key, value);
37
- }
38
- return fields;
39
- }
40
-
41
- get(name) {
42
- const tableEntries = this.#table.get(name.toLowerCase());
43
- if (!tableEntries) {
44
- return [];
45
- }
46
- return tableEntries.map(([, v]) => v);
47
- }
48
-
49
- set(name, values) {
50
- if (this.#immutable) {
51
- throw { tag: 'immutable' };
52
- }
53
- validateHeaderName(name);
54
- for (const value of values) {
55
- validateHeaderValue(value);
56
- }
57
- const lowercased = name.toLowerCase();
58
- if (forbiddenHeaders.has(lowercased)) {
59
- throw { tag: 'forbidden' };
60
- }
61
- const tableEntries = this.#table.get(lowercased);
62
- if (tableEntries) {
63
- this.#entries = this.#entries.filter(
64
- (entry) => !tableEntries.includes(entry)
65
- );
66
- tableEntries.splice(0, tableEntries.length);
67
- } else {
68
- this.#table.set(lowercased, []);
69
- }
70
- const newTableEntries = this.#table.get(lowercased);
71
- for (const value of values) {
72
- const entry = [name, value];
73
- this.#entries.push(entry);
74
- newTableEntries.push(entry);
75
- }
76
- }
77
-
78
- has(name) {
79
- return this.#table.has(name.toLowerCase());
80
- }
81
-
82
- delete(name) {
83
- if (this.#immutable) {
84
- throw { tag: 'immutable' };
85
- }
86
- const lowercased = name.toLowerCase();
87
- const tableEntries = this.#table.get(lowercased);
88
- if (tableEntries) {
89
- this.#entries = this.#entries.filter(
90
- (entry) => !tableEntries.includes(entry)
91
- );
92
- this.#table.delete(lowercased);
93
- }
94
- }
95
-
96
- append(name, value) {
97
- if (this.#immutable) {
98
- throw { tag: 'immutable' };
99
- }
100
- validateHeaderName(name);
101
- validateHeaderValue(value);
102
- const lowercased = name.toLowerCase();
103
- if (forbiddenHeaders.has(lowercased)) {
104
- throw { tag: 'forbidden' };
105
- }
106
- const entry = [name, value];
107
- this.#entries.push(entry);
108
- const tableEntries = this.#table.get(lowercased);
109
- if (tableEntries) {
110
- tableEntries.push(entry);
111
- } else {
112
- this.#table.set(lowercased, [entry]);
113
- }
114
- }
115
-
116
- entries() {
117
- return this.#entries;
118
- }
119
-
120
- clone() {
121
- return fieldsFromEntriesChecked(this.#entries);
122
- }
123
-
124
- static _lock(fields) {
125
- fields.#immutable = true;
126
- return fields;
127
- }
128
-
129
- static _fromEntriesChecked(entries) {
130
- const fields = new Fields();
131
- fields.#entries = entries;
132
- for (const entry of entries) {
133
- const lowercase = entry[0].toLowerCase();
134
- const existing = fields.#table.get(lowercase);
135
- if (existing) {
136
- existing.push(entry);
137
- } else {
138
- fields.#table.set(lowercase, [entry]);
139
- }
140
- }
141
- return fields;
142
- }
29
+ #immutable = false;
30
+ /** @type {[string, Uint8Array][]} */ #entries = [];
31
+ /** @type {Map<string, [string, Uint8Array][]>} */ #table = new Map();
32
+
33
+ static fromList(entries) {
34
+ const fields = new Fields();
35
+ for (const [key, value] of entries) {
36
+ fields.append(key, value);
37
+ }
38
+ return fields;
39
+ }
40
+
41
+ get(name) {
42
+ const tableEntries = this.#table.get(name.toLowerCase());
43
+ if (!tableEntries) {
44
+ return [];
45
+ }
46
+ return tableEntries.map(([, v]) => v);
47
+ }
48
+
49
+ /**
50
+ * WIT spec (https://github.com/WebAssembly/WASI/blob/91bb44c3c3a9b1e09187db23c85fa844d1fd6b15/proposals/http/wit/types.wit#L215-L223):
51
+ *
52
+ * > Set all of the values for a name. Clears any existing values for that
53
+ * > name, if they have been set.
54
+ * >
55
+ * > Fails with `header-error.immutable` if the `fields` are immutable.
56
+ * >
57
+ * > Fails with `header-error.invalid-syntax` if the `field-name` or any of
58
+ * > the `field-value`s are syntactically invalid.
59
+ *
60
+ * The existing-branch splice/reuse is an allocation optimization — values are cleared, not retained.
61
+ */
62
+ set(name, values) {
63
+ if (this.#immutable) {
64
+ throw { tag: "immutable" };
65
+ }
66
+ validateHeaderName(name);
67
+ for (const value of values) {
68
+ validateHeaderValue(value);
69
+ }
70
+ const lowercased = name.toLowerCase();
71
+ if (forbiddenHeaders.has(lowercased)) {
72
+ throw { tag: "forbidden" };
73
+ }
74
+ const tableEntries = this.#table.get(lowercased);
75
+ if (tableEntries) {
76
+ this.#entries = this.#entries.filter((entry) => !tableEntries.includes(entry));
77
+ tableEntries.splice(0, tableEntries.length);
78
+ } else {
79
+ this.#table.set(lowercased, []);
80
+ }
81
+ const newTableEntries = this.#table.get(lowercased);
82
+ for (const value of values) {
83
+ const entry = [name, value];
84
+ this.#entries.push(entry);
85
+ newTableEntries.push(entry);
86
+ }
87
+ }
88
+
89
+ has(name) {
90
+ return this.#table.has(name.toLowerCase());
91
+ }
92
+
93
+ delete(name) {
94
+ if (this.#immutable) {
95
+ throw { tag: "immutable" };
96
+ }
97
+ const lowercased = name.toLowerCase();
98
+ const tableEntries = this.#table.get(lowercased);
99
+ if (tableEntries) {
100
+ this.#entries = this.#entries.filter((entry) => !tableEntries.includes(entry));
101
+ this.#table.delete(lowercased);
102
+ }
103
+ }
104
+
105
+ append(name, value) {
106
+ if (this.#immutable) {
107
+ throw { tag: "immutable" };
108
+ }
109
+ validateHeaderName(name);
110
+ validateHeaderValue(value);
111
+ const lowercased = name.toLowerCase();
112
+ if (forbiddenHeaders.has(lowercased)) {
113
+ throw { tag: "forbidden" };
114
+ }
115
+ const entry = [name, value];
116
+ this.#entries.push(entry);
117
+ const tableEntries = this.#table.get(lowercased);
118
+ if (tableEntries) {
119
+ tableEntries.push(entry);
120
+ } else {
121
+ this.#table.set(lowercased, [entry]);
122
+ }
123
+ }
124
+
125
+ entries() {
126
+ return this.#entries;
127
+ }
128
+
129
+ clone() {
130
+ return fieldsFromEntriesChecked(this.#entries);
131
+ }
132
+
133
+ static _lock(fields) {
134
+ fields.#immutable = true;
135
+ return fields;
136
+ }
137
+
138
+ static _fromEntriesChecked(entries) {
139
+ const fields = new Fields();
140
+ fields.#entries = entries;
141
+ for (const entry of entries) {
142
+ const lowercase = entry[0].toLowerCase();
143
+ const existing = fields.#table.get(lowercase);
144
+ if (existing) {
145
+ existing.push(entry);
146
+ } else {
147
+ fields.#table.set(lowercase, [entry]);
148
+ }
149
+ }
150
+ return fields;
151
+ }
143
152
  }
144
153
  const fieldsLock = Fields._lock;
145
154
  delete Fields._lock;
@@ -147,96 +156,96 @@ const fieldsFromEntriesChecked = Fields._fromEntriesChecked;
147
156
  delete Fields._fromEntriesChecked;
148
157
 
149
158
  class RequestOptions {
150
- #connectTimeout = DEFAULT_HTTP_TIMEOUT_NS;
151
- #firstByteTimeout = DEFAULT_HTTP_TIMEOUT_NS;
152
- #betweenBytesTimeout = DEFAULT_HTTP_TIMEOUT_NS;
153
- connectTimeout() {
154
- return this.#connectTimeout;
155
- }
156
- setConnectTimeout(duration) {
157
- if (duration < 0n) {
158
- throw new Error('duration must not be negative');
159
- }
160
- this.#connectTimeout = duration;
161
- }
162
- firstByteTimeout() {
163
- return this.#firstByteTimeout;
164
- }
165
- setFirstByteTimeout(duration) {
166
- if (duration < 0n) {
167
- throw new Error('duration must not be negative');
168
- }
169
- this.#firstByteTimeout = duration;
170
- }
171
- betweenBytesTimeout() {
172
- return this.#betweenBytesTimeout;
173
- }
174
- setBetweenBytesTimeout(duration) {
175
- if (duration < 0n) {
176
- throw new Error('duration must not be negative');
177
- }
178
- this.#betweenBytesTimeout = duration;
179
- }
159
+ #connectTimeout = DEFAULT_HTTP_TIMEOUT_NS;
160
+ #firstByteTimeout = DEFAULT_HTTP_TIMEOUT_NS;
161
+ #betweenBytesTimeout = DEFAULT_HTTP_TIMEOUT_NS;
162
+ connectTimeout() {
163
+ return this.#connectTimeout;
164
+ }
165
+ setConnectTimeout(duration) {
166
+ if (duration < 0n) {
167
+ throw new Error("duration must not be negative");
168
+ }
169
+ this.#connectTimeout = duration;
170
+ }
171
+ firstByteTimeout() {
172
+ return this.#firstByteTimeout;
173
+ }
174
+ setFirstByteTimeout(duration) {
175
+ if (duration < 0n) {
176
+ throw new Error("duration must not be negative");
177
+ }
178
+ this.#firstByteTimeout = duration;
179
+ }
180
+ betweenBytesTimeout() {
181
+ return this.#betweenBytesTimeout;
182
+ }
183
+ setBetweenBytesTimeout(duration) {
184
+ if (duration < 0n) {
185
+ throw new Error("duration must not be negative");
186
+ }
187
+ this.#betweenBytesTimeout = duration;
188
+ }
180
189
  }
181
190
 
182
191
  class OutgoingBody {
183
- #outputStream = null;
184
- #chunks = [];
185
- #finished = false;
186
-
187
- write() {
188
- const outputStream = this.#outputStream;
189
- if (outputStream === null) {
190
- throw undefined;
191
- }
192
- this.#outputStream = null;
193
- return outputStream;
194
- }
195
-
196
- static finish(body, trailers) {
197
- if (trailers) {
198
- throw { tag: 'internal-error', val: 'trailers unsupported' };
199
- }
200
- if (body.#finished) {
201
- throw { tag: 'internal-error', val: 'body already finished' };
202
- }
203
- body.#finished = true;
204
- }
205
-
206
- static _bodyData(outgoingBody) {
207
- if (outgoingBody.#chunks.length === 0) {
208
- return null;
209
- }
210
- let totalLen = 0;
211
- for (const chunk of outgoingBody.#chunks) {
212
- totalLen += chunk.byteLength;
213
- }
214
- const result = new Uint8Array(totalLen);
215
- let offset = 0;
216
- for (const chunk of outgoingBody.#chunks) {
217
- result.set(chunk, offset);
218
- offset += chunk.byteLength;
219
- }
220
- return result;
221
- }
222
-
223
- static _create() {
224
- const outgoingBody = new OutgoingBody();
225
- const chunks = outgoingBody.#chunks;
226
- outgoingBody.#outputStream = new OutputStream({
227
- write(buf) {
228
- chunks.push(new Uint8Array(buf));
229
- },
230
- flush() {},
231
- blockingFlush() {},
232
- subscribe() {
233
- return new Pollable();
234
- },
235
- });
236
- return outgoingBody;
237
- }
192
+ #outputStream = null;
193
+ #chunks = [];
194
+ #finished = false;
195
+
196
+ write() {
197
+ const outputStream = this.#outputStream;
198
+ if (outputStream === null) {
199
+ throw undefined;
200
+ }
201
+ this.#outputStream = null;
202
+ return outputStream;
203
+ }
204
+
205
+ static finish(body, trailers) {
206
+ if (trailers) {
207
+ throw { tag: "internal-error", val: "trailers unsupported" };
208
+ }
209
+ if (body.#finished) {
210
+ throw { tag: "internal-error", val: "body already finished" };
211
+ }
212
+ body.#finished = true;
213
+ }
214
+
215
+ static _bodyData(outgoingBody) {
216
+ if (outgoingBody.#chunks.length === 0) {
217
+ return null;
218
+ }
219
+ let totalLen = 0;
220
+ for (const chunk of outgoingBody.#chunks) {
221
+ totalLen += chunk.byteLength;
222
+ }
223
+ const result = new Uint8Array(totalLen);
224
+ let offset = 0;
225
+ for (const chunk of outgoingBody.#chunks) {
226
+ result.set(chunk, offset);
227
+ offset += chunk.byteLength;
228
+ }
229
+ return result;
230
+ }
231
+
232
+ static _create() {
233
+ const outgoingBody = new OutgoingBody();
234
+ const chunks = outgoingBody.#chunks;
235
+ outgoingBody.#outputStream = new OutputStream({
236
+ write(buf) {
237
+ chunks.push(new Uint8Array(buf));
238
+ },
239
+ flush() {},
240
+ blockingFlush() {},
241
+ subscribe() {
242
+ return new Pollable();
243
+ },
244
+ });
245
+ return outgoingBody;
246
+ }
238
247
 
239
- [symbolDispose]() {}
248
+ [symbolDispose]() {}
240
249
  }
241
250
  const outgoingBodyCreate = OutgoingBody._create;
242
251
  delete OutgoingBody._create;
@@ -244,449 +253,455 @@ const outgoingBodyData = OutgoingBody._bodyData;
244
253
  delete OutgoingBody._bodyData;
245
254
 
246
255
  class OutgoingRequest {
247
- /** @type {{ tag: string, val?: string }} */ #method = { tag: 'get' };
248
- /** @type {{ tag: string, val?: string } | undefined} */ #scheme = undefined;
249
- /** @type {string | undefined} */ #pathWithQuery = undefined;
250
- /** @type {string | undefined} */ #authority = undefined;
251
- /** @type {Fields} */ #headers;
252
- /** @type {OutgoingBody} */ #body;
253
- #bodyRequested = false;
254
-
255
- constructor(headers) {
256
- fieldsLock(headers);
257
- this.#headers = headers;
258
- this.#body = outgoingBodyCreate();
259
- }
260
-
261
- body() {
262
- if (this.#bodyRequested) {
263
- throw new Error('Body already requested');
264
- }
265
- this.#bodyRequested = true;
266
- return this.#body;
267
- }
256
+ /** @type {{ tag: string, val?: string }} */ #method = { tag: "get" };
257
+ /** @type {{ tag: string, val?: string } | undefined} */ #scheme = undefined;
258
+ /** @type {string | undefined} */ #pathWithQuery = undefined;
259
+ /** @type {string | undefined} */ #authority = undefined;
260
+ /** @type {Fields} */ #headers;
261
+ /** @type {OutgoingBody} */ #body;
262
+ #bodyRequested = false;
268
263
 
269
- method() {
270
- return this.#method;
271
- }
264
+ constructor(headers) {
265
+ fieldsLock(headers);
266
+ this.#headers = headers;
267
+ this.#body = outgoingBodyCreate();
268
+ }
272
269
 
273
- setMethod(method) {
274
- if (method.tag === 'other' && !method.val.match(/^[a-zA-Z-]+$/)) {
275
- throw undefined;
276
- }
277
- this.#method = method;
270
+ body() {
271
+ if (this.#bodyRequested) {
272
+ throw new Error("Body already requested");
278
273
  }
274
+ this.#bodyRequested = true;
275
+ return this.#body;
276
+ }
279
277
 
280
- pathWithQuery() {
281
- return this.#pathWithQuery;
282
- }
278
+ method() {
279
+ return this.#method;
280
+ }
283
281
 
284
- setPathWithQuery(pathWithQuery) {
285
- if (
286
- pathWithQuery &&
287
- !pathWithQuery.match(/^[a-zA-Z0-9.\-_~!$&'()*+,;=:@%?/]+$/)
288
- ) {
289
- throw undefined;
290
- }
291
- this.#pathWithQuery = pathWithQuery;
282
+ setMethod(method) {
283
+ if (method.tag === "other" && !method.val.match(/^[a-zA-Z-]+$/)) {
284
+ throw undefined;
292
285
  }
286
+ this.#method = method;
287
+ }
293
288
 
294
- scheme() {
295
- return this.#scheme;
296
- }
289
+ pathWithQuery() {
290
+ return this.#pathWithQuery;
291
+ }
297
292
 
298
- setScheme(scheme) {
299
- if (scheme?.tag === 'other' && !scheme.val.match(/^[a-zA-Z]+$/)) {
300
- throw undefined;
301
- }
302
- this.#scheme = scheme;
293
+ setPathWithQuery(pathWithQuery) {
294
+ if (pathWithQuery && !pathWithQuery.match(/^[a-zA-Z0-9.\-_~!$&'()*+,;=:@%?/]+$/)) {
295
+ throw undefined;
303
296
  }
297
+ this.#pathWithQuery = pathWithQuery;
298
+ }
304
299
 
305
- authority() {
306
- return this.#authority;
307
- }
300
+ scheme() {
301
+ return this.#scheme;
302
+ }
308
303
 
309
- setAuthority(authority) {
310
- if (authority) {
311
- const [host, port, ...extra] = authority.split(':');
312
- const portNum = Number(port);
313
- if (
314
- extra.length ||
315
- (port !== undefined &&
316
- (portNum.toString() !== port || portNum > 65535)) ||
317
- !host.match(/^[a-zA-Z0-9-.]+$/)
318
- ) {
319
- throw undefined;
320
- }
321
- }
322
- this.#authority = authority;
304
+ setScheme(scheme) {
305
+ if (scheme?.tag === "other" && !scheme.val.match(/^[a-zA-Z]+$/)) {
306
+ throw undefined;
323
307
  }
308
+ this.#scheme = scheme;
309
+ }
310
+
311
+ authority() {
312
+ return this.#authority;
313
+ }
324
314
 
325
- headers() {
326
- return this.#headers;
315
+ setAuthority(authority) {
316
+ if (authority) {
317
+ const [host, port, ...extra] = authority.split(":");
318
+ const portNum = Number(port);
319
+ if (
320
+ extra.length ||
321
+ (port !== undefined && (portNum.toString() !== port || portNum > 65535)) ||
322
+ !host.match(/^[a-zA-Z0-9-.]+$/)
323
+ ) {
324
+ throw undefined;
325
+ }
327
326
  }
327
+ this.#authority = authority;
328
+ }
328
329
 
329
- [symbolDispose]() {}
330
+ headers() {
331
+ return this.#headers;
332
+ }
330
333
 
331
- static _handle(request, options) {
332
- const scheme = schemeString(request.#scheme);
333
- const method = request.#method.val || request.#method.tag;
334
+ [symbolDispose]() {}
334
335
 
335
- if (!request.#pathWithQuery) {
336
- throw { tag: 'HTTP-request-URI-invalid' };
337
- }
336
+ static _handle(request, options) {
337
+ const scheme = schemeString(request.#scheme);
338
+ const method = request.#method.val || request.#method.tag;
338
339
 
339
- const url = `${scheme}//${request.#authority || ''}${request.#pathWithQuery}`;
340
+ if (!request.#pathWithQuery) {
341
+ throw { tag: "HTTP-request-URI-invalid" };
342
+ }
340
343
 
341
- const headers = new Headers();
342
- for (const [key, value] of request.#headers.entries()) {
343
- const lowerKey = key.toLowerCase();
344
- if (!forbiddenHeaders.has(lowerKey)) {
345
- headers.set(key, utf8Decoder.decode(value));
346
- }
347
- }
344
+ const url = `${scheme}//${request.#authority || ""}${request.#pathWithQuery}`;
348
345
 
349
- const bodyData = outgoingBodyData(request.#body);
346
+ const headers = new Headers();
347
+ for (const [key, value] of request.#headers.entries()) {
348
+ const lowerKey = key.toLowerCase();
349
+ if (!forbiddenHeaders.has(lowerKey)) {
350
+ headers.set(key, utf8Decoder.decode(value));
351
+ }
352
+ }
350
353
 
351
- let timeoutMs = Number(DEFAULT_HTTP_TIMEOUT_NS / 1_000_000n);
352
- if (options) {
353
- const ct = options.connectTimeout?.() ?? DEFAULT_HTTP_TIMEOUT_NS;
354
- const fbt = options.firstByteTimeout?.() ?? DEFAULT_HTTP_TIMEOUT_NS;
355
- const minTimeout = ct < fbt ? ct : fbt;
356
- timeoutMs = Number(minTimeout / 1_000_000n);
357
- }
354
+ const bodyData = outgoingBodyData(request.#body);
358
355
 
359
- return futureIncomingResponseCreate(
360
- url, method.toUpperCase(), headers, bodyData, timeoutMs
361
- );
356
+ let timeoutMs = Number(DEFAULT_HTTP_TIMEOUT_NS / 1_000_000n);
357
+ if (options) {
358
+ const ct = options.connectTimeout?.() ?? DEFAULT_HTTP_TIMEOUT_NS;
359
+ const fbt = options.firstByteTimeout?.() ?? DEFAULT_HTTP_TIMEOUT_NS;
360
+ const minTimeout = ct < fbt ? ct : fbt;
361
+ timeoutMs = Number(minTimeout / 1_000_000n);
362
362
  }
363
+
364
+ return futureIncomingResponseCreate(url, method.toUpperCase(), headers, bodyData, timeoutMs);
365
+ }
363
366
  }
364
367
  const outgoingRequestHandle = OutgoingRequest._handle;
365
368
  delete OutgoingRequest._handle;
366
369
 
367
370
  class IncomingBody {
368
- #finished = false;
369
- #stream = undefined;
370
-
371
- stream() {
372
- if (!this.#stream) {
373
- throw undefined;
374
- }
375
- const stream = this.#stream;
376
- this.#stream = null;
377
- return stream;
378
- }
379
-
380
- static finish(incomingBody) {
381
- if (incomingBody.#finished) {
382
- throw new Error('incoming body already finished');
383
- }
384
- incomingBody.#finished = true;
385
- return futureTrailersCreate();
386
- }
387
-
388
- [symbolDispose]() {}
389
-
390
- static _create(fetchResponse) {
391
- const incomingBody = new IncomingBody();
392
- let buffer = null;
393
- let bufferOffset = 0;
394
- let done = false;
395
- let reader = null;
396
- let readPromise = null;
397
-
398
- function ensureReader() {
399
- if (!reader && fetchResponse.body) {
400
- reader = fetchResponse.body.getReader();
371
+ #finished = false;
372
+ #stream = undefined;
373
+
374
+ stream() {
375
+ if (!this.#stream) {
376
+ throw undefined;
377
+ }
378
+ const stream = this.#stream;
379
+ this.#stream = null;
380
+ return stream;
381
+ }
382
+
383
+ static finish(incomingBody) {
384
+ if (incomingBody.#finished) {
385
+ throw new Error("incoming body already finished");
386
+ }
387
+ incomingBody.#finished = true;
388
+ return futureTrailersCreate();
389
+ }
390
+
391
+ [symbolDispose]() {}
392
+
393
+ static _create(fetchResponse) {
394
+ const incomingBody = new IncomingBody();
395
+ let buffer = null;
396
+ let bufferOffset = 0;
397
+ let done = false;
398
+ let reader = null;
399
+ let readPromise = null;
400
+
401
+ function ensureReader() {
402
+ if (!reader && fetchResponse.body) {
403
+ reader = fetchResponse.body.getReader();
404
+ }
405
+ }
406
+
407
+ function startRead() {
408
+ if (readPromise || done) {
409
+ return;
410
+ }
411
+ ensureReader();
412
+ if (!reader) {
413
+ done = true;
414
+ return;
415
+ }
416
+ readPromise = reader.read().then(
417
+ (result) => {
418
+ readPromise = null;
419
+ if (result.done) {
420
+ done = true;
421
+ } else {
422
+ buffer = result.value;
423
+ bufferOffset = 0;
424
+ }
425
+ },
426
+ () => {
427
+ readPromise = null;
428
+ done = true;
429
+ },
430
+ );
431
+ }
432
+
433
+ incomingBody.#stream = new InputStream({
434
+ read(len) {
435
+ if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
436
+ throw { tag: "closed" };
437
+ }
438
+ if (buffer !== null && bufferOffset < buffer.byteLength) {
439
+ const available = buffer.byteLength - bufferOffset;
440
+ const toRead = Math.min(Number(len), available);
441
+ const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
442
+ bufferOffset += toRead;
443
+ if (bufferOffset >= buffer.byteLength) {
444
+ buffer = null;
445
+ bufferOffset = 0;
446
+ if (!done) {
447
+ startRead();
401
448
  }
402
- }
403
-
404
- function startRead() {
405
- if (readPromise || done) { return; }
406
- ensureReader();
407
- if (!reader) {
408
- done = true;
409
- return;
449
+ }
450
+ return slice;
451
+ }
452
+ throw { tag: "would-block" };
453
+ },
454
+ blockingRead(len) {
455
+ if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
456
+ throw { tag: "closed" };
457
+ }
458
+ if (buffer !== null && bufferOffset < buffer.byteLength) {
459
+ const available = buffer.byteLength - bufferOffset;
460
+ const toRead = Math.min(Number(len), available);
461
+ const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
462
+ bufferOffset += toRead;
463
+ if (bufferOffset >= buffer.byteLength) {
464
+ buffer = null;
465
+ bufferOffset = 0;
466
+ if (!done) {
467
+ startRead();
410
468
  }
411
- readPromise = reader.read().then(
412
- (result) => {
413
- readPromise = null;
414
- if (result.done) {
415
- done = true;
416
- } else {
417
- buffer = result.value;
418
- bufferOffset = 0;
419
- }
420
- },
421
- () => {
422
- readPromise = null;
423
- done = true;
424
- }
425
- );
469
+ }
470
+ return slice;
426
471
  }
427
-
428
- incomingBody.#stream = new InputStream({
429
- read(len) {
430
- if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
431
- throw { tag: 'closed' };
432
- }
433
- if (buffer !== null && bufferOffset < buffer.byteLength) {
434
- const available = buffer.byteLength - bufferOffset;
435
- const toRead = Math.min(Number(len), available);
436
- const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
437
- bufferOffset += toRead;
438
- if (bufferOffset >= buffer.byteLength) {
439
- buffer = null;
440
- bufferOffset = 0;
441
- if (!done) { startRead(); }
442
- }
443
- return slice;
444
- }
445
- throw { tag: 'would-block' };
446
- },
447
- blockingRead(len) {
448
- if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
449
- throw { tag: 'closed' };
450
- }
451
- if (buffer !== null && bufferOffset < buffer.byteLength) {
452
- const available = buffer.byteLength - bufferOffset;
453
- const toRead = Math.min(Number(len), available);
454
- const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
455
- bufferOffset += toRead;
456
- if (bufferOffset >= buffer.byteLength) {
457
- buffer = null;
458
- bufferOffset = 0;
459
- if (!done) { startRead(); }
460
- }
461
- return slice;
462
- }
463
- startRead();
464
- const waitFor = readPromise || Promise.resolve();
465
- return waitFor.then(() => {
466
- if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
467
- throw { tag: 'closed' };
468
- }
469
- if (buffer !== null && bufferOffset < buffer.byteLength) {
470
- const available = buffer.byteLength - bufferOffset;
471
- const toRead = Math.min(Number(len), available);
472
- const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
473
- bufferOffset += toRead;
474
- if (bufferOffset >= buffer.byteLength) {
475
- buffer = null;
476
- bufferOffset = 0;
477
- if (!done) { startRead(); }
478
- }
479
- return slice;
480
- }
481
- throw { tag: 'closed' };
482
- });
483
- },
484
- subscribe() {
485
- if (done || (buffer !== null && bufferOffset < buffer.byteLength)) {
486
- return new Pollable();
487
- }
472
+ startRead();
473
+ const waitFor = readPromise || Promise.resolve();
474
+ return waitFor.then(() => {
475
+ if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
476
+ throw { tag: "closed" };
477
+ }
478
+ if (buffer !== null && bufferOffset < buffer.byteLength) {
479
+ const available = buffer.byteLength - bufferOffset;
480
+ const toRead = Math.min(Number(len), available);
481
+ const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
482
+ bufferOffset += toRead;
483
+ if (bufferOffset >= buffer.byteLength) {
484
+ buffer = null;
485
+ bufferOffset = 0;
486
+ if (!done) {
488
487
  startRead();
489
- if (readPromise) {
490
- return new Pollable(readPromise);
491
- }
492
- return new Pollable();
493
- },
488
+ }
489
+ }
490
+ return slice;
491
+ }
492
+ throw { tag: "closed" };
494
493
  });
495
-
494
+ },
495
+ subscribe() {
496
+ if (done || (buffer !== null && bufferOffset < buffer.byteLength)) {
497
+ return new Pollable();
498
+ }
496
499
  startRead();
497
- return incomingBody;
498
- }
500
+ if (readPromise) {
501
+ return new Pollable(readPromise);
502
+ }
503
+ return new Pollable();
504
+ },
505
+ });
506
+
507
+ startRead();
508
+ return incomingBody;
509
+ }
499
510
  }
500
511
  const incomingBodyCreate = IncomingBody._create;
501
512
  delete IncomingBody._create;
502
513
 
503
514
  class IncomingResponse {
504
- /** @type {Fields} */ #headers = undefined;
505
- #status = 0;
506
- /** @type {IncomingBody} */ #body;
507
-
508
- status() {
509
- return this.#status;
510
- }
511
-
512
- headers() {
513
- return this.#headers;
514
- }
515
-
516
- consume() {
517
- if (this.#body === undefined) {
518
- throw undefined;
519
- }
520
- const body = this.#body;
521
- this.#body = undefined;
522
- return body;
523
- }
524
-
525
- [symbolDispose]() {}
526
-
527
- static _create(fetchResponse) {
528
- const res = new IncomingResponse();
529
- res.#status = fetchResponse.status;
530
-
531
- const headerEntries = [];
532
- const encoder = new TextEncoder();
533
- fetchResponse.headers.forEach((value, key) => {
534
- headerEntries.push([key, encoder.encode(value)]);
535
- });
536
- res.#headers = fieldsLock(fieldsFromEntriesChecked(headerEntries));
537
- res.#body = incomingBodyCreate(fetchResponse);
538
- return res;
539
- }
515
+ /** @type {Fields} */ #headers = undefined;
516
+ #status = 0;
517
+ /** @type {IncomingBody} */ #body;
518
+
519
+ status() {
520
+ return this.#status;
521
+ }
522
+
523
+ headers() {
524
+ return this.#headers;
525
+ }
526
+
527
+ consume() {
528
+ if (this.#body === undefined) {
529
+ throw undefined;
530
+ }
531
+ const body = this.#body;
532
+ this.#body = undefined;
533
+ return body;
534
+ }
535
+
536
+ [symbolDispose]() {}
537
+
538
+ static _create(fetchResponse) {
539
+ const res = new IncomingResponse();
540
+ res.#status = fetchResponse.status;
541
+
542
+ const headerEntries = [];
543
+ const encoder = new TextEncoder();
544
+ fetchResponse.headers.forEach((value, key) => {
545
+ headerEntries.push([key, encoder.encode(value)]);
546
+ });
547
+ res.#headers = fieldsLock(fieldsFromEntriesChecked(headerEntries));
548
+ res.#body = incomingBodyCreate(fetchResponse);
549
+ return res;
550
+ }
540
551
  }
541
552
  const incomingResponseCreate = IncomingResponse._create;
542
553
  delete IncomingResponse._create;
543
554
 
544
555
  class FutureTrailers {
545
- #requested = false;
546
- subscribe() {
547
- return new Pollable();
548
- }
549
- get() {
550
- if (this.#requested) {
551
- return { tag: 'err' };
552
- }
553
- this.#requested = true;
554
- return {
555
- tag: 'ok',
556
- val: {
557
- tag: 'ok',
558
- val: undefined,
559
- },
560
- };
561
- }
562
- static _create() {
563
- return new FutureTrailers();
564
- }
556
+ #requested = false;
557
+ subscribe() {
558
+ return new Pollable();
559
+ }
560
+ get() {
561
+ if (this.#requested) {
562
+ return { tag: "err" };
563
+ }
564
+ this.#requested = true;
565
+ return {
566
+ tag: "ok",
567
+ val: {
568
+ tag: "ok",
569
+ val: undefined,
570
+ },
571
+ };
572
+ }
573
+ static _create() {
574
+ return new FutureTrailers();
575
+ }
565
576
  }
566
577
  const futureTrailersCreate = FutureTrailers._create;
567
578
  delete FutureTrailers._create;
568
579
 
569
580
  function mapFetchError(err) {
570
- if (err.name === 'AbortError') {
571
- return { tag: 'connection-timeout' };
572
- }
573
- if (err.name === 'TypeError') {
574
- return { tag: 'connection-refused' };
575
- }
576
- return { tag: 'internal-error', val: err.message };
581
+ if (err.name === "AbortError") {
582
+ return { tag: "connection-timeout" };
583
+ }
584
+ if (err.name === "TypeError") {
585
+ return { tag: "connection-refused" };
586
+ }
587
+ return { tag: "internal-error", val: err.message };
577
588
  }
578
589
 
579
590
  class FutureIncomingResponse {
580
- #result = undefined;
581
- #promise = null;
591
+ #result = undefined;
592
+ #promise = null;
582
593
 
583
- subscribe() {
584
- return new Pollable(this.#promise);
585
- }
594
+ subscribe() {
595
+ return new Pollable(this.#promise);
596
+ }
586
597
 
587
- get() {
588
- if (this.#result === undefined) {
589
- return undefined;
590
- }
591
- const result = this.#result;
592
- this.#result = { tag: 'err' };
593
- return result;
598
+ get() {
599
+ if (this.#result === undefined) {
600
+ return undefined;
594
601
  }
602
+ const result = this.#result;
603
+ this.#result = { tag: "err" };
604
+ return result;
605
+ }
606
+
607
+ [symbolDispose]() {
608
+ this.#promise = null;
609
+ }
595
610
 
596
- [symbolDispose]() {
597
- this.#promise = null;
611
+ static _create(url, method, headers, bodyData, timeoutMs) {
612
+ const future = new FutureIncomingResponse();
613
+
614
+ const controller = new AbortController();
615
+ let timer;
616
+ if (timeoutMs < Infinity) {
617
+ timer = setTimeout(() => controller.abort(), timeoutMs);
598
618
  }
599
619
 
600
- static _create(url, method, headers, bodyData, timeoutMs) {
601
- const future = new FutureIncomingResponse();
620
+ const init = {
621
+ method,
622
+ headers,
623
+ signal: controller.signal,
624
+ };
625
+ if (bodyData && method !== "GET" && method !== "HEAD") {
626
+ init.body = bodyData;
627
+ }
602
628
 
603
- const controller = new AbortController();
604
- let timer;
605
- if (timeoutMs < Infinity) {
606
- timer = setTimeout(() => controller.abort(), timeoutMs);
629
+ future.#promise = fetch(url, init).then(
630
+ (response) => {
631
+ if (timer) {
632
+ clearTimeout(timer);
607
633
  }
608
-
609
- const init = {
610
- method,
611
- headers,
612
- signal: controller.signal,
634
+ future.#result = {
635
+ tag: "ok",
636
+ val: {
637
+ tag: "ok",
638
+ val: incomingResponseCreate(response),
639
+ },
613
640
  };
614
- if (bodyData && method !== 'GET' && method !== 'HEAD') {
615
- init.body = bodyData;
616
- }
617
-
618
- future.#promise = fetch(url, init).then(
619
- (response) => {
620
- if (timer) { clearTimeout(timer); }
621
- future.#result = {
622
- tag: 'ok',
623
- val: {
624
- tag: 'ok',
625
- val: incomingResponseCreate(response),
626
- },
627
- };
628
- },
629
- (err) => {
630
- if (timer) { clearTimeout(timer); }
631
- future.#result = {
632
- tag: 'ok',
633
- val: {
634
- tag: 'err',
635
- val: mapFetchError(err),
636
- },
637
- };
638
- }
639
- );
641
+ },
642
+ (err) => {
643
+ if (timer) {
644
+ clearTimeout(timer);
645
+ }
646
+ future.#result = {
647
+ tag: "ok",
648
+ val: {
649
+ tag: "err",
650
+ val: mapFetchError(err),
651
+ },
652
+ };
653
+ },
654
+ );
640
655
 
641
- return future;
642
- }
656
+ return future;
657
+ }
643
658
  }
644
659
  const futureIncomingResponseCreate = FutureIncomingResponse._create;
645
660
  delete FutureIncomingResponse._create;
646
661
 
647
662
  function schemeString(scheme) {
648
- if (!scheme) {
649
- return 'https:';
650
- }
651
- switch (scheme.tag) {
652
- case 'HTTP':
653
- return 'http:';
654
- case 'HTTPS':
655
- return 'https:';
656
- case 'other':
657
- return scheme.val.toLowerCase() + ':';
658
- }
663
+ if (!scheme) {
664
+ return "https:";
665
+ }
666
+ switch (scheme.tag) {
667
+ case "HTTP":
668
+ return "http:";
669
+ case "HTTPS":
670
+ return "https:";
671
+ case "other":
672
+ return scheme.val.toLowerCase() + ":";
673
+ }
659
674
  }
660
675
 
661
676
  function httpErrorCode(err) {
662
- if (err.payload) {
663
- return err.payload;
664
- }
665
- return {
666
- tag: 'internal-error',
667
- val: err.message,
668
- };
677
+ if (err.payload) {
678
+ return err.payload;
679
+ }
680
+ return {
681
+ tag: "internal-error",
682
+ val: err.message,
683
+ };
669
684
  }
670
685
 
671
686
  export const outgoingHandler = {
672
- handle: outgoingRequestHandle,
687
+ handle: outgoingRequestHandle,
673
688
  };
674
689
 
675
690
  export const incomingHandler = {
676
- handle() {},
691
+ handle() {},
677
692
  };
678
693
 
679
694
  export const types = {
680
- Fields,
681
- FutureIncomingResponse,
682
- FutureTrailers,
683
- IncomingBody,
684
- IncomingRequest: class IncomingRequest {},
685
- IncomingResponse,
686
- OutgoingBody,
687
- OutgoingRequest,
688
- OutgoingResponse: class OutgoingResponse {},
689
- ResponseOutparam: class ResponseOutparam {},
690
- RequestOptions,
691
- httpErrorCode,
695
+ Fields,
696
+ FutureIncomingResponse,
697
+ FutureTrailers,
698
+ IncomingBody,
699
+ IncomingRequest: class IncomingRequest {},
700
+ IncomingResponse,
701
+ OutgoingBody,
702
+ OutgoingRequest,
703
+ OutgoingResponse: class OutgoingResponse {},
704
+ ResponseOutparam: class ResponseOutparam {},
705
+ RequestOptions,
706
+ httpErrorCode,
692
707
  };