goscript 0.2.5 → 0.2.7

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 (264) hide show
  1. package/cmd/goscript/cmd-compile.go +7 -0
  2. package/cmd/goscript/cmd_compile_test.go +83 -0
  3. package/compiler/compile-request.go +3 -0
  4. package/compiler/compiler-cache.go +828 -0
  5. package/compiler/compiler-cache_test.go +705 -0
  6. package/compiler/config.go +2 -0
  7. package/compiler/index.test.ts +26 -1
  8. package/compiler/index.ts +5 -0
  9. package/compiler/lowered-program.go +31 -20
  10. package/compiler/lowering.go +349 -93
  11. package/compiler/lowering_bench_test.go +1 -0
  12. package/compiler/override-facts.go +309 -8
  13. package/compiler/override-parity-verifier.go +45 -1
  14. package/compiler/override-parity-verifier_test.go +100 -0
  15. package/compiler/override-registry_test.go +1 -0
  16. package/compiler/package-graph.go +40 -12
  17. package/compiler/package-graph_test.go +29 -0
  18. package/compiler/runtime-contract.go +8 -0
  19. package/compiler/service.go +98 -11
  20. package/compiler/skeleton_test.go +110 -14
  21. package/compiler/typescript-emitter.go +120 -23
  22. package/dist/compiler/index.d.ts +2 -0
  23. package/dist/compiler/index.js +3 -0
  24. package/dist/compiler/index.js.map +1 -1
  25. package/dist/gs/builtin/builtin.d.ts +24 -33
  26. package/dist/gs/builtin/builtin.js +54 -61
  27. package/dist/gs/builtin/builtin.js.map +1 -1
  28. package/dist/gs/builtin/hostio.d.ts +1 -0
  29. package/dist/gs/builtin/hostio.js +1 -1
  30. package/dist/gs/builtin/hostio.js.map +1 -1
  31. package/dist/gs/builtin/index.d.ts +1 -0
  32. package/dist/gs/builtin/index.js +1 -0
  33. package/dist/gs/builtin/index.js.map +1 -1
  34. package/dist/gs/builtin/panic.d.ts +18 -0
  35. package/dist/gs/builtin/panic.js +98 -0
  36. package/dist/gs/builtin/panic.js.map +1 -0
  37. package/dist/gs/builtin/slice.d.ts +10 -0
  38. package/dist/gs/builtin/slice.js +110 -53
  39. package/dist/gs/builtin/slice.js.map +1 -1
  40. package/dist/gs/builtin/type.js +15 -3
  41. package/dist/gs/builtin/type.js.map +1 -1
  42. package/dist/gs/builtin/varRef.d.ts +1 -1
  43. package/dist/gs/builtin/varRef.js +3 -2
  44. package/dist/gs/builtin/varRef.js.map +1 -1
  45. package/dist/gs/bytes/bytes.gs.js +51 -38
  46. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  47. package/dist/gs/bytes/reader.gs.d.ts +1 -1
  48. package/dist/gs/bytes/reader.gs.js +6 -7
  49. package/dist/gs/bytes/reader.gs.js.map +1 -1
  50. package/dist/gs/cmp/index.d.ts +1 -1
  51. package/dist/gs/cmp/index.js +43 -10
  52. package/dist/gs/cmp/index.js.map +1 -1
  53. package/dist/gs/context/context.d.ts +2 -2
  54. package/dist/gs/context/context.js +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/embed/index.js +1 -1
  57. package/dist/gs/embed/index.js.map +1 -1
  58. package/dist/gs/encoding/binary/index.js +201 -8
  59. package/dist/gs/encoding/binary/index.js.map +1 -1
  60. package/dist/gs/encoding/json/index.d.ts +5 -0
  61. package/dist/gs/encoding/json/index.js +388 -25
  62. package/dist/gs/encoding/json/index.js.map +1 -1
  63. package/dist/gs/errors/errors.js +17 -24
  64. package/dist/gs/errors/errors.js.map +1 -1
  65. package/dist/gs/fmt/fmt.js +129 -35
  66. package/dist/gs/fmt/fmt.js.map +1 -1
  67. package/dist/gs/golang.org/x/crypto/cryptobyte/index.js +1 -1
  68. package/dist/gs/golang.org/x/crypto/cryptobyte/index.js.map +1 -1
  69. package/dist/gs/internal/bytealg/index.js +43 -8
  70. package/dist/gs/internal/bytealg/index.js.map +1 -1
  71. package/dist/gs/internal/byteorder/index.d.ts +2 -2
  72. package/dist/gs/internal/byteorder/index.js +2 -2
  73. package/dist/gs/internal/byteorder/index.js.map +1 -1
  74. package/dist/gs/io/fs/format.js +2 -2
  75. package/dist/gs/io/fs/format.js.map +1 -1
  76. package/dist/gs/io/fs/fs.d.ts +1 -1
  77. package/dist/gs/io/fs/fs.js +1 -1
  78. package/dist/gs/io/fs/fs.js.map +1 -1
  79. package/dist/gs/io/io.d.ts +21 -21
  80. package/dist/gs/io/io.js +49 -50
  81. package/dist/gs/io/io.js.map +1 -1
  82. package/dist/gs/math/bits/index.js +26 -8
  83. package/dist/gs/math/bits/index.js.map +1 -1
  84. package/dist/gs/math/copysign.gs.js +10 -17
  85. package/dist/gs/math/copysign.gs.js.map +1 -1
  86. package/dist/gs/math/pow.gs.js +5 -0
  87. package/dist/gs/math/pow.gs.js.map +1 -1
  88. package/dist/gs/math/signbit.gs.js +6 -2
  89. package/dist/gs/math/signbit.gs.js.map +1 -1
  90. package/dist/gs/mime/index.js +1 -0
  91. package/dist/gs/mime/index.js.map +1 -1
  92. package/dist/gs/net/http/index.d.ts +6 -6
  93. package/dist/gs/net/http/index.js +507 -43
  94. package/dist/gs/net/http/index.js.map +1 -1
  95. package/dist/gs/os/stat.gs.d.ts +2 -2
  96. package/dist/gs/os/types.gs.d.ts +1 -1
  97. package/dist/gs/os/types.gs.js +1 -1
  98. package/dist/gs/os/types.gs.js.map +1 -1
  99. package/dist/gs/os/types_js.gs.d.ts +1 -1
  100. package/dist/gs/os/types_js.gs.js +7 -7
  101. package/dist/gs/os/types_js.gs.js.map +1 -1
  102. package/dist/gs/os/types_unix.gs.d.ts +1 -1
  103. package/dist/gs/os/types_unix.gs.js +1 -1
  104. package/dist/gs/os/types_unix.gs.js.map +1 -1
  105. package/dist/gs/os/zero_copy_posix.gs.d.ts +1 -1
  106. package/dist/gs/os/zero_copy_posix.gs.js +1 -1
  107. package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
  108. package/dist/gs/path/filepath/match.js +8 -4
  109. package/dist/gs/path/filepath/match.js.map +1 -1
  110. package/dist/gs/path/filepath/path.js +216 -42
  111. package/dist/gs/path/filepath/path.js.map +1 -1
  112. package/dist/gs/path/match.js +6 -3
  113. package/dist/gs/path/match.js.map +1 -1
  114. package/dist/gs/reflect/type.d.ts +5 -4
  115. package/dist/gs/reflect/type.js +29 -11
  116. package/dist/gs/reflect/type.js.map +1 -1
  117. package/dist/gs/slices/slices.js +11 -11
  118. package/dist/gs/slices/slices.js.map +1 -1
  119. package/dist/gs/strconv/atof.gs.js +156 -43
  120. package/dist/gs/strconv/atof.gs.js.map +1 -1
  121. package/dist/gs/strconv/atoi.gs.d.ts +3 -2
  122. package/dist/gs/strconv/atoi.gs.js +86 -67
  123. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  124. package/dist/gs/strconv/ftoa.gs.js +73 -3
  125. package/dist/gs/strconv/ftoa.gs.js.map +1 -1
  126. package/dist/gs/strconv/itoa.gs.d.ts +4 -4
  127. package/dist/gs/strconv/itoa.gs.js +5 -4
  128. package/dist/gs/strconv/itoa.gs.js.map +1 -1
  129. package/dist/gs/strconv/quote.gs.d.ts +1 -1
  130. package/dist/gs/strconv/quote.gs.js +311 -103
  131. package/dist/gs/strconv/quote.gs.js.map +1 -1
  132. package/dist/gs/strings/reader.d.ts +1 -1
  133. package/dist/gs/strings/reader.js +8 -8
  134. package/dist/gs/strings/reader.js.map +1 -1
  135. package/dist/gs/strings/strings.js +87 -61
  136. package/dist/gs/strings/strings.js.map +1 -1
  137. package/dist/gs/sync/atomic/doc_64.gs.d.ts +14 -14
  138. package/dist/gs/sync/atomic/doc_64.gs.js +10 -10
  139. package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
  140. package/dist/gs/sync/atomic/type.gs.d.ts +22 -22
  141. package/dist/gs/sync/atomic/type.gs.js +4 -4
  142. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  143. package/dist/gs/sync/sync.js +50 -12
  144. package/dist/gs/sync/sync.js.map +1 -1
  145. package/dist/gs/syscall/fs.d.ts +6 -6
  146. package/dist/gs/syscall/fs.js +1 -1
  147. package/dist/gs/syscall/fs.js.map +1 -1
  148. package/dist/gs/time/time.d.ts +18 -18
  149. package/dist/gs/time/time.js +58 -55
  150. package/dist/gs/time/time.js.map +1 -1
  151. package/dist/gs/unicode/tables.d.ts +11 -0
  152. package/dist/gs/unicode/tables.js +635 -0
  153. package/dist/gs/unicode/tables.js.map +1 -0
  154. package/dist/gs/unicode/unicode.d.ts +58 -38
  155. package/dist/gs/unicode/unicode.js +362 -278
  156. package/dist/gs/unicode/unicode.js.map +1 -1
  157. package/go.sum +13 -0
  158. package/gs/builtin/builtin.ts +83 -93
  159. package/gs/builtin/hostio.ts +1 -1
  160. package/gs/builtin/index.ts +1 -0
  161. package/gs/builtin/panic.test.ts +189 -0
  162. package/gs/builtin/panic.ts +107 -0
  163. package/gs/builtin/runtime-contract.test.ts +5 -5
  164. package/gs/builtin/slice.test.ts +23 -0
  165. package/gs/builtin/slice.ts +133 -95
  166. package/gs/builtin/type.ts +16 -3
  167. package/gs/builtin/varRef.ts +4 -2
  168. package/gs/builtin/wide-int.test.ts +41 -0
  169. package/gs/bytes/bytes.gs.ts +54 -41
  170. package/gs/bytes/bytes.test.ts +18 -1
  171. package/gs/bytes/reader.gs.ts +7 -8
  172. package/gs/cmp/index.test.ts +55 -0
  173. package/gs/cmp/index.ts +45 -9
  174. package/gs/context/context.ts +3 -3
  175. package/gs/embed/index.ts +2 -2
  176. package/gs/encoding/binary/index.test.ts +104 -0
  177. package/gs/encoding/binary/index.ts +259 -11
  178. package/gs/encoding/json/index.test.ts +107 -0
  179. package/gs/encoding/json/index.ts +400 -29
  180. package/gs/errors/errors.test.ts +44 -1
  181. package/gs/errors/errors.ts +15 -31
  182. package/gs/fmt/fmt.test.ts +70 -2
  183. package/gs/fmt/fmt.ts +128 -34
  184. package/gs/golang.org/x/crypto/cryptobyte/index.ts +1 -1
  185. package/gs/internal/bytealg/index.test.ts +26 -1
  186. package/gs/internal/bytealg/index.ts +44 -8
  187. package/gs/internal/byteorder/index.ts +6 -4
  188. package/gs/io/fs/format.ts +2 -2
  189. package/gs/io/fs/fs.ts +2 -2
  190. package/gs/io/fs/stat.test.ts +2 -2
  191. package/gs/io/fs/sub.test.ts +2 -2
  192. package/gs/io/fs/walk.test.ts +2 -2
  193. package/gs/io/io.test.ts +47 -5
  194. package/gs/io/io.ts +73 -73
  195. package/gs/io/limit.test.ts +103 -0
  196. package/gs/math/bits/index.test.ts +128 -0
  197. package/gs/math/bits/index.ts +26 -8
  198. package/gs/math/copysign.gs.test.ts +3 -1
  199. package/gs/math/copysign.gs.ts +10 -22
  200. package/gs/math/pow.gs.test.ts +4 -5
  201. package/gs/math/pow.gs.ts +5 -0
  202. package/gs/math/signbit.gs.test.ts +2 -1
  203. package/gs/math/signbit.gs.ts +6 -3
  204. package/gs/mime/index.ts +1 -0
  205. package/gs/net/http/index.test.ts +683 -2
  206. package/gs/net/http/index.ts +598 -57
  207. package/gs/net/http/meta.json +3 -0
  208. package/gs/os/stat.gs.ts +2 -2
  209. package/gs/os/types.gs.ts +2 -2
  210. package/gs/os/types_js.gs.ts +9 -9
  211. package/gs/os/types_unix.gs.ts +2 -2
  212. package/gs/os/zero_copy_posix.gs.ts +2 -2
  213. package/gs/path/filepath/match.test.ts +16 -0
  214. package/gs/path/filepath/match.ts +8 -4
  215. package/gs/path/filepath/path.test.ts +91 -9
  216. package/gs/path/filepath/path.ts +223 -49
  217. package/gs/path/match.test.ts +32 -0
  218. package/gs/path/match.ts +6 -3
  219. package/gs/reflect/deepequal.test.ts +1 -1
  220. package/gs/reflect/field.test.ts +1 -1
  221. package/gs/reflect/function-types.test.ts +6 -6
  222. package/gs/reflect/sliceat.test.ts +13 -13
  223. package/gs/reflect/structof.test.ts +4 -4
  224. package/gs/reflect/type.ts +34 -14
  225. package/gs/reflect/typefor.test.ts +5 -5
  226. package/gs/runtime/pprof/index.test.ts +20 -0
  227. package/gs/runtime/trace/index.test.ts +3 -0
  228. package/gs/slices/slices.test.ts +31 -0
  229. package/gs/slices/slices.ts +11 -11
  230. package/gs/strconv/append.test.ts +99 -0
  231. package/gs/strconv/atof.gs.ts +156 -42
  232. package/gs/strconv/atof.test.ts +45 -0
  233. package/gs/strconv/atoi.gs.ts +87 -69
  234. package/gs/strconv/atoi.test.ts +49 -0
  235. package/gs/strconv/ftoa.gs.ts +85 -10
  236. package/gs/strconv/ftoa.test.ts +43 -0
  237. package/gs/strconv/itoa.gs.ts +10 -9
  238. package/gs/strconv/quote.gs.ts +335 -108
  239. package/gs/strconv/quote.test.ts +111 -0
  240. package/gs/strings/reader.test.ts +10 -10
  241. package/gs/strings/reader.ts +9 -9
  242. package/gs/strings/strings.test.ts +18 -5
  243. package/gs/strings/strings.ts +81 -68
  244. package/gs/sync/atomic/doc_64.gs.ts +24 -24
  245. package/gs/sync/atomic/doc_64.test.ts +5 -5
  246. package/gs/sync/atomic/type.gs.ts +28 -28
  247. package/gs/sync/sync.test.ts +109 -1
  248. package/gs/sync/sync.ts +46 -12
  249. package/gs/syscall/fs.ts +8 -8
  250. package/gs/syscall/net.test.ts +1 -1
  251. package/gs/time/parse.test.ts +45 -0
  252. package/gs/time/time.test.ts +46 -23
  253. package/gs/time/time.ts +69 -66
  254. package/gs/unicode/gen.go +198 -0
  255. package/gs/unicode/tables.ts +646 -0
  256. package/gs/unicode/unicode.test.ts +69 -0
  257. package/gs/unicode/unicode.ts +396 -312
  258. package/package.json +2 -2
  259. package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +0 -20
  260. package/dist/gs/github.com/aperturerobotics/util/conc/index.js +0 -134
  261. package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +0 -1
  262. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +0 -30
  263. package/gs/github.com/aperturerobotics/util/conc/index.ts +0 -172
  264. package/gs/github.com/aperturerobotics/util/conc/meta.json +0 -9
