goscript 0.1.3 → 0.2.0

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 (330) hide show
  1. package/README.md +5 -2
  2. package/cmd/go_js_wasm_exec/main.go +201 -0
  3. package/cmd/go_js_wasm_exec/main_test.go +83 -0
  4. package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +35 -8
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/cmd/goscript/cmd_compile_test.go +105 -6
  8. package/compiler/build-flags.go +9 -10
  9. package/compiler/compile-request.go +12 -9
  10. package/compiler/compliance_test.go +0 -1
  11. package/compiler/config.go +2 -0
  12. package/compiler/gotest/request.go +28 -0
  13. package/compiler/gotest/runner.go +353 -27
  14. package/compiler/gotest/runner_test.go +400 -1
  15. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  16. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  17. package/compiler/lowered-program.go +24 -17
  18. package/compiler/lowering.go +988 -263
  19. package/compiler/lowering_bench_test.go +364 -0
  20. package/compiler/override-facts.go +15 -0
  21. package/compiler/override-parity-verifier.go +450 -0
  22. package/compiler/override-parity.go +122 -0
  23. package/compiler/override-registry_test.go +559 -0
  24. package/compiler/package-graph.go +61 -4
  25. package/compiler/package-graph_test.go +30 -0
  26. package/compiler/protobuf-ts-binding.go +514 -0
  27. package/compiler/protobuf-ts-binding_test.go +172 -0
  28. package/compiler/semantic-model-types.go +17 -4
  29. package/compiler/semantic-model.go +709 -72
  30. package/compiler/semantic-model_test.go +219 -0
  31. package/compiler/service.go +20 -1
  32. package/compiler/skeleton_test.go +1008 -20
  33. package/compiler/typescript-emitter.go +147 -15
  34. package/dist/gs/builtin/builtin.d.ts +2 -2
  35. package/dist/gs/builtin/builtin.js +20 -0
  36. package/dist/gs/builtin/builtin.js.map +1 -1
  37. package/dist/gs/builtin/slice.d.ts +2 -1
  38. package/dist/gs/builtin/slice.js +34 -4
  39. package/dist/gs/builtin/slice.js.map +1 -1
  40. package/dist/gs/builtin/type.d.ts +14 -6
  41. package/dist/gs/builtin/type.js +224 -64
  42. package/dist/gs/builtin/type.js.map +1 -1
  43. package/dist/gs/builtin/varRef.d.ts +11 -0
  44. package/dist/gs/builtin/varRef.js +57 -2
  45. package/dist/gs/builtin/varRef.js.map +1 -1
  46. package/dist/gs/bytes/buffer.gs.js +1 -1
  47. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  48. package/dist/gs/bytes/reader.gs.js +1 -1
  49. package/dist/gs/bytes/reader.gs.js.map +1 -1
  50. package/dist/gs/compress/zlib/index.d.ts +13 -6
  51. package/dist/gs/compress/zlib/index.js +131 -35
  52. package/dist/gs/compress/zlib/index.js.map +1 -1
  53. package/dist/gs/crypto/sha1/index.js +2 -5
  54. package/dist/gs/crypto/sha1/index.js.map +1 -1
  55. package/dist/gs/crypto/sha256/index.js +2 -5
  56. package/dist/gs/crypto/sha256/index.js.map +1 -1
  57. package/dist/gs/crypto/sha512/index.js +2 -5
  58. package/dist/gs/crypto/sha512/index.js.map +1 -1
  59. package/dist/gs/embed/index.d.ts +6 -0
  60. package/dist/gs/embed/index.js +210 -5
  61. package/dist/gs/embed/index.js.map +1 -1
  62. package/dist/gs/encoding/json/index.d.ts +114 -0
  63. package/dist/gs/encoding/json/index.js +544 -36
  64. package/dist/gs/encoding/json/index.js.map +1 -1
  65. package/dist/gs/fmt/fmt.d.ts +3 -3
  66. package/dist/gs/fmt/fmt.js +29 -16
  67. package/dist/gs/fmt/fmt.js.map +1 -1
  68. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
  69. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
  70. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  71. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
  72. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
  73. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
  74. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  75. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  76. package/dist/gs/go/scanner/index.d.ts +2 -0
  77. package/dist/gs/go/scanner/index.js +29 -5
  78. package/dist/gs/go/scanner/index.js.map +1 -1
  79. package/dist/gs/go/token/index.js +22 -6
  80. package/dist/gs/go/token/index.js.map +1 -1
  81. package/dist/gs/hash/index.d.ts +6 -0
  82. package/dist/gs/hash/index.js +20 -0
  83. package/dist/gs/hash/index.js.map +1 -1
  84. package/dist/gs/internal/goarch/index.d.ts +43 -3
  85. package/dist/gs/internal/goarch/index.js +42 -10
  86. package/dist/gs/internal/goarch/index.js.map +1 -1
  87. package/dist/gs/io/fs/fs.js +26 -14
  88. package/dist/gs/io/fs/fs.js.map +1 -1
  89. package/dist/gs/io/fs/readdir.js +8 -4
  90. package/dist/gs/io/fs/readdir.js.map +1 -1
  91. package/dist/gs/io/fs/sub.js +8 -1
  92. package/dist/gs/io/fs/sub.js.map +1 -1
  93. package/dist/gs/io/io.d.ts +12 -6
  94. package/dist/gs/io/io.js +87 -42
  95. package/dist/gs/io/io.js.map +1 -1
  96. package/dist/gs/math/bits/index.d.ts +31 -5
  97. package/dist/gs/math/bits/index.js +29 -28
  98. package/dist/gs/math/bits/index.js.map +1 -1
  99. package/dist/gs/mime/index.d.ts +16 -0
  100. package/dist/gs/mime/index.js +315 -6
  101. package/dist/gs/mime/index.js.map +1 -1
  102. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  103. package/dist/gs/net/http/httptest/index.js +85 -6
  104. package/dist/gs/net/http/httptest/index.js.map +1 -1
  105. package/dist/gs/net/http/index.d.ts +303 -6
  106. package/dist/gs/net/http/index.js +1615 -58
  107. package/dist/gs/net/http/index.js.map +1 -1
  108. package/dist/gs/os/dir_unix.gs.js +1 -1
  109. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  110. package/dist/gs/os/error.gs.js +1 -1
  111. package/dist/gs/os/error.gs.js.map +1 -1
  112. package/dist/gs/os/exec.gs.d.ts +1 -0
  113. package/dist/gs/os/exec.gs.js +4 -8
  114. package/dist/gs/os/exec.gs.js.map +1 -1
  115. package/dist/gs/os/exec_posix.gs.js +1 -1
  116. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  117. package/dist/gs/os/index.d.ts +1 -1
  118. package/dist/gs/os/index.js +1 -1
  119. package/dist/gs/os/index.js.map +1 -1
  120. package/dist/gs/os/proc.gs.d.ts +4 -0
  121. package/dist/gs/os/proc.gs.js +12 -6
  122. package/dist/gs/os/proc.gs.js.map +1 -1
  123. package/dist/gs/os/root_js.gs.js +1 -1
  124. package/dist/gs/os/root_js.gs.js.map +1 -1
  125. package/dist/gs/os/types.gs.js +1 -1
  126. package/dist/gs/os/types.gs.js.map +1 -1
  127. package/dist/gs/os/types_js.gs.d.ts +6 -2
  128. package/dist/gs/os/types_js.gs.js +170 -9
  129. package/dist/gs/os/types_js.gs.js.map +1 -1
  130. package/dist/gs/os/types_unix.gs.js +1 -1
  131. package/dist/gs/os/types_unix.gs.js.map +1 -1
  132. package/dist/gs/path/path.js +11 -7
  133. package/dist/gs/path/path.js.map +1 -1
  134. package/dist/gs/reflect/index.d.ts +5 -4
  135. package/dist/gs/reflect/index.js +4 -3
  136. package/dist/gs/reflect/index.js.map +1 -1
  137. package/dist/gs/reflect/map.js +15 -0
  138. package/dist/gs/reflect/map.js.map +1 -1
  139. package/dist/gs/reflect/type.d.ts +26 -6
  140. package/dist/gs/reflect/type.js +1498 -279
  141. package/dist/gs/reflect/type.js.map +1 -1
  142. package/dist/gs/reflect/types.d.ts +14 -6
  143. package/dist/gs/reflect/types.js +35 -1
  144. package/dist/gs/reflect/types.js.map +1 -1
  145. package/dist/gs/reflect/value.d.ts +1 -0
  146. package/dist/gs/reflect/value.js +83 -41
  147. package/dist/gs/reflect/value.js.map +1 -1
  148. package/dist/gs/reflect/visiblefields.js +4 -140
  149. package/dist/gs/reflect/visiblefields.js.map +1 -1
  150. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  151. package/dist/gs/runtime/pprof/index.js +50 -30
  152. package/dist/gs/runtime/pprof/index.js.map +1 -1
  153. package/dist/gs/runtime/runtime.js +5 -4
  154. package/dist/gs/runtime/runtime.js.map +1 -1
  155. package/dist/gs/runtime/trace/index.js +5 -19
  156. package/dist/gs/runtime/trace/index.js.map +1 -1
  157. package/dist/gs/strconv/atoi.gs.js +1 -1
  158. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  159. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  160. package/dist/gs/strconv/complex.gs.js +148 -0
  161. package/dist/gs/strconv/complex.gs.js.map +1 -0
  162. package/dist/gs/strconv/index.d.ts +1 -0
  163. package/dist/gs/strconv/index.js +1 -0
  164. package/dist/gs/strconv/index.js.map +1 -1
  165. package/dist/gs/strings/builder.js +1 -1
  166. package/dist/gs/strings/reader.d.ts +1 -1
  167. package/dist/gs/strings/reader.js +11 -7
  168. package/dist/gs/strings/reader.js.map +1 -1
  169. package/dist/gs/strings/replace.js +15 -7
  170. package/dist/gs/strings/replace.js.map +1 -1
  171. package/dist/gs/strings/strings.d.ts +5 -0
  172. package/dist/gs/strings/strings.js +57 -5
  173. package/dist/gs/strings/strings.js.map +1 -1
  174. package/dist/gs/sync/atomic/type.gs.js +9 -9
  175. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  176. package/dist/gs/sync/atomic/value.gs.js +2 -2
  177. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  178. package/dist/gs/sync/sync.d.ts +2 -1
  179. package/dist/gs/sync/sync.js +37 -16
  180. package/dist/gs/sync/sync.js.map +1 -1
  181. package/dist/gs/syscall/env.js +22 -14
  182. package/dist/gs/syscall/env.js.map +1 -1
  183. package/dist/gs/syscall/js/index.js +9 -0
  184. package/dist/gs/syscall/js/index.js.map +1 -1
  185. package/dist/gs/testing/testing.js +59 -15
  186. package/dist/gs/testing/testing.js.map +1 -1
  187. package/dist/gs/time/time.d.ts +24 -1
  188. package/dist/gs/time/time.js +43 -3
  189. package/dist/gs/time/time.js.map +1 -1
  190. package/dist/gs/unique/index.js +7 -1
  191. package/dist/gs/unique/index.js.map +1 -1
  192. package/go.mod +3 -3
  193. package/go.sum +16 -0
  194. package/gs/builtin/builtin.ts +25 -2
  195. package/gs/builtin/runtime-contract.test.ts +260 -18
  196. package/gs/builtin/slice.ts +51 -4
  197. package/gs/builtin/type.ts +310 -63
  198. package/gs/builtin/varRef.ts +85 -2
  199. package/gs/bytes/buffer.gs.ts +1 -1
  200. package/gs/bytes/reader.gs.ts +1 -1
  201. package/gs/compress/zlib/index.test.ts +159 -1
  202. package/gs/compress/zlib/index.ts +164 -37
  203. package/gs/compress/zlib/meta.json +4 -1
  204. package/gs/compress/zlib/parity.json +51 -0
  205. package/gs/crypto/sha1/index.test.ts +19 -2
  206. package/gs/crypto/sha1/index.ts +3 -6
  207. package/gs/crypto/sha256/index.test.ts +14 -2
  208. package/gs/crypto/sha256/index.ts +3 -6
  209. package/gs/crypto/sha512/index.test.ts +17 -2
  210. package/gs/crypto/sha512/index.ts +3 -6
  211. package/gs/embed/index.test.ts +87 -0
  212. package/gs/embed/index.ts +229 -5
  213. package/gs/encoding/json/index.test.ts +360 -6
  214. package/gs/encoding/json/index.ts +679 -38
  215. package/gs/encoding/json/parity.json +81 -0
  216. package/gs/fmt/fmt.test.ts +41 -3
  217. package/gs/fmt/fmt.ts +40 -17
  218. package/gs/fmt/meta.json +6 -1
  219. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
  220. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
  221. package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
  222. package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
  223. package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
  224. package/gs/github.com/pkg/errors/errors.ts +54 -30
  225. package/gs/go/scanner/index.test.ts +39 -56
  226. package/gs/go/scanner/index.ts +33 -5
  227. package/gs/go/scanner/parity.json +27 -0
  228. package/gs/go/token/index.ts +22 -6
  229. package/gs/hash/index.test.ts +20 -33
  230. package/gs/hash/index.ts +28 -0
  231. package/gs/hash/parity.json +21 -0
  232. package/gs/internal/goarch/index.test.ts +32 -0
  233. package/gs/internal/goarch/index.ts +45 -13
  234. package/gs/internal/goarch/parity.json +144 -0
  235. package/gs/io/fs/fs.ts +26 -14
  236. package/gs/io/fs/readdir.test.ts +38 -0
  237. package/gs/io/fs/readdir.ts +8 -4
  238. package/gs/io/fs/sub.ts +8 -1
  239. package/gs/io/io.test.ts +77 -6
  240. package/gs/io/io.ts +115 -52
  241. package/gs/io/meta.json +7 -1
  242. package/gs/io/parity.json +162 -0
  243. package/gs/math/bits/index.test.ts +14 -1
  244. package/gs/math/bits/index.ts +75 -32
  245. package/gs/math/bits/parity.json +156 -0
  246. package/gs/mime/index.test.ts +90 -0
  247. package/gs/mime/index.ts +369 -6
  248. package/gs/mime/parity.json +36 -0
  249. package/gs/net/http/httptest/index.test.ts +98 -2
  250. package/gs/net/http/httptest/index.ts +101 -6
  251. package/gs/net/http/httptest/parity.json +15 -0
  252. package/gs/net/http/index.test.ts +797 -12
  253. package/gs/net/http/index.ts +1874 -136
  254. package/gs/net/http/meta.json +16 -1
  255. package/gs/net/http/parity.json +193 -0
  256. package/gs/os/dir_unix.gs.ts +1 -1
  257. package/gs/os/error.gs.ts +1 -1
  258. package/gs/os/exec.gs.ts +4 -8
  259. package/gs/os/exec_posix.gs.ts +1 -1
  260. package/gs/os/file_unix_js.test.ts +52 -0
  261. package/gs/os/index.test.ts +9 -0
  262. package/gs/os/index.ts +1 -0
  263. package/gs/os/meta.json +4 -0
  264. package/gs/os/parity.json +9 -0
  265. package/gs/os/proc.gs.ts +18 -5
  266. package/gs/os/proc.test.ts +26 -0
  267. package/gs/os/readdir.test.ts +56 -0
  268. package/gs/os/root_js.gs.ts +1 -1
  269. package/gs/os/types.gs.ts +1 -1
  270. package/gs/os/types_js.gs.ts +170 -9
  271. package/gs/os/types_unix.gs.ts +1 -1
  272. package/gs/path/path.ts +11 -7
  273. package/gs/reflect/deepequal.test.ts +10 -1
  274. package/gs/reflect/field.test.ts +37 -15
  275. package/gs/reflect/function-types.test.ts +518 -22
  276. package/gs/reflect/index.ts +8 -6
  277. package/gs/reflect/map.ts +20 -0
  278. package/gs/reflect/meta.json +6 -4
  279. package/gs/reflect/parity.json +234 -0
  280. package/gs/reflect/sliceat.test.ts +156 -0
  281. package/gs/reflect/structof.test.ts +401 -0
  282. package/gs/reflect/type.ts +1980 -365
  283. package/gs/reflect/typefor.test.ts +540 -10
  284. package/gs/reflect/types.ts +43 -18
  285. package/gs/reflect/value.ts +105 -45
  286. package/gs/reflect/visiblefields.ts +5 -168
  287. package/gs/runtime/parity.json +24 -0
  288. package/gs/runtime/pprof/index.test.ts +29 -7
  289. package/gs/runtime/pprof/index.ts +56 -30
  290. package/gs/runtime/pprof/parity.json +27 -0
  291. package/gs/runtime/runtime.test.ts +3 -1
  292. package/gs/runtime/runtime.ts +4 -3
  293. package/gs/runtime/trace/index.test.ts +5 -3
  294. package/gs/runtime/trace/index.ts +8 -20
  295. package/gs/runtime/trace/parity.json +36 -0
  296. package/gs/strconv/atoi.gs.ts +1 -1
  297. package/gs/strconv/complex.gs.ts +174 -0
  298. package/gs/strconv/complex.test.ts +65 -0
  299. package/gs/strconv/index.ts +1 -0
  300. package/gs/strconv/parity.json +120 -0
  301. package/gs/strings/builder.ts +1 -1
  302. package/gs/strings/meta.json +5 -2
  303. package/gs/strings/parity.json +186 -0
  304. package/gs/strings/reader.test.ts +2 -2
  305. package/gs/strings/reader.ts +11 -7
  306. package/gs/strings/replace.ts +15 -7
  307. package/gs/strings/strings.test.ts +22 -2
  308. package/gs/strings/strings.ts +64 -6
  309. package/gs/sync/atomic/type.gs.ts +9 -9
  310. package/gs/sync/atomic/value.gs.ts +2 -2
  311. package/gs/sync/meta.json +1 -0
  312. package/gs/sync/sync.test.ts +41 -1
  313. package/gs/sync/sync.ts +41 -16
  314. package/gs/syscall/env.ts +29 -14
  315. package/gs/syscall/js/index.test.ts +18 -0
  316. package/gs/syscall/js/index.ts +12 -0
  317. package/gs/testing/testing.test.ts +99 -3
  318. package/gs/testing/testing.ts +95 -24
  319. package/gs/time/parity.json +225 -0
  320. package/gs/time/time.test.ts +20 -2
  321. package/gs/time/time.ts +49 -7
  322. package/gs/unique/index.ts +7 -1
  323. package/package.json +4 -2
  324. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  325. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -814
  326. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  327. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -31
  328. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1233
  329. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
  330. /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
