goscript 0.2.2 → 0.2.3

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 (136) hide show
  1. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  2. package/compiler/lowering.go +223 -9
  3. package/compiler/override-registry_test.go +50 -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 +473 -15
  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/zlib/index.js +5 -2
  25. package/dist/gs/compress/zlib/index.js.map +1 -1
  26. package/dist/gs/crypto/ecdh/index.js +27 -8
  27. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  28. package/dist/gs/crypto/ed25519/index.js +3 -3
  29. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  30. package/dist/gs/crypto/rand/index.js +6 -3
  31. package/dist/gs/crypto/rand/index.js.map +1 -1
  32. package/dist/gs/embed/index.js +9 -3
  33. package/dist/gs/embed/index.js.map +1 -1
  34. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  35. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  36. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  37. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  38. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  39. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  40. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  41. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  42. package/dist/gs/hash/fnv/index.js +13 -5
  43. package/dist/gs/hash/fnv/index.js.map +1 -1
  44. package/dist/gs/io/fs/glob.d.ts +3 -3
  45. package/dist/gs/io/fs/glob.js +8 -8
  46. package/dist/gs/io/fs/glob.js.map +1 -1
  47. package/dist/gs/io/fs/readdir.d.ts +2 -2
  48. package/dist/gs/io/fs/readdir.js +13 -74
  49. package/dist/gs/io/fs/readdir.js.map +1 -1
  50. package/dist/gs/io/fs/sub.js +4 -4
  51. package/dist/gs/io/fs/sub.js.map +1 -1
  52. package/dist/gs/io/fs/walk.js +1 -1
  53. package/dist/gs/io/fs/walk.js.map +1 -1
  54. package/dist/gs/maps/iter.js.map +1 -1
  55. package/dist/gs/maps/maps.js.map +1 -1
  56. package/dist/gs/mime/index.js +5 -2
  57. package/dist/gs/mime/index.js.map +1 -1
  58. package/dist/gs/net/http/httptest/index.js +6 -3
  59. package/dist/gs/net/http/httptest/index.js.map +1 -1
  60. package/dist/gs/net/http/index.d.ts +16 -4
  61. package/dist/gs/net/http/index.js +236 -40
  62. package/dist/gs/net/http/index.js.map +1 -1
  63. package/dist/gs/net/http/pprof/index.js.map +1 -1
  64. package/dist/gs/reflect/iter.js +1 -1
  65. package/dist/gs/reflect/iter.js.map +1 -1
  66. package/dist/gs/reflect/type.d.ts +2 -0
  67. package/dist/gs/reflect/type.js +53 -21
  68. package/dist/gs/reflect/type.js.map +1 -1
  69. package/dist/gs/runtime/pprof/index.js.map +1 -1
  70. package/dist/gs/runtime/runtime.js +2 -2
  71. package/dist/gs/runtime/runtime.js.map +1 -1
  72. package/dist/gs/runtime/trace/index.js.map +1 -1
  73. package/dist/gs/slices/slices.d.ts +1 -1
  74. package/dist/gs/slices/slices.js +37 -4
  75. package/dist/gs/slices/slices.js.map +1 -1
  76. package/gs/builtin/builtin.ts +11 -14
  77. package/gs/builtin/defer.ts +2 -2
  78. package/gs/builtin/hostio.ts +5 -5
  79. package/gs/builtin/map.ts +4 -1
  80. package/gs/builtin/slice.test.ts +14 -0
  81. package/gs/builtin/slice.ts +64 -0
  82. package/gs/builtin/type.ts +72 -0
  83. package/gs/bytes/bytes.test.ts +14 -13
  84. package/gs/compress/zlib/index.test.ts +19 -5
  85. package/gs/compress/zlib/index.ts +16 -7
  86. package/gs/context/context.test.ts +3 -1
  87. package/gs/crypto/ecdh/index.test.ts +6 -2
  88. package/gs/crypto/ecdh/index.ts +49 -12
  89. package/gs/crypto/ed25519/index.ts +20 -7
  90. package/gs/crypto/rand/index.ts +6 -3
  91. package/gs/embed/index.test.ts +3 -3
  92. package/gs/embed/index.ts +9 -3
  93. package/gs/fmt/fmt.test.ts +29 -4
  94. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  95. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  96. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  97. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  98. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  99. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  100. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  101. package/gs/hash/fnv/index.test.ts +1 -8
  102. package/gs/hash/fnv/index.ts +27 -10
  103. package/gs/io/fs/glob.ts +13 -10
  104. package/gs/io/fs/meta.json +2 -0
  105. package/gs/io/fs/readdir.test.ts +63 -2
  106. package/gs/io/fs/readdir.ts +33 -30
  107. package/gs/io/fs/sub.ts +4 -4
  108. package/gs/io/fs/walk.ts +1 -1
  109. package/gs/maps/iter.ts +9 -9
  110. package/gs/maps/maps.ts +4 -4
  111. package/gs/math/bits/index.test.ts +10 -1
  112. package/gs/mime/index.test.ts +33 -15
  113. package/gs/mime/index.ts +9 -2
  114. package/gs/net/http/httptest/index.test.ts +17 -3
  115. package/gs/net/http/httptest/index.ts +8 -3
  116. package/gs/net/http/index.test.ts +645 -123
  117. package/gs/net/http/index.ts +548 -113
  118. package/gs/net/http/pprof/index.ts +24 -6
  119. package/gs/os/file_unix_js.test.ts +22 -0
  120. package/gs/reflect/iter.ts +4 -2
  121. package/gs/reflect/map.test.ts +56 -1
  122. package/gs/reflect/type.ts +76 -37
  123. package/gs/runtime/pprof/index.test.ts +7 -1
  124. package/gs/runtime/pprof/index.ts +5 -1
  125. package/gs/runtime/runtime.test.ts +7 -0
  126. package/gs/runtime/runtime.ts +2 -4
  127. package/gs/runtime/trace/index.test.ts +9 -1
  128. package/gs/runtime/trace/index.ts +5 -1
  129. package/gs/slices/meta.json +3 -0
  130. package/gs/slices/slices.test.ts +59 -21
  131. package/gs/slices/slices.ts +61 -20
  132. package/gs/strconv/complex.test.ts +17 -3
  133. package/gs/sync/atomic/doc_64.test.ts +2 -9
  134. package/gs/sync/sync.test.ts +18 -8
  135. package/gs/syscall/js/index.test.ts +9 -4
  136. package/package.json +5 -4
@@ -110,33 +110,55 @@ export class MaxBytesError {
110
110
  }
111
111
 
112
112
  export const ErrNotSupported = new ProtocolError('feature not supported')
113
- export const ErrUnexpectedTrailer = new ProtocolError('trailer header without chunked transfer encoding')
114
- export const ErrMissingBoundary = new ProtocolError('no multipart boundary param in Content-Type')
115
- export const ErrNotMultipart = new ProtocolError("request Content-Type isn't multipart/form-data")
113
+ export const ErrUnexpectedTrailer = new ProtocolError(
114
+ 'trailer header without chunked transfer encoding',
115
+ )
116
+ export const ErrMissingBoundary = new ProtocolError(
117
+ 'no multipart boundary param in Content-Type',
118
+ )
119
+ export const ErrNotMultipart = new ProtocolError(
120
+ "request Content-Type isn't multipart/form-data",
121
+ )
116
122
  export const ErrHeaderTooLong = new ProtocolError('header too long')
117
123
  export const ErrShortBody = new ProtocolError('entity body too short')
