goscript 0.1.2 → 0.1.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 (49) hide show
  1. package/compiler/lowered-program.go +1 -0
  2. package/compiler/lowering.go +715 -44
  3. package/compiler/override-registry_test.go +43 -0
  4. package/compiler/skeleton_test.go +464 -12
  5. package/compiler/typescript-emitter.go +28 -2
  6. package/dist/gs/builtin/channel.js +36 -9
  7. package/dist/gs/builtin/channel.js.map +1 -1
  8. package/dist/gs/builtin/type.js +8 -3
  9. package/dist/gs/builtin/type.js.map +1 -1
  10. package/dist/gs/bytes/bytes.gs.d.ts +7 -5
  11. package/dist/gs/bytes/bytes.gs.js +10 -4
  12. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  13. package/dist/gs/crypto/sha1/index.d.ts +5 -0
  14. package/dist/gs/crypto/sha1/index.js +106 -0
  15. package/dist/gs/crypto/sha1/index.js.map +1 -0
  16. package/dist/gs/fmt/fmt.d.ts +1 -1
  17. package/dist/gs/fmt/fmt.js +64 -3
  18. package/dist/gs/fmt/fmt.js.map +1 -1
  19. package/dist/gs/io/io.d.ts +8 -5
  20. package/dist/gs/io/io.js +20 -2
  21. package/dist/gs/io/io.js.map +1 -1
  22. package/dist/gs/net/http/httptest/index.js +7 -5
  23. package/dist/gs/net/http/httptest/index.js.map +1 -1
  24. package/dist/gs/net/http/index.d.ts +8 -0
  25. package/dist/gs/net/http/index.js +139 -10
  26. package/dist/gs/net/http/index.js.map +1 -1
  27. package/dist/gs/os/zero_copy_posix.gs.js +1 -1
  28. package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
  29. package/gs/builtin/channel.ts +47 -9
  30. package/gs/builtin/runtime-contract.test.ts +33 -0
  31. package/gs/builtin/type.ts +12 -3
  32. package/gs/bytes/bytes.gs.ts +19 -10
  33. package/gs/bytes/bytes.test.ts +17 -0
  34. package/gs/context/context.test.ts +5 -1
  35. package/gs/crypto/sha1/index.test.ts +28 -0
  36. package/gs/crypto/sha1/index.ts +130 -0
  37. package/gs/crypto/sha1/meta.json +8 -0
  38. package/gs/fmt/fmt.test.ts +20 -0
  39. package/gs/fmt/fmt.ts +75 -5
  40. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +1 -1
  41. package/gs/io/io.test.ts +64 -0
  42. package/gs/io/io.ts +30 -12
  43. package/gs/net/http/httptest/index.test.ts +34 -2
  44. package/gs/net/http/httptest/index.ts +23 -8
  45. package/gs/net/http/index.test.ts +30 -0
  46. package/gs/net/http/index.ts +159 -10
  47. package/gs/os/zero_copy_posix.gs.ts +1 -2
  48. package/gs/sync/meta.json +1 -0
  49. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import * as $ from '@goscript/builtin/index.js'
2
+ import * as bytes from '@goscript/bytes/index.js'
2
3
  import * as context from '@goscript/context/index.js'
3
4
  import * as errors from '@goscript/errors/index.js'
4
5
  import * as fs from '@goscript/io/fs/fs.js'
@@ -10,9 +11,14 @@ export const StatusCreated = 201
10
11
  export const StatusPartialContent = 206
11
12
  export const StatusMovedPermanently = 301
12
13
  export const StatusBadRequest = 400
14
+ export const StatusUnauthorized = 401
15
+ export const StatusForbidden = 403
16
+ export const StatusMethodNotAllowed = 405
13
17
  export const StatusRequestTimeout = 408
14
18
  export const StatusConflict = 409
15
19
  export const StatusNotFound = 404
20
+ export const StatusUnsupportedMediaType = 415
21
+ export const StatusTeapot = 418
16
22
  export const StatusTooManyRequests = 429
17
23
  export const StatusRequestedRangeNotSatisfiable = 416
18
24
  export const StatusInternalServerError = 500