@@ -0,0 +1,110 @@
1
+ import * as $ from '@goscript/builtin/index.js'
2
+ import * as os from '@goscript/os/index.js'
3
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+ import { afterEach, describe, expect, it } from 'vitest'
7
+
8
+ import { New } from './index.js'
9
+
10
+ const roots: string[] = []
11
+
12
+ afterEach(() => {
13
+ for (const root of roots.splice(0)) {
14
+ rmSync(root, { force: true, recursive: true })
15
+ }
16
+ })
17
+
18
+ function tempRoot(): string {
19
+ const root = mkdtempSync(join(tmpdir(), 'goscript-billy-osfs-'))
20
+ roots.push(root)
21
+ return root
22
+ }
23
+
24
+ describe('go-billy osfs override', () => {
25
+ it('writes through to the host directory used by os.DirFS', () => {
26
+ const root = tempRoot()
27
+ const fsys = New(root)
28
+
29
+ const [file, openErr] = fsys.OpenFile(
30
+ 'README.md',
31
+ os.O_WRONLY | os.O_CREATE | os.O_TRUNC,
32
+ 0o666,
33
+ )
34
+ expect(openErr).toBeNull()
35
+ const [n, writeErr] = file!.Write($.stringToBytes('hello\n'))
36
+ expect(writeErr).toBeNull()
37
+ expect(n).toBe(6)
38
+ expect(file!.Close()).toBeNull()
39
+
40
+ expect(readFileSync(join(root, 'README.md'), 'utf8')).toBe('hello\n')
41
+ })
42
+
43
+ it('keeps chrooted writes under the child host directory', () => {
44
+ const root = tempRoot()
45
+ const fsys = New(root)
46
+ const [child, chrootErr] = fsys.Chroot('sub')
47
+ expect(chrootErr).toBeNull()
48
+
49
+ const [file, openErr] = child!.Create('nested/file.txt')
50
+ expect(openErr).toBeNull()
51
+ const [, writeErr] = file!.Write($.stringToBytes('child'))
52
+ expect(writeErr).toBeNull()
53
+ expect(file!.Close()).toBeNull()
54
+
55
+ expect(readFileSync(join(root, 'sub', 'nested', 'file.txt'), 'utf8')).toBe(
56
+ 'child',
57
+ )
58
+ })
59
+
60
+ it('rejects parent traversal outside the base directory', () => {
61
+ const root = tempRoot()
62
+ const fsys = New(root)
63
+
64
+ const [file, err] = fsys.Open('../escape.txt')
65
+
66
+ expect(file).toBeNull()
67
+ expect(err).not.toBeNull()
68
+ })
69
+
70
+ it('accepts absolute paths inside the base directory', () => {
71
+ const root = tempRoot()
72
+ const fsys = New(root)
73
+ const absolute = join(root, 'inside.txt')
74
+
75
+ const [file, openErr] = fsys.Create(absolute)
76
+ expect(openErr).toBeNull()
77
+ const [, writeErr] = file!.Write($.stringToBytes('inside'))
78
+ expect(writeErr).toBeNull()
79
+ expect(file!.Close()).toBeNull()
80
+
81
+ expect(readFileSync(absolute, 'utf8')).toBe('inside')
82
+ })
83
+
84
+ it('rejects absolute paths outside the base directory', () => {
85
+ const root = tempRoot()
86
+ const outside = tempRoot()
87
+ const fsys = New(root)
88
+
89
+ const [file, err] = fsys.Create(join(outside, 'outside.txt'))
90
+
91
+ expect(file).toBeNull()
92
+ expect(err).not.toBeNull()
93
+ })
94
+
95
+ it('reads host directory entries', () => {
96
+ const root = tempRoot()
97
+ writeFileSync(join(root, 'one.txt'), 'one')
98
+ writeFileSync(join(root, 'two.txt'), 'two')
99
+ const fsys = New(root)
100
+
101
+ const [entries, err] = fsys.ReadDir('.')
102
+
103
+ expect(err).toBeNull()
104
+ expect(
105
+ Array.from(entries ?? [])
106
+ .map((entry) => entry.Name())
107
+ .sort(),
108
+ ).toEqual(['one.txt', 'two.txt'])
109
+ })
110
+ })
@@ -0,0 +1,280 @@
1
+ import * as $ from '@goscript/builtin/index.js'
2
+ import * as fs from '@goscript/io/fs/index.js'
3
+ import * as os from '@goscript/os/index.js'
4
+ import * as filepath from '@goscript/path/filepath/index.js'
5
+
6
+ export type Option = (o: options) => void
7
+ export type Type = number
8
+
9
+ export const BoundOSFS: Type = 0
10
+
11
+ const allCapabilities = 127
12
+ const defaultCreateMode = 0o666
13
+ const defaultDirectoryMode = 0o777
14
+ const ErrCrossedBoundary = $.newError('chroot boundary crossed')
15
+ const ErrBaseDirCannotBeRemoved = $.newError('base dir cannot be removed')
16
+ const ErrBaseDirCannotBeRenamed = $.newError('base dir cannot be renamed')
17
+
18
+ type JoinElement = string | $.Slice<string>
19
+
20
+ class options {
21
+ public Type: Type = BoundOSFS
22
+ public mmap = false
23
+ }
24
+
25
+ export function New(
26
+ baseDir: string,
27
+ ...opts: Array<Option | $.Slice<Option> | undefined>
28
+ ): BoundOS {
29
+ const o = new options()
30
+ for (const opt of normalizeOptions(opts)) {
31
+ opt(o)
32
+ }
33
+ return new BoundOS({ baseDir, mmap: o.mmap })
34
+ }
35
+
36
+ function normalizeOptions(
37
+ opts: Array<Option | $.Slice<Option> | undefined>,
38
+ ): Option[] {
39
+ if (opts.length === 0) {
40
+ return []
41
+ }
42
+ if (opts.length === 1 && typeof opts[0] !== 'function') {
43
+ const slice = opts[0] ?? null
44
+ const out: Option[] = []
45
+ for (let i = 0; i < $.len(slice); i++) {
46
+ out.push(slice![i])
47
+ }
48
+ return out
49
+ }
50
+ return opts.filter((opt): opt is Option => typeof opt === 'function')
51
+ }
52
+
53
+ export function WithBoundOS(): Option {
54
+ return (o: options): void => {
55
+ o.Type = BoundOSFS
56
+ }
57
+ }
58
+
59
+ export function WithMmap(): Option {
60
+ return (o: options): void => {
61
+ o.mmap = true
62
+ }
63
+ }
64
+
65
+ export class BoundOS {
66
+ public baseDir: string
67
+ public mmap: boolean
68
+
69
+ constructor(init?: Partial<{ baseDir?: string; mmap?: boolean }>) {
70
+ const baseDir = init?.baseDir ?? '/'
71
+ this.baseDir = baseDir === '' ? '/' : filepath.Clean(baseDir)
72
+ this.mmap = init?.mmap ?? false
73
+ }
74
+
75
+ public clone(): BoundOS {
76
+ return new BoundOS({ baseDir: this.baseDir, mmap: this.mmap })
77
+ }
78
+
79
+ public Capabilities(): number {
80
+ return allCapabilities
81
+ }
82
+
83
+ public Create(name: string): [os.File | null, $.GoError] {
84
+ return this.OpenFile(
85
+ name,
86
+ os.O_RDWR | os.O_CREATE | os.O_TRUNC,
87
+ defaultCreateMode,
88
+ )
89
+ }
90
+
91
+ public Open(name: string): [os.File | null, $.GoError] {
92
+ return this.OpenFile(name, os.O_RDONLY, 0)
93
+ }
94
+
95
+ public OpenFile(
96
+ name: string,
97
+ flag: number,
98
+ perm: fs.FileMode,
99
+ ): [os.File | null, $.GoError] {
100
+ const [full, err] = this.abs(name)
101
+ if (err !== null) {
102
+ return [null, err]
103
+ }
104
+ if ((flag & os.O_CREATE) !== 0) {
105
+ const dir = filepath.Dir(full)
106
+ if (dir !== '.' && dir !== '') {
107
+ const mkdirErr = os.MkdirAll(dir, defaultDirectoryMode)
108
+ if (mkdirErr !== null) {
109
+ return [null, mkdirErr]
110
+ }
111
+ }
112
+ }
113
+ return os.OpenFile(full, flag, perm)
114
+ }
115
+
116
+ public Stat(name: string): [fs.FileInfo, $.GoError] {
117
+ const [full, err] = this.abs(name)
118
+ if (err !== null) {
119
+ return [null, err]
120
+ }
121
+ return os.Stat(full)
122
+ }
123
+
124
+ public Lstat(name: string): [fs.FileInfo, $.GoError] {
125
+ const [full, err] = this.abs(name)
126
+ if (err !== null) {
127
+ return [null, err]
128
+ }
129
+ return os.Lstat(full)
130
+ }
131
+
132
+ public ReadDir(name: string): [$.Slice<fs.DirEntry>, $.GoError] {
133
+ const [full, err] = this.abs(name)
134
+ if (err !== null) {
135
+ return [null, err]
136
+ }
137
+ const [file, openErr] = os.Open(full)
138
+ if (openErr !== null) {
139
+ return [null, openErr]
140
+ }
141
+ const [entries, readErr] = file!.ReadDir(-1)
142
+ const closeErr = file!.Close()
143
+ return [entries, readErr ?? closeErr]
144
+ }
145
+
146
+ public Rename(from: string, to: string): $.GoError {
147
+ if (this.isRoot(from)) {
148
+ return ErrBaseDirCannotBeRenamed
149
+ }
150
+ const [fromFull, fromErr] = this.abs(from)
151
+ if (fromErr !== null) {
152
+ return fromErr
153
+ }
154
+ const [toFull, toErr] = this.abs(to)
155
+ if (toErr !== null) {
156
+ return toErr
157
+ }
158
+ const mkdirErr = os.MkdirAll(filepath.Dir(toFull), defaultDirectoryMode)
159
+ if (mkdirErr !== null) {
160
+ return mkdirErr
161
+ }
162
+ return os.Rename(fromFull, toFull)
163
+ }
164
+
165
+ public Remove(name: string): $.GoError {
166
+ if (this.isRoot(name)) {
167
+ return ErrBaseDirCannotBeRemoved
168
+ }
169
+ const [full, err] = this.abs(name)
170
+ if (err !== null) {
171
+ return err
172
+ }
173
+ return os.Remove(full)
174
+ }
175
+
176
+ public RemoveAll(name: string): $.GoError {
177
+ if (this.isRoot(name)) {
178
+ return ErrBaseDirCannotBeRemoved
179
+ }
180
+ const [full, err] = this.abs(name)
181
+ if (err !== null) {
182
+ return err
183
+ }
184
+ return os.RemoveAll(full)
185
+ }
186
+
187
+ public MkdirAll(name: string, perm: fs.FileMode): $.GoError {
188
+ const [full, err] = this.abs(name)
189
+ if (err !== null) {
190
+ return err
191
+ }
192
+ return os.MkdirAll(full, perm)
193
+ }
194
+
195
+ public Symlink(target: string, link: string): $.GoError {
196
+ const [full, err] = this.abs(link)
197
+ if (err !== null) {
198
+ return err
199
+ }
200
+ const mkdirErr = os.MkdirAll(filepath.Dir(full), defaultDirectoryMode)
201
+ if (mkdirErr !== null) {
202
+ return mkdirErr
203
+ }
204
+ return os.Symlink(target, full)
205
+ }
206
+
207
+ public Readlink(link: string): [string, $.GoError] {
208
+ const [full, err] = this.abs(link)
209
+ if (err !== null) {
210
+ return ['', err]
211
+ }
212
+ return os.Readlink(full)
213
+ }
214
+
215
+ public TempFile(dir: string, prefix: string): [os.File | null, $.GoError] {
216
+ const targetDir = dir === '' ? '.tmp' : dir
217
+ const [fullDir, err] = this.abs(targetDir)
218
+ if (err !== null) {
219
+ return [null, err]
220
+ }
221
+ const mkdirErr = os.MkdirAll(fullDir, defaultDirectoryMode)
222
+ if (mkdirErr !== null) {
223
+ return [null, mkdirErr]
224
+ }
225
+ return os.CreateTemp(fullDir, prefix === '' ? 'tmp-*' : prefix + '*')
226
+ }
227
+
228
+ public Join(...elem: JoinElement[]): string {
229
+ if (elem.length === 1 && typeof elem[0] !== 'string') {
230
+ return filepath.Join(elem[0])
231
+ }
232
+ return filepath.Join(...(elem as string[]))
233
+ }
234
+
235
+ public Chroot(path: string): [BoundOS | null, $.GoError] {
236
+ const [full, err] = this.abs(path)
237
+ if (err !== null) {
238
+ return [null, err]
239
+ }
240
+ return [new BoundOS({ baseDir: full, mmap: this.mmap }), null]
241
+ }
242
+
243
+ public Root(): string {
244
+ return this.baseDir
245
+ }
246
+
247
+ private isRoot(name: string): boolean {
248
+ return name === '' || name === '.'
249
+ }
250
+
251
+ private abs(name: string): [string, $.GoError] {
252
+ if (this.isRoot(name)) {
253
+ return [this.baseDir, null]
254
+ }
255
+ const normalized = filepath.Clean(filepath.ToSlash(name))
256
+ if (filepath.IsAbs(normalized)) {
257
+ const full = filepath.Clean(filepath.FromSlash(normalized))
258
+ if (!this.insideBase(full)) {
259
+ return ['', ErrCrossedBoundary]
260
+ }
261
+ return [full, null]
262
+ }
263
+ if (normalized === '..' || normalized.startsWith('../')) {
264
+ return ['', ErrCrossedBoundary]
265
+ }
266
+ return [filepath.Join(this.baseDir, filepath.FromSlash(normalized)), null]
267
+ }
268
+
269
+ private insideBase(path: string): boolean {
270
+ const full = filepath.Clean(path)
271
+ if (full === this.baseDir) {
272
+ return true
273
+ }
274
+ return full.startsWith(
275
+ this.baseDir.endsWith('/') ? this.baseDir : this.baseDir + '/',
276
+ )
277
+ }
278
+ }
279
+
280
+ export const Default = New('/')
@@ -0,0 +1,8 @@
1
+ {
2
+ "dependencies": [
3
+ "io/fs",
4
+ "os",
5
+ "path/filepath"
6
+ ],
7
+ "asyncMethods": {}
8
+ }
@@ -109,10 +109,18 @@ class fundamental {
109
109
  },
