goscript 0.0.25 → 0.0.28

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 (190) hide show
  1. package/README.md +4 -4
  2. package/cmd/goscript/cmd_compile.go +0 -3
  3. package/cmd/goscript/deps.go +11 -0
  4. package/compiler/analysis.go +259 -55
  5. package/compiler/assignment.go +2 -2
  6. package/compiler/builtin_test.go +1 -1
  7. package/compiler/compiler.go +201 -49
  8. package/compiler/compiler_test.go +53 -0
  9. package/compiler/composite-lit.go +32 -8
  10. package/compiler/decl.go +6 -6
  11. package/compiler/expr-call.go +83 -0
  12. package/compiler/expr.go +1 -1
  13. package/compiler/protobuf.go +557 -0
  14. package/compiler/spec-struct.go +4 -0
  15. package/compiler/spec-value.go +11 -3
  16. package/compiler/spec.go +18 -1
  17. package/compiler/stmt-assign.go +35 -0
  18. package/compiler/type-assert.go +87 -0
  19. package/compiler/type.go +5 -2
  20. package/dist/gs/builtin/builtin.d.ts +19 -1
  21. package/dist/gs/builtin/builtin.js +85 -5
  22. package/dist/gs/builtin/builtin.js.map +1 -1
  23. package/dist/gs/builtin/channel.js.map +1 -1
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +59 -26
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/cmp/index.js.map +1 -1
  28. package/dist/gs/context/context.d.ts +1 -1
  29. package/dist/gs/context/context.js +20 -11
  30. package/dist/gs/context/context.js.map +1 -1
  31. package/dist/gs/errors/errors.d.ts +7 -0
  32. package/dist/gs/errors/errors.js +190 -0
  33. package/dist/gs/errors/errors.js.map +1 -0
  34. package/dist/gs/errors/index.d.ts +1 -0
  35. package/dist/gs/errors/index.js +2 -0
  36. package/dist/gs/errors/index.js.map +1 -0
  37. package/dist/gs/internal/goarch/index.js +1 -1
  38. package/dist/gs/internal/goarch/index.js.map +1 -1
  39. package/dist/gs/io/index.d.ts +1 -0
  40. package/dist/gs/io/index.js +2 -0
  41. package/dist/gs/io/index.js.map +1 -0
  42. package/dist/gs/io/io.d.ts +107 -0
  43. package/dist/gs/io/io.js +385 -0
  44. package/dist/gs/io/io.js.map +1 -0
  45. package/dist/gs/iter/iter.js.map +1 -1
  46. package/dist/gs/math/bits/index.js +34 -32
  47. package/dist/gs/math/bits/index.js.map +1 -1
  48. package/dist/gs/runtime/runtime.d.ts +1 -0
  49. package/dist/gs/runtime/runtime.js +15 -18
  50. package/dist/gs/runtime/runtime.js.map +1 -1
  51. package/dist/gs/slices/slices.d.ts +1 -1
  52. package/dist/gs/slices/slices.js +1 -1
  53. package/dist/gs/slices/slices.js.map +1 -1
  54. package/dist/gs/strings/builder.d.ts +18 -0
  55. package/dist/gs/strings/builder.js +205 -0
  56. package/dist/gs/strings/builder.js.map +1 -0
  57. package/dist/gs/strings/clone.d.ts +1 -0
  58. package/dist/gs/strings/clone.js +16 -0
  59. package/dist/gs/strings/clone.js.map +1 -0
  60. package/dist/gs/strings/compare.d.ts +1 -0
  61. package/dist/gs/strings/compare.js +14 -0
  62. package/dist/gs/strings/compare.js.map +1 -0
  63. package/dist/gs/strings/index.d.ts +2 -0
  64. package/dist/gs/strings/index.js +3 -0
  65. package/dist/gs/strings/index.js.map +1 -0
  66. package/dist/gs/strings/iter.d.ts +8 -0
  67. package/dist/gs/strings/iter.js +160 -0
  68. package/dist/gs/strings/iter.js.map +1 -0
  69. package/dist/gs/strings/reader.d.ts +34 -0
  70. package/dist/gs/strings/reader.js +418 -0
  71. package/dist/gs/strings/reader.js.map +1 -0
  72. package/dist/gs/strings/replace.d.ts +106 -0
  73. package/dist/gs/strings/replace.js +1136 -0
  74. package/dist/gs/strings/replace.js.map +1 -0
  75. package/dist/gs/strings/search.d.ts +24 -0
  76. package/dist/gs/strings/search.js +169 -0
  77. package/dist/gs/strings/search.js.map +1 -0
  78. package/dist/gs/strings/strings.d.ts +47 -0
  79. package/dist/gs/strings/strings.js +418 -0
  80. package/dist/gs/strings/strings.js.map +1 -0
  81. package/dist/gs/stringslite/index.d.ts +1 -0
  82. package/dist/gs/stringslite/index.js +2 -0
  83. package/dist/gs/stringslite/index.js.map +1 -0
  84. package/dist/gs/stringslite/strings.d.ts +11 -0
  85. package/dist/gs/stringslite/strings.js +67 -0
  86. package/dist/gs/stringslite/strings.js.map +1 -0
  87. package/dist/gs/sync/index.d.ts +1 -0
  88. package/dist/gs/sync/index.js +2 -0
  89. package/dist/gs/sync/index.js.map +1 -0
  90. package/dist/gs/sync/sync.d.ts +79 -0
  91. package/dist/gs/sync/sync.js +392 -0
  92. package/dist/gs/sync/sync.js.map +1 -0
  93. package/dist/gs/time/time.js +7 -7
  94. package/dist/gs/time/time.js.map +1 -1
  95. package/dist/gs/unicode/index.d.ts +1 -0
  96. package/dist/gs/unicode/index.js +2 -0
  97. package/dist/gs/unicode/index.js.map +1 -0
  98. package/dist/gs/unicode/unicode.d.ts +105 -0
  99. package/dist/gs/unicode/unicode.js +332 -0
  100. package/dist/gs/unicode/unicode.js.map +1 -0
  101. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  102. package/dist/gs/unicode/utf8/index.js +3 -0
  103. package/dist/gs/unicode/utf8/index.js.map +1 -0
  104. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  105. package/dist/gs/unicode/utf8/utf8.js +196 -0
  106. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  107. package/dist/gs/unsafe/index.d.ts +1 -0
  108. package/dist/gs/unsafe/index.js +2 -0
  109. package/dist/gs/unsafe/index.js.map +1 -0
  110. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  111. package/dist/gs/unsafe/unsafe.js +44 -0
  112. package/dist/gs/unsafe/unsafe.js.map +1 -0
  113. package/go.mod +2 -1
  114. package/go.sum +6 -2
  115. package/gs/README.md +6 -0
  116. package/gs/builtin/builtin.ts +158 -0
  117. package/gs/builtin/channel.ts +683 -0
  118. package/gs/builtin/defer.ts +58 -0
  119. package/gs/builtin/index.ts +1 -0
  120. package/gs/builtin/io.ts +22 -0
  121. package/gs/builtin/map.ts +50 -0
  122. package/gs/builtin/slice.ts +1030 -0
  123. package/gs/builtin/type.ts +1106 -0
  124. package/gs/builtin/varRef.ts +25 -0
  125. package/gs/cmp/godoc.txt +8 -0
  126. package/gs/cmp/index.ts +29 -0
  127. package/gs/context/context.ts +401 -0
  128. package/gs/context/godoc.txt +69 -0
  129. package/gs/context/index.ts +1 -0
  130. package/gs/errors/errors.ts +223 -0
  131. package/gs/errors/godoc.txt +63 -0
  132. package/gs/errors/index.ts +1 -0
  133. package/gs/internal/goarch/godoc.txt +39 -0
  134. package/gs/internal/goarch/index.ts +18 -0
  135. package/gs/io/godoc.txt +61 -0
  136. package/gs/io/index.ts +1 -0
  137. package/gs/io/io.go +75 -0
  138. package/gs/io/io.ts +546 -0
  139. package/gs/iter/godoc.txt +203 -0
  140. package/gs/iter/index.ts +1 -0
  141. package/gs/iter/iter.ts +117 -0
  142. package/gs/math/bits/index.ts +356 -0
  143. package/gs/math/godoc.txt +76 -0
  144. package/gs/runtime/godoc.txt +331 -0
  145. package/gs/runtime/index.ts +1 -0
  146. package/gs/runtime/runtime.ts +178 -0
  147. package/gs/slices/godoc.txt +44 -0
  148. package/gs/slices/index.ts +1 -0
  149. package/gs/slices/slices.ts +22 -0
  150. package/gs/strings/builder.test.ts +121 -0
  151. package/gs/strings/builder.ts +223 -0
  152. package/gs/strings/clone.test.ts +43 -0
  153. package/gs/strings/clone.ts +17 -0
  154. package/gs/strings/compare.test.ts +84 -0
  155. package/gs/strings/compare.ts +13 -0
  156. package/gs/strings/godoc.txt +66 -0
  157. package/gs/strings/index.ts +2 -0
  158. package/gs/strings/iter.test.ts +343 -0
  159. package/gs/strings/iter.ts +171 -0
  160. package/gs/strings/reader.test.ts +243 -0
  161. package/gs/strings/reader.ts +451 -0
  162. package/gs/strings/replace.test.ts +181 -0
  163. package/gs/strings/replace.ts +1310 -0
  164. package/gs/strings/search.test.ts +214 -0
  165. package/gs/strings/search.ts +213 -0
  166. package/gs/strings/strings.test.ts +477 -0
  167. package/gs/strings/strings.ts +510 -0
  168. package/gs/stringslite/godoc.txt +17 -0
  169. package/gs/stringslite/index.ts +1 -0
  170. package/gs/stringslite/strings.ts +82 -0
  171. package/gs/sync/godoc.txt +21 -0
  172. package/gs/sync/index.ts +1 -0
  173. package/gs/sync/sync.go +64 -0
  174. package/gs/sync/sync.ts +449 -0
  175. package/gs/time/godoc.md +116 -0
  176. package/gs/time/godoc.txt +116 -0
  177. package/gs/time/index.ts +1 -0
  178. package/gs/time/time.ts +272 -0
  179. package/gs/unicode/godoc.txt +52 -0
  180. package/gs/unicode/index.ts +1 -0
  181. package/gs/unicode/unicode.go +38 -0
  182. package/gs/unicode/unicode.ts +418 -0
  183. package/gs/unicode/utf8/godoc.txt +22 -0
  184. package/gs/unicode/utf8/index.ts +2 -0
  185. package/gs/unicode/utf8/utf8.ts +227 -0
  186. package/gs/unsafe/godoc.txt +19 -0
  187. package/gs/unsafe/index.ts +1 -0
  188. package/gs/unsafe/unsafe.test.ts +68 -0
  189. package/gs/unsafe/unsafe.ts +77 -0
  190. package/package.json +6 -4
