goscript 0.2.2 → 0.2.4

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 (160) hide show
  1. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  2. package/compiler/lowering.go +279 -16
  3. package/compiler/override-registry_test.go +175 -0
  4. package/compiler/protobuf-ts-binding.go +154 -6
  5. package/compiler/protobuf-ts-binding_test.go +7 -2
  6. package/compiler/runtime-contract.go +2 -0
  7. package/compiler/runtime-contract_test.go +1 -0
  8. package/compiler/semantic-model.go +16 -0
  9. package/compiler/semantic-model_test.go +38 -0
  10. package/compiler/skeleton_test.go +522 -17
  11. package/compiler/typescript-emitter.go +4 -0
  12. package/dist/gs/builtin/builtin.js +7 -9
  13. package/dist/gs/builtin/builtin.js.map +1 -1
  14. package/dist/gs/builtin/defer.js +2 -2
  15. package/dist/gs/builtin/hostio.js +5 -5
  16. package/dist/gs/builtin/hostio.js.map +1 -1
  17. package/dist/gs/builtin/map.js +2 -1
  18. package/dist/gs/builtin/map.js.map +1 -1
  19. package/dist/gs/builtin/slice.d.ts +3 -0
  20. package/dist/gs/builtin/slice.js +39 -0
  21. package/dist/gs/builtin/slice.js.map +1 -1
  22. package/dist/gs/builtin/type.js +49 -0
  23. package/dist/gs/builtin/type.js.map +1 -1
  24. package/dist/gs/compress/gzip/index.d.ts +41 -0
  25. package/dist/gs/compress/gzip/index.js +235 -0
  26. package/dist/gs/compress/gzip/index.js.map +1 -0
  27. package/dist/gs/compress/zlib/index.js +5 -2
  28. package/dist/gs/compress/zlib/index.js.map +1 -1
  29. package/dist/gs/crypto/ecdh/index.js +27 -8
  30. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  31. package/dist/gs/crypto/ed25519/index.js +3 -3
  32. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  33. package/dist/gs/crypto/rand/index.js +6 -3
  34. package/dist/gs/crypto/rand/index.js.map +1 -1
  35. package/dist/gs/embed/index.js +9 -3
  36. package/dist/gs/embed/index.js.map +1 -1
  37. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  38. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  39. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  40. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  41. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  42. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  43. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  44. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  45. package/dist/gs/hash/fnv/index.js +13 -5
  46. package/dist/gs/hash/fnv/index.js.map +1 -1
  47. package/dist/gs/io/fs/glob.d.ts +3 -3
  48. package/dist/gs/io/fs/glob.js +9 -9
  49. package/dist/gs/io/fs/glob.js.map +1 -1
  50. package/dist/gs/io/fs/readdir.d.ts +2 -2
  51. package/dist/gs/io/fs/readdir.js +13 -74
  52. package/dist/gs/io/fs/readdir.js.map +1 -1
  53. package/dist/gs/io/fs/readlink.d.ts +1 -1
  54. package/dist/gs/io/fs/readlink.js +2 -2
  55. package/dist/gs/io/fs/readlink.js.map +1 -1
  56. package/dist/gs/io/fs/stat.d.ts +4 -2
  57. package/dist/gs/io/fs/stat.js +12 -73
  58. package/dist/gs/io/fs/stat.js.map +1 -1
  59. package/dist/gs/io/fs/sub.d.ts +2 -2
  60. package/dist/gs/io/fs/sub.js +11 -11
  61. package/dist/gs/io/fs/sub.js.map +1 -1
  62. package/dist/gs/io/fs/walk.js +2 -2
  63. package/dist/gs/io/fs/walk.js.map +1 -1
  64. package/dist/gs/maps/iter.js.map +1 -1
  65. package/dist/gs/maps/maps.js.map +1 -1
  66. package/dist/gs/mime/index.js +5 -2
  67. package/dist/gs/mime/index.js.map +1 -1
  68. package/dist/gs/net/http/httptest/index.js +6 -3
  69. package/dist/gs/net/http/httptest/index.js.map +1 -1
  70. package/dist/gs/net/http/index.d.ts +34 -18
  71. package/dist/gs/net/http/index.js +280 -63
  72. package/dist/gs/net/http/index.js.map +1 -1
  73. package/dist/gs/net/http/pprof/index.d.ts +5 -5
  74. package/dist/gs/net/http/pprof/index.js +21 -21
  75. package/dist/gs/net/http/pprof/index.js.map +1 -1
  76. package/dist/gs/reflect/iter.js +1 -1
  77. package/dist/gs/reflect/iter.js.map +1 -1
  78. package/dist/gs/reflect/type.d.ts +2 -0
  79. package/dist/gs/reflect/type.js +53 -21
  80. package/dist/gs/reflect/type.js.map +1 -1
  81. package/dist/gs/runtime/pprof/index.js.map +1 -1
  82. package/dist/gs/runtime/runtime.js +2 -2
  83. package/dist/gs/runtime/runtime.js.map +1 -1
  84. package/dist/gs/runtime/trace/index.js.map +1 -1
  85. package/dist/gs/slices/slices.d.ts +1 -1
  86. package/dist/gs/slices/slices.js +37 -4
  87. package/dist/gs/slices/slices.js.map +1 -1
  88. package/gs/builtin/builtin.ts +11 -14
  89. package/gs/builtin/defer.ts +2 -2
  90. package/gs/builtin/hostio.ts +5 -5
  91. package/gs/builtin/map.ts +4 -1
  92. package/gs/builtin/runtime-contract.test.ts +25 -0
  93. package/gs/builtin/slice.test.ts +14 -0
  94. package/gs/builtin/slice.ts +64 -0
  95. package/gs/builtin/type.ts +72 -0
  96. package/gs/bytes/bytes.test.ts +14 -13
  97. package/gs/compress/gzip/index.test.ts +86 -0
  98. package/gs/compress/gzip/index.ts +297 -0
  99. package/gs/compress/gzip/meta.json +6 -0
  100. package/gs/compress/gzip/parity.json +45 -0
  101. package/gs/compress/zlib/index.test.ts +19 -5
  102. package/gs/compress/zlib/index.ts +16 -7
  103. package/gs/context/context.test.ts +3 -1
  104. package/gs/crypto/ecdh/index.test.ts +6 -2
  105. package/gs/crypto/ecdh/index.ts +49 -12
  106. package/gs/crypto/ed25519/index.ts +20 -7
  107. package/gs/crypto/rand/index.ts +6 -3
  108. package/gs/embed/index.test.ts +4 -4
  109. package/gs/embed/index.ts +9 -3
  110. package/gs/fmt/fmt.test.ts +29 -4
  111. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  112. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  113. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  114. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  115. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  116. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  117. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  118. package/gs/hash/fnv/index.test.ts +1 -8
  119. package/gs/hash/fnv/index.ts +27 -10
  120. package/gs/io/fs/glob.ts +14 -11
  121. package/gs/io/fs/meta.json +5 -0
  122. package/gs/io/fs/readdir.test.ts +63 -2
  123. package/gs/io/fs/readdir.ts +33 -30
  124. package/gs/io/fs/readlink.test.ts +2 -2
  125. package/gs/io/fs/readlink.ts +5 -2
  126. package/gs/io/fs/stat.test.ts +79 -0
  127. package/gs/io/fs/stat.ts +24 -10
  128. package/gs/io/fs/sub.test.ts +93 -0
  129. package/gs/io/fs/sub.ts +13 -13
  130. package/gs/io/fs/walk.ts +2 -2
  131. package/gs/maps/iter.ts +9 -9
  132. package/gs/maps/maps.ts +4 -4
  133. package/gs/math/bits/index.test.ts +10 -1
  134. package/gs/mime/index.test.ts +33 -15
  135. package/gs/mime/index.ts +9 -2
  136. package/gs/net/http/httptest/index.test.ts +17 -3
  137. package/gs/net/http/httptest/index.ts +8 -3
  138. package/gs/net/http/index.test.ts +851 -124
  139. package/gs/net/http/index.ts +612 -146
  140. package/gs/net/http/meta.json +3 -1
  141. package/gs/net/http/pprof/index.test.ts +4 -4
  142. package/gs/net/http/pprof/index.ts +43 -22
  143. package/gs/os/file_unix_js.test.ts +22 -0
  144. package/gs/reflect/iter.ts +4 -2
  145. package/gs/reflect/map.test.ts +56 -1
  146. package/gs/reflect/type.ts +76 -37
  147. package/gs/runtime/pprof/index.test.ts +7 -1
  148. package/gs/runtime/pprof/index.ts +5 -1
  149. package/gs/runtime/runtime.test.ts +7 -0
  150. package/gs/runtime/runtime.ts +2 -4
  151. package/gs/runtime/trace/index.test.ts +9 -1
  152. package/gs/runtime/trace/index.ts +5 -1
  153. package/gs/slices/meta.json +3 -0
  154. package/gs/slices/slices.test.ts +59 -21
  155. package/gs/slices/slices.ts +61 -20
  156. package/gs/strconv/complex.test.ts +17 -3
  157. package/gs/sync/atomic/doc_64.test.ts +2 -9
  158. package/gs/sync/sync.test.ts +18 -8
  159. package/gs/syscall/js/index.test.ts +9 -4
  160. package/package.json +5 -4