110
110
  ],
111
111
  fundamental,
112
- {
113
- msg: { kind: $.TypeKind.Basic, name: 'string' },
114
- stack: { kind: $.TypeKind.Pointer, elemType: 'stack' },
115
- },
112
+ [
113
+ {
114
+ name: 'msg',
115
+ key: 'msg',
116
+ type: { kind: $.TypeKind.Basic, name: 'string' },
117
+ },
118
+ {
119
+ name: 'stack',
120
+ key: 'stack',
121
+ type: { kind: $.TypeKind.Pointer, elemType: 'stack' },
122
+ },
123
+ ],
116
124
  )
117
125
  }
118
126
 
@@ -231,20 +239,28 @@ class withStack {
231
239
  },
232
240
  ],
233
241
  withStack,
234
- {
235
- error: {
236
- kind: $.TypeKind.Interface,
237
- name: 'GoError',
238
- methods: [
239
- {
240
- name: 'Error',
241
- args: [],
242
- returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
243
- },
244
- ],
242
+ [
243
+ {
244
+ name: 'error',
245
+ key: 'error',
246
+ type: {
247
+ kind: $.TypeKind.Interface,
248
+ name: 'GoError',
249
+ methods: [
250
+ {
251
+ name: 'Error',
252
+ args: [],
253
+ returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
254
+ },
255
+ ],
256
+ },
245
257
  },
246
- stack: { kind: $.TypeKind.Pointer, elemType: 'stack' },
247
- },
258
+ {
259
+ name: 'stack',
260
+ key: 'stack',
261
+ type: { kind: $.TypeKind.Pointer, elemType: 'stack' },
262
+ },
263
+ ],
248
264
  )