118
- export const ErrMissingContentLength = new ProtocolError('missing ContentLength in HEAD response')
119
- export const ErrBodyNotAllowed = errors.New('http: request method or response status code does not allow body')
120
- export const ErrBodyReadAfterClose = errors.New('http: invalid Read on closed Body')
121
- export const ErrContentLength = errors.New('http: wrote more than the declared Content-Length')
124
+ export const ErrMissingContentLength = new ProtocolError(
125
+ 'missing ContentLength in HEAD response',
126
+ )
127
+ export const ErrBodyNotAllowed = errors.New(
128
+ 'http: request method or response status code does not allow body',
129
+ )
130
+ export const ErrBodyReadAfterClose = errors.New(
131
+ 'http: invalid Read on closed Body',
132
+ )
133
+ export const ErrContentLength = errors.New(
134
+ 'http: wrote more than the declared Content-Length',
135
+ )
122
136
  export const ErrHandlerTimeout = errors.New('http: Handler timeout')
123
137
  export const ErrHijacked = errors.New('http: connection has been hijacked')
124
138
  export const ErrLineTooLong = errors.New('header line too long')
125
139
  export const ErrMissingFile = errors.New('http: no such file')
126
140
  export const ErrNoCookie = errors.New('http: named cookie not present')
127
141
  export const ErrNoLocation = errors.New('http: no Location header in response')
128
- export const ErrSchemeMismatch = errors.New('http: server gave HTTP response to HTTPS client')
142
+ export const ErrSchemeMismatch = errors.New(
143
+ 'http: server gave HTTP response to HTTPS client',
144
+ )
129
145
  export const ErrServerClosed = errors.New('http: Server closed')
130
146
  export const ErrAbortHandler = errors.New('net/http: abort Handler')
131
- export const ErrSkipAltProtocol = errors.New('net/http: skip alternate protocol')
147
+ export const ErrSkipAltProtocol = errors.New(
148
+ 'net/http: skip alternate protocol',
149
+ )
132
150
  export const ErrUseLastResponse = errors.New('net/http: use last response')
133
151
  export const ErrWriteAfterFlush = errors.New('unused')
134
152
  const errBlankCookie = errors.New('http: blank cookie')
135
153
  const errEqualNotFoundInCookie = errors.New("http: '=' not found in cookie")
136
154
  const errInvalidCookieName = errors.New('http: invalid cookie name')
137
155
  const errInvalidCookieValue = errors.New('http: invalid cookie value')
138
- const errCookieNumLimitExceeded = errors.New('http: number of cookies exceeded limit')
139
- const errCrossOriginRequest = errors.New('cross-origin request detected from Sec-Fetch-Site header')
156
+ const errCookieNumLimitExceeded = errors.New(
157
+ 'http: number of cookies exceeded limit',
158
+ )
159
+ const errCrossOriginRequest = errors.New(
160
+ 'cross-origin request detected from Sec-Fetch-Site header',
161
+ )
140
162
  const errCrossOriginRequestFromOldBrowser = errors.New(
141
163
  'cross-origin request detected, and/or browser is out of date: Sec-Fetch-Site is missing, and Origin does not match Host',
142
164
  )