@@ -4,6 +4,8 @@ import * as context from '@goscript/context/index.js';
4
4
  import * as errors from '@goscript/errors/index.js';
5
5
  import * as fs from '@goscript/io/fs/fs.js';
6
6
  import * as io from '@goscript/io/index.js';
7
+ import * as mime from '@goscript/mime/index.js';
8
+ import * as path from '@goscript/path/index.js';
7
9
  import * as strings from '@goscript/strings/index.js';
8
10
  import * as time from '@goscript/time/index.js';
9
11
  export const StatusContinue = 100;
@@ -210,28 +212,39 @@ export const Header = Map;
210
212
  export function CanonicalHeaderKey(s) {
211
213
  return canonicalMIMEHeaderKey(s);
212
214
  }
215
+ function headerMap(h) {
216
+ let value = $.pointerValue(h);
217
+ while (value !== null &&
218
+ value !== undefined &&
219
+ typeof value === 'object' &&
220
+ '__goValue' in value) {
221
+ value = $.pointerValue(value.__goValue);
222
+ }
223
+ return value;
224
+ }
213
225
  export function Header_Add(h, key, value) {
226
+ const headers = headerMap(h);
214
227
  key = canonicalMIMEHeaderKey(key);
215
- const values = Array.from(h.get(key) ?? []);
228
+ const values = Array.from(headers.get(key) ?? []);
216
229
  values.push(value);
217
- h.set(key, $.arrayToSlice(values));
230
+ headers.set(key, $.arrayToSlice(values));
218
231
  }