249
265
  }
250
266
 
@@ -405,20 +421,28 @@ class withMessage {
405
421
  },
406
422
  ],
407
423
  withMessage,
408
- {
409
- cause: {
410
- kind: $.TypeKind.Interface,
411
- name: 'GoError',
412
- methods: [
413
- {
414
- name: 'Error',
415
- args: [],
416
- returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
417
- },
418
- ],
424
+ [
425
+ {
426
+ name: 'cause',
427
+ key: 'cause',
428
+ type: {
429
+ kind: $.TypeKind.Interface,
430
+ name: 'GoError',
431
+ methods: [
432
+ {
433
+ name: 'Error',
434
+ args: [],
435
+ returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
436
+ },
437
+ ],
438
+ },
419
439
  },
420
- msg: { kind: $.TypeKind.Basic, name: 'string' },
421
- },
440
+ {
441
+ name: 'msg',
442
+ key: 'msg',
443
+ type: { kind: $.TypeKind.Basic, name: 'string' },
444
+ },
445
+ ],
422
446
  )
423
447
  }
424
448
 
@@ -3,70 +3,53 @@ import { describe, expect, it } from 'vitest'
3
3
  import * as $ from '@goscript/builtin/index.js'
4
4
  import * as token from '@goscript/go/token/index.js'