@@ -228,7 +250,7 @@ export function StatusText(code: number): string {
228
250
  export type Header = Map<string, $.Slice<string>>
229
251
 
230
252
  export const Header = Map as {
231
- new(entries?: Iterable<readonly [string, $.Slice<string>]> | null): Header
253
+ new (entries?: Iterable<readonly [string, $.Slice<string>]> | null): Header
232
254
  }
233
255
 
234
256
  export function CanonicalHeaderKey(s: string): string {
@@ -271,7 +293,11 @@ export function Header_Write(h: Header, w: io.Writer): $.GoError {
271
293
  return Header_WriteSubset(h, w, null)
272
294
  }
273
295
 
274
- export function Header_WriteSubset(h: Header, w: io.Writer, exclude: Map<string, boolean> | null): $.GoError {
296
+ export function Header_WriteSubset(
297
+ h: Header,
298
+ w: io.Writer,
299
+ exclude: Map<string, boolean> | null,
300
+ ): $.GoError {
275
301
  for (const [key, values] of h.entries()) {
276
302
  if (exclude?.get(key) === true) {
277
303
  continue
@@ -377,12 +403,15 @@ function parseRequestURL(rawURL: string): [RequestURL | null, $.GoError] {
377
403
  }
378
404
  const parsed = new URL(rawURL, 'http://goscript.invalid')
379
405
  const hasHost = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(rawURL)
380
- return [new RequestURL(
381
- parsed.pathname,
382
- parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search,
383
- hasHost ? parsed.protocol.replace(/:$/, '') : '',
384
- hasHost ? parsed.host : '',
385
- ), null]
406
+ return [
407
+ new RequestURL(
408
+ parsed.pathname,
409
+ parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search,
410
+ hasHost ? parsed.protocol.replace(/:$/, '') : '',
411
+ hasHost ? parsed.host : '',
412
+ ),
413
+ null,
414
+ ]
386
415
  } catch {
387
416
  return [null, errors.New(`parse "${rawURL}": invalid URL`)]
388
417
  }
@@ -404,6 +433,50 @@ class responseBody implements io.ReadCloser {
404
433
  }
405
434
  }
406
435
 
436
+ class fetchResponseBody {
437
+ private reader: bytes.Reader | null = null
438
+ private closed = false
439
+
440
+ constructor(
441
+ private fetched: globalThis.Response,
442
+ private requestContext: context.Context,
443
+ private abortFetch: () => void,
444
+ private stopContextWatch: () => void,
445
+ ) {}
446
+
447
+ public async Read(p: $.Bytes): Promise<[number, $.GoError]> {
448
+ if (this.closed) {
449
+ return [0, ErrBodyReadAfterClose]
450
+ }
451
+ if (this.reader == null) {
452
+ const [data, err] = await readFetchBody(
453
+ this.fetched,
454
+ this.requestContext,
455
+ this.abortFetch,
456
+ )
457
+ if (err != null) {
458
+ this.stopContextWatch()
459
+ return [0, err]
460
+ }
461
+ this.stopContextWatch()
462
+ this.reader = bytes.NewReader(data)
463
+ }
464
+ return this.reader.Read(p)
465
+ }
466
+
467
+ public Close(): $.GoError {
468
+ if (this.closed) {
469
+ return null
470
+ }
471
+ this.closed = true
472
+ this.stopContextWatch()
473
+ if (this.reader == null) {
474
+ this.abortFetch()
475
+ }
476
+ return null
477
+ }
478
+ }
479
+
407
480
  class noBody implements io.ReadCloser {
408
481
  public Read(_p: $.Bytes): [number, $.GoError] {
409
482
  return [0, io.EOF]
@@ -454,7 +527,9 @@ export class Cookie {
454
527
  }
455
528
 
456
529
  public String(): string {
457
- const parts = [`${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`]
530
+ const parts = [
531
+ `${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`,
532
+ ]
458
533
  if (this.Path !== '') {
459
534
  parts.push(`Path=${this.Path}`)
460
535
  }
@@ -499,13 +574,27 @@ function isToken(value: string): boolean {
499
574
  }
500
575
 
501
576
  function validCookieValueByte(code: number): boolean {
502
- return code >= 0x20 && code < 0x7f && code !== 0x22 && code !== 0x3b && code !== 0x5c
503
- }
504
-
505
- function parseCookieValue(raw: string, allowDoubleQuote: boolean): [string, boolean, boolean] {
577
+ return (
578
+ code >= 0x20 &&
579
+ code < 0x7f &&
580
+ code !== 0x22 &&
581
+ code !== 0x3b &&
582
+ code !== 0x5c
583
+ )
584
+ }
585
+
586
+ function parseCookieValue(
587
+ raw: string,
588
+ allowDoubleQuote: boolean,
589
+ ): [string, boolean, boolean] {
506
590
  let value = raw
507
591
  let quoted = false
508
- if (allowDoubleQuote && value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
592
+ if (
593
+ allowDoubleQuote &&
594
+ value.length > 1 &&
595
+ value[0] === '"' &&
596
+ value[value.length - 1] === '"'
597
+ ) {
509
598
  value = value.slice(1, -1)
510
599
  quoted = true
511
600
  }
@@ -526,7 +615,10 @@ function asciiLower(value: string): [string, boolean] {
526
615
  return [value.toLowerCase(), true]
527
616
  }
528
617
 
529
- export function SetCookie(w: ResponseWriter | null, cookie: Cookie | $.VarRef<Cookie> | null): void {
618
+ export function SetCookie(
619
+ w: ResponseWriter | null,
620
+ cookie: Cookie | $.VarRef<Cookie> | null,
621
+ ): void {
530
622
  const c = $.pointerValue<Cookie | null>(cookie)
531
623
  if (w == null || c == null) {
532
624
  return
@@ -592,7 +684,7 @@ function inProcessServerRequest(request: Request): Request {
592
684
  const rawQuery = request.URL?.RawQuery ?? ''
593
685
  const query = rawQuery === '' ? '' : `?${rawQuery}`
594
686
  req.RequestURI = `${request.URL?.Path ?? '/'}${query}`
595
- req.Host = request.Host === '' ? request.URL?.Host ?? '' : request.Host
687
+ req.Host = request.Host === '' ? (request.URL?.Host ?? '') : request.Host
596
688
  if (req.URL?.clone != null) {
597
689
  req.URL = req.URL.clone()
598
690
  req.URL.Scheme = ''
@@ -653,7 +745,9 @@ export class Request {
653
745
  this.Cancel = init?.Cancel ?? null
654
746
  this.Response = init?.Response ?? null
655
747
  this.Pattern = init?.Pattern ?? ''
656
- this.ctx = (init as { ctx?: context.Context } | undefined)?.ctx ?? context.Background()
748
+ this.ctx =
749
+ (init as { ctx?: context.Context } | undefined)?.ctx ??
750
+ context.Background()
657
751
  }
658
752
 
659
753
  public Context(): context.Context {
@@ -667,7 +761,10 @@ export class Request {
667
761
  public Clone(ctx: context.Context): Request {
668
762
  return new Request({
669
763
  Method: this.Method,
670
- URL: this.URL?.clone != null ? this.URL.clone() : this.URL == null ? null : { ...this.URL },
764
+ URL:
765
+ this.URL?.clone != null ? this.URL.clone()
766
+ : this.URL == null ? null
767
+ : { ...this.URL },
671
768
  Proto: this.Proto,
672
769
  ProtoMajor: this.ProtoMajor,
673
770
  ProtoMinor: this.ProtoMinor,
@@ -700,7 +797,10 @@ export class Request {
700
797
  }
701
798
 
702
799
  public ProtoAtLeast(major: number, minor: number): boolean {
703
- return this.ProtoMajor > major || (this.ProtoMajor === major && this.ProtoMinor >= minor)
800
+ return (
801
+ this.ProtoMajor > major ||
802
+ (this.ProtoMajor === major && this.ProtoMinor >= minor)
803
+ )
704
804
  }
705
805
 
706
806
  public Cookie(name: string): [Cookie | null, $.GoError] {
@@ -785,7 +885,8 @@ export class Response {
785
885
  this.TLS = init?.TLS ?? null
786
886
  if (this.Status === '' && this.StatusCode !== 0) {
787
887
  const text = StatusText(this.StatusCode)
788
- this.Status = text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`
888
+ this.Status =
889
+ text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`
789
890
  }
790
891
  }
791
892
 
@@ -836,7 +937,10 @@ export class Response {
836
937
  }
837
938
 
838
939
  public ProtoAtLeast(major: number, minor: number): boolean {
839
- return this.ProtoMajor > major || (this.ProtoMajor === major && this.ProtoMinor >= minor)
940
+ return (
941
+ this.ProtoMajor > major ||
942
+ (this.ProtoMajor === major && this.ProtoMinor >= minor)
943
+ )
840
944
  }
841
945
 
842
946
  public Write(w: io.Writer): $.GoError {
@@ -910,7 +1014,11 @@ export class Client {
910
1014
  return await this.Do(req)
911
1015
  }
912
1016
 
913
- public async Post(url: string, contentType: string, body: io.Reader | null): Promise<[Response | null, $.GoError]> {
1017
+ public async Post(
1018
+ url: string,
1019
+ contentType: string,
1020
+ body: io.Reader | null,
1021
+ ): Promise<[Response | null, $.GoError]> {
914
1022
  const [req, err] = NewRequest(MethodPost, url, body)
915
1023
  if (err != null || req == null) {
916
1024
  return [null, err]
@@ -919,12 +1027,21 @@ export class Client {
919
1027
  return await this.Do(req)
920
1028
  }
921
1029
 
922
- public async PostForm(url: string, data: any): Promise<[Response | null, $.GoError]> {
923
- return await this.Post(url, 'application/x-www-form-urlencoded', bytes.NewReader($.stringToBytes(encodeFormData(data))))
1030
+ public async PostForm(
1031
+ url: string,
1032
+ data: any,
1033
+ ): Promise<[Response | null, $.GoError]> {
1034
+ return await this.Post(
1035
+ url,
1036
+ 'application/x-www-form-urlencoded',
1037
+ bytes.NewReader($.stringToBytes(encodeFormData(data))),
1038
+ )
924
1039
  }
925
1040
 
926
1041
  public CloseIdleConnections(): void {
927
- const closer = this.Transport as { CloseIdleConnections?: () => void } | null
1042
+ const closer = this.Transport as {
1043
+ CloseIdleConnections?: () => void
1044
+ } | null
928
1045
  closer?.CloseIdleConnections?.()
929
1046
  }
930
1047
  }
@@ -944,10 +1061,8 @@ function encodeFormData(data: any): string {
944
1061
  return data.toString()
945
1062
  }
946
1063
  const entries =
947
- data instanceof Map ?
948
- Array.from(data.entries())
949
- : typeof data === 'object' ?
950
- Object.entries(data)
1064
+ data instanceof Map ? Array.from(data.entries())
1065
+ : typeof data === 'object' ? Object.entries(data)
951
1066
  : []
952
1067
  entries.sort(([a], [b]) => String(a).localeCompare(String(b)))
953
1068
  const params = new URLSearchParams()
@@ -957,7 +1072,11 @@ function encodeFormData(data: any): string {
957
1072
  return params.toString()
958
1073
  }
959
1074
 
960
- function appendFormValue(params: URLSearchParams, key: string, value: unknown): void {
1075
+ function appendFormValue(
1076
+ params: URLSearchParams,
1077
+ key: string,
1078
+ value: unknown,
1079
+ ): void {
961
1080
  const unwrapped = unwrapFormValue(value)
962
1081
  if (unwrapped == null) {
963
1082
  return
@@ -982,7 +1101,9 @@ function unwrapFormValue(value: unknown): unknown {
982
1101
  }
983
1102
 
984
1103
  export interface RoundTripper {
985
- RoundTrip(req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
1104
+ RoundTrip(
1105
+ req: Request | $.VarRef<Request> | null,
1106
+ ): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
986
1107
  }
987
1108
 
988
1109
  export class Protocols {
@@ -1051,8 +1172,17 @@ export class HTTP2Config {
1051
1172
  }
1052
1173
 
1053
1174
  export class Transport implements RoundTripper {
1054
- public Proxy: ((req: Request | $.VarRef<Request> | null) => [any, $.GoError]) | null = null
1055
- public OnProxyConnectResponse: ((ctx: context.Context, proxyURL: any, connectReq: Request, connectRes: Response) => $.GoError) | null = null
1175
+ public Proxy:
1176
+ | ((req: Request | $.VarRef<Request> | null) => [any, $.GoError])
1177
+ | null = null
1178
+ public OnProxyConnectResponse:
1179
+ | ((
1180
+ ctx: context.Context,
1181
+ proxyURL: any,
1182
+ connectReq: Request,
1183
+ connectRes: Response,
1184
+ ) => $.GoError)
1185
+ | null = null
1056
1186
  public DialContext: any = null
1057
1187
  public Dial: any = null
1058
1188
  public DialTLSContext: any = null
@@ -1069,7 +1199,13 @@ export class Transport implements RoundTripper {
1069
1199
  public ExpectContinueTimeout = 0
1070
1200
  public TLSNextProto: Map<string, any> | null = null
1071
1201
  public ProxyConnectHeader = new Header()
1072
- public GetProxyConnectHeader: ((ctx: context.Context, proxyURL: any, target: string) => [Header | null, $.GoError]) | null = null
1202
+ public GetProxyConnectHeader:
1203
+ | ((
1204
+ ctx: context.Context,
1205
+ proxyURL: any,
1206
+ target: string,
1207
+ ) => [Header | null, $.GoError])
1208
+ | null = null
1073
1209
  public MaxResponseHeaderBytes = 0
1074
1210
  public WriteBufferSize = 0
1075
1211
  public ReadBufferSize = 0
@@ -1081,7 +1217,9 @@ export class Transport implements RoundTripper {
1081
1217
  Object.assign(this, init)
1082
1218
  }
1083
1219
 
1084
- public async RoundTrip(req: Request | $.VarRef<Request> | null): Promise<[Response | null, $.GoError]> {
1220
+ public async RoundTrip(
1221
+ req: Request | $.VarRef<Request> | null,
1222
+ ): Promise<[Response | null, $.GoError]> {
1085
1223
  const request = $.pointerValue<Request | null>(req)
1086
1224
  if (request == null) {
1087
1225
  return [null, errors.New('net/http: nil Request')]
@@ -1094,7 +1232,10 @@ export class Transport implements RoundTripper {
1094
1232
  const recorder = new memoryResponseWriter()
1095
1233
  let closeErr: $.GoError | undefined
1096
1234
  try {
1097
- const served = handler.ServeHTTP(recorder, inProcessServerRequest(request))
1235
+ const served = handler.ServeHTTP(
1236
+ recorder,
1237
+ inProcessServerRequest(request),
1238
+ )
1098
1239
  if (served instanceof Promise) {
1099
1240
  await served
1100
1241
  }
@@ -1117,7 +1258,11 @@ export class Transport implements RoundTripper {
1117
1258
 
1118
1259
  public RegisterProtocol(_scheme: string, _rt: RoundTripper): void {}
1119
1260
 
1120
- public NewClientConn(_ctx: context.Context, _scheme: string, _address: string): [ClientConn | null, $.GoError] {
1261
+ public NewClientConn(
1262
+ _ctx: context.Context,
1263
+ _scheme: string,
1264
+ _address: string,
1265
+ ): [ClientConn | null, $.GoError] {
1121
1266
  return [null, ErrNotSupported]
1122
1267
  }
1123
1268
 
@@ -1131,7 +1276,9 @@ export const DefaultTransport: RoundTripper = new Transport()
1131
1276
  class fileTransport implements RoundTripper {
1132
1277
  constructor(private root: FileSystem | null) {}
1133
1278
 
1134
- public async RoundTrip(req: Request | $.VarRef<Request> | null): Promise<[Response | null, $.GoError]> {
1279
+ public async RoundTrip(
1280
+ req: Request | $.VarRef<Request> | null,
1281
+ ): Promise<[Response | null, $.GoError]> {
1135
1282
  const request = $.pointerValue<Request | null>(req)
1136
1283
  const recorder = new memoryResponseWriter()
1137
1284
  let closeErr: $.GoError | undefined
@@ -1155,7 +1302,9 @@ export function NewFileTransportFS(fsys: fs.FS): RoundTripper {
1155
1302
  return NewFileTransport(FS(fsys))
1156
1303
  }
1157
1304
 
1158
- async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoError]> {
1305
+ async function fetchRoundTrip(
1306
+ request: Request,
1307
+ ): Promise<[Response | null, $.GoError]> {
1159
1308
  const requestBody = request.Body
1160
1309
  const closeRequestBody = (): $.GoError => {
1161
1310
  if (requestBody == null) {
@@ -1165,7 +1314,10 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
1165
1314
  }
1166
1315
  if (typeof globalThis.fetch !== 'function') {
1167
1316
  closeRequestBody()
1168
- return [null, errors.New('net/http: Client.Do is not implemented in GoScript')]
1317
+ return [
1318
+ null,
1319
+ errors.New('net/http: Client.Do is not implemented in GoScript'),
1320
+ ]
1169
1321
  }
1170
1322
  const ctxErr = request.Context()?.Err?.()
1171
1323
  if (ctxErr != null) {
@@ -1179,7 +1331,11 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
1179
1331
  }
1180
1332
  }
1181
1333
  let body: Uint8Array | undefined
1182
- if (requestBody != null && request.Method !== MethodGet && request.Method !== MethodHead) {
1334
+ if (
1335
+ requestBody != null &&
1336
+ request.Method !== MethodGet &&
1337
+ request.Method !== MethodHead
1338
+ ) {
1183
1339
  const [data, err] = await io.ReadAll(requestBody)
1184
1340
  const closeErr = closeRequestBody()
1185
1341
  if (err != null) {
@@ -1195,21 +1351,40 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
1195
1351
  return [null, closeErr]
1196
1352
  }
1197
1353
  }
1354
+ const fetchContext = newFetchContext(request.Context())
1198
1355
  try {
1199
1356
  const bodyInit = body == null ? undefined : Uint8Array.from(body).buffer
1200
- const fetched = await globalThis.fetch(request.URL?.String?.() ?? '', {
1201
- method: request.Method || MethodGet,
1202
- headers,
1203
- body: bodyInit,
1204
- })
1205
- const data = new Uint8Array(await fetched.arrayBuffer())
1357
+ const [fetched, fetchErr] = await fetchContext.wait(
1358
+ globalThis.fetch(request.URL?.String?.() ?? '', {
1359
+ method: request.Method || MethodGet,
1360
+ headers,
1361
+ body: bodyInit,
1362
+ signal: fetchContext.signal,
1363
+ }),
1364
+ )
1365
+ if (fetchErr != null || fetched == null) {
1366
+ fetchContext.stop()
1367
+ return [null, fetchErr]
1368
+ }
1206
1369
  const respHeader = new Header()
1207
1370
  fetched.headers.forEach((value, key) => Header_Add(respHeader, key, value))
1371
+ const responseBody: io.ReadCloser =
1372
+ request.Method === MethodHead ?
1373
+ NoBody
1374
+ : (new fetchResponseBody(
1375
+ fetched,
1376
+ request.Context(),
1377
+ fetchContext.abort,
1378
+ fetchContext.stop,
1379
+ ) as unknown as io.ReadCloser)
1380
+ if (request.Method === MethodHead) {
1381
+ fetchContext.stop()
1382
+ }
1208
1383
  return [
1209
1384
  new Response({
1210
1385
  Status: `${fetched.status} ${fetched.statusText}`,
1211
1386
  StatusCode: fetched.status,
1212
- Body: new responseBody(data),
1387
+ Body: responseBody,
1213
1388
  Header: respHeader,
1214
1389
  ContentLength: Number(fetched.headers.get('content-length') ?? -1),
1215
1390
  Request: request,
@@ -1217,20 +1392,123 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
1217
1392
  null,
1218
1393
  ]
1219
1394
  } catch (err) {
1220
- const message = typeof err === 'object' && err != null && 'message' in err
1221
- ? String((err as { message: unknown }).message)
1222
- : String(err)
1223
- return [null, errors.New(message)]
1395
+ fetchContext.stop()
1396
+ return [null, errorFromUnknown(err)]
1397
+ }
1398
+ }
1399
+
1400
+ function newFetchContext(requestContext: context.Context): {
1401
+ signal: AbortSignal | undefined
1402
+ abort: () => void
1403
+ stop: () => void
1404
+ wait: <T>(promise: Promise<T>) => Promise<[T | null, $.GoError]>
1405
+ } {
1406
+ let stopped = false
1407
+ const watchController =
1408
+ typeof AbortController === 'undefined' ? null : new AbortController()
1409
+ const controller =
1410
+ typeof AbortController === 'undefined' ? null : new AbortController()
1411
+ const abort = () => {
1412
+ if (controller != null && !controller.signal.aborted) {
1413
+ controller.abort(requestContext?.Err?.() ?? context.Canceled)
1414
+ }
1415
+ }
1416
+ const donePromise =
1417
+ requestContext == null ? null : (
1418
+ (async (): Promise<$.GoError> => {
1419
+ try {
1420
+ await requestContext.Done().selectReceive(0, watchController?.signal)
1421
+ } catch {
1422
+ // Closed channels and receive wakeups both mean the context is done.
1423
+ }
1424
+ const err = requestContext.Err() ?? context.Canceled
1425
+ if (!stopped) {
1426
+ abort()
1427
+ }
1428
+ return err
1429
+ })()
1430
+ )
1431
+ return {
1432
+ signal: controller?.signal,
1433
+ abort,
1434
+ stop: () => {
1435
+ stopped = true
1436
+ watchController?.abort()
1437
+ },
1438
+ wait: async <T>(promise: Promise<T>): Promise<[T | null, $.GoError]> => {
1439
+ const settle = promise.then(
1440
+ (value) => ({ value }),
1441
+ (thrown) => ({ thrown }),
1442
+ )
1443
+ if (donePromise == null) {
1444
+ const result = await settle
1445
+ if ('thrown' in result) {
1446
+ return [null, errorFromUnknown(result.thrown)]
1447
+ }
1448
+ return [result.value, null]
1449
+ }
1450
+ const result = await Promise.race<
1451
+ { value: T } | { err: $.GoError } | { thrown: unknown }
1452
+ >([settle, donePromise.then((err) => ({ err }))])
1453
+ if ('err' in result) {
1454
+ abort()
1455
+ return [null, result.err]
1456
+ }
1457
+ if ('thrown' in result) {
1458
+ return [
1459
+ null,
1460
+ requestContext?.Err?.() ?? errorFromUnknown(result.thrown),
1461
+ ]
1462
+ }
1463
+ return [result.value, null]
1464
+ },
1224
1465
  }
1225
1466
  }
1226
1467
 
1468
+ function errorFromUnknown(err: unknown): $.GoError {
1469
+ const message =
1470
+ typeof err === 'object' && err != null && 'message' in err ?
1471
+ String((err as { message: unknown }).message)
1472
+ : String(err)
1473
+ return errors.New(message)
1474
+ }
1475
+
1476
+ async function readFetchBody(
1477
+ fetched: globalThis.Response,
1478
+ requestContext: context.Context,
1479
+ abortFetch: () => void,
1480
+ ): Promise<[Uint8Array, $.GoError]> {
1481
+ const fetchContext = newFetchContext(requestContext)
1482
+ const [buffer, err] = await fetchContext.wait(fetched.arrayBuffer())
1483
+ fetchContext.stop()
1484
+ if (err != null) {
1485
+ abortFetch()
1486
+ return [new Uint8Array(), err]
1487
+ }
1488
+ return [new Uint8Array(buffer ?? new ArrayBuffer(0)), null]
1489
+ }
1490
+
1491
+ type maybePromise<T> = T | Promise<T>
1492
+
1227
1493
  export interface FileSystem {
1228
1494
  Open(name: string): [File | null, $.GoError]
1229
1495
  }
1230
1496
 
1231
1497
  export interface File extends io.Closer, io.Reader, io.Seeker {
1232
- Readdir(count: number): [$.Slice<fs.FileInfo>, $.GoError]
1233
- Stat(): [fs.FileInfo, $.GoError]
1498
+ Readdir(count: number): [$.Slice<fs.FileInfo> | null, $.GoError]
1499
+ Stat(): [fs.FileInfo | null, $.GoError]
1500
+ }
1501
+
1502
+ interface fileServerFileSystem {
1503
+ Open(name: string): maybePromise<[fileServerFile | null, $.GoError]>
1504
+ }
1505
+
1506
+ interface fileServerFile {
1507
+ Close(): maybePromise<$.GoError>
1508
+ Read(p: $.Bytes): maybePromise<[number, $.GoError]>
1509
+ Seek(offset: number, whence: number): maybePromise<[number, $.GoError]>
1510
+ Readdir(count: number): maybePromise<[$.Slice<fs.FileInfo> | null, $.GoError]>
1511
+ Stat(): maybePromise<[fs.FileInfo | null, $.GoError]>
1234
1512
  }
1235
1513
 
1236
1514
  export function FS(fsys: fs.FS): FileSystem {
@@ -1248,17 +1526,23 @@ export function FS(fsys: fs.FS): FileSystem {
1248
1526
 
1249
1527
  function httpFileFromFSFile(file: Exclude<fs.File, null>): File {
1250
1528
  const seek = (file as Partial<io.Seeker>).Seek
1251
- const readdir = (file as { Readdir?: (count: number) => [$.Slice<fs.FileInfo>, $.GoError] }).Readdir
1529
+ const readdir = (
1530
+ file as { Readdir?: (count: number) => [$.Slice<fs.FileInfo>, $.GoError] }
1531
+ ).Readdir
1252
1532
  return {
1253
- Read: (p) => file.Read(p instanceof Uint8Array ? p : Uint8Array.from(p ?? [])),
1533
+ Read: (p) =>
1534
+ file.Read(p instanceof Uint8Array ? p : Uint8Array.from(p ?? [])),
1254
1535
  Close: () => file.Close(),
1255
1536
  Stat: () => file.Stat(),
1256
- Seek: seek == null ? () => [0, errors.New('net/http: file does not support seek')] : seek.bind(file),
1537
+ Seek:
1538
+ seek == null ?
1539
+ () => [0, errors.New('net/http: file does not support seek')]
1540
+ : seek.bind(file),
1257
1541
  Readdir: readdir == null ? () => [null, io.EOF] : readdir.bind(file),
1258
1542
  }
1259
1543
  }
1260
1544
 
1261
- export function FileServer(root: FileSystem | null): Handler {
1545
+ export function FileServer(root: fileServerFileSystem | null): Handler {
1262
1546
  return {
1263
1547
  async ServeHTTP(w, r): Promise<void> {
1264
1548
  const req = $.pointerValue<Request | null>(r)
@@ -1269,13 +1553,15 @@ export function FileServer(root: FileSystem | null): Handler {
1269
1553
  Error(w, 'method not allowed', StatusMethodNotAllowed)
1270
1554
  return
1271
1555
  }
1272
- const [file, err] = root?.Open(cleanFileServerPath(req.URL?.Path ?? '')) ?? [null, fs.ErrInvalid]
1556
+ const [file, err] = (await root?.Open(
1557
+ cleanFileServerPath(req.URL?.Path ?? ''),
1558
+ )) ?? [null, fs.ErrInvalid]
1273
1559
  if (err != null || file == null) {
1274
1560
  NotFound(w, req)
1275
1561
  return
1276
1562
  }
1277
1563
  try {
1278
- const [info, statErr] = file.Stat()
1564
+ const [info, statErr] = await file.Stat()
1279
1565
  if (statErr != null) {
1280
1566
  Error(w, statErr.Error(), StatusInternalServerError)
1281
1567
  return
@@ -1284,7 +1570,7 @@ export function FileServer(root: FileSystem | null): Handler {
1284
1570
  NotFound(w, req)
1285
1571
  return
1286
1572
  }
1287
- const [data, readErr] = await io.ReadAll(file)
1573
+ const [data, readErr] = await io.ReadAll(file as io.Reader)
1288
1574
  if (readErr != null) {
1289
1575
  Error(w, readErr.Error(), StatusInternalServerError)
1290
1576
  return
@@ -1297,7 +1583,7 @@ export function FileServer(root: FileSystem | null): Handler {
1297
1583
  w.Write(data)
1298
1584
  }
1299
1585
  } finally {
1300
- file.Close()
1586
+ await file.Close()
1301
1587
  }
1302
1588
  },
1303
1589
  }
@@ -1348,8 +1634,25 @@ function cleanFileServerPath(name: string): string {
1348
1634
  }
1349
1635
 
1350
1636
  export interface Handler {
1351
- ServeHTTP(w: ResponseWriter | null, r: Request | $.VarRef<Request> | null): void | Promise<void>
1352
- }
1637
+ ServeHTTP(
1638
+ w: ResponseWriter | null,
1639
+ r: Request | $.VarRef<Request> | null,
1640
+ ): void | Promise<void>
1641
+ }
1642
+
1643
+ $.registerInterfaceType('http.Handler', null, [
1644
+ {
1645
+ name: 'ServeHTTP',
1646
+ args: [
1647
+ { name: 'w', type: 'http.ResponseWriter' },
1648
+ {
1649
+ name: 'r',
1650
+ type: { kind: $.TypeKind.Pointer, elemType: 'http.Request' },
1651
+ },
1652
+ ],
1653
+ returns: [],
1654
+ },
1655
+ ])
1353
1656
 
1354
1657
  export type HandlerFunc = (
1355
1658
  w: ResponseWriter | null,
@@ -1357,10 +1660,13 @@ export type HandlerFunc = (
1357
1660
  ) => void | Promise<void>
1358
1661
 
1359
1662
  export function HandlerFunc_ServeHTTP(
1360
- h: HandlerFunc,
1663
+ h: HandlerFunc | null,
1361
1664
  w: ResponseWriter | null,
1362
1665
  r: Request | $.VarRef<Request> | null,
1363
1666
  ): void | Promise<void> {
1667
+ if (!h) {
1668
+ throw new globalThis.Error('http: nil HandlerFunc')
1669
+ }
1364
1670
  return h(w, r)
1365
1671
  }
1366
1672
 
@@ -1512,7 +1818,9 @@ function wildcardPatternMatches(pattern: string, path: string): boolean {
1512
1818
  export class Server {
1513
1819
  public Addr: string
1514
1820
  public BaseContext: ((listener: any) => context.Context) | null
1515
- public ConnContext: ((ctx: context.Context, conn: any) => context.Context) | null
1821
+ public ConnContext:
1822
+ | ((ctx: context.Context, conn: any) => context.Context)
1823
+ | null
1516
1824
  public Handler: Handler | null
1517
1825
  public DisableGeneralOptionsHandler: boolean
1518
1826
  public TLSConfig: any
@@ -1533,7 +1841,8 @@ export class Server {
1533
1841
  this.BaseContext = init?.BaseContext ?? null
1534
1842
  this.ConnContext = init?.ConnContext ?? null
1535
1843
  this.Handler = init?.Handler ?? null
1536
- this.DisableGeneralOptionsHandler = init?.DisableGeneralOptionsHandler ?? false
1844
+ this.DisableGeneralOptionsHandler =
1845
+ init?.DisableGeneralOptionsHandler ?? false
1537
1846
  this.TLSConfig = init?.TLSConfig ?? null
1538
1847
  this.ReadTimeout = init?.ReadTimeout ?? 0
1539
1848
  this.ReadTimeoutHandler = (init as any)?.ReadTimeoutHandler ?? null
@@ -1549,11 +1858,15 @@ export class Server {
1549
1858
  }
1550
1859
 
1551
1860
  public ListenAndServe(): $.GoError {
1552
- return errors.New('net/http: Server.ListenAndServe is not implemented in GoScript')
1861
+ return errors.New(
1862
+ 'net/http: Server.ListenAndServe is not implemented in GoScript',
1863
+ )
1553
1864
  }
1554
1865
 
1555
1866
  public ListenAndServeTLS(_certFile: string, _keyFile: string): $.GoError {
1556
- return errors.New('net/http: Server.ListenAndServeTLS is not implemented in GoScript')
1867
+ return errors.New(
1868
+ 'net/http: Server.ListenAndServeTLS is not implemented in GoScript',
1869
+ )
1557
1870
  }
1558
1871
 
1559
1872
  public Close(): $.GoError {
@@ -1568,11 +1881,18 @@ export class Server {
1568
1881
  return ErrNotSupported
1569
1882
  }
1570
1883
 
1571
- public ServeTLS(_listener: any, _certFile: string, _keyFile: string): $.GoError {
1884
+ public ServeTLS(
1885
+ _listener: any,
1886
+ _certFile: string,
1887
+ _keyFile: string,
1888
+ ): $.GoError {
1572
1889
  return ErrNotSupported
1573
1890
  }
1574
1891
 
1575
- public ServeHTTP(w: ResponseWriter | null, r: Request | $.VarRef<Request> | null): void | Promise<void> {
1892
+ public ServeHTTP(
1893
+ w: ResponseWriter | null,
1894
+ r: Request | $.VarRef<Request> | null,
1895
+ ): void | Promise<void> {
1576
1896
  return (this.Handler ?? DefaultServeMux).ServeHTTP(w, r)
1577
1897
  }
1578
1898
 
@@ -1581,7 +1901,10 @@ export class Server {
1581
1901
  public SetKeepAlivesEnabled(_v: boolean): void {}
1582
1902
  }
1583
1903
 
1584
- export function ListenAndServe(_addr: string, _handler: Handler | null): $.GoError {
1904
+ export function ListenAndServe(
1905
+ _addr: string,
1906
+ _handler: Handler | null,
1907
+ ): $.GoError {
1585
1908
  return ErrNotSupported
1586
1909
  }
1587
1910
 
@@ -1624,7 +1947,10 @@ export interface Hijacker {
1624
1947
  }
1625
1948
 
1626
1949
  export interface Pusher {
1627
- Push(target: string, opts: PushOptions | $.VarRef<PushOptions> | null): $.GoError
1950
+ Push(
1951
+ target: string,
1952
+ opts: PushOptions | $.VarRef<PushOptions> | null,
1953
+ ): $.GoError
1628
1954
  }
1629
1955
 
1630
1956
  export class ResponseController {
@@ -1657,7 +1983,9 @@ export class ResponseController {
1657
1983
  }
1658
1984
  }
1659
1985
 
1660
- export function NewResponseController(rw: ResponseWriter | null): ResponseController {
1986
+ export function NewResponseController(
1987
+ rw: ResponseWriter | null,
1988
+ ): ResponseController {
1661
1989
  return new ResponseController(rw)
1662
1990
  }
1663
1991
 
@@ -1666,7 +1994,10 @@ class maxBytesReader implements io.ReadCloser {
1666
1994
  private remaining: number
1667
1995
  private err: $.GoError = null
1668
1996
 
1669
- constructor(private reader: io.ReadCloser, limit: number) {
1997
+ constructor(
1998
+ private reader: io.ReadCloser,
1999
+ limit: number,
2000
+ ) {
1670
2001
  this.initialLimit = Math.max(0, limit)
1671
2002
  this.remaining = this.initialLimit
1672
2003
  }
@@ -1719,14 +2050,19 @@ export class ServeMux implements Handler {
1719
2050
  this.Handle(pattern, { ServeHTTP: handler })
1720
2051
  }
1721
2052
 
1722
- public Handler(r: Request | $.VarRef<Request> | null): [Handler | null, string] {
2053
+ public Handler(
2054
+ r: Request | $.VarRef<Request> | null,
2055
+ ): [Handler | null, string] {
1723
2056
  const req = $.pointerValue<Request | null>(r)
1724
2057
  const path = req?.URL?.Path ?? ''
1725
2058
  const handler = this.handlers.get(path) ?? null
1726
2059
  return [handler, handler == null ? '' : path]
1727
2060
  }
1728
2061
 
1729
- public ServeHTTP(w: ResponseWriter | null, r: Request | $.VarRef<Request> | null): void | Promise<void> {
2062
+ public ServeHTTP(
2063
+ w: ResponseWriter | null,
2064
+ r: Request | $.VarRef<Request> | null,
2065
+ ): void | Promise<void> {
1730
2066
  const [handler] = this.Handler(r)
1731
2067
  if (handler == null) {
1732
2068
  NotFound(w, r)
@@ -1754,7 +2090,11 @@ export function StripPrefix(prefix: string, handler: Handler | null): Handler {
1754
2090
  return {
1755
2091
  ServeHTTP(w, r) {
1756
2092
  const req = $.pointerValue<Request | null>(r)
1757
- if (req?.URL != null && typeof req.URL.Path === 'string' && req.URL.Path.startsWith(prefix)) {
2093
+ if (
2094
+ req?.URL != null &&
2095
+ typeof req.URL.Path === 'string' &&
2096
+ req.URL.Path.startsWith(prefix)
2097
+ ) {
1758
2098
  req.URL = { ...req.URL, Path: req.URL.Path.slice(prefix.length) || '/' }
1759
2099
  }
1760
2100
  return handler?.ServeHTTP(w, req)
@@ -1796,7 +2136,11 @@ export function RedirectHandler(url: string, code: number): Handler {
1796
2136
  }
1797
2137
  }
1798
2138
 
1799
- export function TimeoutHandler(handler: Handler | null, _dt: number, msg: string): Handler {
2139
+ export function TimeoutHandler(
2140
+ handler: Handler | null,
2141
+ _dt: number,
2142
+ msg: string,
2143
+ ): Handler {
1800
2144
  return {
1801
2145
  ServeHTTP(w, r) {
1802
2146
  if (handler == null) {
@@ -1808,12 +2152,19 @@ export function TimeoutHandler(handler: Handler | null, _dt: number, msg: string
1808
2152
  }
1809
2153
  }
1810
2154
 
1811
- export function Error(w: ResponseWriter | null, error: string, code: number): void {
2155
+ export function Error(
2156
+ w: ResponseWriter | null,
2157
+ error: string,
2158
+ code: number,
2159
+ ): void {
1812
2160
  w?.WriteHeader(code)
1813
2161
  w?.Write($.stringToBytes(error + '\n'))
1814
2162
  }
1815
2163
 
1816
- export function NotFound(w: ResponseWriter | null, _r: Request | $.VarRef<Request> | null): void {
2164
+ export function NotFound(
2165
+ w: ResponseWriter | null,
2166
+ _r: Request | $.VarRef<Request> | null,
2167
+ ): void {
1817
2168
  Error(w, '404 page not found', StatusNotFound)
1818
2169
  }
1819
2170
 
@@ -1833,7 +2184,10 @@ export function Redirect(
1833
2184
  export function ParseTime(text: string): [time.Time, $.GoError] {
1834
2185
  const date = new globalThis.Date(text)
1835
2186
  if (isNaN(date.getTime())) {
1836
- return [new time.Time(), $.newError(`parsing time "${text}" as HTTP-date: cannot parse`)]
2187
+ return [
2188
+ new time.Time(),
2189
+ $.newError(`parsing time "${text}" as HTTP-date: cannot parse`),
2190
+ ]
1837
2191
  }
1838
2192
  return [time.UnixMilli(date.getTime()), null]
1839
2193
  }
@@ -1881,7 +2235,10 @@ export function DetectContentType(data: $.Slice<number>): string {
1881
2235
  if (startsWithBytes(bytes, new Uint8Array([0xef, 0xbb, 0xbf]))) {
1882
2236
  return 'text/plain; charset=utf-8'
1883
2237
  }
1884
- if (startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) || startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))) {
2238
+ if (
2239
+ startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) ||
2240
+ startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))
2241
+ ) {
1885
2242
  return 'image/x-icon'
1886
2243
  }
1887
2244
  if (startsWithASCII(bytes, 'BM')) {
@@ -1893,7 +2250,12 @@ export function DetectContentType(data: $.Slice<number>): string {
1893
2250
  if (isRIFFSignature(bytes, 'WEBPVP')) {
1894
2251
  return 'image/webp'
1895
2252
  }
1896
- if (startsWithBytes(bytes, new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
2253
+ if (
2254
+ startsWithBytes(
2255
+ bytes,
2256
+ new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
2257
+ )
2258
+ ) {
1897
2259
  return 'image/png'
1898
2260
  }
1899
2261
  if (startsWithBytes(bytes, new Uint8Array([0xff, 0xd8, 0xff]))) {
@@ -1908,7 +2270,12 @@ export function DetectContentType(data: $.Slice<number>): string {
1908
2270
  if (startsWithBytes(bytes, new Uint8Array([0x4f, 0x67, 0x67, 0x53, 0x00]))) {
1909
2271
  return 'application/ogg'
1910
2272
  }
1911
- if (startsWithBytes(bytes, new Uint8Array([0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06]))) {
2273
+ if (
2274
+ startsWithBytes(
2275
+ bytes,
2276
+ new Uint8Array([0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06]),
2277
+ )
2278
+ ) {
1912
2279
  return 'audio/midi'
1913
2280
  }
1914
2281
  if (isRIFFSignature(bytes, 'AVI ')) {
@@ -1947,17 +2314,32 @@ export function DetectContentType(data: $.Slice<number>): string {
1947
2314
  if (startsWithBytes(bytes, new Uint8Array([0x50, 0x4b, 0x03, 0x04]))) {
1948
2315
  return 'application/zip'
1949
2316
  }
1950
- if (startsWithBytes(bytes, new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00]))) {
2317
+ if (
2318
+ startsWithBytes(
2319
+ bytes,
2320
+ new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00]),
2321
+ )
2322
+ ) {
1951
2323
  return 'application/x-rar-compressed'
1952
2324
  }
1953
- if (startsWithBytes(bytes, new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00]))) {
2325
+ if (
2326
+ startsWithBytes(
2327
+ bytes,
2328
+ new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00]),
2329
+ )
2330
+ ) {
1954
2331
  return 'application/x-rar-compressed'
1955
2332
  }
1956
2333
  if (startsWithBytes(bytes, new Uint8Array([0x00, 0x61, 0x73, 0x6d]))) {
1957
2334
  return 'application/wasm'
1958
2335
  }
1959
2336
  for (const byte of afterWS) {
1960
- if (byte <= 0x08 || byte === 0x0b || (byte >= 0x0e && byte <= 0x1a) || (byte >= 0x1c && byte <= 0x1f)) {
2337
+ if (
2338
+ byte <= 0x08 ||
2339
+ byte === 0x0b ||
2340
+ (byte >= 0x0e && byte <= 0x1a) ||
2341
+ (byte >= 0x1c && byte <= 0x1f)
2342
+ ) {
1961
2343
  return 'application/octet-stream'
1962
2344
  }
1963
2345
  }
@@ -1967,7 +2349,13 @@ export function DetectContentType(data: $.Slice<number>): string {
1967
2349
  function firstNonWhitespace(data: Uint8Array): number {
1968
2350
  for (let i = 0; i < data.length; i++) {
1969
2351
  const byte = data[i]
1970
- if (byte !== 0x09 && byte !== 0x0a && byte !== 0x0c && byte !== 0x0d && byte !== 0x20) {
2352
+ if (
2353
+ byte !== 0x09 &&
2354
+ byte !== 0x0a &&
2355
+ byte !== 0x0c &&
2356
+ byte !== 0x0d &&
2357
+ byte !== 0x20
2358
+ ) {
1971
2359
  return i
1972
2360
  }
1973
2361
  }
@@ -1990,7 +2378,11 @@ function startsWithASCII(data: Uint8Array, prefix: string): boolean {
1990
2378
  return asciiMatchesAt(data, 0, prefix)
1991
2379
  }
1992
2380
 
1993
- function asciiMatchesAt(data: Uint8Array, offset: number, text: string): boolean {
2381
+ function asciiMatchesAt(
2382
+ data: Uint8Array,
2383
+ offset: number,
2384
+ text: string,
2385
+ ): boolean {
1994
2386
  if (data.length < offset + text.length) {
1995
2387
  return false
1996
2388
  }
@@ -2102,7 +2494,12 @@ export function ParseSetCookie(line: string): [Cookie | null, $.GoError] {
2102
2494
  if (!ok) {
2103
2495
  return [null, errInvalidCookieValue]
2104
2496
  }
2105
- const cookie = new Cookie({ Name: name, Value: value, Quoted: quoted, Raw: line })
2497
+ const cookie = new Cookie({
2498
+ Name: name,
2499
+ Value: value,
2500
+ Quoted: quoted,
2501
+ Raw: line,
2502
+ })
2106
2503
  const unparsed: string[] = []
2107
2504
  for (const raw of parts.slice(1)) {
2108
2505
  const part = raw.trim()
@@ -2158,7 +2555,10 @@ export function ParseSetCookie(line: string): [Cookie | null, $.GoError] {
2158
2555
  break
2159
2556
  }
2160
2557
  let secs = Number.parseInt(attrValue, 10)
2161
- if ((secs !== 0 && attrValue[0] === '0') || !Number.isSafeInteger(secs)) {
2558
+ if (
2559
+ (secs !== 0 && attrValue[0] === '0') ||
2560
+ !Number.isSafeInteger(secs)
2561
+ ) {
2162
2562
  break
2163
2563
  }
2164
2564
  if (secs <= 0) {
@@ -2207,7 +2607,10 @@ export function NewRequestWithContext(
2207
2607
  method = MethodGet
2208
2608
  }
2209
2609
  if (!isToken(method)) {
2210
- return [null, errors.New(`net/http: invalid method ${JSON.stringify(method)}`)]
2610
+ return [
2611
+ null,
2612
+ errors.New(`net/http: invalid method ${JSON.stringify(method)}`),
2613
+ ]
2211
2614
  }
2212
2615
  if (ctx == null) {
2213
2616
  return [null, errors.New('net/http: nil Context')]
@@ -2217,7 +2620,17 @@ export function NewRequestWithContext(
2217
2620
  return [null, err]
2218
2621
  }
2219
2622
  const bodyInfo = requestBodyInfo(body, 0)
2220
- return [new Request({ Method: method, URL: parsedURL, Body: bodyInfo.Body, ContentLength: bodyInfo.ContentLength, Host: parsedURL.Host, ctx }), null]
2623
+ return [
2624
+ new Request({
2625
+ Method: method,
2626
+ URL: parsedURL,
2627
+ Body: bodyInfo.Body,
2628
+ ContentLength: bodyInfo.ContentLength,
2629
+ Host: parsedURL.Host,
2630
+ ctx,
2631
+ }),
2632
+ null,
2633
+ ]
2221
2634
  }
2222
2635
 
2223
2636
  export async function Get(_url: string): Promise<[Response | null, $.GoError]> {
@@ -2228,7 +2641,9 @@ export async function Get(_url: string): Promise<[Response | null, $.GoError]> {
2228
2641
  return await DefaultClient.Do(req)
2229
2642
  }
2230
2643
 
2231
- export async function Head(_url: string): Promise<[Response | null, $.GoError]> {
2644
+ export async function Head(
2645
+ _url: string,
2646
+ ): Promise<[Response | null, $.GoError]> {
2232
2647
  const [req, err] = NewRequest(MethodHead, _url, null)
2233
2648
  if (err != null) {
2234
2649
  return [null, err]
@@ -2249,15 +2664,22 @@ export async function Post(
2249
2664
  return await DefaultClient.Do(req)
2250
2665
  }
2251
2666
 
2252
- export async function PostForm(_url: string, data: any): Promise<[Response | null, $.GoError]> {
2667
+ export async function PostForm(
2668
+ _url: string,
2669
+ data: any,
2670
+ ): Promise<[Response | null, $.GoError]> {
2253
2671
  return await DefaultClient.PostForm(_url, data)
2254
2672
  }
2255
2673
 
2256
- export function ProxyFromEnvironment(_req: Request | $.VarRef<Request> | null): [any, $.GoError] {
2674
+ export function ProxyFromEnvironment(
2675
+ _req: Request | $.VarRef<Request> | null,
2676
+ ): [any, $.GoError] {
2257
2677
  return [null, null]
2258
2678
  }
2259
2679
 
2260
- export function ProxyURL(fixedURL: any): (req: Request | $.VarRef<Request> | null) => [any, $.GoError] {
2680
+ export function ProxyURL(
2681
+ fixedURL: any,
2682
+ ): (req: Request | $.VarRef<Request> | null) => [any, $.GoError] {
2261
2683
  return () => [fixedURL, null]
2262
2684
  }
2263
2685
 
@@ -2265,7 +2687,10 @@ export function ReadRequest(_reader: any): [Request | null, $.GoError] {
2265
2687
  return [null, ErrNotSupported]
2266
2688
  }
2267
2689
 
2268
- export function ReadResponse(_reader: any, _req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] {
2690
+ export function ReadResponse(
2691
+ _reader: any,
2692
+ _req: Request | $.VarRef<Request> | null,
2693
+ ): [Response | null, $.GoError] {
2269
2694
  return [null, ErrNotSupported]
2270
2695
  }
2271
2696
 
@@ -2303,7 +2728,10 @@ function readCloserForBody(body: io.Reader | null): io.ReadCloser | null {
2303
2728
  return io.NopCloser(body)
2304
2729
  }
2305
2730
 
2306
- function requestBodyInfo(body: io.Reader | null, unknownLength: number): { Body: io.ReadCloser | null; ContentLength: number } {
2731
+ function requestBodyInfo(
2732
+ body: io.Reader | null,
2733
+ unknownLength: number,
2734
+ ): { Body: io.ReadCloser | null; ContentLength: number } {
2307
2735
  if (body == null) {
2308
2736
  return { Body: null, ContentLength: 0 }
2309
2737
  }
@@ -2311,9 +2739,16 @@ function requestBodyInfo(body: io.Reader | null, unknownLength: number): { Body:
2311
2739
  if (value === NoBody) {
2312
2740
  return { Body: NoBody, ContentLength: 0 }
2313
2741
  }
2314
- if (value instanceof bytes.Buffer || value instanceof bytes.Reader || value instanceof strings.Reader) {
2742
+ if (
2743
+ value instanceof bytes.Buffer ||
2744
+ value instanceof bytes.Reader ||
2745
+ value instanceof strings.Reader
2746
+ ) {
2315
2747
  const length = value.Len()
2316
- return { Body: length === 0 ? NoBody : readCloserForBody(body), ContentLength: length }
2748
+ return {
2749
+ Body: length === 0 ? NoBody : readCloserForBody(body),
2750
+ ContentLength: length,
2751
+ }
2317
2752
  }
2318
2753
  return { Body: readCloserForBody(body), ContentLength: unknownLength }
2319
2754
  }