tsgo-wasm 2025.5.19-11.7 → 2025.5.19-13.1

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/README.md CHANGED
@@ -10,8 +10,13 @@ Read more about the TypeScript 7 [here](https://devblogs.microsoft.com/typescrip
10
10
 
11
11
  ## Usage
12
12
 
13
+ Use this version if your operating system or architecture is not supported by the native build, or if you need to run it in a browser.
14
+
13
15
  ```bash
14
- npm install tsgo-wasm
16
+ npm install -g tsgo-wasm
17
+
18
+ tsgo-wasm -v
19
+ # Version 7.0.0-dev.20250519
15
20
  ```
16
21
 
17
22
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsgo-wasm",
3
- "version": "2025.5.19-11.7",
3
+ "version": "2025.5.19-13.1",
4
4
  "description": "TypeScript Go for WASM",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -18,9 +18,13 @@
18
18
  "directory": "packages/wasm"
19
19
  },
20
20
  "files": [
21
+ "tsgo-wasm",
21
22
  "tsgo.wasm"
22
23
  ],
23
24
  "main": "tsgo.wasm",
25
+ "bin": {
26
+ "tsgo-wasm": "tsgo-wasm"
27
+ },
24
28
  "publishConfig": {
25
29
  "access": "public",
26
30
  "tag": "latest"
package/tsgo-wasm ADDED
@@ -0,0 +1,522 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import path from 'node:path'
5
+ import { tmpdir } from 'node:os'
6
+ import { fileURLToPath } from 'node:url'
7
+
8
+ const require = createRequire(import.meta.url)
9
+ globalThis.fs = fs
10
+ globalThis.path = path
11
+ globalThis.require = require
12
+
13
+ const filename = fileURLToPath(import.meta.url)
14
+
15
+ const encoder = new TextEncoder('utf-8')
16
+ const decoder = new TextDecoder('utf-8')
17
+
18
+ class Go {
19
+ constructor() {
20
+ this._exitPromise = new Promise((resolve) => {
21
+ this._resolveExitPromise = resolve
22
+ })
23
+ this._pendingEvent = null
24
+ this._scheduledTimeouts = new Map()
25
+ this._nextCallbackTimeoutID = 1
26
+
27
+ const setInt64 = (addr, v) => {
28
+ this.mem.setUint32(addr + 0, v, true)
29
+ this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true)
30
+ }
31
+
32
+ const getInt64 = (addr) => {
33
+ const low = this.mem.getUint32(addr + 0, true)
34
+ const high = this.mem.getInt32(addr + 4, true)
35
+ return low + high * 4294967296
36
+ }
37
+
38
+ const loadValue = (addr) => {
39
+ const f = this.mem.getFloat64(addr, true)
40
+ if (f === 0) {
41
+ return undefined
42
+ }
43
+ if (!isNaN(f)) {
44
+ return f
45
+ }
46
+
47
+ const id = this.mem.getUint32(addr, true)
48
+ return this._values[id]
49
+ }
50
+
51
+ const storeValue = (addr, v) => {
52
+ const nanHead = 0x7ff80000
53
+
54
+ if (typeof v === 'number' && v !== 0) {
55
+ if (isNaN(v)) {
56
+ this.mem.setUint32(addr + 4, nanHead, true)
57
+ this.mem.setUint32(addr, 0, true)
58
+ return
59
+ }
60
+ this.mem.setFloat64(addr, v, true)
61
+ return
62
+ }
63
+
64
+ if (v === undefined) {
65
+ this.mem.setFloat64(addr, 0, true)
66
+ return
67
+ }
68
+
69
+ let id = this._ids.get(v)
70
+ if (id === undefined) {
71
+ id = this._idPool.pop()
72
+ if (id === undefined) {
73
+ id = this._values.length
74
+ }
75
+ this._values[id] = v
76
+ this._goRefCounts[id] = 0
77
+ this._ids.set(v, id)
78
+ }
79
+ this._goRefCounts[id]++
80
+ let typeFlag = 0
81
+ switch (typeof v) {
82
+ case 'object':
83
+ if (v !== null) {
84
+ typeFlag = 1
85
+ }
86
+ break
87
+ case 'string':
88
+ typeFlag = 2
89
+ break
90
+ case 'symbol':
91
+ typeFlag = 3
92
+ break
93
+ case 'function':
94
+ typeFlag = 4
95
+ break
96
+ }
97
+ this.mem.setUint32(addr + 4, nanHead | typeFlag, true)
98
+ this.mem.setUint32(addr, id, true)
99
+ }
100
+
101
+ const loadSlice = (addr) => {
102
+ const array = getInt64(addr + 0)
103
+ const len = getInt64(addr + 8)
104
+ return new Uint8Array(this._inst.exports.mem.buffer, array, len)
105
+ }
106
+
107
+ const loadSliceOfValues = (addr) => {
108
+ const array = getInt64(addr + 0)
109
+ const len = getInt64(addr + 8)
110
+ const a = new Array(len)
111
+ for (let i = 0; i < len; i++) {
112
+ a[i] = loadValue(array + i * 8)
113
+ }
114
+ return a
115
+ }
116
+
117
+ const loadString = (addr) => {
118
+ const saddr = getInt64(addr + 0)
119
+ const len = getInt64(addr + 8)
120
+ return decoder.decode(
121
+ new DataView(this._inst.exports.mem.buffer, saddr, len),
122
+ )
123
+ }
124
+
125
+ const testCallExport = (a, b) => {
126
+ this._inst.exports.testExport0()
127
+ return this._inst.exports.testExport(a, b)
128
+ }
129
+
130
+ const timeOrigin = Date.now() - performance.now()
131
+ this.importObject = {
132
+ _gotest: {
133
+ add: (a, b) => a + b,
134
+ callExport: testCallExport,
135
+ },
136
+ gojs: {
137
+ // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
138
+ // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
139
+ // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
140
+ // This changes the SP, thus we have to update the SP used by the imported function.
141
+
142
+ // func wasmExit(code int32)
143
+ 'runtime.wasmExit': (sp) => {
144
+ sp >>>= 0
145
+ const code = this.mem.getInt32(sp + 8, true)
146
+ this.exited = true
147
+ delete this._inst
148
+ delete this._values
149
+ delete this._goRefCounts
150
+ delete this._ids
151
+ delete this._idPool
152
+ this.exit(code)
153
+ },
154
+
155
+ // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
156
+ 'runtime.wasmWrite': (sp) => {
157
+ sp >>>= 0
158
+ const fd = getInt64(sp + 8)
159
+ const p = getInt64(sp + 16)
160
+ const n = this.mem.getInt32(sp + 24, true)
161
+ fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n))
162
+ },
163
+
164
+ // func resetMemoryDataView()
165
+ 'runtime.resetMemoryDataView': (sp) => {
166
+ sp >>>= 0
167
+ this.mem = new DataView(this._inst.exports.mem.buffer)
168
+ },
169
+
170
+ // func nanotime1() int64
171
+ 'runtime.nanotime1': (sp) => {
172
+ sp >>>= 0
173
+ setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000)
174
+ },
175
+
176
+ // func walltime() (sec int64, nsec int32)
177
+ 'runtime.walltime': (sp) => {
178
+ sp >>>= 0
179
+ const msec = new Date().getTime()
180
+ setInt64(sp + 8, msec / 1000)
181
+ this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true)
182
+ },
183
+
184
+ // func scheduleTimeoutEvent(delay int64) int32
185
+ 'runtime.scheduleTimeoutEvent': (sp) => {
186
+ sp >>>= 0
187
+ const id = this._nextCallbackTimeoutID
188
+ this._nextCallbackTimeoutID++
189
+ this._scheduledTimeouts.set(
190
+ id,
191
+ setTimeout(
192
+ () => {
193
+ this._resume()
194
+ while (this._scheduledTimeouts.has(id)) {
195
+ // for some reason Go failed to register the timeout event, log and try again
196
+ // (temporary workaround for https://github.com/golang/go/issues/28975)
197
+ console.warn('scheduleTimeoutEvent: missed timeout event')
198
+ this._resume()
199
+ }
200
+ },
201
+ getInt64(sp + 8),
202
+ ),
203
+ )
204
+ this.mem.setInt32(sp + 16, id, true)
205
+ },
206
+
207
+ // func clearTimeoutEvent(id int32)
208
+ 'runtime.clearTimeoutEvent': (sp) => {
209
+ sp >>>= 0
210
+ const id = this.mem.getInt32(sp + 8, true)
211
+ clearTimeout(this._scheduledTimeouts.get(id))
212
+ this._scheduledTimeouts.delete(id)
213
+ },
214
+
215
+ // func getRandomData(r []byte)
216
+ 'runtime.getRandomData': (sp) => {
217
+ sp >>>= 0
218
+ crypto.getRandomValues(loadSlice(sp + 8))
219
+ },
220
+
221
+ // func finalizeRef(v ref)
222
+ 'syscall/js.finalizeRef': (sp) => {
223
+ sp >>>= 0
224
+ const id = this.mem.getUint32(sp + 8, true)
225
+ this._goRefCounts[id]--
226
+ if (this._goRefCounts[id] === 0) {
227
+ const v = this._values[id]
228
+ this._values[id] = null
229
+ this._ids.delete(v)
230
+ this._idPool.push(id)
231
+ }
232
+ },
233
+
234
+ // func stringVal(value string) ref
235
+ 'syscall/js.stringVal': (sp) => {
236
+ sp >>>= 0
237
+ storeValue(sp + 24, loadString(sp + 8))
238
+ },
239
+
240
+ // func valueGet(v ref, p string) ref
241
+ 'syscall/js.valueGet': (sp) => {
242
+ sp >>>= 0
243
+ const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16))
244
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
245
+ storeValue(sp + 32, result)
246
+ },
247
+
248
+ // func valueSet(v ref, p string, x ref)
249
+ 'syscall/js.valueSet': (sp) => {
250
+ sp >>>= 0
251
+ Reflect.set(
252
+ loadValue(sp + 8),
253
+ loadString(sp + 16),
254
+ loadValue(sp + 32),
255
+ )
256
+ },
257
+
258
+ // func valueDelete(v ref, p string)
259
+ 'syscall/js.valueDelete': (sp) => {
260
+ sp >>>= 0
261
+ Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16))
262
+ },
263
+
264
+ // func valueIndex(v ref, i int) ref
265
+ 'syscall/js.valueIndex': (sp) => {
266
+ sp >>>= 0
267
+ storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)))
268
+ },
269
+
270
+ // valueSetIndex(v ref, i int, x ref)
271
+ 'syscall/js.valueSetIndex': (sp) => {
272
+ sp >>>= 0
273
+ Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24))
274
+ },
275
+
276
+ // func valueCall(v ref, m string, args []ref) (ref, bool)
277
+ 'syscall/js.valueCall': (sp) => {
278
+ sp >>>= 0
279
+ try {
280
+ const v = loadValue(sp + 8)
281
+ const m = Reflect.get(v, loadString(sp + 16))
282
+ const args = loadSliceOfValues(sp + 32)
283
+ const result = Reflect.apply(m, v, args)
284
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
285
+ storeValue(sp + 56, result)
286
+ this.mem.setUint8(sp + 64, 1)
287
+ } catch (err) {
288
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
289
+ storeValue(sp + 56, err)
290
+ this.mem.setUint8(sp + 64, 0)
291
+ }
292
+ },
293
+
294
+ // func valueInvoke(v ref, args []ref) (ref, bool)
295
+ 'syscall/js.valueInvoke': (sp) => {
296
+ sp >>>= 0
297
+ try {
298
+ const v = loadValue(sp + 8)
299
+ const args = loadSliceOfValues(sp + 16)
300
+ const result = Reflect.apply(v, undefined, args)
301
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
302
+ storeValue(sp + 40, result)
303
+ this.mem.setUint8(sp + 48, 1)
304
+ } catch (err) {
305
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
306
+ storeValue(sp + 40, err)
307
+ this.mem.setUint8(sp + 48, 0)
308
+ }
309
+ },
310
+
311
+ // func valueNew(v ref, args []ref) (ref, bool)
312
+ 'syscall/js.valueNew': (sp) => {
313
+ sp >>>= 0
314
+ try {
315
+ const v = loadValue(sp + 8)
316
+ const args = loadSliceOfValues(sp + 16)
317
+ const result = Reflect.construct(v, args)
318
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
319
+ storeValue(sp + 40, result)
320
+ this.mem.setUint8(sp + 48, 1)
321
+ } catch (err) {
322
+ sp = this._inst.exports.getsp() >>> 0 // see comment above
323
+ storeValue(sp + 40, err)
324
+ this.mem.setUint8(sp + 48, 0)
325
+ }
326
+ },
327
+
328
+ // func valueLength(v ref) int
329
+ 'syscall/js.valueLength': (sp) => {
330
+ sp >>>= 0
331
+ setInt64(sp + 16, parseInt(loadValue(sp + 8).length))
332
+ },
333
+
334
+ // valuePrepareString(v ref) (ref, int)
335
+ 'syscall/js.valuePrepareString': (sp) => {
336
+ sp >>>= 0
337
+ const str = encoder.encode(String(loadValue(sp + 8)))
338
+ storeValue(sp + 16, str)
339
+ setInt64(sp + 24, str.length)
340
+ },
341
+
342
+ // valueLoadString(v ref, b []byte)
343
+ 'syscall/js.valueLoadString': (sp) => {
344
+ sp >>>= 0
345
+ const str = loadValue(sp + 8)
346
+ loadSlice(sp + 16).set(str)
347
+ },
348
+
349
+ // func valueInstanceOf(v ref, t ref) bool
350
+ 'syscall/js.valueInstanceOf': (sp) => {
351
+ sp >>>= 0
352
+ this.mem.setUint8(
353
+ sp + 24,
354
+ loadValue(sp + 8) instanceof loadValue(sp + 16) ? 1 : 0,
355
+ )
356
+ },
357
+
358
+ // func copyBytesToGo(dst []byte, src ref) (int, bool)
359
+ 'syscall/js.copyBytesToGo': (sp) => {
360
+ sp >>>= 0
361
+ const dst = loadSlice(sp + 8)
362
+ const src = loadValue(sp + 32)
363
+ if (
364
+ !(src instanceof Uint8Array || src instanceof Uint8ClampedArray)
365
+ ) {
366
+ this.mem.setUint8(sp + 48, 0)
367
+ return
368
+ }
369
+ const toCopy = src.subarray(0, dst.length)
370
+ dst.set(toCopy)
371
+ setInt64(sp + 40, toCopy.length)
372
+ this.mem.setUint8(sp + 48, 1)
373
+ },
374
+
375
+ // func copyBytesToJS(dst ref, src []byte) (int, bool)
376
+ 'syscall/js.copyBytesToJS': (sp) => {
377
+ sp >>>= 0
378
+ const dst = loadValue(sp + 8)
379
+ const src = loadSlice(sp + 16)
380
+ if (
381
+ !(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)
382
+ ) {
383
+ this.mem.setUint8(sp + 48, 0)
384
+ return
385
+ }
386
+ const toCopy = src.subarray(0, dst.length)
387
+ dst.set(toCopy)
388
+ setInt64(sp + 40, toCopy.length)
389
+ this.mem.setUint8(sp + 48, 1)
390
+ },
391
+
392
+ debug: (value) => {
393
+ console.log(value)
394
+ },
395
+ },
396
+ }
397
+ }
398
+
399
+ async run(instance) {
400
+ if (!(instance instanceof WebAssembly.Instance)) {
401
+ throw new Error('Go.run: WebAssembly.Instance expected')
402
+ }
403
+ this._inst = instance
404
+ this.mem = new DataView(this._inst.exports.mem.buffer)
405
+ this._values = [
406
+ // JS values that Go currently has references to, indexed by reference id
407
+ NaN,
408
+ 0,
409
+ null,
410
+ true,
411
+ false,
412
+ globalThis,
413
+ this,
414
+ ]
415
+ this._goRefCounts = new Array(this._values.length).fill(Infinity) // number of references that Go has to a JS value, indexed by reference id
416
+ this._ids = new Map([
417
+ // mapping from JS values to reference ids
418
+ [0, 1],
419
+ [null, 2],
420
+ [true, 3],
421
+ [false, 4],
422
+ [globalThis, 5],
423
+ [this, 6],
424
+ ])
425
+ this._idPool = [] // unused ids that have been garbage collected
426
+ this.exited = false // whether the Go program has exited
427
+
428
+ // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
429
+ let offset = 4096
430
+
431
+ const strPtr = (str) => {
432
+ const ptr = offset
433
+ const bytes = encoder.encode(str + '\0')
434
+ new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes)
435
+ offset += bytes.length
436
+ if (offset % 8 !== 0) {
437
+ offset += 8 - (offset % 8)
438
+ }
439
+ return ptr
440
+ }
441
+
442
+ const argc = this.argv.length
443
+
444
+ const argvPtrs = []
445
+ this.argv.forEach((arg) => {
446
+ argvPtrs.push(strPtr(arg))
447
+ })
448
+ argvPtrs.push(0)
449
+
450
+ const keys = Object.keys(this.env).sort()
451
+ keys.forEach((key) => {
452
+ argvPtrs.push(strPtr(`${key}=${this.env[key]}`))
453
+ })
454
+ argvPtrs.push(0)
455
+
456
+ const argv = offset
457
+ argvPtrs.forEach((ptr) => {
458
+ this.mem.setUint32(offset, ptr, true)
459
+ this.mem.setUint32(offset + 4, 0, true)
460
+ offset += 8
461
+ })
462
+
463
+ // The linker guarantees global data starts from at least wasmMinDataAddr.
464
+ // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
465
+ const wasmMinDataAddr = 4096 + 8192
466
+ if (offset >= wasmMinDataAddr) {
467
+ throw new Error(
468
+ 'total length of command line and environment variables exceeds limit',
469
+ )
470
+ }
471
+
472
+ this._inst.exports.run(argc, argv)
473
+ if (this.exited) {
474
+ this._resolveExitPromise()
475
+ }
476
+ await this._exitPromise
477
+ }
478
+
479
+ _resume() {
480
+ if (this.exited) {
481
+ throw new Error('Go program has already exited')
482
+ }
483
+ this._inst.exports.resume()
484
+ if (this.exited) {
485
+ this._resolveExitPromise()
486
+ }
487
+ }
488
+
489
+ _makeFuncWrapper(id) {
490
+ const go = this
491
+ return function () {
492
+ const event = { id: id, this: this, args: arguments }
493
+ go._pendingEvent = event
494
+ go._resume()
495
+ return event.result
496
+ }
497
+ }
498
+ }
499
+
500
+ const go = new Go()
501
+ go.argv = process.argv.slice(2)
502
+ go.env = Object.assign({ TMPDIR: tmpdir() }, process.env)
503
+ go.exit = process.exit
504
+ await WebAssembly.instantiate(
505
+ fs.readFileSync(path.resolve(filename, '../tsgo.wasm')),
506
+ go.importObject,
507
+ )
508
+ .then((result) => {
509
+ process.on('exit', (code) => {
510
+ // Node.js exits if no event handler is pending
511
+ if (code === 0 && !go.exited) {
512
+ // deadlock, make Go print error and stack traces
513
+ go._pendingEvent = { id: 0 }
514
+ go._resume()
515
+ }
516
+ })
517
+ return go.run(result.instance)
518
+ })
519
+ .catch((error) => {
520
+ console.error(error)
521
+ process.exit(1)
522
+ })
package/tsgo.wasm CHANGED
Binary file