5
5
 
6
- import {
7
- ErrorList_Add,
8
- ErrorList_Err,
9
- ErrorList_Error,
10
- ErrorList_Len,
11
- ErrorList_RemoveMultiples,
12
- Scanner,
13
- type ErrorList,
14
- } from './index.js'
6
+ import { Error as ScannerError, ErrorList_Err, PrintError } from './index.js'
15
7
 
16
8
  describe('go/scanner override', () => {
17
- it('adds and formats scanner errors', () => {
18
- const list: $.VarRef<ErrorList> = $.varRef(null)
19
-
20
- ErrorList_Add(
21
- list,
22
- $.markAsStructValue(
23
- new token.Position({
24
- Filename: 'test.go',
25
- Line: 1,
26
- Column: 1,
27
- }),
28
- ),
29
- 'test error',
30
- )
31
-
32
- expect(ErrorList_Len(list.value)).toBe(1)
33
- expect(ErrorList_Error(list.value)).toBe('test.go:1:1: test error')
9
+ it('prints errors to an io.Writer', () => {
10
+ const chunks: string[] = []
11
+ const writer = {
12
+ Write(p: $.Bytes): [number, $.GoError] {
13
+ chunks.push($.bytesToString(p))
14
+ return [$.len(p), null]
15
+ },
16
+ }
17
+
18
+ PrintError(writer, $.newError('scan failed'))
19
+
20
+ expect(chunks).toEqual(['scan failed\n'])
34
21
  })
35
22
 
36
- it('removes repeated line errors', () => {
37
- const list: $.VarRef<ErrorList> = $.varRef(null)
38
- const pos = $.markAsStructValue(
39
- new token.Position({
40
- Filename: 'test.go',
41
- Line: 1,
42
- Column: 1,
23
+ it('prints every ErrorList entry', () => {
24
+ const chunks: string[] = []
25
+ const writer = {
26
+ Write(p: $.Bytes): [number, $.GoError] {
27
+ chunks.push($.bytesToString(p))
28
+ return [$.len(p), null]
29
+ },
30
+ }
31
+
32
+ const errors = [
33
+ new ScannerError({
34
+ Pos: new token.Position({ Filename: 'a.go', Line: 1, Column: 2 }),
35
+ Msg: 'first',
43
36
  }),
44
- )
45
-
46
- ErrorList_Add(list, pos, 'first')
47
- ErrorList_Add(list, pos, 'second')
48
- ErrorList_RemoveMultiples(list)
49
-
50
- expect(ErrorList_Len(list.value)).toBe(1)
51
- })
52
-
53
- it('returns nil Err for an empty list and an error for non-empty lists', () => {
54
- const empty: ErrorList = null
55
- expect(ErrorList_Err(empty)).toBeNull()
37
+ new ScannerError({
38
+ Pos: new token.Position({ Filename: 'a.go', Line: 3, Column: 4 }),
39
+ Msg: 'second',
40
+ }),
41
+ ]
56
42
 
57
- const list: $.VarRef<ErrorList> = $.varRef(null)
58
- ErrorList_Add(list, new token.Position({ Line: 1 }), 'first')
59
- expect(ErrorList_Err(list.value)?.Error()).toBe('1: first')
60
- })
43
+ PrintError(writer, errors as $.GoError)
61
44
 
62
- it('provides a Scanner surface for generated go/parser code', () => {
63
- const fset = token.NewFileSet()
64
- const file = fset.AddFile('test.go', -1, 0)
65
- const scanner = new Scanner()
45
+ expect(chunks).toEqual(['a.go:1:2: first\n', 'a.go:3:4: second\n'])
66
46
 
67
- scanner.Init(file, null, null, 0)
47
+ chunks.length = 0
48
+ const err = ErrorList_Err(errors)
49
+ expect(Array.isArray(err)).toBe(true)
50
+ expect(err?.Error()).toBe('a.go:1:2: first (and 1 more errors)')
51
+ PrintError(writer, err)
68
52
 
69
- expect(scanner.Scan()).toEqual([file.Pos(0), token.EOF, ''])
70
- expect(scanner.Scan()).toEqual([file.Pos(0), token.EOF, ''])
53
+ expect(chunks).toEqual(['a.go:1:2: first\n', 'a.go:3:4: second\n'])
71
54
  })
72
55
  })
@@ -1,5 +1,6 @@
1
1
  import * as $ from '@goscript/builtin/index.js'
2
2
  import * as token from '@goscript/go/token/index.js'
3
+ import type * as io from '@goscript/io/index.js'
3
4
 
4
5
  export class Error {
5
6
  public get Pos(): token.Position {
@@ -54,10 +55,14 @@ export class Error {
54
55
  },
55
56
  ],
56
57
  Error,
57
- {
58
- Pos: { type: 'go/token.Position' },
59
- Msg: { kind: $.TypeKind.Basic, name: 'string' },
60
- },
58
+ [
59
+ { name: 'Pos', key: 'Pos', type: 'go/token.Position' },
60
+ {
61
+ name: 'Msg',
62
+ key: 'Msg',
63
+ type: { kind: $.TypeKind.Basic, name: 'string' },
64
+ },
65
+ ],
61
66
  )
62
67
  }
63
68
 
@@ -196,9 +201,32 @@ export function ErrorList_Error(list: ErrorList): string {
196
201
  return `${errors[0]!.Error()} (and ${errors.length - 1} more errors)`
197
202
  }
198
203
 
204
+ type errorListWithError = ErrorList & { Error?: () => string }
205
+
199
206
  export function ErrorList_Err(list: ErrorList): $.GoError {
200
207
  if ($.len(list) === 0) {
201
208
  return null
202
209
  }
203
- return $.newError(ErrorList_Error(list))
210
+ const err = list as errorListWithError
211
+ err.Error = () => ErrorList_Error(list)
212
+ return err as unknown as $.GoError
213
+ }
214
+
215
+ export function PrintError(w: io.Writer, err: $.GoError): void {
216
+ if (err === null) {
217
+ return
218
+ }
219
+ if (Array.isArray(err)) {
220
+ for (const entry of Array.from((err as ErrorList) ?? [])) {
221
+ if (entry == null) {
222
+ continue
223
+ }
224
+ w.Write($.stringToBytes(`${entry.Error()}\n`))
225
+ }
226
+ return
227
+ }
228
+ const text = err.Error()
229
+ for (const line of text.split('\n')) {
230
+ w.Write($.stringToBytes(line + '\n'))
231
+ }
204
232
  }
@@ -0,0 +1,27 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "strict": true,
4
+ "symbols": {
5
+ "Error": {
6
+ "status": "real"
7
+ },
8
+ "ErrorHandler": {
9
+ "status": "real"
10
+ },
11
+ "ErrorList": {
12
+ "status": "real"
13
+ },
14
+ "Mode": {
15
+ "status": "real"
16
+ },
17
+ "PrintError": {
18
+ "status": "real"
19
+ },
20
+ "ScanComments": {
21
+ "status": "real"
22
+ },
23
+ "Scanner": {
24
+ "status": "real"
25
+ }
26
+ }
27
+ }