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.
- package/compiler/lowered-program.go +1 -0
- package/compiler/lowering.go +715 -44
- package/compiler/override-registry_test.go +43 -0
- package/compiler/skeleton_test.go +464 -12
- package/compiler/typescript-emitter.go +28 -2
- package/dist/gs/builtin/channel.js +36 -9
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -3
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.d.ts +7 -5
- package/dist/gs/bytes/bytes.gs.js +10 -4
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/crypto/sha1/index.d.ts +5 -0
- package/dist/gs/crypto/sha1/index.js +106 -0
- package/dist/gs/crypto/sha1/index.js.map +1 -0
- package/dist/gs/fmt/fmt.d.ts +1 -1
- package/dist/gs/fmt/fmt.js +64 -3
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/io/io.d.ts +8 -5
- package/dist/gs/io/io.js +20 -2
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/net/http/httptest/index.js +7 -5
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +8 -0
- package/dist/gs/net/http/index.js +139 -10
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
- package/gs/builtin/channel.ts +47 -9
- package/gs/builtin/runtime-contract.test.ts +33 -0
- package/gs/builtin/type.ts +12 -3
- package/gs/bytes/bytes.gs.ts +19 -10
- package/gs/bytes/bytes.test.ts +17 -0
- package/gs/context/context.test.ts +5 -1
- package/gs/crypto/sha1/index.test.ts +28 -0
- package/gs/crypto/sha1/index.ts +130 -0
- package/gs/crypto/sha1/meta.json +8 -0
- package/gs/fmt/fmt.test.ts +20 -0
- package/gs/fmt/fmt.ts +75 -5
- package/gs/github.com/aperturerobotics/util/conc/index.test.ts +1 -1
- package/gs/io/io.test.ts +64 -0
- package/gs/io/io.ts +30 -12
- package/gs/net/http/httptest/index.test.ts +34 -2
- package/gs/net/http/httptest/index.ts +23 -8
- package/gs/net/http/index.test.ts +30 -0
- package/gs/net/http/index.ts +159 -10
- package/gs/os/zero_copy_posix.gs.ts +1 -2
- package/gs/sync/meta.json +1 -0
- package/package.json +1 -1
package/gs/net/http/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
224
|
-
public async RoundTrip(
|
|
225
|
-
|
|
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
|
|
378
|
+
export const DefaultTransport: RoundTripper = new defaultTransport()
|
|
230
379
|
|
|
231
380
|
export interface FileSystem {
|
|
232
381
|
Open(name: string): [File | null, $.GoError]
|
package/gs/sync/meta.json
CHANGED