219
232
  export function Header_Del(h, key) {
220
- h.delete(canonicalMIMEHeaderKey(key));
233
+ headerMap(h).delete(canonicalMIMEHeaderKey(key));
221
234
  }
222
235
  export function Header_Get(h, key) {
223
- const values = h.get(canonicalMIMEHeaderKey(key));
236
+ const values = headerMap(h).get(canonicalMIMEHeaderKey(key));
224
237
  return values == null || values.length === 0 ? '' : String(values[0]);
225
238
  }
226
239
  export function Header_Set(h, key, value) {
227
- h.set(canonicalMIMEHeaderKey(key), $.arrayToSlice([value]));
240
+ headerMap(h).set(canonicalMIMEHeaderKey(key), $.arrayToSlice([value]));
228
241
  }
229
242
  export function Header_Values(h, key) {
230
- return h.get(canonicalMIMEHeaderKey(key)) ?? null;
243
+ return headerMap(h).get(canonicalMIMEHeaderKey(key)) ?? null;
231
244
  }
232
245
  export function Header_Clone(h) {
233
246
  const cloned = new Header();
234
- for (const [key, values] of h.entries()) {
247
+ for (const [key, values] of headerMap(h).entries()) {
235
248
  cloned.set(key, $.arrayToSlice(Array.from(values ?? [])));
236
249
  }
237
250
  return cloned;
@@ -240,7 +253,7 @@ export function Header_Write(h, w) {
240
253
  return Header_WriteSubset(h, w, null);
241
254
  }
242
255
  export function Header_WriteSubset(h, w, exclude) {
243
- for (const [key, values] of h.entries()) {
256
+ for (const [key, values] of headerMap(h).entries()) {
244
257
  if (exclude?.get(key) === true) {
245
258
  continue;
246
259
  }
@@ -324,7 +337,10 @@ function parseRequestURL(rawURL) {
324
337
  }
325
338
  const parsed = new URL(rawURL, 'http://goscript.invalid');
326
339
  const hasHost = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(rawURL);
327
- return [new RequestURL(parsed.pathname, parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search, hasHost ? parsed.protocol.replace(/:$/, '') : '', hasHost ? parsed.host : ''), null];
340
+ return [
341
+ new RequestURL(parsed.pathname, parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search, hasHost ? parsed.protocol.replace(/:$/, '') : '', hasHost ? parsed.host : ''),
342
+ null,
343
+ ];
328
344
  }
329
345
  catch {
330
346
  return [null, errors.New(`parse "${rawURL}": invalid URL`)];
@@ -342,6 +358,46 @@ class responseBody {
342
358
  return null;
343
359
  }
344
360
  }
361
+ class fetchResponseBody {
362
+ fetched;
363
+ requestContext;
364
+ abortFetch;
365
+ stopContextWatch;
366
+ reader = null;
367
+ closed = false;
368
+ constructor(fetched, requestContext, abortFetch, stopContextWatch) {
369
+ this.fetched = fetched;
370
+ this.requestContext = requestContext;
371
+ this.abortFetch = abortFetch;
372
+ this.stopContextWatch = stopContextWatch;
373
+ }
374
+ async Read(p) {
375
+ if (this.closed) {
376
+ return [0, ErrBodyReadAfterClose];
377
+ }
378
+ if (this.reader == null) {
379
+ const [data, err] = await readFetchBody(this.fetched, this.requestContext, this.abortFetch);
380
+ if (err != null) {
381
+ this.stopContextWatch();
382
+ return [0, err];
383
+ }
384
+ this.stopContextWatch();
385
+ this.reader = bytes.NewReader(data);
386
+ }
387
+ return this.reader.Read(p);
388
+ }
389
+ Close() {
390
+ if (this.closed) {
391
+ return null;
392
+ }
393
+ this.closed = true;
394
+ this.stopContextWatch();
395
+ if (this.reader == null) {
396
+ this.abortFetch();
397
+ }
398
+ return null;
399
+ }
400
+ }
345
401
  class noBody {
346
402
  Read(_p) {
347
403
  return [0, io.EOF];
@@ -386,7 +442,9 @@ export class Cookie {
386
442
  this.Unparsed = init?.Unparsed ?? null;
387
443
  }
388
444
  String() {
389
- const parts = [`${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`];
445
+ const parts = [
446
+ `${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`,
447
+ ];
390
448
  if (this.Path !== '') {
391
449
  parts.push(`Path=${this.Path}`);
392
450
  }
@@ -429,12 +487,19 @@ function isToken(value) {
429
487
  return /^[!#$%&'*+\-.^_`{}|~0-9A-Za-z]+$/.test(value);
430
488
  }
431
489
  function validCookieValueByte(code) {
432
- return code >= 0x20 && code < 0x7f && code !== 0x22 && code !== 0x3b && code !== 0x5c;
490
+ return (code >= 0x20 &&
491
+ code < 0x7f &&
492
+ code !== 0x22 &&
493
+ code !== 0x3b &&
494
+ code !== 0x5c);
433
495
  }
434
496
  function parseCookieValue(raw, allowDoubleQuote) {
435
497
  let value = raw;
436
498
  let quoted = false;
437
- if (allowDoubleQuote && value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
499
+ if (allowDoubleQuote &&
500
+ value.length > 1 &&
501
+ value[0] === '"' &&
502
+ value[value.length - 1] === '"') {
438
503
  value = value.slice(1, -1);
439
504
  quoted = true;
440
505
  }
@@ -453,12 +518,12 @@ function asciiLower(value) {
453
518
  }
454
519
  return [value.toLowerCase(), true];
455
520
  }
456
- export function SetCookie(w, cookie) {
521
+ export async function SetCookie(w, cookie) {
457
522
  const c = $.pointerValue(cookie);
458
523
  if (w == null || c == null) {
459
524
  return;
460
525
  }
461
- Header_Add(w.Header(), 'Set-Cookie', c.String());
526
+ Header_Add(await w.Header(), 'Set-Cookie', c.String());
462
527
  }
463
528
  class memoryResponseWriter {
464
529
  Code = StatusOK;
@@ -507,7 +572,7 @@ function inProcessServerRequest(request) {
507
572
  const rawQuery = request.URL?.RawQuery ?? '';
508
573
  const query = rawQuery === '' ? '' : `?${rawQuery}`;
509
574
  req.RequestURI = `${request.URL?.Path ?? '/'}${query}`;
510
- req.Host = request.Host === '' ? request.URL?.Host ?? '' : request.Host;
575
+ req.Host = request.Host === '' ? (request.URL?.Host ?? '') : request.Host;
511
576
  if (req.URL?.clone != null) {
512
577
  req.URL = req.URL.clone();
513
578
  req.URL.Scheme = '';
@@ -560,7 +625,9 @@ export class Request {
560
625
  this.Cancel = init?.Cancel ?? null;
561
626
  this.Response = init?.Response ?? null;
562
627
  this.Pattern = init?.Pattern ?? '';
563
- this.ctx = init?.ctx ?? context.Background();
628
+ this.ctx =
629
+ init?.ctx ??
630
+ context.Background();
564
631
  }
565
632
  Context() {
566
633
  return this.ctx;
@@ -571,7 +638,9 @@ export class Request {
571
638
  Clone(ctx) {
572
639
  return new Request({
573
640
  Method: this.Method,
574
- URL: this.URL?.clone != null ? this.URL.clone() : this.URL == null ? null : { ...this.URL },
641
+ URL: this.URL?.clone != null ? this.URL.clone()
642
+ : this.URL == null ? null
643
+ : { ...this.URL },
575
644
  Proto: this.Proto,
576
645
  ProtoMajor: this.ProtoMajor,
577
646
  ProtoMinor: this.ProtoMinor,
@@ -601,7 +670,8 @@ export class Request {
601
670
  return Header_Get(this.Header, 'Referer');
602
671
  }
603
672
  ProtoAtLeast(major, minor) {
604
- return this.ProtoMajor > major || (this.ProtoMajor === major && this.ProtoMinor >= minor);
673
+ return (this.ProtoMajor > major ||
674
+ (this.ProtoMajor === major && this.ProtoMinor >= minor));
605
675
  }
606
676
  Cookie(name) {
607
677
  for (const cookie of Array.from(this.Cookies() ?? [])) {
@@ -678,7 +748,8 @@ export class Response {
678
748
  this.TLS = init?.TLS ?? null;
679
749
  if (this.Status === '' && this.StatusCode !== 0) {
680
750
  const text = StatusText(this.StatusCode);
681
- this.Status = text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`;
751
+ this.Status =
752
+ text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`;
682
753
  }
683
754
  }
684
755
  clone() {
@@ -726,7 +797,8 @@ export class Response {
726
797
  }
727
798
  }
728
799
  ProtoAtLeast(major, minor) {
729
- return this.ProtoMajor > major || (this.ProtoMajor === major && this.ProtoMinor >= minor);
800
+ return (this.ProtoMajor > major ||
801
+ (this.ProtoMajor === major && this.ProtoMinor >= minor));
730
802
  }
731
803
  Write(w) {
732
804
  const write = (data) => {
@@ -820,10 +892,8 @@ function encodeFormData(data) {
820
892
  if (data instanceof URLSearchParams) {
821
893
  return data.toString();
822
894
  }
823
- const entries = data instanceof Map ?
824
- Array.from(data.entries())
825
- : typeof data === 'object' ?
826
- Object.entries(data)
895
+ const entries = data instanceof Map ? Array.from(data.entries())
896
+ : typeof data === 'object' ? Object.entries(data)
827
897
  : [];
828
898
  entries.sort(([a], [b]) => String(a).localeCompare(String(b)));
829
899
  const params = new URLSearchParams();
@@ -1015,7 +1085,10 @@ async function fetchRoundTrip(request) {
1015
1085
  };
1016
1086
  if (typeof globalThis.fetch !== 'function') {
1017
1087
  closeRequestBody();
1018
- return [null, errors.New('net/http: Client.Do is not implemented in GoScript')];
1088
+ return [
1089
+ null,
1090
+ errors.New('net/http: Client.Do is not implemented in GoScript'),
1091
+ ];
1019
1092
  }
1020
1093
  const ctxErr = request.Context()?.Err?.();
1021
1094
  if (ctxErr != null) {
@@ -1029,7 +1102,9 @@ async function fetchRoundTrip(request) {
1029
1102
  }
1030
1103
  }
1031
1104
  let body;
1032
- if (requestBody != null && request.Method !== MethodGet && request.Method !== MethodHead) {
1105
+ if (requestBody != null &&
1106
+ request.Method !== MethodGet &&
1107
+ request.Method !== MethodHead) {
1033
1108
  const [data, err] = await io.ReadAll(requestBody);
1034
1109
  const closeErr = closeRequestBody();
1035
1110
  if (err != null) {
@@ -1046,21 +1121,32 @@ async function fetchRoundTrip(request) {
1046
1121
  return [null, closeErr];
1047
1122
  }
1048
1123
  }
1124
+ const fetchContext = newFetchContext(request.Context());
1049
1125
  try {
1050
1126
  const bodyInit = body == null ? undefined : Uint8Array.from(body).buffer;
1051
- const fetched = await globalThis.fetch(request.URL?.String?.() ?? '', {
1127
+ const [fetched, fetchErr] = await fetchContext.wait(globalThis.fetch(request.URL?.String?.() ?? '', {
1052
1128
  method: request.Method || MethodGet,
1053
1129
  headers,
1054
1130
  body: bodyInit,
1055
- });
1056
- const data = new Uint8Array(await fetched.arrayBuffer());
1131
+ signal: fetchContext.signal,
1132
+ }));
1133
+ if (fetchErr != null || fetched == null) {
1134
+ fetchContext.stop();
1135
+ return [null, fetchErr];
1136
+ }
1057
1137
  const respHeader = new Header();
1058
1138
  fetched.headers.forEach((value, key) => Header_Add(respHeader, key, value));
1139
+ const responseBody = request.Method === MethodHead ?
1140
+ NoBody
1141
+ : new fetchResponseBody(fetched, request.Context(), fetchContext.abort, fetchContext.stop);
1142
+ if (request.Method === MethodHead) {
1143
+ fetchContext.stop();
1144
+ }
1059
1145
  return [
1060
1146
  new Response({
1061
1147
  Status: `${fetched.status} ${fetched.statusText}`,
1062
1148
  StatusCode: fetched.status,
1063
- Body: new responseBody(data),
1149
+ Body: responseBody,
1064
1150
  Header: respHeader,
1065
1151
  ContentLength: Number(fetched.headers.get('content-length') ?? -1),
1066
1152
  Request: request,
@@ -1069,17 +1155,84 @@ async function fetchRoundTrip(request) {
1069
1155
  ];
1070
1156
  }
1071
1157
  catch (err) {
1072
- const message = typeof err === 'object' && err != null && 'message' in err
1073
- ? String(err.message)
1074
- : String(err);
1075
- return [null, errors.New(message)];
1158
+ fetchContext.stop();
1159
+ return [null, errorFromUnknown(err)];
1160
+ }
1161
+ }
1162
+ function newFetchContext(requestContext) {
1163
+ let stopped = false;
1164
+ const watchController = typeof AbortController === 'undefined' ? null : new AbortController();
1165
+ const controller = typeof AbortController === 'undefined' ? null : new AbortController();
1166
+ const abort = () => {
1167
+ if (controller != null && !controller.signal.aborted) {
1168
+ controller.abort(requestContext?.Err?.() ?? context.Canceled);
1169
+ }
1170
+ };
1171
+ const donePromise = requestContext == null ? null : ((async () => {
1172
+ try {
1173
+ await requestContext.Done().selectReceive(0, watchController?.signal);
1174
+ }
1175
+ catch {
1176
+ // Closed channels and receive wakeups both mean the context is done.
1177
+ }
1178
+ const err = requestContext.Err() ?? context.Canceled;
1179
+ if (!stopped) {
1180
+ abort();
1181
+ }
1182
+ return err;
1183
+ })());
1184
+ return {
1185
+ signal: controller?.signal,
1186
+ abort,
1187
+ stop: () => {
1188
+ stopped = true;
1189
+ watchController?.abort();
1190
+ },
1191
+ wait: async (promise) => {
1192
+ const settle = promise.then((value) => ({ value }), (thrown) => ({ thrown }));
1193
+ if (donePromise == null) {
1194
+ const result = await settle;
1195
+ if ('thrown' in result) {
1196
+ return [null, errorFromUnknown(result.thrown)];
1197
+ }
1198
+ return [result.value, null];
1199
+ }
1200
+ const result = await Promise.race([settle, donePromise.then((err) => ({ err }))]);
1201
+ if ('err' in result) {
1202
+ abort();
1203
+ return [null, result.err];
1204
+ }
1205
+ if ('thrown' in result) {
1206
+ return [
1207
+ null,
1208
+ requestContext?.Err?.() ?? errorFromUnknown(result.thrown),
1209
+ ];
1210
+ }
1211
+ return [result.value, null];
1212
+ },
1213
+ };
1214
+ }
1215
+ function errorFromUnknown(err) {
1216
+ const message = typeof err === 'object' && err != null && 'message' in err ?
1217
+ String(err.message)
1218
+ : String(err);
1219
+ return errors.New(message);
1220
+ }
1221
+ async function readFetchBody(fetched, requestContext, abortFetch) {
1222
+ const fetchContext = newFetchContext(requestContext);
1223
+ const [buffer, err] = await fetchContext.wait(fetched.arrayBuffer());
1224
+ fetchContext.stop();
1225
+ if (err != null) {
1226
+ abortFetch();
1227
+ return [new Uint8Array(), err];
1076
1228
  }
1229
+ return [new Uint8Array(buffer ?? new ArrayBuffer(0)), null];
1077
1230
  }
1078
1231
  export function FS(fsys) {
1079
1232
  return {
1080
- Open(name) {
1233
+ async Open(name) {
1081
1234
  const cleaned = cleanFileServerPath(name);
1082
- const [file, err] = fsys?.Open(cleaned) ?? [null, fs.ErrInvalid];
1235
+ const [file, err] = (await fsys?.Open(cleaned)) ?? [null, fs.ErrInvalid];
1083
1236
  if (err != null || file == null) {
1084
1237
  return [null, err];
1085
1238
  }
@@ -1094,7 +1247,9 @@ function httpFileFromFSFile(file) {
1094
1247
  Read: (p) => file.Read(p instanceof Uint8Array ? p : Uint8Array.from(p ?? [])),
1095
1248
  Close: () => file.Close(),
1096
1249
  Stat: () => file.Stat(),
1097
- Seek: seek == null ? () => [0, errors.New('net/http: file does not support seek')] : seek.bind(file),
1250
+ Seek: seek == null ?
1251
+ () => [0, errors.New('net/http: file does not support seek')]
1252
+ : seek.bind(file),
1098
1253
  Readdir: readdir == null ? () => [null, io.EOF] : readdir.bind(file),
1099
1254
  };
1100
1255
  }
@@ -1109,13 +1264,13 @@ export function FileServer(root) {
1109
1264
  Error(w, 'method not allowed', StatusMethodNotAllowed);
1110
1265
  return;
1111
1266
  }
1112
- const [file, err] = root?.Open(cleanFileServerPath(req.URL?.Path ?? '')) ?? [null, fs.ErrInvalid];
1267
+ const [file, err] = (await root?.Open(cleanFileServerPath(req.URL?.Path ?? ''))) ?? [null, fs.ErrInvalid];
1113
1268
  if (err != null || file == null) {
1114
1269
  NotFound(w, req);
1115
1270
  return;
1116
1271
  }
1117
1272
  try {
1118
- const [info, statErr] = file.Stat();
1273
+ const [info, statErr] = await file.Stat();
1119
1274
  if (statErr != null) {
1120
1275
  Error(w, statErr.Error(), StatusInternalServerError);
1121
1276
  return;
@@ -1124,21 +1279,26 @@ export function FileServer(root) {
1124
1279
  NotFound(w, req);
1125
1280
  return;
1126
1281
  }
1127
- const [data, readErr] = await io.ReadAll(file);
1128
- if (readErr != null) {
1129
- Error(w, readErr.Error(), StatusInternalServerError);
1130
- return;
1282
+ const header = await w.Header();
1283
+ if (Header_Get(header, 'Content-Type') === '') {
1284
+ const contentType = mime.TypeByExtension(path.Ext(info?.Name?.() || req.URL?.Path || ''));
1285
+ if (contentType !== '') {
1286
+ Header_Set(header, 'Content-Type', contentType);
1287
+ }
1131
1288
  }
1132
1289
  if (info?.Size != null) {
1133
- Header_Set(w.Header(), 'Content-Length', String(info.Size()));
1290
+ Header_Set(header, 'Content-Length', String(info.Size()));
1134
1291
  }
1135
- w.WriteHeader(StatusOK);
1292
+ await w.WriteHeader(StatusOK);
1136
1293
  if (req.Method !== MethodHead) {
1137
- w.Write(data);
1294
+ const [, copyErr] = await io.Copy(w, file);
1295
+ if (copyErr != null) {
1296
+ return;
1297
+ }
1138
1298
  }
1139
1299
  }
1140
1300
  finally {
1141
- file.Close();
1301
+ await file.Close();
1142
1302
  }
1143
1303
  },
1144
1304
  };
@@ -1174,7 +1334,23 @@ function cleanFileServerPath(name) {
1174
1334
  }
1175
1335
  return parts.length === 0 ? '.' : parts.join('/');
1176
1336
  }
1337
+ $.registerInterfaceType('http.Handler', null, [
1338
+ {
1339
+ name: 'ServeHTTP',
1340
+ args: [
1341
+ { name: 'w', type: 'http.ResponseWriter' },
1342
+ {
1343
+ name: 'r',
1344
+ type: { kind: $.TypeKind.Pointer, elemType: 'http.Request' },
1345
+ },
1346
+ ],
1347
+ returns: [],
1348
+ },
1349
+ ]);
1177
1350
  export function HandlerFunc_ServeHTTP(h, w, r) {
1351
+ if (!h) {
1352
+ throw new globalThis.Error('http: nil HandlerFunc');
1353
+ }
1178
1354
  return h(w, r);
1179
1355
  }
1180
1356
  export class CrossOriginProtection {
@@ -1334,7 +1510,8 @@ export class Server {
1334
1510
  this.BaseContext = init?.BaseContext ?? null;
1335
1511
  this.ConnContext = init?.ConnContext ?? null;
1336
1512
  this.Handler = init?.Handler ?? null;
1337
- this.DisableGeneralOptionsHandler = init?.DisableGeneralOptionsHandler ?? false;
1513
+ this.DisableGeneralOptionsHandler =
1514
+ init?.DisableGeneralOptionsHandler ?? false;
1338
1515
  this.TLSConfig = init?.TLSConfig ?? null;
1339
1516
  this.ReadTimeout = init?.ReadTimeout ?? 0;
1340
1517
  this.ReadTimeoutHandler = init?.ReadTimeoutHandler ?? null;
@@ -1492,7 +1669,9 @@ export function StripPrefix(prefix, handler) {
1492
1669
  return {
1493
1670
  ServeHTTP(w, r) {
1494
1671
  const req = $.pointerValue(r);
1495
- if (req?.URL != null && typeof req.URL.Path === 'string' && req.URL.Path.startsWith(prefix)) {
1672
+ if (req?.URL != null &&
1673
+ typeof req.URL.Path === 'string' &&
1674
+ req.URL.Path.startsWith(prefix)) {
1496
1675
  req.URL = { ...req.URL, Path: req.URL.Path.slice(prefix.length) || '/' };
1497
1676
  }
1498
1677
  return handler?.ServeHTTP(w, req);
@@ -1522,7 +1701,7 @@ export function NotFoundHandler() {
1522
1701
  export function RedirectHandler(url, code) {
1523
1702
  return {
1524
1703
  ServeHTTP(w, r) {
1525
- Redirect(w, r, url, code);
1704
+ return Redirect(w, r, url, code);
1526
1705
  },
1527
1706
  };
1528
1707
  }
@@ -1544,17 +1723,23 @@ export function Error(w, error, code) {
1544
1723
  export function NotFound(w, _r) {
1545
1724
  Error(w, '404 page not found', StatusNotFound);
1546
1725
  }
1547
- export function Redirect(w, _r, url, code) {
1548
- const header = w?.Header();
1726
+ export async function Redirect(w, _r, url, code) {
1727
+ if (w == null) {
1728
+ return;
1729
+ }
1730
+ const header = await w.Header();
1549
1731
  if (header != null) {
1550
1732
  Header_Set(header, 'Location', url);
1551
1733
  }
1552
- w?.WriteHeader(code);
1734
+ await w.WriteHeader(code);
1553
1735
  }
1554
1736
  export function ParseTime(text) {
1555
1737
  const date = new globalThis.Date(text);
1556
1738
  if (isNaN(date.getTime())) {
1557
- return [new time.Time(), $.newError(`parsing time "${text}" as HTTP-date: cannot parse`)];
1739
+ return [
1740
+ new time.Time(),
1741
+ $.newError(`parsing time "${text}" as HTTP-date: cannot parse`),
1742
+ ];
1558
1743
  }
1559
1744
  return [time.UnixMilli(date.getTime()), null];
1560
1745
  }
@@ -1599,7 +1784,8 @@ export function DetectContentType(data) {
1599
1784
  if (startsWithBytes(bytes, new Uint8Array([0xef, 0xbb, 0xbf]))) {
1600
1785
  return 'text/plain; charset=utf-8';
1601
1786
  }
1602
- if (startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) || startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))) {
1787
+ if (startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) ||
1788
+ startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))) {
1603
1789
  return 'image/x-icon';
1604
1790
  }
1605
1791
  if (startsWithASCII(bytes, 'BM')) {
@@ -1675,7 +1861,10 @@ export function DetectContentType(data) {
1675
1861
  return 'application/wasm';
1676
1862
  }
1677
1863
  for (const byte of afterWS) {
1678
- if (byte <= 0x08 || byte === 0x0b || (byte >= 0x0e && byte <= 0x1a) || (byte >= 0x1c && byte <= 0x1f)) {
1864
+ if (byte <= 0x08 ||
1865
+ byte === 0x0b ||
1866
+ (byte >= 0x0e && byte <= 0x1a) ||
1867
+ (byte >= 0x1c && byte <= 0x1f)) {
1679
1868
  return 'application/octet-stream';
1680
1869
  }
1681
1870
  }
@@ -1684,7 +1873,11 @@ export function DetectContentType(data) {
1684
1873
  function firstNonWhitespace(data) {
1685
1874
  for (let i = 0; i < data.length; i++) {
1686
1875
  const byte = data[i];
1687
- if (byte !== 0x09 && byte !== 0x0a && byte !== 0x0c && byte !== 0x0d && byte !== 0x20) {
1876
+ if (byte !== 0x09 &&
1877
+ byte !== 0x0a &&
1878
+ byte !== 0x0c &&
1879
+ byte !== 0x0d &&
1880
+ byte !== 0x20) {
1688
1881
  return i;
1689
1882
  }
1690
1883
  }
@@ -1807,7 +2000,12 @@ export function ParseSetCookie(line) {
1807
2000
  if (!ok) {
1808
2001
  return [null, errInvalidCookieValue];
1809
2002
  }
1810
- const cookie = new Cookie({ Name: name, Value: value, Quoted: quoted, Raw: line });
2003
+ const cookie = new Cookie({
2004
+ Name: name,
2005
+ Value: value,
2006
+ Quoted: quoted,
2007
+ Raw: line,
2008
+ });
1811
2009
  const unparsed = [];
1812
2010
  for (const raw of parts.slice(1)) {
1813
2011
  const part = raw.trim();
@@ -1863,7 +2061,8 @@ export function ParseSetCookie(line) {
1863
2061
  break;
1864
2062
  }
1865
2063
  let secs = Number.parseInt(attrValue, 10);
1866
- if ((secs !== 0 && attrValue[0] === '0') || !Number.isSafeInteger(secs)) {
2064
+ if ((secs !== 0 && attrValue[0] === '0') ||
2065
+ !Number.isSafeInteger(secs)) {
1867
2066
  break;
1868
2067
  }
1869
2068
  if (secs <= 0) {
@@ -1901,7 +2100,10 @@ export function NewRequestWithContext(ctx, method, url, body) {
1901
2100
  method = MethodGet;
1902
2101
  }
1903
2102
  if (!isToken(method)) {
1904
- return [null, errors.New(`net/http: invalid method ${JSON.stringify(method)}`)];
2103
+ return [
2104
+ null,
2105
+ errors.New(`net/http: invalid method ${JSON.stringify(method)}`),
2106
+ ];
1905
2107
  }
1906
2108
  if (ctx == null) {
1907
2109
  return [null, errors.New('net/http: nil Context')];
@@ -1911,7 +2113,17 @@ export function NewRequestWithContext(ctx, method, url, body) {
1911
2113
  return [null, err];
1912
2114
  }
1913
2115
  const bodyInfo = requestBodyInfo(body, 0);
1914
- return [new Request({ Method: method, URL: parsedURL, Body: bodyInfo.Body, ContentLength: bodyInfo.ContentLength, Host: parsedURL.Host, ctx }), null];
2116
+ return [
2117
+ new Request({
2118
+ Method: method,
2119
+ URL: parsedURL,
2120
+ Body: bodyInfo.Body,
2121
+ ContentLength: bodyInfo.ContentLength,
2122
+ Host: parsedURL.Host,
2123
+ ctx,
2124
+ }),
2125
+ null,
2126
+ ];
1915
2127
  }
1916
2128
  export async function Get(_url) {
1917
2129
  const [req, err] = NewRequest(MethodGet, _url, null);
@@ -1984,9 +2196,14 @@ function requestBodyInfo(body, unknownLength) {
1984
2196
  if (value === NoBody) {
1985
2197
  return { Body: NoBody, ContentLength: 0 };
1986
2198
  }
1987
- if (value instanceof bytes.Buffer || value instanceof bytes.Reader || value instanceof strings.Reader) {
2199
+ if (value instanceof bytes.Buffer ||
2200
+ value instanceof bytes.Reader ||
2201
+ value instanceof strings.Reader) {
1988
2202
  const length = value.Len();
1989
- return { Body: length === 0 ? NoBody : readCloserForBody(body), ContentLength: length };
2203
+ return {
2204
+ Body: length === 0 ? NoBody : readCloserForBody(body),
2205
+ ContentLength: length,
2206
+ };
1990
2207
  }
1991
2208
  return { Body: readCloserForBody(body), ContentLength: unknownLength };
1992
2209
  }