goscript 0.2.2 → 0.2.4
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/gotest/testdata/browserapi/browserapi_test.go +36 -0
- package/compiler/lowering.go +279 -16
- package/compiler/override-registry_test.go +175 -0
- package/compiler/protobuf-ts-binding.go +154 -6
- package/compiler/protobuf-ts-binding_test.go +7 -2
- package/compiler/runtime-contract.go +2 -0
- package/compiler/runtime-contract_test.go +1 -0
- package/compiler/semantic-model.go +16 -0
- package/compiler/semantic-model_test.go +38 -0
- package/compiler/skeleton_test.go +522 -17
- package/compiler/typescript-emitter.go +4 -0
- package/dist/gs/builtin/builtin.js +7 -9
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/defer.js +2 -2
- package/dist/gs/builtin/hostio.js +5 -5
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/map.js +2 -1
- package/dist/gs/builtin/map.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +3 -0
- package/dist/gs/builtin/slice.js +39 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +49 -0
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/compress/gzip/index.d.ts +41 -0
- package/dist/gs/compress/gzip/index.js +235 -0
- package/dist/gs/compress/gzip/index.js.map +1 -0
- package/dist/gs/compress/zlib/index.js +5 -2
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/crypto/ecdh/index.js +27 -8
- package/dist/gs/crypto/ecdh/index.js.map +1 -1
- package/dist/gs/crypto/ed25519/index.js +3 -3
- package/dist/gs/crypto/ed25519/index.js.map +1 -1
- package/dist/gs/crypto/rand/index.js +6 -3
- package/dist/gs/crypto/rand/index.js.map +1 -1
- package/dist/gs/embed/index.js +9 -3
- package/dist/gs/embed/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
- package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
- package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
- package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
- package/dist/gs/hash/fnv/index.js +13 -5
- package/dist/gs/hash/fnv/index.js.map +1 -1
- package/dist/gs/io/fs/glob.d.ts +3 -3
- package/dist/gs/io/fs/glob.js +9 -9
- package/dist/gs/io/fs/glob.js.map +1 -1
- package/dist/gs/io/fs/readdir.d.ts +2 -2
- package/dist/gs/io/fs/readdir.js +13 -74
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/readlink.d.ts +1 -1
- package/dist/gs/io/fs/readlink.js +2 -2
- package/dist/gs/io/fs/readlink.js.map +1 -1
- package/dist/gs/io/fs/stat.d.ts +4 -2
- package/dist/gs/io/fs/stat.js +12 -73
- package/dist/gs/io/fs/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.d.ts +2 -2
- package/dist/gs/io/fs/sub.js +11 -11
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/fs/walk.js +2 -2
- package/dist/gs/io/fs/walk.js.map +1 -1
- package/dist/gs/maps/iter.js.map +1 -1
- package/dist/gs/maps/maps.js.map +1 -1
- package/dist/gs/mime/index.js +5 -2
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/httptest/index.js +6 -3
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +34 -18
- package/dist/gs/net/http/index.js +280 -63
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/net/http/pprof/index.d.ts +5 -5
- package/dist/gs/net/http/pprof/index.js +21 -21
- package/dist/gs/net/http/pprof/index.js.map +1 -1
- package/dist/gs/reflect/iter.js +1 -1
- package/dist/gs/reflect/iter.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +2 -0
- package/dist/gs/reflect/type.js +53 -21
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/runtime/pprof/index.js.map +1 -1
- package/dist/gs/runtime/runtime.js +2 -2
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +1 -1
- package/dist/gs/slices/slices.js +37 -4
- package/dist/gs/slices/slices.js.map +1 -1
- package/gs/builtin/builtin.ts +11 -14
- package/gs/builtin/defer.ts +2 -2
- package/gs/builtin/hostio.ts +5 -5
- package/gs/builtin/map.ts +4 -1
- package/gs/builtin/runtime-contract.test.ts +25 -0
- package/gs/builtin/slice.test.ts +14 -0
- package/gs/builtin/slice.ts +64 -0
- package/gs/builtin/type.ts +72 -0
- package/gs/bytes/bytes.test.ts +14 -13
- package/gs/compress/gzip/index.test.ts +86 -0
- package/gs/compress/gzip/index.ts +297 -0
- package/gs/compress/gzip/meta.json +6 -0
- package/gs/compress/gzip/parity.json +45 -0
- package/gs/compress/zlib/index.test.ts +19 -5
- package/gs/compress/zlib/index.ts +16 -7
- package/gs/context/context.test.ts +3 -1
- package/gs/crypto/ecdh/index.test.ts +6 -2
- package/gs/crypto/ecdh/index.ts +49 -12
- package/gs/crypto/ed25519/index.ts +20 -7
- package/gs/crypto/rand/index.ts +6 -3
- package/gs/embed/index.test.ts +4 -4
- package/gs/embed/index.ts +9 -3
- package/gs/fmt/fmt.test.ts +29 -4
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
- package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
- package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
- package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
- package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
- package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
- package/gs/hash/fnv/index.test.ts +1 -8
- package/gs/hash/fnv/index.ts +27 -10
- package/gs/io/fs/glob.ts +14 -11
- package/gs/io/fs/meta.json +5 -0
- package/gs/io/fs/readdir.test.ts +63 -2
- package/gs/io/fs/readdir.ts +33 -30
- package/gs/io/fs/readlink.test.ts +2 -2
- package/gs/io/fs/readlink.ts +5 -2
- package/gs/io/fs/stat.test.ts +79 -0
- package/gs/io/fs/stat.ts +24 -10
- package/gs/io/fs/sub.test.ts +93 -0
- package/gs/io/fs/sub.ts +13 -13
- package/gs/io/fs/walk.ts +2 -2
- package/gs/maps/iter.ts +9 -9
- package/gs/maps/maps.ts +4 -4
- package/gs/math/bits/index.test.ts +10 -1
- package/gs/mime/index.test.ts +33 -15
- package/gs/mime/index.ts +9 -2
- package/gs/net/http/httptest/index.test.ts +17 -3
- package/gs/net/http/httptest/index.ts +8 -3
- package/gs/net/http/index.test.ts +851 -124
- package/gs/net/http/index.ts +612 -146
- package/gs/net/http/meta.json +3 -1
- package/gs/net/http/pprof/index.test.ts +4 -4
- package/gs/net/http/pprof/index.ts +43 -22
- package/gs/os/file_unix_js.test.ts +22 -0
- package/gs/reflect/iter.ts +4 -2
- package/gs/reflect/map.test.ts +56 -1
- package/gs/reflect/type.ts +76 -37
- package/gs/runtime/pprof/index.test.ts +7 -1
- package/gs/runtime/pprof/index.ts +5 -1
- package/gs/runtime/runtime.test.ts +7 -0
- package/gs/runtime/runtime.ts +2 -4
- package/gs/runtime/trace/index.test.ts +9 -1
- package/gs/runtime/trace/index.ts +5 -1
- package/gs/slices/meta.json +3 -0
- package/gs/slices/slices.test.ts +59 -21
- package/gs/slices/slices.ts +61 -20
- package/gs/strconv/complex.test.ts +17 -3
- package/gs/sync/atomic/doc_64.test.ts +2 -9
- package/gs/sync/sync.test.ts +18 -8
- package/gs/syscall/js/index.test.ts +9 -4
- package/package.json +5 -4
package/gs/hash/fnv/index.ts
CHANGED
|
@@ -65,7 +65,10 @@ class FNV32 {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
AppendBinary(prefix: $.Bytes | null): [$.Bytes, $.GoError] {
|
|
68
|
-
return [
|
|
68
|
+
return [
|
|
69
|
+
appendBytes($.bytesToUint8Array(prefix), this.magic, be32(this.hash)),
|
|
70
|
+
null,
|
|
71
|
+
]
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
MarshalBinary(): [$.Bytes, $.GoError] {
|
|
@@ -136,7 +139,10 @@ class FNV64 {
|
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
AppendBinary(prefix: $.Bytes | null): [$.Bytes, $.GoError] {
|
|
139
|
-
return [
|
|
142
|
+
return [
|
|
143
|
+
appendBytes($.bytesToUint8Array(prefix), this.magic, be64(this.hash)),
|
|
144
|
+
null,
|
|
145
|
+
]
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
MarshalBinary(): [$.Bytes, $.GoError] {
|
|
@@ -185,7 +191,11 @@ class FNV128 {
|
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
async Sum(prefix: $.Bytes | null): Promise<$.Bytes> {
|
|
188
|
-
return appendBytes(
|
|
194
|
+
return appendBytes(
|
|
195
|
+
$.bytesToUint8Array(prefix),
|
|
196
|
+
be64(this.high()),
|
|
197
|
+
be64(this.low()),
|
|
198
|
+
)
|
|
189
199
|
}
|
|
190
200
|
|
|
191
201
|
Reset(): void {
|
|
@@ -202,7 +212,12 @@ class FNV128 {
|
|
|
202
212
|
|
|
203
213
|
AppendBinary(prefix: $.Bytes | null): [$.Bytes, $.GoError] {
|
|
204
214
|
return [
|
|
205
|
-
appendBytes(
|
|
215
|
+
appendBytes(
|
|
216
|
+
$.bytesToUint8Array(prefix),
|
|
217
|
+
this.magic,
|
|
218
|
+
be64(this.high()),
|
|
219
|
+
be64(this.low()),
|
|
220
|
+
),
|
|
206
221
|
null,
|
|
207
222
|
]
|
|
208
223
|
}
|
|
@@ -217,7 +232,8 @@ class FNV128 {
|
|
|
217
232
|
if (err != null) {
|
|
218
233
|
return err
|
|
219
234
|
}
|
|
220
|
-
this.hash =
|
|
235
|
+
this.hash =
|
|
236
|
+
((readBE64(state, 4) << 64n) | readBE64(state, 12)) & uint128Mask
|
|
221
237
|
return null
|
|
222
238
|
}
|
|
223
239
|
|
|
@@ -304,11 +320,12 @@ function be64(value: bigint): Uint8Array {
|
|
|
304
320
|
|
|
305
321
|
function readBE32(bytes: Uint8Array, offset: number): number {
|
|
306
322
|
return (
|
|
307
|
-
((bytes[offset] << 24) >>> 0) |
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
323
|
+
(((bytes[offset] << 24) >>> 0) |
|
|
324
|
+
(bytes[offset + 1] << 16) |
|
|
325
|
+
(bytes[offset + 2] << 8) |
|
|
326
|
+
bytes[offset + 3]) >>>
|
|
327
|
+
0
|
|
328
|
+
)
|
|
312
329
|
}
|
|
313
330
|
|
|
314
331
|
function readBE64(bytes: Uint8Array, offset: number): bigint {
|
package/gs/io/fs/glob.ts
CHANGED
|
@@ -60,15 +60,18 @@ $.registerInterfaceType(
|
|
|
60
60
|
// If fs implements [GlobFS], Glob calls fs.Glob.
|
|
61
61
|
// Otherwise, Glob uses [ReadDir] to traverse the directory tree
|
|
62
62
|
// and look for matches for the pattern.
|
|
63
|
-
export function Glob(
|
|
64
|
-
|
|
63
|
+
export async function Glob(
|
|
64
|
+
fsys: FS,
|
|
65
|
+
pattern: string,
|
|
66
|
+
): Promise<[$.Slice<string>, $.GoError]> {
|
|
67
|
+
return await globWithLimit(fsys, pattern, 0)
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
export function globWithLimit(
|
|
70
|
+
export async function globWithLimit(
|
|
68
71
|
fsys: FS,
|
|
69
72
|
pattern: string,
|
|
70
73
|
depth: number,
|
|
71
|
-
): [$.Slice<string>, $.GoError] {
|
|
74
|
+
): Promise<[$.Slice<string>, $.GoError]> {
|
|
72
75
|
let matches: $.Slice<string> = null
|
|
73
76
|
{
|
|
74
77
|
// This limit is added to prevent stack exhaustion issues. See
|
|
@@ -93,7 +96,7 @@ export function globWithLimit(
|
|
|
93
96
|
}
|
|
94
97
|
if (!hasMeta(pattern)) {
|
|
95
98
|
{
|
|
96
|
-
const [, statErr] = Stat(fsys, pattern)
|
|
99
|
+
const [, statErr] = await Stat(fsys, pattern)
|
|
97
100
|
if (statErr != null) {
|
|
98
101
|
return [null, null]
|
|
99
102
|
}
|
|
@@ -105,7 +108,7 @@ export function globWithLimit(
|
|
|
105
108
|
dir = cleanGlobPath(dir)
|
|
106
109
|
|
|
107
110
|
if (!hasMeta(dir)) {
|
|
108
|
-
return glob(fsys, dir, file, null)
|
|
111
|
+
return await glob(fsys, dir, file, null)
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
// Prevent infinite recursion. See issue 15879.
|
|
@@ -113,14 +116,14 @@ export function globWithLimit(
|
|
|
113
116
|
return [null, path.ErrBadPattern]
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
const [dirs, dirErr] = globWithLimit(fsys, dir, depth + 1)
|
|
119
|
+
const [dirs, dirErr] = await globWithLimit(fsys, dir, depth + 1)
|
|
117
120
|
if (dirErr != null) {
|
|
118
121
|
return [null, dirErr]
|
|
119
122
|
}
|
|
120
123
|
for (let _i = 0; _i < $.len(dirs); _i++) {
|
|
121
124
|
const d = dirs![_i]
|
|
122
125
|
{
|
|
123
|
-
const [nextMatches, globErr] = glob(fsys, d, file, matches)
|
|
126
|
+
const [nextMatches, globErr] = await glob(fsys, d, file, matches)
|
|
124
127
|
matches = nextMatches
|
|
125
128
|
if (globErr != null) {
|
|
126
129
|
return [matches, globErr]
|
|
@@ -148,15 +151,15 @@ export function cleanGlobPath(path: string): string {
|
|
|
148
151
|
// and appends them to matches, returning the updated slice.
|
|
149
152
|
// If the directory cannot be opened, glob returns the existing matches.
|
|
150
153
|
// New matches are added in lexicographical order.
|
|
151
|
-
export function glob(
|
|
154
|
+
export async function glob(
|
|
152
155
|
fs: FS,
|
|
153
156
|
dir: string,
|
|
154
157
|
pattern: string,
|
|
155
158
|
matches: $.Slice<string>,
|
|
156
|
-
): [$.Slice<string>, $.GoError] {
|
|
159
|
+
): Promise<[$.Slice<string>, $.GoError]> {
|
|
157
160
|
let m = matches
|
|
158
161
|
{
|
|
159
|
-
let [infos, err] = ReadDir(fs, dir)
|
|
162
|
+
let [infos, err] = await ReadDir(fs, dir)
|
|
160
163
|
|
|
161
164
|
// ignore I/O error
|
|
162
165
|
if (err != null) {
|
package/gs/io/fs/meta.json
CHANGED
package/gs/io/fs/readdir.test.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as $ from '@goscript/builtin/index.js'
|
|
|
4
4
|
import { File, FileInfo, ReadDir } from './index.js'
|
|
5
5
|
|
|
6
6
|
describe('io/fs ReadDir override', () => {
|
|
7
|
-
it('returns a nil entry slice without sorting when directory read fails', () => {
|
|
7
|
+
it('returns a nil entry slice without sorting when directory read fails', async () => {
|
|
8
8
|
const readErr = $.newError('read failed')
|
|
9
9
|
const file: File & {
|
|
10
10
|
ReadDir(n: number): [null, $.GoError]
|
|
@@ -23,7 +23,7 @@ describe('io/fs ReadDir override', () => {
|
|
|
23
23
|
},
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const [list, err] = ReadDir(
|
|
26
|
+
const [list, err] = await ReadDir(
|
|
27
27
|
{
|
|
28
28
|
Open(_name: string): [File, $.GoError] {
|
|
29
29
|
return [file, null]
|
|
@@ -35,4 +35,65 @@ describe('io/fs ReadDir override', () => {
|
|
|
35
35
|
expect(list).toBeNull()
|
|
36
36
|
expect(err).toBe(readErr)
|
|
37
37
|
})
|
|
38
|
+
|
|
39
|
+
it('awaits async FS.Open fallback implementations', async () => {
|
|
40
|
+
const entries = [
|
|
41
|
+
{
|
|
42
|
+
Name(): string {
|
|
43
|
+
return 'b.txt'
|
|
44
|
+
},
|
|
45
|
+
IsDir(): boolean {
|
|
46
|
+
return false
|
|
47
|
+
},
|
|
48
|
+
Info(): [FileInfo, $.GoError] {
|
|
49
|
+
return [null, null]
|
|
50
|
+
},
|
|
51
|
+
Type(): number {
|
|
52
|
+
return 0
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
Name(): string {
|
|
57
|
+
return 'a.txt'
|
|
58
|
+
},
|
|
59
|
+
IsDir(): boolean {
|
|
60
|
+
return false
|
|
61
|
+
},
|
|
62
|
+
Info(): [FileInfo, $.GoError] {
|
|
63
|
+
return [null, null]
|
|
64
|
+
},
|
|
65
|
+
Type(): number {
|
|
66
|
+
return 0
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
const file: File & {
|
|
71
|
+
ReadDir(n: number): Promise<[$.Slice<any>, $.GoError]>
|
|
72
|
+
} = {
|
|
73
|
+
Close(): $.GoError {
|
|
74
|
+
return null
|
|
75
|
+
},
|
|
76
|
+
Read(_p0: Uint8Array): [number, $.GoError] {
|
|
77
|
+
return [0, null]
|
|
78
|
+
},
|
|
79
|
+
Stat(): [FileInfo, $.GoError] {
|
|
80
|
+
return [null, null]
|
|
81
|
+
},
|
|
82
|
+
async ReadDir(_n: number): Promise<[$.Slice<any>, $.GoError]> {
|
|
83
|
+
return [$.arrayToSlice(entries), null]
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const [list, err] = await ReadDir(
|
|
88
|
+
{
|
|
89
|
+
async Open(_name: string): Promise<[File, $.GoError]> {
|
|
90
|
+
return [file, null]
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
'dir',
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
expect(err).toBeNull()
|
|
97
|
+
expect(list?.map((entry) => entry?.Name())).toEqual(['a.txt', 'b.txt'])
|
|
98
|
+
})
|
|
38
99
|
})
|
package/gs/io/fs/readdir.ts
CHANGED
|
@@ -17,7 +17,11 @@ export type ReadDirFS =
|
|
|
17
17
|
| ({
|
|
18
18
|
// ReadDir reads the named directory
|
|
19
19
|
// and returns a list of directory entries sorted by filename.
|
|
20
|
-
ReadDir(
|
|
20
|
+
ReadDir(
|
|
21
|
+
name: string,
|
|
22
|
+
):
|
|
23
|
+
| [$.Slice<DirEntry>, $.GoError]
|
|
24
|
+
| Promise<[$.Slice<DirEntry>, $.GoError]>
|
|
21
25
|
} & FS)
|
|
22
26
|
|
|
23
27
|
$.registerInterfaceType(
|
|
@@ -55,51 +59,50 @@ $.registerInterfaceType(
|
|
|
55
59
|
// If fs implements [ReadDirFS], ReadDir calls fs.ReadDir.
|
|
56
60
|
// Otherwise ReadDir calls fs.Open and uses ReadDir and Close
|
|
57
61
|
// on the returned file.
|
|
58
|
-
export function ReadDir(
|
|
62
|
+
export async function ReadDir(
|
|
59
63
|
fsys: FS,
|
|
60
64
|
name: string,
|
|
61
|
-
): [$.Slice<DirEntry>, $.GoError] {
|
|
62
|
-
using __defer = new $.DisposableStack()
|
|
65
|
+
): Promise<[$.Slice<DirEntry>, $.GoError]> {
|
|
63
66
|
{
|
|
64
67
|
let { value: fsysTyped, ok: ok } = $.typeAssert<ReadDirFS>(
|
|
65
68
|
fsys,
|
|
66
69
|
'ReadDirFS',
|
|
67
70
|
)
|
|
68
71
|
if (ok) {
|
|
69
|
-
return fsysTyped!.ReadDir(name)
|
|
72
|
+
return await fsysTyped!.ReadDir(name)
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
let [file, err] = fsys!.Open(name)
|
|
76
|
+
let [file, err] = await fsys!.Open(name)
|
|
74
77
|
if (err != null) {
|
|
75
78
|
return [null, err]
|
|
76
79
|
}
|
|
77
|
-
|
|
78
|
-
file
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}),
|
|
90
|
-
]
|
|
91
|
-
}
|
|
80
|
+
try {
|
|
81
|
+
let { value: dir, ok: ok } = $.typeAssert<ReadDirFile>(file, 'ReadDirFile')
|
|
82
|
+
if (!ok) {
|
|
83
|
+
return [
|
|
84
|
+
null,
|
|
85
|
+
new PathError({
|
|
86
|
+
Err: errors.New('not implemented'),
|
|
87
|
+
Op: 'readdir',
|
|
88
|
+
Path: name,
|
|
89
|
+
}),
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
let list: $.Slice<DirEntry>
|
|
94
|
+
;[list, err] = await dir!.ReadDir(-1)
|
|
95
|
+
if (list) {
|
|
96
|
+
list.sort((a: DirEntry, b: DirEntry): number => {
|
|
97
|
+
return $.pointerValue<Exclude<DirEntry, null>>(a)
|
|
98
|
+
.Name()
|
|
99
|
+
.localeCompare($.pointerValue<Exclude<DirEntry, null>>(b).Name())
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
return [list, err]
|
|
103
|
+
} finally {
|
|
104
|
+
await file!.Close()
|
|
101
105
|
}
|
|
102
|
-
return [list, err]
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
class dirInfo {
|
|
@@ -35,8 +35,8 @@ describe('io/fs ReadLink', () => {
|
|
|
35
35
|
expect(err?.Error()).toBe('readlink link: invalid argument')
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
it('uses Lstat from ReadLinkFS implementations', () => {
|
|
39
|
-
const [, err] = Lstat(new linkFS(), 'link')
|
|
38
|
+
it('uses Lstat from ReadLinkFS implementations', async () => {
|
|
39
|
+
const [, err] = await Lstat(new linkFS(), 'link')
|
|
40
40
|
|
|
41
41
|
expect(err).toBe(ErrInvalid)
|
|
42
42
|
})
|
package/gs/io/fs/readlink.ts
CHANGED
|
@@ -60,10 +60,13 @@ export function ReadLink(fsys: FS, name: string): [string, $.GoError] {
|
|
|
60
60
|
return sym!.ReadLink(name)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export function Lstat(
|
|
63
|
+
export async function Lstat(
|
|
64
|
+
fsys: FS,
|
|
65
|
+
name: string,
|
|
66
|
+
): Promise<[FileInfo, $.GoError]> {
|
|
64
67
|
const { value: sym, ok } = $.typeAssert<ReadLinkFS>(fsys, 'ReadLinkFS')
|
|
65
68
|
if (!ok) {
|
|
66
|
-
return Stat(fsys, name)
|
|
69
|
+
return await Stat(fsys, name)
|
|
67
70
|
}
|
|
68
71
|
return sym!.Lstat(name)
|
|
69
72
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
3
|
+
|
|
4
|
+
import { File, FileInfo, Stat } from './index.js'
|
|
5
|
+
|
|
6
|
+
function fileInfo(name: string, isDir = false): FileInfo {
|
|
7
|
+
return {
|
|
8
|
+
IsDir(): boolean {
|
|
9
|
+
return isDir
|
|
10
|
+
},
|
|
11
|
+
ModTime(): any {
|
|
12
|
+
return null
|
|
13
|
+
},
|
|
14
|
+
Mode(): number {
|
|
15
|
+
return isDir ? 0o040000 : 0
|
|
16
|
+
},
|
|
17
|
+
Name(): string {
|
|
18
|
+
return name
|
|
19
|
+
},
|
|
20
|
+
Size(): number {
|
|
21
|
+
return 0
|
|
22
|
+
},
|
|
23
|
+
Sys(): null {
|
|
24
|
+
return null
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('io/fs Stat override', () => {
|
|
30
|
+
it('awaits async StatFS implementations', async () => {
|
|
31
|
+
const info = fileInfo('pkg', true)
|
|
32
|
+
|
|
33
|
+
const [got, err] = await Stat(
|
|
34
|
+
{
|
|
35
|
+
async Stat(_name: string): Promise<[FileInfo, $.GoError]> {
|
|
36
|
+
return [info, null]
|
|
37
|
+
},
|
|
38
|
+
Open(_name: string): [File, $.GoError] {
|
|
39
|
+
throw new Error('StatFS path should not open')
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
'pkg',
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
expect(err).toBeNull()
|
|
46
|
+
expect(got).toBe(info)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('awaits async Open, File.Stat, and Close fallback implementations', async () => {
|
|
50
|
+
const info = fileInfo('file.txt')
|
|
51
|
+
let closed = false
|
|
52
|
+
const file: File = {
|
|
53
|
+
async Close(): Promise<$.GoError> {
|
|
54
|
+
closed = true
|
|
55
|
+
return null
|
|
56
|
+
},
|
|
57
|
+
Read(_p0: Uint8Array): [number, $.GoError] {
|
|
58
|
+
return [0, null]
|
|
59
|
+
},
|
|
60
|
+
async Stat(): Promise<[FileInfo, $.GoError]> {
|
|
61
|
+
expect(closed).toBe(false)
|
|
62
|
+
return [info, null]
|
|
63
|
+
},
|
|
64
|
+
} as unknown as File
|
|
65
|
+
|
|
66
|
+
const [got, err] = await Stat(
|
|
67
|
+
{
|
|
68
|
+
async Open(_name: string): Promise<[File, $.GoError]> {
|
|
69
|
+
return [file, null]
|
|
70
|
+
},
|
|
71
|
+
} as any,
|
|
72
|
+
'file.txt',
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
expect(err).toBeNull()
|
|
76
|
+
expect(got).toBe(info)
|
|
77
|
+
expect(closed).toBe(true)
|
|
78
|
+
})
|
|
79
|
+
})
|
package/gs/io/fs/stat.ts
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
-
import { FS, FileInfo } from './fs.js'
|
|
2
|
+
import { FS, File, FileInfo } from './fs.js'
|
|
3
|
+
|
|
4
|
+
type maybePromise<T> = T | Promise<T>
|
|
3
5
|
|
|
4
6
|
export type StatFS =
|
|
5
7
|
| null
|
|
6
8
|
| ({
|
|
7
9
|
// Stat returns a FileInfo describing the file.
|
|
8
10
|
// If there is an error, it should be of type *PathError.
|
|
9
|
-
Stat(name: string): [FileInfo, $.GoError]
|
|
11
|
+
Stat(name: string): maybePromise<[FileInfo, $.GoError]>
|
|
10
12
|
} & FS)
|
|
11
13
|
|
|
14
|
+
type asyncFS = null | {
|
|
15
|
+
Open(name: string): maybePromise<[File, $.GoError]>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type asyncFile = null | {
|
|
19
|
+
Close(): maybePromise<$.GoError>
|
|
20
|
+
Stat(): maybePromise<[FileInfo, $.GoError]>
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
$.registerInterfaceType(
|
|
13
24
|
'StatFS',
|
|
14
25
|
null, // Zero value for interface is null
|
|
@@ -42,21 +53,24 @@ $.registerInterfaceType(
|
|
|
42
53
|
//
|
|
43
54
|
// If fs implements [StatFS], Stat calls fs.Stat.
|
|
44
55
|
// Otherwise, Stat opens the [File] to stat it.
|
|
45
|
-
export function Stat(
|
|
46
|
-
|
|
56
|
+
export async function Stat(
|
|
57
|
+
fsys: FS,
|
|
58
|
+
name: string,
|
|
59
|
+
): Promise<[FileInfo, $.GoError]> {
|
|
47
60
|
{
|
|
48
61
|
let { value: fsysTyped, ok: ok } = $.typeAssert<StatFS>(fsys, 'StatFS')
|
|
49
62
|
if (ok) {
|
|
50
|
-
return fsysTyped!.Stat(name)
|
|
63
|
+
return await fsysTyped!.Stat(name)
|
|
51
64
|
}
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
let [file, err] = fsys!.Open(name)
|
|
67
|
+
let [file, err] = await (fsys as asyncFS)!.Open(name)
|
|
55
68
|
if (err != null) {
|
|
56
69
|
return [null, err]
|
|
57
70
|
}
|
|
58
|
-
|
|
59
|
-
file!.
|
|
60
|
-
}
|
|
61
|
-
|
|
71
|
+
try {
|
|
72
|
+
return await (file as asyncFile)!.Stat()
|
|
73
|
+
} finally {
|
|
74
|
+
await (file as asyncFile)!.Close()
|
|
75
|
+
}
|
|
62
76
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
3
|
+
|
|
4
|
+
import { File, FileInfo, Sub, Stat } from './index.js'
|
|
5
|
+
|
|
6
|
+
function fileInfo(name: string, isDir = false): FileInfo {
|
|
7
|
+
return {
|
|
8
|
+
IsDir(): boolean {
|
|
9
|
+
return isDir
|
|
10
|
+
},
|
|
11
|
+
ModTime(): any {
|
|
12
|
+
return null
|
|
13
|
+
},
|
|
14
|
+
Mode(): number {
|
|
15
|
+
return isDir ? 0o040000 : 0
|
|
16
|
+
},
|
|
17
|
+
Name(): string {
|
|
18
|
+
return name
|
|
19
|
+
},
|
|
20
|
+
Size(): number {
|
|
21
|
+
return 0
|
|
22
|
+
},
|
|
23
|
+
Sys(): null {
|
|
24
|
+
return null
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('io/fs Sub override', () => {
|
|
30
|
+
it('preserves the backing filesystem and directory in fallback subFS', async () => {
|
|
31
|
+
const opened: string[] = []
|
|
32
|
+
const dirInfo = fileInfo('pkg', true)
|
|
33
|
+
const file: File = {
|
|
34
|
+
async Close(): Promise<$.GoError> {
|
|
35
|
+
return null
|
|
36
|
+
},
|
|
37
|
+
Read(_p0: Uint8Array): [number, $.GoError] {
|
|
38
|
+
return [0, null]
|
|
39
|
+
},
|
|
40
|
+
async Stat(): Promise<[FileInfo, $.GoError]> {
|
|
41
|
+
return [dirInfo, null]
|
|
42
|
+
},
|
|
43
|
+
} as unknown as File
|
|
44
|
+
|
|
45
|
+
const [sub, subErr] = await Sub(
|
|
46
|
+
{
|
|
47
|
+
async Open(name: string): Promise<[File, $.GoError]> {
|
|
48
|
+
opened.push(name)
|
|
49
|
+
return [file, null]
|
|
50
|
+
},
|
|
51
|
+
} as any,
|
|
52
|
+
'pkg',
|
|
53
|
+
)
|
|
54
|
+
expect(subErr).toBeNull()
|
|
55
|
+
|
|
56
|
+
const [got, statErr] = await Stat(sub, '.')
|
|
57
|
+
expect(statErr).toBeNull()
|
|
58
|
+
expect(got).toBe(dirInfo)
|
|
59
|
+
expect(opened).toEqual(['pkg'])
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('joins nested fallback subFS directories', async () => {
|
|
63
|
+
const opened: string[] = []
|
|
64
|
+
const fileInfoValue = fileInfo('index.js')
|
|
65
|
+
const file: File = {
|
|
66
|
+
async Close(): Promise<$.GoError> {
|
|
67
|
+
return null
|
|
68
|
+
},
|
|
69
|
+
Read(_p0: Uint8Array): [number, $.GoError] {
|
|
70
|
+
return [0, null]
|
|
71
|
+
},
|
|
72
|
+
async Stat(): Promise<[FileInfo, $.GoError]> {
|
|
73
|
+
return [fileInfoValue, null]
|
|
74
|
+
},
|
|
75
|
+
} as unknown as File
|
|
76
|
+
|
|
77
|
+
const backingFS = {
|
|
78
|
+
async Open(name: string): Promise<[File, $.GoError]> {
|
|
79
|
+
opened.push(name)
|
|
80
|
+
return [file, null]
|
|
81
|
+
},
|
|
82
|
+
} as any
|
|
83
|
+
const [pkgFS, pkgErr] = await Sub(backingFS, '@scope')
|
|
84
|
+
expect(pkgErr).toBeNull()
|
|
85
|
+
const [clientFS, clientErr] = await Sub(pkgFS, 'client')
|
|
86
|
+
expect(clientErr).toBeNull()
|
|
87
|
+
|
|
88
|
+
const [got, statErr] = await Stat(clientFS, 'index.js')
|
|
89
|
+
expect(statErr).toBeNull()
|
|
90
|
+
expect(got).toBe(fileInfoValue)
|
|
91
|
+
expect(opened).toEqual(['@scope/client/index.js'])
|
|
92
|
+
})
|
|
93
|
+
})
|