@@ -31,6 +37,12 @@ export function StatusText(code: number): string {
31
37
  return 'OK'
32
38
  case StatusMovedPermanently:
33
39
  return 'Moved Permanently'
40
+ case StatusUnauthorized:
41
+ return 'Unauthorized'
42
+ case StatusForbidden:
43
+ return 'Forbidden'
44
+ case StatusMethodNotAllowed:
45
+ return 'Method Not Allowed'
34
46
  case StatusBadRequest:
35
47
  return 'Bad Request'
36
48
  case StatusRequestTimeout:
@@ -39,12 +51,16 @@ export function StatusText(code: number): string {
39
51
  return 'Conflict'
40
52
  case StatusNotFound:
41
53
  return 'Not Found'
54
+ case StatusUnsupportedMediaType:
55
+ return 'Unsupported Media Type'
42
56
  case StatusTooManyRequests:
43
57
  return 'Too Many Requests'
44
58
  case StatusPartialContent:
45
59
  return 'Partial Content'
46
60
  case StatusRequestedRangeNotSatisfiable:
47
61
  return 'Requested Range Not Satisfiable'
62
+ case StatusTeapot:
63
+ return "I'm a teapot"
48
64
  case StatusInternalServerError:
49
65
  return 'Internal Server Error'
50
66
  case StatusServiceUnavailable:
@@ -61,22 +77,39 @@ export const Header = Map as {
61
77
  }
62
78
 
63
79
  export function Header_Add(h: Header, key: string, value: string): void {
80
+ key = canonicalMIMEHeaderKey(key)
64
81
  const values = Array.from(h.get(key) ?? [])
65
82
  values.push(value)
66
83
  h.set(key, $.arrayToSlice(values))
67
84
  }
68
85
 
69
86
  export function Header_Del(h: Header, key: string): void {
70
- h.delete(key)
87
+ h.delete(canonicalMIMEHeaderKey(key))
71
88
  }
72
89
 
73
90
  export function Header_Get(h: Header, key: string): string {
74
- const values = h.get(key)
91
+ const values = h.get(canonicalMIMEHeaderKey(key))
75
92
  return values == null || values.length === 0 ? '' : String(values[0])
76
93
  }
77
94
 
78
95
  export function Header_Set(h: Header, key: string, value: string): void {
79
- h.set(key, $.arrayToSlice([value]))
96
+ h.set(canonicalMIMEHeaderKey(key), $.arrayToSlice([value]))
97
+ }
98
+
99
+ function canonicalMIMEHeaderKey(key: string): string {
100
+ let upper = true
101
+ let out = ''
102
+ for (let i = 0; i < key.length; i++) {
103
+ const ch = key[i]
104
+ if (ch === '-') {
105
+ upper = true
106
+ out += ch
107
+ continue
108
+ }
109
+ out += upper ? ch.toUpperCase() : ch.toLowerCase()
110
+ upper = false
111
+ }
112
+ return out
80
113
  }
81
114
 
82
115
  class QueryValues extends Map<string, $.Slice<string>> {
@@ -90,13 +123,27 @@ class QueryValues extends Map<string, $.Slice<string>> {
90
123
  const values = this.get(key)
91
124
  return values == null || values.length === 0 ? '' : String(values[0])
92
125
  }
126
+
127
+ public Encode(): string {
128
+ const params = new URLSearchParams()
129
+ for (const [key, values] of this.entries()) {
130
+ for (const value of Array.from(values ?? [])) {
131
+ params.append(key, String(value))
132
+ }
133
+ }
134
+ return params.toString()
135
+ }
93
136
  }
94
137
 
95
138
  class RequestURL {
139
+ public Scheme: string
140
+ public Host: string
96
141
  public Path: string
97
142
  public RawQuery: string
98
143
 
99
- constructor(path: string, rawQuery: string) {
144
+ constructor(path: string, rawQuery: string, scheme = '', host = '') {
145
+ this.Scheme = scheme
146
+ this.Host = host
100
147
  this.Path = path
101
148
  this.RawQuery = rawQuery
102
149
  }
@@ -109,19 +156,100 @@ class RequestURL {
109
156
  }
110
157
 
111
158
  public clone(): RequestURL {
112
- return new RequestURL(this.Path, this.RawQuery)
159
+ return new RequestURL(this.Path, this.RawQuery, this.Scheme, this.Host)
160
+ }
161
+
162
+ public String(): string {
163
+ const query = this.RawQuery === '' ? '' : `?${this.RawQuery}`
164
+ const path = this.Path === '' ? '/' : this.Path
165
+ if (this.Scheme === '' || this.Host === '') {
166
+ return `${path}${query}`
167
+ }
168
+ return `${this.Scheme}://${this.Host}${path}${query}`
113
169
  }
114
170
  }
115
171
 
116
172
  function parseRequestURL(rawURL: string): RequestURL {
117
173
  try {
118
174
  const parsed = new URL(rawURL, 'http://goscript.invalid')
119
- return new RequestURL(parsed.pathname, parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search)
175
+ const hasHost = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(rawURL)
176
+ return new RequestURL(
177
+ parsed.pathname,
178
+ parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search,
179
+ hasHost ? parsed.protocol.replace(/:$/, '') : '',
180
+ hasHost ? parsed.host : '',
181
+ )
120
182
  } catch {
121
183
  return new RequestURL('', '')
122
184
  }
123
185
  }
124
186
 
187
+ class responseBody implements io.ReadCloser {
188
+ private reader: bytes.Reader
189
+
190
+ constructor(data: $.Bytes) {
191
+ this.reader = bytes.NewReader(Uint8Array.from(data ?? []))
192
+ }
193
+
194
+ public Read(p: $.Bytes): [number, $.GoError] {
195
+ return this.reader.Read(p)
196
+ }
197
+
198
+ public Close(): $.GoError {
199
+ return null
200
+ }
201
+ }
202
+
203
+ class memoryResponseWriter implements ResponseWriter {
204
+ public Code = StatusOK
205
+ public Body = new bytes.Buffer()
206
+ private headerMap = new Header()
207
+ private wroteHeader = false
208
+
209
+ public Header(): Header {
210
+ return this.headerMap
211
+ }
212
+
213
+ public Write(p: $.Slice<number>): [number, $.GoError] {
214
+ if (!this.wroteHeader) {
215
+ this.WriteHeader(StatusOK)
216
+ }
217
+ return this.Body.Write(p)
218
+ }
219
+
220
+ public WriteHeader(statusCode: number): void {
221
+ if (this.wroteHeader) {
222
+ return
223
+ }
224
+ this.wroteHeader = true
225
+ this.Code = statusCode
226
+ }
227
+
228
+ public Result(): Response {
229
+ return new Response({
230
+ Body: new responseBody(this.Body.Bytes()),
231
+ Header: this.headerMap,
232
+ StatusCode: this.Code,
233
+ })
234
+ }
235
+ }
236
+
237
+ const inProcessServers = new Map<string, Handler>()
238
+ let nextInProcessServerID = 1
239
+
240
+ export function RegisterInProcessServer(handler: Handler | null): string {
241
+ const host = `goscript-httptest-${nextInProcessServerID++}.invalid`
242
+ inProcessServers.set(host, handler ?? { ServeHTTP: NotFound })
243
+ return `http://${host}`
244
+ }
245
+
246
+ export function UnregisterInProcessServer(rawURL: string): void {
247
+ const parsed = parseRequestURL(rawURL)
248
+ if (parsed.Host !== '') {
249
+ inProcessServers.delete(parsed.Host)
250
+ }
251
+ }
252
+
125
253
  export interface ResponseWriter {
126
254
  Header(): Header
127
255
  Write(p: $.Slice<number>): [number, $.GoError]
@@ -181,20 +309,27 @@ export class Request {
181
309
  }
182
310
 
183
311
  export class Response {
312
+ public Status: string
184
313
  public StatusCode: number
185
314
  public Body: io.ReadCloser | null
186
315
  public Header: Header
187
316
 
188
317
  constructor(init?: Partial<Response>) {
318
+ this.Status = init?.Status ?? ''
189
319
  this.StatusCode = init?.StatusCode ?? 0
190
320
  this.Body = init?.Body ?? null
191
321
  this.Header = init?.Header ?? new Header()
322
+ if (this.Status === '' && this.StatusCode !== 0) {
323
+ const text = StatusText(this.StatusCode)
324
+ this.Status = text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`
325
+ }
192
326
  }
193
327
 
194
328
  public clone(): Response {
195
329
  return new Response({
196
330
  Body: this.Body,
197
331
  Header: this.Header,
332
+ Status: this.Status,
198
333
  StatusCode: this.StatusCode,
199
334
  })
200
335
  }
@@ -220,13 +355,27 @@ export interface RoundTripper {
220
355
  RoundTrip(req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
221
356
  }
222
357
 
223
- class unsupportedTransport implements RoundTripper {
224
- public async RoundTrip(_req: Request | $.VarRef<Request> | null): Promise<[Response | null, $.GoError]> {
225
- return [null, errors.New('net/http: Client.Do is not implemented in GoScript')]
358
+ class defaultTransport implements RoundTripper {
359
+ public async RoundTrip(req: Request | $.VarRef<Request> | null): Promise<[Response | null, $.GoError]> {
360
+ const request = $.pointerValue<Request | null>(req)
361
+ if (request == null) {
362
+ return [null, errors.New('net/http: nil Request')]
363
+ }
364
+ const host = request.URL?.Host ?? ''
365
+ const handler = host === '' ? null : inProcessServers.get(host)
366
+ if (handler == null) {
367
+ return [null, errors.New('net/http: Client.Do is not implemented in GoScript')]
368
+ }
369
+ const recorder = new memoryResponseWriter()
370
+ const served = handler.ServeHTTP(recorder, request)
371
+ if (served instanceof Promise) {
372
+ await served
373
+ }
374
+ return [recorder.Result(), null]
226
375
  }
227
376
  }
228
377
 
229
- export const DefaultTransport: RoundTripper = new unsupportedTransport()
378
+ export const DefaultTransport: RoundTripper = new defaultTransport()
230
379
 
231
380
  export interface FileSystem {
232
381
  Open(name: string): [File | null, $.GoError]
@@ -30,6 +30,5 @@ export function tryLimitedReader(r: io.Reader): [io.LimitedReader | null, io.Rea
30
30
  }
31
31
 
32
32
  remain = lr!.N
33
- return [lr, lr!.R, remain]
33
+ return [lr, lr!.R ?? r, remain]
34
34
  }
35
-
package/gs/sync/meta.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "unsafe"
4
4
  ],
5
5
  "asyncMethods": {
6
+ "Locker.Lock": true,
6
7
  "Mutex.Lock": true,
7
8
  "RWMutex.Lock": true,
8
9
  "RWMutex.RLock": true,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "goscript",
3
3
  "description": "Go to TypeScript transpiler",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "author": {
6
6
  "name": "Aperture Robotics LLC.",
7
7
  "email": "support@aperture.us",