@@ -0,0 +1,1030 @@
1
+ /**
2
+ * GoSliceObject contains metadata for complex slice views
3
+ */
4
+ interface GoSliceObject<T> {
5
+ backing: T[] // The backing array
6
+ offset: number // Offset into the backing array
7
+ length: number // Length of the slice
8
+ capacity: number // Capacity of the slice
9
+ }
10
+
11
+ /**
12
+ * SliceProxy is a proxy object for complex slices
13
+ */
14
+ export type SliceProxy<T> = T[] & {
15
+ __meta__: GoSliceObject<T>
16
+ }
17
+
18
+ /**
19
+ * Slice<T> is a union type that is either a plain array or a proxy
20
+ * null represents the nil state.
21
+ *
22
+ * Slice<number> can be represented as Uint8Array.
23
+ */
24
+ export type Slice<T> =
25
+ | T[]
26
+ | SliceProxy<T>
27
+ | null
28
+ | (T extends number ? Uint8Array : never)
29
+
30
+ // asArray converts a slice to a JavaScript array.
31
+ export function asArray<T>(slice: Slice<T>): T[] {
32
+ if (slice === null || slice === undefined) {
33
+ return []
34
+ }
35
+
36
+ if (slice instanceof Uint8Array) {
37
+ return Array.from(slice) as T[]
38
+ }
39
+
40
+ if (isComplexSlice(slice)) {
41
+ const result: T[] = []
42
+ for (let i = 0; i < slice.__meta__.length; i++) {
43
+ result.push(slice.__meta__.backing[slice.__meta__.offset + i])
44
+ }
45
+ return result
46
+ }
47
+
48
+ if (Array.isArray(slice)) {
49
+ return slice
50
+ }
51
+
52
+ return []
53
+ }
54
+
55
+ /**
56
+ * isComplexSlice checks if a slice is a complex slice (has __meta__ property)
57
+ */
58
+ function isComplexSlice<T>(slice: Slice<T>): slice is SliceProxy<T> {
59
+ return (
60
+ slice !== null &&
61
+ slice !== undefined &&
62
+ typeof slice === 'object' &&
63
+ '__meta__' in slice &&
64
+ slice.__meta__ !== undefined
65
+ )
66
+ }
67
+
68
+ /**
69
+ * Creates a new slice with the specified length and capacity.
70
+ * @param length The length of the slice.
71
+ * @param capacity The capacity of the slice (optional).
72
+ * @returns A new slice.
73
+ */
74
+ export const makeSlice = <T>(
75
+ length: number,
76
+ capacity?: number,
77
+ typeHint?: string,
78
+ ): Slice<T> => {
79
+ if (typeHint === 'byte') {
80
+ // Uint8Array is initialized to zeros by default.
81
+ // Capacity for Uint8Array is its length.
82
+ return new Uint8Array(length) as Slice<T>
83
+ }
84
+
85
+ const actualCapacity = capacity === undefined ? length : capacity
86
+ if (length < 0 || actualCapacity < 0 || length > actualCapacity) {
87
+ throw new Error(
88
+ `Invalid slice length (${length}) or capacity (${actualCapacity})`,
89
+ )
90
+ }
91
+
92
+ let zeroVal: any
93
+ switch (typeHint) {
94
+ case 'number':
95
+ zeroVal = 0
96
+ break
97
+ case 'boolean':
98
+ zeroVal = false
99
+ break
100
+ case 'string':
101
+ zeroVal = ''
102
+ break
103
+ default:
104
+ zeroVal = null // Default for objects, complex types, or unspecified
105
+ }
106
+
107
+ const backingArr = new Array<T>(actualCapacity)
108
+ // Initialize the relevant part of the backing array
109
+ for (let i = 0; i < length; i++) {
110
+ backingArr[i] = zeroVal
111
+ }
112
+ // The rest of backingArr (from length to actualCapacity-1) remains uninitialized (undefined),
113
+ // representing available capacity.
114
+
115
+ // The proxyTargetArray serves as the shell for the proxy.
116
+ // Its elements up to 'length' should reflect the initialized part of the slice.
117
+ const proxyTargetArray = new Array<T>(length)
118
+ for (let i = 0; i < length; i++) {
119
+ proxyTargetArray[i] = backingArr[i] // Or simply zeroVal
120
+ }
121
+
122
+ const proxy = proxyTargetArray as SliceProxy<T>
123
+ proxy.__meta__ = {
124
+ backing: backingArr,
125
+ offset: 0,
126
+ length: length,
127
+ capacity: actualCapacity,
128
+ }
129
+
130
+ return proxy
131
+ }
132
+
133
+ /**
134
+ * goSlice creates a slice from s[low:high:max]
135
+ * Arguments mirror Go semantics; omitted indices are undefined.
136
+ *
137
+ * @param s The original slice
138
+ * @param low Starting index (defaults to 0)
139
+ * @param high Ending index (defaults to s.length)
140
+ * @param max Capacity limit (defaults to original capacity)
141
+ */
142
+ export const goSlice = <T>( // T can be number for Uint8Array case
143
+ s: Slice<T>,
144
+ low?: number,
145
+ high?: number,
146
+ max?: number,
147
+ ): Slice<T> => {
148
+ const handler = {
149
+ get(target: any, prop: string | symbol): any {
150
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
151
+ const index = Number(prop)
152
+ if (index >= 0 && index < target.__meta__.length) {
153
+ return target.__meta__.backing[target.__meta__.offset + index]
154
+ }
155
+ throw new Error(
156
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
157
+ )
158
+ }
159
+
160
+ if (prop === 'length') {
161
+ return target.__meta__.length
162
+ }
163
+
164
+ if (prop === '__meta__') {
165
+ return target.__meta__
166
+ }
167
+
168
+ if (
169
+ prop === 'slice' ||
170
+ prop === 'map' ||
171
+ prop === 'filter' ||
172
+ prop === 'reduce' ||
173
+ prop === 'forEach' ||
174
+ prop === Symbol.iterator
175
+ ) {
176
+ const backingSlice = target.__meta__.backing.slice(
177
+ target.__meta__.offset,
178
+ target.__meta__.offset + target.__meta__.length,
179
+ )
180
+ return backingSlice[prop].bind(backingSlice)
181
+ }
182
+
183
+ return Reflect.get(target, prop)
184
+ },
185
+
186
+ set(target: any, prop: string | symbol, value: any): boolean {
187
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
188
+ const index = Number(prop)
189
+ if (index >= 0 && index < target.__meta__.length) {
190
+ target.__meta__.backing[target.__meta__.offset + index] = value
191
+ return true
192
+ }
193
+ if (
194
+ index === target.__meta__.length &&
195
+ target.__meta__.length < target.__meta__.capacity
196
+ ) {
197
+ target.__meta__.backing[target.__meta__.offset + index] = value
198
+ target.__meta__.length++
199
+ return true
200
+ }
201
+ throw new Error(
202
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
203
+ )
204
+ }
205
+
206
+ if (prop === 'length' || prop === '__meta__') {
207
+ return false
208
+ }
209
+
210
+ return Reflect.set(target, prop, value)
211
+ },
212
+ }
213
+
214
+ if (s instanceof Uint8Array) {
215
+ const actualLow = low ?? 0
216
+ const actualHigh = high ?? s.length
217
+
218
+ if (actualLow < 0 || actualHigh < actualLow || actualHigh > s.length) {
219
+ throw new Error(
220
+ `Invalid slice indices: low ${actualLow}, high ${actualHigh} for Uint8Array of length ${s.length}`,
221
+ )
222
+ }
223
+
224
+ const subArrayView = s.subarray(actualLow, actualHigh) // This is Uint8Array
225
+
226
+ if (max !== undefined) {
227
+ if (max < actualHigh || max > s.length) {
228
+ // max is relative to the original s.length (capacity)
229
+ throw new Error(
230
+ `Invalid max index: ${max}. Constraints: low ${actualLow} <= high ${actualHigh} <= max <= original_length ${s.length}`,
231
+ )
232
+ }
233
+
234
+ const newLength = subArrayView.length // actualHigh - actualLow
235
+ const newCap = max - actualLow // Capacity of the new slice view
236
+
237
+ if (newCap !== newLength) {
238
+ // Capacity is different from length, so return SliceProxy<number>
239
+ // The original s was Uint8Array, so T is effectively 'number' for this path.
240
+ const backingNumbers = Array.from(subArrayView) // Convert Uint8Array data to number[]
241
+
242
+ const proxyTarget = {
243
+ __meta__: {
244
+ backing: backingNumbers, // number[]
245
+ offset: 0, // Offset is 0 because backingNumbers is a direct copy
246
+ length: newLength,
247
+ capacity: newCap,
248
+ },
249
+ }
250
+ // Explicitly cast to Slice<T> after ensuring T is number for this branch.
251
+ return new Proxy(
252
+ proxyTarget,
253
+ handler,
254
+ ) as unknown as SliceProxy<number> as Slice<T>
255
+ } else {
256
+ // newCap === newLength, standard Uint8Array is fine.
257
+ return subArrayView as Slice<T> // T is number
258
+ }
259
+ } else {
260
+ // max is not defined, return the Uint8Array subarray view directly.
261
+ return subArrayView as Slice<T> // T is number
262
+ }
263
+ }
264
+
265
+ if (s === null || s === undefined) {
266
+ throw new Error('Cannot slice nil')
267
+ }
268
+
269
+ const slen = len(s)
270
+ low = low ?? 0
271
+ high = high ?? slen
272
+
273
+ if (low < 0 || high < low) {
274
+ throw new Error(`Invalid slice indices: ${low}:${high}`)
275
+ }
276
+
277
+ // In Go, high can be up to capacity, not just length
278
+ const scap = cap(s)
279
+ if (high > scap) {
280
+ throw new Error(`Slice index out of range: ${high} > ${scap}`)
281
+ }
282
+
283
+ if (
284
+ Array.isArray(s) &&
285
+ !isComplexSlice(s) &&
286
+ low === 0 &&
287
+ high === s.length &&
288
+ max === undefined
289
+ ) {
290
+ return s
291
+ }
292
+
293
+ let backing: T[]
294
+ let oldOffset = 0
295
+ let oldCap = scap
296
+
297
+ // Get the backing array and offset
298
+ if (isComplexSlice(s)) {
299
+ backing = s.__meta__.backing
300
+ oldOffset = s.__meta__.offset
301
+ oldCap = s.__meta__.capacity
302
+ } else {
303
+ backing = s as T[]
304
+ }
305
+
306
+ let newCap
307
+ if (max !== undefined) {
308
+ if (max < high) {
309
+ throw new Error(`Invalid slice indices: ${low}:${high}:${max}`)
310
+ }
311
+ if (isComplexSlice(s) && max > oldOffset + oldCap) {
312
+ throw new Error(
313
+ `Slice index out of range: ${max} > ${oldOffset + oldCap}`,
314
+ )
315
+ }
316
+ if (!isComplexSlice(s) && max > s.length) {
317
+ throw new Error(`Slice index out of range: ${max} > ${s.length}`)
318
+ }
319
+ newCap = max - low
320
+ } else {
321
+ // For slices of slices, capacity should be the capacity of the original slice minus the low index
322
+ if (isComplexSlice(s)) {
323
+ newCap = oldCap - low
324
+ } else {
325
+ newCap = s.length - low
326
+ }
327
+ }
328
+
329
+ const newLength = high - low
330
+ const newOffset = oldOffset + low
331
+
332
+ const target = {
333
+ __meta__: {
334
+ backing: backing,
335
+ offset: newOffset,
336
+ length: newLength,
337
+ capacity: newCap,
338
+ },
339
+ }
340
+
341
+ // const handler = { ... } // Handler is now defined at the top
342
+
343
+ return new Proxy(target, handler) as unknown as SliceProxy<T>
344
+ }
345
+
346
+ /**
347
+ * Converts a JavaScript array to a Go slice.
348
+ * For multi-dimensional arrays, recursively converts nested arrays to slices.
349
+ * @param arr The JavaScript array to convert
350
+ * @param depth How many levels of nesting to convert (default: 1, use Infinity for all levels)
351
+ * @returns A Go slice containing the same elements
352
+ */
353
+ export const arrayToSlice = <T>(
354
+ arr: T[] | null | undefined,
355
+ depth: number = 1,
356
+ ): Slice<T> => {
357
+ if (arr == null) return [] as T[]
358
+
359
+ if (arr.length === 0) return arr
360
+
361
+ const target = {
362
+ __meta__: {
363
+ backing: arr,
364
+ offset: 0,
365
+ length: arr.length,
366
+ capacity: arr.length,
367
+ },
368
+ }
369
+
370
+ const handler = {
371
+ get(target: any, prop: string | symbol): any {
372
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
373
+ const index = Number(prop)
374
+ if (index >= 0 && index < target.__meta__.length) {
375
+ return target.__meta__.backing[target.__meta__.offset + index]
376
+ }
377
+ throw new Error(
378
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
379
+ )
380
+ }
381
+
382
+ if (prop === 'length') {
383
+ return target.__meta__.length
384
+ }
385
+
386
+ if (prop === '__meta__') {
387
+ return target.__meta__
388
+ }
389
+
390
+ if (
391
+ prop === 'slice' ||
392
+ prop === 'map' ||
393
+ prop === 'filter' ||
394
+ prop === 'reduce' ||
395
+ prop === 'forEach' ||
396
+ prop === Symbol.iterator
397
+ ) {
398
+ const backingSlice = target.__meta__.backing.slice(
399
+ target.__meta__.offset,
400
+ target.__meta__.offset + target.__meta__.length,
401
+ )
402
+ return backingSlice[prop].bind(backingSlice)
403
+ }
404
+
405
+ return Reflect.get(target, prop)
406
+ },
407
+
408
+ set(target: any, prop: string | symbol, value: any): boolean {
409
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
410
+ const index = Number(prop)
411
+ if (index >= 0 && index < target.__meta__.length) {
412
+ target.__meta__.backing[target.__meta__.offset + index] = value
413
+ return true
414
+ }
415
+ if (
416
+ index === target.__meta__.length &&
417
+ target.__meta__.length < target.__meta__.capacity
418
+ ) {
419
+ target.__meta__.backing[target.__meta__.offset + index] = value
420
+ target.__meta__.length++
421
+ return true
422
+ }
423
+ throw new Error(
424
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
425
+ )
426
+ }
427
+
428
+ if (prop === 'length' || prop === '__meta__') {
429
+ return false
430
+ }
431
+
432
+ return Reflect.set(target, prop, value)
433
+ },
434
+ }
435
+
436
+ // Recursively convert nested arrays if depth > 1
437
+ if (depth > 1 && arr.length > 0) {
438
+ for (let i = 0; i < arr.length; i++) {
439
+ const item = arr[i]
440
+ if (isComplexSlice(item as any)) {
441
+ } else if (Array.isArray(item)) {
442
+ arr[i] = arrayToSlice(item as any[], depth - 1) as any
443
+ } else if (
444
+ item &&
445
+ typeof item === 'object' &&
446
+ isComplexSlice(item as any)
447
+ ) {
448
+ // Preserve capacity information for complex slices
449
+ }
450
+ }
451
+ }
452
+
453
+ return new Proxy(target, handler) as unknown as SliceProxy<T>
454
+ }
455
+
456
+ /**
457
+ * Returns the length of a collection (string, array, slice, map, or set).
458
+ * @param obj The collection to get the length of.
459
+ * @returns The length of the collection.
460
+ */
461
+ export const len = <T = unknown, V = unknown>(
462
+ obj:
463
+ | string
464
+ | Array<T>
465
+ | Slice<T>
466
+ | Map<T, V>
467
+ | Set<T>
468
+ | Uint8Array
469
+ | null
470
+ | undefined,
471
+ ): number => {
472
+ if (obj === null || obj === undefined) {
473
+ return 0
474
+ }
475
+
476
+ if (typeof obj === 'string') {
477
+ return stringLen(obj) // Call new stringLen for strings
478
+ }
479
+
480
+ if (obj instanceof Map || obj instanceof Set) {
481
+ return obj.size
482
+ }
483
+
484
+ if (obj instanceof Uint8Array) {
485
+ return obj.length
486
+ }
487
+
488
+ if (isComplexSlice(obj)) {
489
+ return obj.__meta__.length
490
+ }
491
+
492
+ if (Array.isArray(obj)) {
493
+ return obj.length
494
+ }
495
+
496
+ throw new Error('cannot determine len of this type')
497
+ }
498
+
499
+ /**
500
+ * Returns the capacity of a slice.
501
+ * @param obj The slice.
502
+ * @returns The capacity of the slice.
503
+ */
504
+ export const cap = <T>(obj: Slice<T> | Uint8Array): number => {
505
+ if (obj === null || obj === undefined) {
506
+ return 0
507
+ }
508
+
509
+ if (obj instanceof Uint8Array) {
510
+ return obj.length // Uint8Array capacity is its length
511
+ }
512
+
513
+ if (isComplexSlice(obj)) {
514
+ return obj.__meta__.capacity
515
+ }
516
+
517
+ if (Array.isArray(obj)) {
518
+ return obj.length
519
+ }
520
+
521
+ return 0
522
+ }
523
+
524
+ /**
525
+ * Appends elements to a slice.
526
+ * Note: In Go, append can return a new slice if the underlying array is reallocated.
527
+ * This helper emulates that by returning the modified or new slice.
528
+ * @param slice The slice to append to.
529
+ * @param elements The elements to append.
530
+ * @returns The modified or new slice.
531
+ */
532
+ export const append = <T>(
533
+ slice: Slice<T> | Uint8Array,
534
+ ...elements: any[]
535
+ ): Slice<T> => {
536
+ // 1. Flatten all elements from the varargs `...elements` into `varargsElements`.
537
+ // Determine if the result should be a Uint8Array.
538
+ const inputIsUint8Array = slice instanceof Uint8Array
539
+ const appendingUint8Array = elements.some((el) => el instanceof Uint8Array)
540
+ const produceUint8Array =
541
+ inputIsUint8Array ||
542
+ appendingUint8Array ||
543
+ (slice === null && appendingUint8Array)
544
+
545
+ // If producing Uint8Array, all elements must be numbers and potentially flattened from other Uint8Arrays/number slices.
546
+ if (produceUint8Array) {
547
+ let combinedBytes: number[] = []
548
+ // Add bytes from the original slice if it exists and is numeric.
549
+ if (inputIsUint8Array) {
550
+ combinedBytes.push(...Array.from(slice as Uint8Array))
551
+ } else if (slice !== null && slice !== undefined) {
552
+ // Original was Slice<number> or number[]
553
+ const sliceLen = len(slice)
554
+ for (let i = 0; i < sliceLen; i++) {
555
+ const val = (slice as any)[i]
556
+ if (typeof val !== 'number') {
557
+ throw new Error(
558
+ 'Cannot produce Uint8Array: original slice contains non-number elements.',
559
+ )
560
+ }
561
+ combinedBytes.push(val)
562
+ }
563
+ }
564
+ // Add bytes from the varargs elements.
565
+ // For Uint8Array, elements are always flattened if they are slices/Uint8Arrays.
566
+ for (const item of elements) {
567
+ if (item instanceof Uint8Array) {
568
+ combinedBytes.push(...Array.from(item))
569
+ } else if (isComplexSlice(item) || Array.isArray(item)) {
570
+ const itemLen = len(item as Slice<any>)
571
+ for (let i = 0; i < itemLen; i++) {
572
+ const val = (item as any)[i]
573
+ if (typeof val !== 'number') {
574
+ throw new Error(
575
+ 'Cannot produce Uint8Array: appended elements contain non-numbers.',
576
+ )
577
+ }
578
+ combinedBytes.push(val)
579
+ }
580
+ } else {
581
+ if (typeof item !== 'number') {
582
+ throw new Error(
583
+ 'Cannot produce Uint8Array: appended elements contain non-numbers.',
584
+ )
585
+ }
586
+ combinedBytes.push(item)
587
+ }
588
+ }
589
+ const newArr = new Uint8Array(combinedBytes.length)
590
+ newArr.set(combinedBytes)
591
+ return newArr as Slice<T>
592
+ }
593
+
594
+ // Handle generic Slice<T> (non-Uint8Array result).
595
+ // In this case, `elements` are treated as individual items to append,
596
+ // as the Go transpiler is responsible for spreading (`...`) if needed.
597
+ const numAdded = elements.length
598
+
599
+ if (numAdded === 0) {
600
+ return slice
601
+ }
602
+
603
+ let originalElements: T[] = []
604
+ let oldCapacity: number
605
+ let isOriginalComplex = false
606
+ let originalBacking: T[] | undefined = undefined
607
+ let originalOffset = 0
608
+
609
+ if (slice === null || slice === undefined) {
610
+ oldCapacity = 0
611
+ } else if (isComplexSlice(slice)) {
612
+ const meta = slice.__meta__
613
+ for (let i = 0; i < meta.length; i++)
614
+ originalElements.push(meta.backing[meta.offset + i])
615
+ oldCapacity = meta.capacity
616
+ isOriginalComplex = true
617
+ originalBacking = meta.backing
618
+ originalOffset = meta.offset
619
+ } else {
620
+ // Simple T[] array
621
+ originalElements = (slice as T[]).slice()
622
+ oldCapacity = (slice as T[]).length
623
+ }
624
+ const oldLength = originalElements.length
625
+ const newLength = oldLength + numAdded
626
+
627
+ // Case 1: Modify in-place if original was SliceProxy and has enough capacity.
628
+ if (isOriginalComplex && newLength <= oldCapacity && originalBacking) {
629
+ for (let i = 0; i < numAdded; i++) {
630
+ originalBacking[originalOffset + oldLength + i] = elements[i] as T
631
+ }
632
+ const resultProxy = new Array(newLength) as SliceProxy<T>
633
+ for (let i = 0; i < newLength; i++)
634
+ resultProxy[i] = originalBacking[originalOffset + i]
635
+ resultProxy.__meta__ = {
636
+ backing: originalBacking,
637
+ offset: originalOffset,
638
+ length: newLength,
639
+ capacity: oldCapacity,
640
+ }
641
+ return resultProxy
642
+ }
643
+
644
+ // Case 2: Reallocation is needed.
645
+ let newCapacity = oldCapacity
646
+ if (newCapacity === 0) {
647
+ newCapacity = newLength
648
+ } else if (oldLength < 1024) {
649
+ newCapacity = Math.max(oldCapacity * 2, newLength)
650
+ } else {
651
+ newCapacity = Math.max(oldCapacity + Math.floor(oldCapacity / 4), newLength)
652
+ }
653
+ if (newCapacity < newLength) {
654
+ newCapacity = newLength
655
+ }
656
+
657
+ const newBacking = new Array<T>(newCapacity)
658
+ for (let i = 0; i < oldLength; i++) {
659
+ newBacking[i] = originalElements[i]
660
+ }
661
+ for (let i = 0; i < numAdded; i++) {
662
+ newBacking[oldLength + i] = elements[i] as T
663
+ }
664
+
665
+ const resultProxy = new Array(newLength) as SliceProxy<T>
666
+ for (let i = 0; i < newLength; i++) resultProxy[i] = newBacking[i]
667
+ resultProxy.__meta__ = {
668
+ backing: newBacking,
669
+ offset: 0,
670
+ length: newLength,
671
+ capacity: newCapacity,
672
+ }
673
+ return resultProxy
674
+ }
675
+
676
+ /**
677
+ * Copies elements from src to dst.
678
+ * @param dst The destination slice.
679
+ * @param src The source slice.
680
+ * @returns The number of elements copied.
681
+ */
682
+ export const copy = <T>(dst: Slice<T>, src: Slice<T>): number => {
683
+ if (dst === null || src === null) {
684
+ return 0
685
+ }
686
+
687
+ const dstLen = len(dst)
688
+ const srcLen = len(src)
689
+ const count = Math.min(dstLen, srcLen)
690
+
691
+ if (count === 0) {
692
+ return 0
693
+ }
694
+
695
+ const isDstUint8Array = dst instanceof Uint8Array
696
+ const isSrcUint8Array = src instanceof Uint8Array
697
+
698
+ if (isDstUint8Array && isSrcUint8Array) {
699
+ ;(dst as Uint8Array).set((src as Uint8Array).subarray(0, count))
700
+ } else if (isDstUint8Array) {
701
+ // dst is Uint8Array, src is Slice<number> or number[]
702
+ const dstUint8 = dst as Uint8Array
703
+ if (isComplexSlice(src)) {
704
+ const srcMeta = (src as SliceProxy<number>).__meta__
705
+ for (let i = 0; i < count; i++) {
706
+ dstUint8[i] = srcMeta.backing[srcMeta.offset + i]
707
+ }
708
+ } else {
709
+ // src is number[]
710
+ const srcArray = src as number[]
711
+ for (let i = 0; i < count; i++) {
712
+ dstUint8[i] = srcArray[i]
713
+ }
714
+ }
715
+ } else if (isSrcUint8Array) {
716
+ // src is Uint8Array, dst is Slice<number> or number[]
717
+ const srcUint8 = src as Uint8Array
718
+ if (isComplexSlice(dst)) {
719
+ const dstMeta = (dst as SliceProxy<number>).__meta__
720
+ const dstBacking = dstMeta.backing
721
+ const dstOffset = dstMeta.offset
722
+ for (let i = 0; i < count; i++) {
723
+ dstBacking[dstOffset + i] = srcUint8[i]
724
+ // Also update the proxy view if dst is a proxy
725
+ ;(dst as any)[i] = srcUint8[i]
726
+ }
727
+ } else {
728
+ // dst is number[]
729
+ const dstArray = dst as number[]
730
+ for (let i = 0; i < count; i++) {
731
+ dstArray[i] = srcUint8[i]
732
+ }
733
+ }
734
+ } else {
735
+ // Both are Slice<T> or T[] (original logic)
736
+ if (isComplexSlice(dst)) {
737
+ const dstOffset = (dst as SliceProxy<T>).__meta__.offset
738
+ const dstBacking = (dst as SliceProxy<T>).__meta__.backing
739
+
740
+ if (isComplexSlice(src)) {
741
+ const srcOffset = (src as SliceProxy<T>).__meta__.offset
742
+ const srcBacking = (src as SliceProxy<T>).__meta__.backing
743
+ for (let i = 0; i < count; i++) {
744
+ dstBacking[dstOffset + i] = srcBacking[srcOffset + i]
745
+ ;(dst as any)[i] = srcBacking[srcOffset + i] // Update proxy
746
+ }
747
+ } else {
748
+ // src is T[]
749
+ const srcArray = src as T[]
750
+ for (let i = 0; i < count; i++) {
751
+ dstBacking[dstOffset + i] = srcArray[i]
752
+ ;(dst as any)[i] = srcArray[i] // Update proxy
753
+ }
754
+ }
755
+ } else {
756
+ // dst is T[]
757
+ const dstArray = dst as T[]
758
+ if (isComplexSlice(src)) {
759
+ const srcOffset = (src as SliceProxy<T>).__meta__.offset
760
+ const srcBacking = (src as SliceProxy<T>).__meta__.backing
761
+ for (let i = 0; i < count; i++) {
762
+ dstArray[i] = srcBacking[srcOffset + i]
763
+ }
764
+ } else {
765
+ // src is T[]
766
+ const srcArray = src as T[]
767
+ for (let i = 0; i < count; i++) {
768
+ dstArray[i] = srcArray[i]
769
+ }
770
+ }
771
+ }
772
+ }
773
+ return count
774
+ }
775
+
776
+ /**
777
+ * Accesses an element at a specific index for various Go-like types (string, slice, array).
778
+ * Mimics Go's indexing behavior: `myCollection[index]`
779
+ * For strings, it returns the byte value at the specified byte index.
780
+ * For slices/arrays, it returns the element at the specified index.
781
+ * This is used when dealing with types like "string | []byte"
782
+ * @param collection The string, Slice, or Array to access.
783
+ * @param index The index.
784
+ * @returns The element or byte value at the specified index.
785
+ * @throws Error if index is out of bounds or type is unsupported.
786
+ */
787
+ export function index<T>(
788
+ collection: string | Slice<T> | T[],
789
+ index: number,
790
+ ): T | number {
791
+ if (collection === null || collection === undefined) {
792
+ throw new Error('runtime error: index on nil or undefined collection')
793
+ }
794
+
795
+ if (typeof collection === 'string') {
796
+ return indexString(collection, index) // Use the existing indexString for byte access
797
+ } else if (collection instanceof Uint8Array) {
798
+ if (index < 0 || index >= collection.length) {
799
+ throw new Error(
800
+ `runtime error: index out of range [${index}] with length ${collection.length}`,
801
+ )
802
+ }
803
+ return collection[index]
804
+ } else if (isComplexSlice(collection)) {
805
+ if (index < 0 || index >= collection.__meta__.length) {
806
+ throw new Error(
807
+ `runtime error: index out of range [${index}] with length ${collection.__meta__.length}`,
808
+ )
809
+ }
810
+ return collection.__meta__.backing[collection.__meta__.offset + index]
811
+ } else if (Array.isArray(collection)) {
812
+ if (index < 0 || index >= collection.length) {
813
+ throw new Error(
814
+ `runtime error: index out of range [${index}] with length ${collection.length}`,
815
+ )
816
+ }
817
+ return collection[index]
818
+ }
819
+ throw new Error('runtime error: index on unsupported type')
820
+ }
821
+
822
+ /**
823
+ * Converts a string to an array of Unicode code points (runes).
824
+ * @param str The input string.
825
+ * @returns An array of numbers representing the Unicode code points.
826
+ */
827
+ export const stringToRunes = (str: string): number[] => {
828
+ return Array.from(str).map((c) => c.codePointAt(0) || 0)
829
+ }
830
+
831
+ /**
832
+ * Converts an array of Unicode code points (runes) to a string.
833
+ * @param runes The input array of numbers representing Unicode code points.
834
+ * @returns The resulting string.
835
+ */
836
+ export const runesToString = (runes: Slice<number>): string => {
837
+ return runes?.length ? String.fromCharCode(...runes) : ''
838
+ }
839
+
840
+ /**
841
+ * Converts a number to a byte (uint8) by truncating to the range 0-255.
842
+ * Equivalent to Go's byte() conversion.
843
+ * @param n The number to convert to a byte.
844
+ * @returns The byte value (0-255).
845
+ */
846
+ export const byte = (n: number): number => {
847
+ return n & 0xff // Bitwise AND with 255 ensures we get a value in the range 0-255
848
+ }
849
+
850
+ /**
851
+ * Accesses the byte value at a specific index of a UTF-8 encoded string.
852
+ * Mimics Go's string indexing behavior: `myString[index]`
853
+ * @param str The string to access.
854
+ * @param index The byte index.
855
+ * @returns The byte value (0-255) at the specified index.
856
+ * @throws Error if index is out of bounds.
857
+ */
858
+ export const indexString = (str: string, index: number): number => {
859
+ const bytes = new TextEncoder().encode(str)
860
+ if (index < 0 || index >= bytes.length) {
861
+ throw new Error(
862
+ `runtime error: index out of range [${index}] with length ${bytes.length}`,
863
+ )
864
+ }
865
+ return bytes[index]
866
+ }
867
+
868
+ /**
869
+ * Returns the byte length of a string.
870
+ * Mimics Go's `len(string)` behavior.
871
+ * @param str The string.
872
+ * @returns The number of bytes in the UTF-8 representation of the string.
873
+ */
874
+ export const stringLen = (str: string): number => {
875
+ return new TextEncoder().encode(str).length
876
+ }
877
+
878
+ /**
879
+ * Slices a string based on byte indices.
880
+ * Mimics Go's string slicing behavior: `myString[low:high]` for valid UTF-8 slices only.
881
+ * @param str The string to slice.
882
+ * @param low The starting byte index (inclusive). Defaults to 0.
883
+ * @param high The ending byte index (exclusive). Defaults to string byte length.
884
+ * @returns The sliced string.
885
+ * @throws Error if the slice would create invalid UTF-8.
886
+ */
887
+ export const sliceString = (
888
+ str: string,
889
+ low?: number,
890
+ high?: number,
891
+ ): string => {
892
+ const bytes = new TextEncoder().encode(str)
893
+ const actualLow = low === undefined ? 0 : low
894
+ const actualHigh = high === undefined ? bytes.length : high
895
+
896
+ if (actualLow < 0 || actualHigh < actualLow || actualHigh > bytes.length) {
897
+ // Go's behavior for out-of-bounds slice on string is a panic.
898
+ // For simple slices like s[len(s):len(s)], it should produce an empty string.
899
+ // For s[len(s)+1:], it panics.
900
+ // Let's ensure high <= bytes.length and low <= high.
901
+ // If low == high, it's an empty string.
902
+ if (
903
+ actualLow === actualHigh &&
904
+ actualLow >= 0 &&
905
+ actualLow <= bytes.length
906
+ ) {
907
+ return ''
908
+ }
909
+ throw new Error(
910
+ `runtime error: slice bounds out of range [${actualLow}:${actualHigh}] with length ${bytes.length}`,
911
+ )
912
+ }
913
+
914
+ const slicedBytes = bytes.subarray(actualLow, actualHigh)
915
+
916
+ try {
917
+ // Attempt to decode with strict UTF-8 validation
918
+ const result = new TextDecoder('utf-8', { fatal: true }).decode(slicedBytes)
919
+ return result
920
+ } catch (e) {
921
+ // If we get here, the slice would create invalid UTF-8
922
+ // This is a fundamental limitation of JavaScript string handling
923
+ throw new Error(
924
+ `Cannot slice string at byte indices [${actualLow}:${actualHigh}] because it would create invalid UTF-8. ` +
925
+ `This is a limitation of JavaScript's string handling.`,
926
+ )
927
+ }
928
+ }
929
+
930
+ /**
931
+ * Converts a Slice<number> (byte array) to a string using TextDecoder.
932
+ * @param bytes The Slice<number> to convert.
933
+ * @returns The resulting string.
934
+ */
935
+ export const bytesToString = (
936
+ bytes: Slice<number> | Uint8Array | string,
937
+ ): string => {
938
+ if (bytes === null) return ''
939
+ // If it's already a string, just return it
940
+ if (typeof bytes === 'string') return bytes
941
+ if (bytes instanceof Uint8Array) {
942
+ return new TextDecoder().decode(bytes)
943
+ }
944
+ // Ensure we get a plain number[] for Uint8Array.from
945
+ let byteArray: number[]
946
+ if (isComplexSlice(bytes)) {
947
+ // For complex slices, extract the relevant part of the backing array
948
+ byteArray = bytes.__meta__.backing.slice(
949
+ bytes.__meta__.offset,
950
+ bytes.__meta__.offset + bytes.__meta__.length,
951
+ )
952
+ } else {
953
+ // For simple T[] slices
954
+ byteArray = bytes
955
+ }
956
+ return new TextDecoder().decode(Uint8Array.from(byteArray))
957
+ }
958
+
959
+ /**
960
+ * Converts a string to a Uint8Array (byte slice).
961
+ * @param s The input string.
962
+ * @returns A Uint8Array representing the UTF-8 bytes of the string.
963
+ */
964
+ export function stringToBytes(s: string): Uint8Array {
965
+ return new TextEncoder().encode(s)
966
+ }
967
+
968
+ /**
969
+ * Handles string() conversion for values that could be either string or []byte.
970
+ * Used for generic type parameters with constraint []byte|string.
971
+ * @param value Value that is either a string or Uint8Array
972
+ * @returns The string representation
973
+ */
974
+ export function genericBytesOrStringToString(
975
+ value: string | Uint8Array,
976
+ ): string {
977
+ if (typeof value === 'string') {
978
+ return value
979
+ }
980
+ return bytesToString(value as unknown as number[])
981
+ }
982
+
983
+ /**
984
+ * Indexes into a value that could be either a string or Uint8Array.
985
+ * Used for generic type parameters with constraint string | []byte.
986
+ * Both cases return a byte value (number).
987
+ * @param value Value that is either a string or Uint8Array
988
+ * @param index The index to access
989
+ * @returns The byte value at the specified index
990
+ */
991
+ export function indexStringOrBytes(
992
+ value: string | Uint8Array,
993
+ index: number,
994
+ ): number {
995
+ if (typeof value === 'string') {
996
+ return indexString(value, index)
997
+ } else {
998
+ // For Uint8Array, direct access returns the byte value
999
+ if (index < 0 || index >= value.length) {
1000
+ throw new Error(
1001
+ `runtime error: index out of range [${index}] with length ${value.length}`,
1002
+ )
1003
+ }
1004
+ return value[index]
1005
+ }
1006
+ }
1007
+
1008
+ /**
1009
+ * Slices a value that could be either a string or Uint8Array.
1010
+ * Used for generic type parameters with constraint string | []byte.
1011
+ * @param value Value that is either a string or Uint8Array
1012
+ * @param low Starting index (inclusive). Defaults to 0.
1013
+ * @param high Ending index (exclusive). Defaults to length.
1014
+ * @param max Capacity limit (only used for Uint8Array, ignored for strings)
1015
+ * @returns The sliced value of the same type as input
1016
+ */
1017
+ export function sliceStringOrBytes<T extends string | Uint8Array>(
1018
+ value: T,
1019
+ low?: number,
1020
+ high?: number,
1021
+ max?: number,
1022
+ ): T {
1023
+ if (typeof value === 'string') {
1024
+ // For strings, use sliceString and ignore max parameter
1025
+ return sliceString(value, low, high) as T
1026
+ } else {
1027
+ // For Uint8Array, use goSlice
1028
+ return goSlice(value as Slice<number>, low, high, max) as T
1029
+ }
1030
+ }