@@ -0,0 +1,828 @@
1
+ package compiler
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "crypto/sha256"
7
+ "encoding/hex"
8
+ "errors"
9
+ "go/parser"
10
+ "go/token"
11
+ "io"
12
+ "os"
13
+ "path"
14
+ "path/filepath"
15
+ "runtime"
16
+ "runtime/debug"
17
+ "slices"
18
+ "strconv"
19
+ "strings"
20
+
21
+ jsoniter "github.com/aperturerobotics/json-iterator-lite"
22
+ )
23
+
24
+ const compilerCacheSchema = "goscript-package-artifact-v1"
25
+
26
+ type compilerCacheEntryKind string
27
+
28
+ const (
29
+ compilerCacheEntryGenerated compilerCacheEntryKind = "generated"
30
+ compilerCacheEntryCopied compilerCacheEntryKind = "copied"
31
+ )
32
+
33
+ // CompilerCacheOwner owns persistent compiler artifact lookup, replay, and store.
34
+ type CompilerCacheOwner struct{}
35
+
36
+ type compilerCacheEntry struct {
37
+ key string
38
+ kind compilerCacheEntryKind
39
+ packagePath string
40
+ }
41
+
42
+ type compilerCacheManifest struct {
43
+ schema string
44
+ key string
45
+ kind compilerCacheEntryKind
46
+ packagePath string
47
+ compiledPackages []string
48
+ copiedPackages []string
49
+ files []compilerCacheManifestFile
50
+ }
51
+
52
+ type compilerCacheManifestFile struct {
53
+ path string
54
+ kind string
55
+ sha256 string
56
+ size uint64
57
+ blob string
58
+ }
59
+
60
+ // NewCompilerCacheOwner creates the compiler cache owner.
61
+ func NewCompilerCacheOwner() *CompilerCacheOwner {
62
+ return &CompilerCacheOwner{}
63
+ }
64
+
65
+ func (o *CompilerCacheOwner) Enabled(req *CompileRequest) bool {
66
+ return req != nil && strings.TrimSpace(req.CacheRoot) != ""
67
+ }
68
+
69
+ func (o *CompilerCacheOwner) Entries(
70
+ req *CompileRequest,
71
+ graph *PackageGraph,
72
+ overridePlan *overrideCopyPlan,
73
+ ) []compilerCacheEntry {
74
+ if !o.Enabled(req) || graph == nil {
75
+ return nil
76
+ }
77
+
78
+ keyOwner := newCompilerCacheKeyOwner(req, graph)
79
+ entries := make([]compilerCacheEntry, 0, len(graph.Nodes))
80
+ for _, node := range graph.Nodes {
81
+ if node.OverrideCandidate {
82
+ continue
83
+ }
84
+ entries = append(entries, compilerCacheEntry{
85
+ key: keyOwner.generatedKey(node),
86
+ kind: compilerCacheEntryGenerated,
87
+ packagePath: node.PkgPath,
88
+ })
89
+ }
90
+ if overridePlan != nil {
91
+ for _, pkg := range overridePlan.packages {
92
+ entries = append(entries, compilerCacheEntry{
93
+ key: keyOwner.copiedKey(pkg),
94
+ kind: compilerCacheEntryCopied,
95
+ packagePath: pkg.path,
96
+ })
97
+ }
98
+ }
99
+ return entries
100
+ }
101
+
102
+ func (o *CompilerCacheOwner) Replay(
103
+ ctx context.Context,
104
+ req *CompileRequest,
105
+ entries []compilerCacheEntry,
106
+ ) (*CompilationResult, bool) {
107
+ if !o.Enabled(req) || len(entries) == 0 {
108
+ return nil, false
109
+ }
110
+ result := &CompilationResult{}
111
+ for _, entry := range entries {
112
+ if err := ctx.Err(); err != nil {
113
+ return nil, false
114
+ }
115
+ manifest, ok := o.readManifest(req, entry)
116
+ if !ok || manifest.kind != entry.kind || manifest.packagePath != entry.packagePath {
117
+ return nil, false
118
+ }
119
+ if !o.replayManifest(req, manifest) {
120
+ return nil, false
121
+ }
122
+ result.CompiledPackages = append(result.CompiledPackages, manifest.compiledPackages...)
123
+ result.CopiedPackages = append(result.CopiedPackages, manifest.copiedPackages...)
124
+ }
125
+ return result, true
126
+ }
127
+
128
+ func (o *CompilerCacheOwner) StoreGenerated(
129
+ req *CompileRequest,
130
+ entries []compilerCacheEntry,
131
+ program *LoweredProgram,
132
+ files map[string]string,
133
+ ) {
134
+ if !o.Enabled(req) || program == nil || len(files) == 0 {
135
+ return
136
+ }
137
+ entriesByPackage := entriesByKindAndPackage(entries, compilerCacheEntryGenerated)
138
+ for _, pkg := range program.packages {
139
+ entry, ok := entriesByPackage[pkg.pkgPath]
140
+ if !ok {
141
+ continue
142
+ }
143
+ prefix := "@goscript/" + pkg.pkgPath + "/"
144
+ manifest := compilerCacheManifest{
145
+ schema: compilerCacheSchema,
146
+ key: entry.key,
147
+ kind: compilerCacheEntryGenerated,
148
+ packagePath: pkg.pkgPath,
149
+ compiledPackages: []string{pkg.pkgPath},
150
+ }
151
+ for filePath, contents := range files {
152
+ if !strings.HasPrefix(filePath, prefix) {
153
+ continue
154
+ }
155
+ manifest.files = append(manifest.files, compilerCacheManifestFile{
156
+ path: filePath,
157
+ kind: generatedArtifactKind(filePath),
158
+ sha256: sha256Hex([]byte(contents)),
159
+ size: uint64(len(contents)),
160
+ blob: o.storeBlob(req, []byte(contents)),
161
+ })
162
+ }
163
+ slices.SortFunc(manifest.files, func(a, b compilerCacheManifestFile) int {
164
+ return strings.Compare(a.path, b.path)
165
+ })
166
+ o.storeManifest(req, manifest)
167
+ }
168
+ }
169
+
170
+ func (o *CompilerCacheOwner) StoreCopied(
171
+ req *CompileRequest,
172
+ entries []compilerCacheEntry,
173
+ plan *overrideCopyPlan,
174
+ ) {
175
+ if !o.Enabled(req) || plan == nil {
176
+ return
177
+ }
178
+ entriesByPackage := entriesByKindAndPackage(entries, compilerCacheEntryCopied)
179
+ for _, pkg := range plan.packages {
180
+ entry, ok := entriesByPackage[pkg.path]
181
+ if !ok {
182
+ continue
183
+ }
184
+ manifest := compilerCacheManifest{
185
+ schema: compilerCacheSchema,
186
+ key: entry.key,
187
+ kind: compilerCacheEntryCopied,
188
+ packagePath: pkg.path,
189
+ copiedPackages: []string{pkg.path},
190
+ }
191
+ for _, file := range pkg.files {
192
+ artifactPath := "@goscript/" + filepath.ToSlash(file.path)
193
+ manifest.files = append(manifest.files, compilerCacheManifestFile{
194
+ path: artifactPath,
195
+ kind: "override",
196
+ size: uint64(len(file.data)),
197
+ blob: o.storeBlob(req, file.data),
198
+ sha256: sha256Hex(file.data),
199
+ })
200
+ }
201
+ slices.SortFunc(manifest.files, func(a, b compilerCacheManifestFile) int {
202
+ return strings.Compare(a.path, b.path)
203
+ })
204
+ o.storeManifest(req, manifest)
205
+ }
206
+ }
207
+
208
+ func (o *CompilerCacheOwner) readManifest(req *CompileRequest, entry compilerCacheEntry) (compilerCacheManifest, bool) {
209
+ path := filepath.Join(o.entryDir(req, entry.key), "manifest.json")
210
+ data, err := os.ReadFile(path)
211
+ if err != nil {
212
+ return compilerCacheManifest{}, false
213
+ }
214
+ if _, err := os.Stat(filepath.Join(o.entryDir(req, entry.key), "complete")); err != nil {
215
+ return compilerCacheManifest{}, false
216
+ }
217
+ manifest := parseCompilerCacheManifest(data)
218
+ if manifest.schema != compilerCacheSchema || manifest.key != entry.key {
219
+ return compilerCacheManifest{}, false
220
+ }
221
+ if len(manifest.files) == 0 {
222
+ return compilerCacheManifest{}, false
223
+ }
224
+ return manifest, true
225
+ }
226
+
227
+ func (o *CompilerCacheOwner) replayManifest(req *CompileRequest, manifest compilerCacheManifest) bool {
228
+ for _, file := range manifest.files {
229
+ if !safeOutputArtifactPath(file.path) || !safeCacheBlobPath(file.blob) {
230
+ return false
231
+ }
232
+ blobPath := filepath.Join(o.schemaRoot(req), filepath.FromSlash(file.blob))
233
+ data, err := os.ReadFile(blobPath)
234
+ if err != nil || uint64(len(data)) != file.size || sha256Hex(data) != file.sha256 {
235
+ return false
236
+ }
237
+ dest := filepath.Join(req.OutputPath, filepath.FromSlash(file.path))
238
+ if err := writeFileAtomic(dest, data, 0o644); err != nil {
239
+ return false
240
+ }
241
+ }
242
+ return true
243
+ }
244
+
245
+ func (o *CompilerCacheOwner) storeManifest(req *CompileRequest, manifest compilerCacheManifest) {
246
+ if len(manifest.files) == 0 {
247
+ return
248
+ }
249
+ tmpRoot := filepath.Join(o.schemaRoot(req), "tmp")
250
+ if err := os.MkdirAll(tmpRoot, 0o755); err != nil {
251
+ return
252
+ }
253
+ tmpDir, err := os.MkdirTemp(tmpRoot, "entry-")
254
+ if err != nil {
255
+ return
256
+ }
257
+ defer os.RemoveAll(tmpDir)
258
+
259
+ data := formatCompilerCacheManifest(manifest)
260
+ if err := os.WriteFile(filepath.Join(tmpDir, "manifest.json"), data, 0o644); err != nil {
261
+ return
262
+ }
263
+ if err := os.WriteFile(filepath.Join(tmpDir, "complete"), []byte("complete\n"), 0o644); err != nil {
264
+ return
265
+ }
266
+ entryDir := o.entryDir(req, manifest.key)
267
+ if err := os.MkdirAll(filepath.Dir(entryDir), 0o755); err != nil {
268
+ return
269
+ }
270
+ if err := os.Rename(tmpDir, entryDir); err != nil {
271
+ if _, statErr := os.Stat(filepath.Join(entryDir, "complete")); statErr == nil {
272
+ return
273
+ }
274
+ }
275
+ }
276
+
277
+ func (o *CompilerCacheOwner) storeBlob(req *CompileRequest, data []byte) string {
278
+ digest := sha256Hex(data)
279
+ rel := path.Join("blobs", "sha256", digest[:2], digest)
280
+ blobPath := filepath.Join(o.schemaRoot(req), filepath.FromSlash(rel))
281
+ if _, err := os.Stat(blobPath); err == nil {
282
+ return rel
283
+ }
284
+ if err := os.MkdirAll(filepath.Dir(blobPath), 0o755); err != nil {
285
+ return rel
286
+ }
287
+ tmp, err := os.CreateTemp(filepath.Dir(blobPath), "blob-")
288
+ if err != nil {
289
+ return rel
290
+ }
291
+ tmpName := tmp.Name()
292
+ written, writeErr := tmp.Write(data)
293
+ closeErr := tmp.Close()
294
+ if writeErr != nil || closeErr != nil || written != len(data) {
295
+ os.Remove(tmpName)
296
+ return rel
297
+ }
298
+ if err := os.Rename(tmpName, blobPath); err != nil {
299
+ os.Remove(tmpName)
300
+ }
301
+ return rel
302
+ }
303
+
304
+ func (o *CompilerCacheOwner) schemaRoot(req *CompileRequest) string {
305
+ return filepath.Join(req.CacheRoot, compilerCacheSchema)
306
+ }
307
+
308
+ func (o *CompilerCacheOwner) entryDir(req *CompileRequest, key string) string {
309
+ return filepath.Join(o.schemaRoot(req), "entries", key[:2], key)
310
+ }
311
+
312
+ func entriesByKindAndPackage(entries []compilerCacheEntry, kind compilerCacheEntryKind) map[string]compilerCacheEntry {
313
+ out := make(map[string]compilerCacheEntry)
314
+ for _, entry := range entries {
315
+ if entry.kind == kind {
316
+ out[entry.packagePath] = entry
317
+ }
318
+ }
319
+ return out
320
+ }
321
+
322
+ type compilerCacheKeyOwner struct {
323
+ req *CompileRequest
324
+ graph *PackageGraph
325
+ graphDigests map[string]string
326
+ }
327
+
328
+ func newCompilerCacheKeyOwner(req *CompileRequest, graph *PackageGraph) *compilerCacheKeyOwner {
329
+ return &compilerCacheKeyOwner{
330
+ req: req,
331
+ graph: graph,
332
+ graphDigests: make(map[string]string),
333
+ }
334
+ }
335
+
336
+ func (o *compilerCacheKeyOwner) generatedKey(node *PackageGraphNode) string {
337
+ var b strings.Builder
338
+ writeKeyField(&b, "schema", compilerCacheSchema)
339
+ writeCompilerIdentity(&b)
340
+ writeRequestIdentity(&b, o.req)
341
+ writeKeyField(&b, "kind", string(compilerCacheEntryGenerated))
342
+ writeKeyField(&b, "package-digest", o.nodeDigest(node.PkgPath))
343
+ return sha256String(b.String())
344
+ }
345
+
346
+ func (o *compilerCacheKeyOwner) copiedKey(pkg overrideCopyPackage) string {
347
+ var b strings.Builder
348
+ writeKeyField(&b, "schema", compilerCacheSchema)
349
+ writeCompilerIdentity(&b)
350
+ writeRequestIdentity(&b, o.req)
351
+ writeKeyField(&b, "kind", string(compilerCacheEntryCopied))
352
+ writeKeyField(&b, "package", pkg.path)
353
+ for _, file := range pkg.files {
354
+ writeKeyField(&b, "override-file", file.path)
355
+ writeKeyField(&b, "override-file-sha256", sha256Hex(file.data))
356
+ }
357
+ return sha256String(b.String())
358
+ }
359
+
360
+ func (o *compilerCacheKeyOwner) nodeDigest(pkgPath string) string {
361
+ if digest := o.graphDigests[pkgPath]; digest != "" {
362
+ return digest
363
+ }
364
+ node := o.graph.NodesByPackagePath[pkgPath]
365
+ if node == nil {
366
+ return ""
367
+ }
368
+ var b strings.Builder
369
+ writeKeyField(&b, "id", node.ID)
370
+ writeKeyField(&b, "path", node.PkgPath)
371
+ writeKeyField(&b, "name", node.Name)
372
+ writeKeyField(&b, "module-path", node.ModulePath)
373
+ writeKeyField(&b, "module-dir", node.ModuleDir)
374
+ writeKeyField(&b, "for-test", node.ForTest)
375
+ writeKeyField(&b, "requested", strconv.FormatBool(node.Requested))
376
+ writeKeyField(&b, "override-candidate", strconv.FormatBool(node.OverrideCandidate))
377
+ for _, file := range node.GoFiles {
378
+ writeKeyField(&b, "go-file", fileIdentity(file))
379
+ }
380
+ for _, file := range node.CompiledGoFiles {
381
+ writeKeyField(&b, "compiled-file", fileIdentity(file))
382
+ }
383
+ for _, input := range compilerCacheSideInputs(o.req, node) {
384
+ writeKeyField(&b, "side-input", input)
385
+ }
386
+ for _, file := range moduleIdentityFiles(node.ModuleDir) {
387
+ writeKeyField(&b, "module-file", fileIdentity(file))
388
+ }
389
+ for _, importPath := range node.Imports {
390
+ writeKeyField(&b, "import", importPath)
391
+ if o.graph.NodesByPackagePath[importPath] != nil {
392
+ writeKeyField(&b, "import-digest", o.nodeDigest(importPath))
393
+ }
394
+ }
395
+ digest := sha256String(b.String())
396
+ o.graphDigests[pkgPath] = digest
397
+ return digest
398
+ }
399
+
400
+ func writeCompilerIdentity(b *strings.Builder) {
401
+ writeKeyField(b, "go-version", runtime.Version())
402
+ if info, ok := debug.ReadBuildInfo(); ok {
403
+ writeKeyField(b, "module", info.Main.Path+"@"+info.Main.Version)
404
+ for _, dep := range info.Deps {
405
+ writeKeyField(b, "dep", dep.Path+"@"+dep.Version)
406
+ if dep.Replace != nil {
407
+ writeKeyField(b, "dep-replace", dep.Replace.Path+"@"+dep.Replace.Version)
408
+ }
409
+ }
410
+ }
411
+ }
412
+
413
+ func writeRequestIdentity(b *strings.Builder, req *CompileRequest) {
414
+ if req == nil {
415
+ return
416
+ }
417
+ for _, pattern := range req.Patterns {
418
+ writeKeyField(b, "pattern", pattern)
419
+ }
420
+ writeKeyField(b, "dir", cleanAbs(req.Dir))
421
+ if req.ProtobufTypeScriptBinding {
422
+ writeKeyField(b, "protobuf-output", cleanAbs(req.OutputPath))
423
+ }
424
+ for _, flag := range goScriptBuildFlags(req.BuildFlags) {
425
+ writeKeyField(b, "build-flag", flag)
426
+ }
427
+ for _, dir := range req.OverrideDirs {
428
+ writeKeyField(b, "override-dir", cleanAbs(dir))
429
+ }
430
+ for _, path := range req.PackageBlocklist {
431
+ writeKeyField(b, "blocklist", path)
432
+ }
433
+ writeKeyField(b, "dependency-mode", string(req.DependencyMode))
434
+ writeKeyField(b, "runtime-mode", string(req.RuntimeEmissionMode))
435
+ writeKeyField(b, "protobuf-ts-binding", strconv.FormatBool(req.ProtobufTypeScriptBinding))
436
+ writeKeyField(b, "tests", strconv.FormatBool(req.Tests))
437
+ for _, key := range goLoaderEnvKeys() {
438
+ writeKeyField(b, "env-"+key, os.Getenv(key))
439
+ }
440
+ }
441
+
442
+ func writeKeyField(b *strings.Builder, key, value string) {
443
+ b.WriteString(key)
444
+ b.WriteByte('=')
445
+ b.WriteString(strconv.Quote(value))
446
+ b.WriteByte('\n')
447
+ }
448
+
449
+ func goLoaderEnvKeys() []string {
450
+ return []string{
451
+ "CGO_ENABLED",
452
+ "GOFLAGS",
453
+ "GOMODCACHE",
454
+ "GONOPROXY",
455
+ "GONOSUMDB",
456
+ "GOPATH",
457
+ "GOPRIVATE",
458
+ "GOPROXY",
459
+ "GOROOT",
460
+ "GOSUMDB",
461
+ "GOWORK",
462
+ }
463
+ }
464
+
465
+ func compilerCacheSideInputs(req *CompileRequest, node *PackageGraphNode) []string {
466
+ if node == nil {
467
+ return nil
468
+ }
469
+ var inputs []string
470
+ for _, file := range node.CompiledGoFiles {
471
+ inputs = append(inputs, compilerCacheGoEmbedSideInputs(file)...)
472
+ }
473
+ inputs = append(inputs, compilerCacheProtobufSideInputs(req, node)...)
474
+ slices.Sort(inputs)
475
+ return inputs
476
+ }
477
+
478
+ func compilerCacheGoEmbedSideInputs(goFile string) []string {
479
+ syntax, err := parser.ParseFile(token.NewFileSet(), goFile, nil, parser.ParseComments)
480
+ if err != nil {
481
+ return []string{"go:embed-parse|" + cleanAbs(goFile) + "|" + err.Error()}
482
+ }
483
+ patterns := goEmbedPatterns(syntax.Comments...)
484
+ if len(patterns) == 0 {
485
+ return nil
486
+ }
487
+ pkgDir := filepath.Dir(goFile)
488
+ var inputs []string
489
+ for _, pattern := range patterns {
490
+ files, err := compilerCacheGoEmbedFiles(pkgDir, pattern)
491
+ if err != nil {
492
+ inputs = append(inputs, "go:embed-error|"+cleanAbs(goFile)+"|"+pattern+"|"+err.Error())
493
+ continue
494
+ }
495
+ for _, file := range files {
496
+ inputs = append(inputs, "go:embed-file|"+pattern+"|"+fileIdentity(file))
497
+ }
498
+ }
499
+ return inputs
500
+ }
501
+
502
+ func compilerCacheGoEmbedFiles(pkgDir, pattern string) ([]string, error) {
503
+ pattern = strings.Trim(pattern, "`\"")
504
+ all := false
505
+ if strings.HasPrefix(pattern, "all:") {
506
+ all = true
507
+ pattern = strings.TrimPrefix(pattern, "all:")
508
+ }
509
+ cleanPattern := path.Clean(pattern)
510
+ if pattern == "" ||
511
+ path.IsAbs(pattern) ||
512
+ cleanPattern == "." ||
513
+ cleanPattern == ".." ||
514
+ strings.HasPrefix(cleanPattern, "../") {
515
+ return nil, errors.New("unsupported go:embed pattern")
516
+ }
517
+
518
+ paths := []string{filepath.Join(pkgDir, filepath.FromSlash(cleanPattern))}
519
+ if strings.ContainsAny(cleanPattern, "*?[") {
520
+ matches, err := filepath.Glob(filepath.Join(pkgDir, filepath.FromSlash(cleanPattern)))
521
+ if err != nil {
522
+ return nil, err
523
+ }
524
+ if len(matches) == 0 {
525
+ return nil, errors.New("go:embed pattern matched no files")
526
+ }
527
+ paths = matches
528
+ }
529
+
530
+ var files []string
531
+ for _, absPath := range paths {
532
+ collected, err := compilerCacheCollectGoEmbedPath(absPath, all)
533
+ if err != nil {
534
+ return nil, err
535
+ }
536
+ files = append(files, collected...)
537
+ }
538
+ slices.Sort(files)
539
+ return files, nil
540
+ }
541
+
542
+ func compilerCacheCollectGoEmbedPath(absPath string, all bool) ([]string, error) {
543
+ info, err := os.Stat(absPath)
544
+ if err != nil {
545
+ return nil, err
546
+ }
547
+ if !info.IsDir() {
548
+ return []string{absPath}, nil
549
+ }
550
+
551
+ var files []string
552
+ err = filepath.WalkDir(absPath, func(path string, entry os.DirEntry, err error) error {
553
+ if err != nil {
554
+ return err
555
+ }
556
+ if path != absPath && !all && (strings.HasPrefix(entry.Name(), ".") || strings.HasPrefix(entry.Name(), "_")) {
557
+ if entry.IsDir() {
558
+ return filepath.SkipDir
559
+ }
560
+ return nil
561
+ }
562
+ if entry.IsDir() {
563
+ return nil
564
+ }
565
+ files = append(files, path)
566
+ return nil
567
+ })
568
+ if err != nil {
569
+ return nil, err
570
+ }
571
+ if len(files) == 0 {
572
+ return nil, errors.New("go:embed directory matched no files")
573
+ }
574
+ return files, nil
575
+ }
576
+
577
+ func compilerCacheProtobufSideInputs(req *CompileRequest, node *PackageGraphNode) []string {
578
+ if req == nil || !req.ProtobufTypeScriptBinding {
579
+ return nil
580
+ }
581
+ sourceRoot := protobufTypeScriptBindingRoot(req.Dir)
582
+ var inputs []string
583
+ for _, sourcePath := range node.CompiledGoFiles {
584
+ if !strings.HasSuffix(sourcePath, ".pb.go") ||
585
+ strings.HasSuffix(filepath.Base(sourcePath), "_srpc.pb.go") ||
586
+ !protobufTypeScriptBindingInSourceRoot(sourceRoot, sourcePath) {
587
+ continue
588
+ }
589
+ tsPath := strings.TrimSuffix(sourcePath, ".go") + ".ts"
590
+ inputs = append(inputs, "protobuf-ts-binding|"+fileIdentity(tsPath))
591
+ }
592
+ return inputs
593
+ }
594
+
595
+ func moduleIdentityFiles(moduleDir string) []string {
596
+ if moduleDir == "" {
597
+ return nil
598
+ }
599
+ var files []string
600
+ for _, name := range []string{"go.mod", "go.sum", "vendor/modules.txt"} {
601
+ file := filepath.Join(moduleDir, name)
602
+ if _, err := os.Stat(file); err == nil {
603
+ files = append(files, file)
604
+ }
605
+ }
606
+ return files
607
+ }
608
+
609
+ func fileIdentity(file string) string {
610
+ data, err := os.ReadFile(file)
611
+ if err != nil {
612
+ return filepath.ToSlash(file) + "|missing|" + err.Error()
613
+ }
614
+ info, err := os.Stat(file)
615
+ if err != nil {
616
+ return filepath.ToSlash(file) + "|stat|" + err.Error()
617
+ }
618
+ return strings.Join([]string{
619
+ cleanAbs(file),
620
+ strconv.FormatInt(info.Size(), 10),
621
+ strconv.FormatInt(info.ModTime().UnixNano(), 10),
622
+ sha256Hex(data),
623
+ }, "|")
624
+ }
625
+
626
+ func cleanAbs(file string) string {
627
+ if file == "" {
628
+ return ""
629
+ }
630
+ abs, err := filepath.Abs(file)
631
+ if err != nil {
632
+ return filepath.ToSlash(filepath.Clean(file))
633
+ }
634
+ return filepath.ToSlash(filepath.Clean(abs))
635
+ }
636
+
637
+ func generatedArtifactKind(filePath string) string {
638
+ if strings.HasSuffix(filePath, "/index.ts") {
639
+ return "package-index"
640
+ }
641
+ return "generated"
642
+ }
643
+
644
+ func safeOutputArtifactPath(filePath string) bool {
645
+ if filePath == "" || strings.Contains(filePath, "\\") {
646
+ return false
647
+ }
648
+ clean := path.Clean(filePath)
649
+ return clean == filePath &&
650
+ !path.IsAbs(clean) &&
651
+ !strings.HasPrefix(clean, "../") &&
652
+ clean != ".." &&
653
+ (strings.HasPrefix(clean, "@goscript/") || clean == "@goscript")
654
+ }
655
+
656
+ func safeCacheBlobPath(filePath string) bool {
657
+ if filePath == "" || strings.Contains(filePath, "\\") {
658
+ return false
659
+ }
660
+ clean := path.Clean(filePath)
661
+ return clean == filePath &&
662
+ !path.IsAbs(clean) &&
663
+ !strings.HasPrefix(clean, "../") &&
664
+ clean != ".." &&
665
+ strings.HasPrefix(clean, "blobs/sha256/")
666
+ }
667
+
668
+ func writeFileAtomic(path string, data []byte, perm os.FileMode) error {
669
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
670
+ return err
671
+ }
672
+ tmp, err := os.CreateTemp(filepath.Dir(path), ".goscript-cache-*")
673
+ if err != nil {
674
+ return err
675
+ }
676
+ tmpName := tmp.Name()
677
+ written, writeErr := tmp.Write(data)
678
+ closeErr := tmp.Close()
679
+ if writeErr != nil {
680
+ os.Remove(tmpName)
681
+ return writeErr
682
+ }
683
+ if closeErr != nil {
684
+ os.Remove(tmpName)
685
+ return closeErr
686
+ }
687
+ if written != len(data) {
688
+ os.Remove(tmpName)
689
+ return io.ErrShortWrite
690
+ }
691
+ if err := os.Chmod(tmpName, perm); err != nil {
692
+ os.Remove(tmpName)
693
+ return err
694
+ }
695
+ return os.Rename(tmpName, path)
696
+ }
697
+
698
+ func sha256Hex(data []byte) string {
699
+ sum := sha256.Sum256(data)
700
+ return hex.EncodeToString(sum[:])
701
+ }
702
+
703
+ func sha256String(data string) string {
704
+ return sha256Hex([]byte(data))
705
+ }
706
+
707
+ func formatCompilerCacheManifest(manifest compilerCacheManifest) []byte {
708
+ var buf bytes.Buffer
709
+ stream := jsoniter.NewStream(&buf, 4096, 2)
710
+ stream.WriteObjectStart()
711
+ stream.WriteObjectField("schema")
712
+ stream.WriteString(manifest.schema)
713
+ stream.WriteMore()
714
+ stream.WriteObjectField("key")
715
+ stream.WriteString(manifest.key)
716
+ stream.WriteMore()
717
+ stream.WriteObjectField("kind")
718
+ stream.WriteString(string(manifest.kind))
719
+ stream.WriteMore()
720
+ stream.WriteObjectField("packagePath")
721
+ stream.WriteString(manifest.packagePath)
722
+ stream.WriteMore()
723
+ writeStringArray(stream, "compiledPackages", manifest.compiledPackages)
724
+ stream.WriteMore()
725
+ writeStringArray(stream, "copiedPackages", manifest.copiedPackages)
726
+ stream.WriteMore()
727
+ stream.WriteObjectField("files")
728
+ stream.WriteArrayStart()
729
+ for idx, file := range manifest.files {
730
+ if idx != 0 {
731
+ stream.WriteMore()
732
+ }
733
+ stream.WriteObjectStart()
734
+ stream.WriteObjectField("path")
735
+ stream.WriteString(file.path)
736
+ stream.WriteMore()
737
+ stream.WriteObjectField("kind")
738
+ stream.WriteString(file.kind)
739
+ stream.WriteMore()
740
+ stream.WriteObjectField("sha256")
741
+ stream.WriteString(file.sha256)
742
+ stream.WriteMore()
743
+ stream.WriteObjectField("size")
744
+ stream.WriteUint64(file.size)
745
+ stream.WriteMore()
746
+ stream.WriteObjectField("blob")
747
+ stream.WriteString(file.blob)
748
+ stream.WriteObjectEnd()
749
+ }
750
+ stream.WriteArrayEnd()
751
+ stream.WriteObjectEnd()
752
+ if stream.Error != nil {
753
+ return nil
754
+ }
755
+ return append([]byte(nil), stream.Buffer()...)
756
+ }
757
+
758
+ func writeStringArray(stream *jsoniter.Stream, field string, values []string) {
759
+ stream.WriteObjectField(field)
760
+ stream.WriteArrayStart()
761
+ for idx, value := range values {
762
+ if idx != 0 {
763
+ stream.WriteMore()
764
+ }
765
+ stream.WriteString(value)
766
+ }
767
+ stream.WriteArrayEnd()
768
+ }
769
+
770
+ func parseCompilerCacheManifest(data []byte) compilerCacheManifest {
771
+ var manifest compilerCacheManifest
772
+ iter := jsoniter.ParseBytes(data)
773
+ for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
774
+ switch field {
775
+ case "schema":
776
+ manifest.schema = iter.ReadString()
777
+ case "key":
778
+ manifest.key = iter.ReadString()
779
+ case "kind":
780
+ manifest.kind = compilerCacheEntryKind(iter.ReadString())
781
+ case "packagePath":
782
+ manifest.packagePath = iter.ReadString()
783
+ case "compiledPackages":
784
+ manifest.compiledPackages = readStringArray(iter)
785
+ case "copiedPackages":
786
+ manifest.copiedPackages = readStringArray(iter)
787
+ case "files":
788
+ for iter.ReadArray() {
789
+ manifest.files = append(manifest.files, readManifestFile(iter))
790
+ }
791
+ default:
792
+ iter.Skip()
793
+ }
794
+ }
795
+ if iter.Error != nil && !errors.Is(iter.Error, io.EOF) {
796
+ return compilerCacheManifest{}
797
+ }
798
+ return manifest
799
+ }
800
+
801
+ func readStringArray(iter *jsoniter.Iterator) []string {
802
+ var values []string
803
+ for iter.ReadArray() {
804
+ values = append(values, iter.ReadString())
805
+ }
806
+ return values
807
+ }
808
+
809
+ func readManifestFile(iter *jsoniter.Iterator) compilerCacheManifestFile {
810
+ var file compilerCacheManifestFile
811
+ for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
812
+ switch field {
813
+ case "path":
814
+ file.path = iter.ReadString()
815
+ case "kind":
816
+ file.kind = iter.ReadString()
817
+ case "sha256":
818
+ file.sha256 = iter.ReadString()
819
+ case "size":
820
+ file.size = iter.ReadUint64()
821
+ case "blob":
822
+ file.blob = iter.ReadString()
823
+ default:
824
+ iter.Skip()
825
+ }
826
+ }
827
+ return file
828
+ }