goscript 0.0.62 → 0.0.63

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.
@@ -12,7 +12,7 @@ import (
12
12
  )
13
13
 
14
14
  // TestAnalysisVarRefLogic verifies that the analysis correctly identifies
15
- // which variables need variable references based on actual compliance test cases
15
+ // which variables need variable references based on actual tests test cases
16
16
  func TestAnalysisVarRefLogic(t *testing.T) {
17
17
  tests := []struct {
18
18
  name string
@@ -235,8 +235,8 @@ func main() {
235
235
 
236
236
  // TestWrapperTypeDetection verifies that the analysis correctly identifies wrapper types
237
237
  func TestWrapperTypeDetection(t *testing.T) {
238
- // Use the actual compliance test case
239
- testPath := "../compliance/tests/wrapper_type_args"
238
+ // Use the actual tests test case
239
+ testPath := "../tests/tests/wrapper_type_args"
240
240
 
241
241
  // Load the package using the packages config like the main compiler does
242
242
  cfg := &packages.Config{
@@ -148,9 +148,32 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
148
148
  }
149
149
  }
150
150
 
151
+ // Check for integer division compound assignment (i /= n)
152
+ // Go's integer division truncates, but JavaScript's /= doesn't
153
+ // We need to convert `i /= n` to `i = Math.trunc(i / n)`
154
+ isIntegerDivisionAssign := false
155
+ if tok == token.QUO_ASSIGN && len(lhs) == 1 && len(rhs) == 1 {
156
+ if lhsType := c.pkg.TypesInfo.TypeOf(lhs[0]); lhsType != nil {
157
+ if basic, ok := lhsType.Underlying().(*types.Basic); ok {
158
+ if basic.Info()&types.IsInteger != 0 {
159
+ isIntegerDivisionAssign = true
160
+ }
161
+ }
162
+ }
163
+ }
164
+
151
165
  // Only write the assignment operator for regular variables, not for map assignments handled by mapSet
152
166
  if isMapIndexLHS && len(lhs) == 1 { // Only skip operator if it's a single map assignment
153
167
  // Continue, we've already written part of the mapSet() function call
168
+ } else if isIntegerDivisionAssign {
169
+ // For integer division compound assignment, convert `i /= n` to `i = Math.trunc(i / n)`
170
+ c.tsw.WriteLiterally(" = Math.trunc(")
171
+ // Write LHS again as first operand of division
172
+ if err := c.WriteValueExpr(lhs[0]); err != nil {
173
+ return err
174
+ }
175
+ c.tsw.WriteLiterally(" / ")
176
+ // RHS will be written below, then we close the parenthesis
154
177
  } else {
155
178
  c.tsw.WriteLiterally(" ")
156
179
  if err := c.writeAssignmentOperator(tok); err != nil {
@@ -298,6 +321,11 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
298
321
  c.tsw.WriteLiterally(")")
299
322
  }
300
323
 
324
+ // Close the parenthesis for integer division compound assignment
325
+ if isIntegerDivisionAssign {
326
+ c.tsw.WriteLiterally(")")
327
+ }
328
+
301
329
  // If the LHS was a single map index, close the mapSet call
302
330
  if isMapIndexLHS && len(lhs) == 1 {
303
331
  c.tsw.WriteLiterally(")")
@@ -24,7 +24,7 @@ func TestEmitBuiltinOption(t *testing.T) {
24
24
  log.SetLevel(logrus.DebugLevel)
25
25
  le := logrus.NewEntry(log)
26
26
 
27
- // Case 1: DisableEmitBuiltin = true (default behavior in compliance tests)
27
+ // Case 1: DisableEmitBuiltin = true (default behavior in tests tests)
28
28
  t.Run("DisableEmitBuiltin=true", func(t *testing.T) {
29
29
  outputDir := filepath.Join(tempDir, "disabled")
30
30
  config := &Config{
@@ -593,7 +593,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
593
593
  c.codeWriter = NewTSCodeWriter(of)
594
594
 
595
595
  // Pass analysis to compiler
596
- goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, c.Analysis)
596
+ goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, c.Analysis, c.fullPath)
597
597
 
598
598
  // Add import for the goscript runtime using namespace import and alias
599
599
  c.codeWriter.WriteLinef("import * as $ from %q", "@goscript/builtin/index.js")
@@ -701,15 +701,18 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
701
701
  }
702
702
 
703
703
  // Write synthetic imports (for promoted methods from embedded structs)
704
- // Sort by package name for consistent output
705
- var syntheticPkgNames []string
706
- for pkgName := range c.Analysis.SyntheticImports {
707
- syntheticPkgNames = append(syntheticPkgNames, pkgName)
708
- }
709
- slices.Sort(syntheticPkgNames)
710
- for _, pkgName := range syntheticPkgNames {
711
- imp := c.Analysis.SyntheticImports[pkgName]
712
- c.codeWriter.WriteImport(pkgName, imp.importPath+"/index.js")
704
+ // Use per-file synthetic imports to avoid adding unused imports
705
+ if syntheticImports := c.Analysis.SyntheticImportsPerFile[c.fullPath]; syntheticImports != nil {
706
+ // Sort by package name for consistent output
707
+ var syntheticPkgNames []string
708
+ for pkgName := range syntheticImports {
709
+ syntheticPkgNames = append(syntheticPkgNames, pkgName)
710
+ }
711
+ slices.Sort(syntheticPkgNames)
712
+ for _, pkgName := range syntheticPkgNames {
713
+ imp := syntheticImports[pkgName]
714
+ c.codeWriter.WriteImport(pkgName, imp.importPath+"/index.js")
715
+ }
713
716
  }
714
717
 
715
718
  c.codeWriter.WriteLine("") // Add a newline after imports
@@ -731,6 +734,10 @@ type GoToTSCompiler struct {
731
734
 
732
735
  analysis *Analysis
733
736
 
737
+ // currentFilePath is the path of the file being compiled
738
+ // Used for looking up per-file synthetic imports
739
+ currentFilePath string
740
+
734
741
  // Context flags
735
742
  insideAddressOf bool // true when processing operand of & operator
736
743
 
@@ -739,15 +746,15 @@ type GoToTSCompiler struct {
739
746
  renamedVars map[types.Object]string
740
747
  }
741
748
 
742
- // It initializes the compiler with a `TSCodeWriter` for output,
743
- // Go package information (`packages.Package`), and pre-computed
744
- // analysis results (`Analysis`) to guide the translation process.
745
- func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
749
+ // NewGoToTSCompiler creates a new GoToTSCompiler with a TSCodeWriter for output,
750
+ // Go package information, pre-computed analysis results, and the current file path.
751
+ func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis, filePath string) *GoToTSCompiler {
746
752
  return &GoToTSCompiler{
747
- tsw: tsw,
748
- pkg: pkg,
749
- analysis: analysis,
750
- renamedVars: make(map[types.Object]string),
753
+ tsw: tsw,
754
+ pkg: pkg,
755
+ analysis: analysis,
756
+ currentFilePath: filePath,
757
+ renamedVars: make(map[types.Object]string),
751
758
  }
752
759
  }
753
760
 
@@ -12,10 +12,10 @@ import (
12
12
  "sync/atomic"
13
13
  "testing"
14
14
 
15
- "github.com/aperturerobotics/goscript/compliance"
15
+ "github.com/aperturerobotics/goscript/tests"
16
16
  )
17
17
 
18
- // NOTE: this is here instead of compliance/compliance_test.go so coverage ends up in this package.
18
+ // NOTE: this is here instead of tests/tests_test.go so coverage ends up in this package.
19
19
 
20
20
  func TestCompliance(t *testing.T) {
21
21
  // Get workspace directory (project root)
@@ -26,7 +26,7 @@ func TestCompliance(t *testing.T) {
26
26
  workspaceDir = filepath.Join(workspaceDir, "..")
27
27
 
28
28
  // First collect all test paths
29
- testsDir := filepath.Join(workspaceDir, "compliance/tests")
29
+ testsDir := filepath.Join(workspaceDir, "tests/tests")
30
30
  dirs, err := os.ReadDir(testsDir)
31
31
  if err != nil {
32
32
  t.Fatalf("failed to read tests dir: %v", err)
@@ -50,7 +50,7 @@ func TestCompliance(t *testing.T) {
50
50
  slices.Sort(testPaths)
51
51
 
52
52
  // limit concurrency
53
- simulLimit := make(chan struct{}, runtime.GOMAXPROCS(-1)*2)
53
+ simulLimit := make(chan struct{}, runtime.GOMAXPROCS(-1))
54
54
  for range cap(simulLimit) {
55
55
  simulLimit <- struct{}{}
56
56
  }
@@ -71,7 +71,7 @@ func TestCompliance(t *testing.T) {
71
71
  defer func() {
72
72
  simulLimit <- struct{}{}
73
73
  }()
74
- compliance.RunGoScriptTestDir(t, workspaceDir, path) // Pass workspaceDir
74
+ tests.RunGoScriptTestDir(t, workspaceDir, path) // Pass workspaceDir
75
75
 
76
76
  // Remove dir if everything passed
77
77
  if !t.Failed() {
@@ -89,13 +89,13 @@ func TestCompliance(t *testing.T) {
89
89
  t.Run("typecheck", func(t *testing.T) {
90
90
  t.Helper()
91
91
  if failed {
92
- t.Log("at least one compliance test failed: skipping typecheck")
92
+ t.Log("at least one tests test failed: skipping typecheck")
93
93
  t.SkipNow()
94
94
  }
95
95
 
96
96
  // NOTE: typecheck does not yet pass, so we skip for now.
97
97
  if ranTests.Load() != 0 {
98
- t.Log("at least one compliance test ran: skipping typecheck")
98
+ t.Log("at least one tests test ran: skipping typecheck")
99
99
  t.SkipNow()
100
100
  }
101
101
 
@@ -106,7 +106,7 @@ func TestCompliance(t *testing.T) {
106
106
  }
107
107
 
108
108
  // Create global typecheck tsconfig
109
- tsconfigPath := compliance.WriteGlobalTypeCheckConfig(t, parentModPath, workspaceDir)
109
+ tsconfigPath := tests.WriteGlobalTypeCheckConfig(t, parentModPath, workspaceDir)
110
110
 
111
111
  // Run TypeScript type checking
112
112
  typecheckDir := filepath.Dir(tsconfigPath)
@@ -129,7 +129,7 @@ func TestCompliance(t *testing.T) {
129
129
  }
130
130
 
131
131
  // getParentGoModulePath is a helper function to get the parent Go module path
132
- // This is similar to the one in compliance.go but simplified for use in tests
132
+ // This is similar to the one in tests.go but simplified for use in tests
133
133
  func getParentGoModulePath() (string, error) {
134
134
  cmd := exec.Command("go", "list", "-m")
135
135
  output, err := cmd.Output()
@@ -223,8 +223,8 @@ func (c *GoToTSCompiler) writeArgumentWithTypeHandling(arg ast.Expr, funcSig *ty
223
223
  return nil
224
224
  }
225
225
 
226
- // resolveImportAlias returns the import alias for a given package
227
- // This is the single source of truth for import alias resolution
226
+ // resolveImportAlias returns the import alias for a given package.
227
+ // This is the single source of truth for import alias resolution.
228
228
  func (c *GoToTSCompiler) resolveImportAlias(pkg *types.Package) (alias string, found bool) {
229
229
  if c.analysis == nil || pkg == nil {
230
230
  return "", false
@@ -232,11 +232,18 @@ func (c *GoToTSCompiler) resolveImportAlias(pkg *types.Package) (alias string, f
232
232
 
233
233
  pkgName := pkg.Name()
234
234
 
235
- // Try to match by the actual package name
235
+ // Try to match by the actual package name in regular imports
236
236
  if _, exists := c.analysis.Imports[pkgName]; exists {
237
237
  return pkgName, true
238
238
  }
239
239
 
240
+ // Also check synthetic imports for the current file
241
+ if syntheticImports := c.analysis.SyntheticImportsPerFile[c.currentFilePath]; syntheticImports != nil {
242
+ if _, exists := syntheticImports[pkgName]; exists {
243
+ return pkgName, true
244
+ }
245
+ }
246
+
240
247
  return "", false
241
248
  }
242
249
 
package/compiler/expr.go CHANGED
@@ -472,6 +472,26 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
472
472
  }
473
473
  }
474
474
 
475
+ // Check if this is integer division (Go's / operator truncates for integers)
476
+ isIntegerDivision := false
477
+ if exp.Op == token.QUO && c.pkg != nil && c.pkg.TypesInfo != nil {
478
+ leftType := c.pkg.TypesInfo.TypeOf(exp.X)
479
+ rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
480
+ if leftType != nil && rightType != nil {
481
+ leftBasic, leftIsBasic := leftType.Underlying().(*types.Basic)
482
+ rightBasic, rightIsBasic := rightType.Underlying().(*types.Basic)
483
+ if leftIsBasic && rightIsBasic {
484
+ // Check if both are integer types (not floating point)
485
+ isIntegerDivision = (leftBasic.Info()&types.IsInteger != 0) && (rightBasic.Info()&types.IsInteger != 0)
486
+ }
487
+ }
488
+ }
489
+
490
+ if isIntegerDivision {
491
+ // Wrap integer division in Math.trunc() to match Go's integer division behavior
492
+ c.tsw.WriteLiterally("Math.trunc(")
493
+ }
494
+
475
495
  if needsNumberCast {
476
496
  c.tsw.WriteLiterally("Number(")
477
497
  }
@@ -495,6 +515,10 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
495
515
  return fmt.Errorf("failed to write binary expression right operand: %w", err)
496
516
  }
497
517
 
518
+ if isIntegerDivision {
519
+ c.tsw.WriteLiterally(")") // Close Math.trunc()
520
+ }
521
+
498
522
  if isBitwise {
499
523
  c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
500
524
  }
package/compiler/spec.go CHANGED
@@ -638,8 +638,10 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
638
638
  // Skip writing the import if it was already written as a synthetic import
639
639
  // This prevents duplicate imports when a file needs an import both from
640
640
  // its AST and for promoted methods from embedded structs
641
- if _, isSynthetic := c.analysis.SyntheticImports[impName]; isSynthetic {
642
- return
641
+ if syntheticImports := c.analysis.SyntheticImportsPerFile[c.currentFilePath]; syntheticImports != nil {
642
+ if _, isSynthetic := syntheticImports[impName]; isSynthetic {
643
+ return
644
+ }
643
645
  }
644
646
 
645
647
  c.tsw.WriteImport(impName, tsImportPath+"/index.js")
@@ -320,6 +320,59 @@ func (c *GoToTSCompiler) writeMultiVarAssignFromCall(lhs []ast.Expr, callExpr *a
320
320
  }
321
321
 
322
322
  if allNewVars && anyNewVars {
323
+ // Check if any variable needs VarRef - if so, we need a different approach
324
+ anyNeedsVarRef := false
325
+ needsVarRefVars := make([]bool, len(lhs))
326
+ for i, lhsExpr := range lhs {
327
+ if ident, ok := lhsExpr.(*ast.Ident); ok && ident.Name != "_" {
328
+ if obj := c.pkg.TypesInfo.Defs[ident]; obj != nil {
329
+ if c.analysis.NeedsVarRef(obj) {
330
+ needsVarRefVars[i] = true
331
+ anyNeedsVarRef = true
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ if anyNeedsVarRef {
338
+ // Use temp variables for destructuring, then wrap in VarRef as needed
339
+ c.tsw.WriteLiterally("let [")
340
+ for i, lhsExpr := range lhs {
341
+ if i != 0 {
342
+ c.tsw.WriteLiterally(", ")
343
+ }
344
+ if ident, ok := lhsExpr.(*ast.Ident); ok {
345
+ if ident.Name == "_" {
346
+ // Empty slot for blank identifier
347
+ } else if needsVarRefVars[i] {
348
+ c.tsw.WriteLiterally("_varref_tmp_")
349
+ c.tsw.WriteLiterally(ident.Name)
350
+ } else {
351
+ c.WriteIdent(ident, false)
352
+ }
353
+ } else {
354
+ c.WriteValueExpr(lhsExpr)
355
+ }
356
+ }
357
+ c.tsw.WriteLiterally("] = ")
358
+ c.WriteValueExpr(callExpr)
359
+ c.tsw.WriteLine("")
360
+
361
+ // Now declare the VarRef-wrapped variables
362
+ for i, lhsExpr := range lhs {
363
+ if ident, ok := lhsExpr.(*ast.Ident); ok && ident.Name != "_" && needsVarRefVars[i] {
364
+ c.tsw.WriteLiterally("let ")
365
+ c.WriteIdent(ident, false)
366
+ c.tsw.WriteLiterally(" = $.varRef(_varref_tmp_")
367
+ c.tsw.WriteLiterally(ident.Name)
368
+ // Add non-null assertion to handle cases where the tuple type includes null
369
+ c.tsw.WriteLiterally("!)")
370
+ c.tsw.WriteLine("")
371
+ }
372
+ }
373
+ return nil
374
+ }
375
+
323
376
  c.tsw.WriteLiterally("let [")
324
377
 
325
378
  for i, lhsExpr := range lhs {
package/compiler/stmt.go CHANGED
@@ -167,9 +167,17 @@ func (c *GoToTSCompiler) WriteStmtIncDec(stmt *ast.IncDecStmt) error {
167
167
  func (c *GoToTSCompiler) WriteStmtBranch(stmt *ast.BranchStmt) error {
168
168
  switch stmt.Tok {
169
169
  case token.BREAK:
170
- c.tsw.WriteLine("break") // No semicolon needed
170
+ if stmt.Label != nil {
171
+ c.tsw.WriteLinef("break %s", stmt.Label.Name)
172
+ } else {
173
+ c.tsw.WriteLine("break")
174
+ }
171
175
  case token.CONTINUE:
172
- c.tsw.WriteLine("continue") // No semicolon needed
176
+ if stmt.Label != nil {
177
+ c.tsw.WriteLinef("continue %s", stmt.Label.Name)
178
+ } else {
179
+ c.tsw.WriteLine("continue")
180
+ }
173
181
  case token.GOTO:
174
182
  // TypeScript doesn't support goto, but we can handle it by skipping it
175
183
  // since the labeled statement restructuring should handle the control flow
@@ -484,17 +492,10 @@ func (c *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
484
492
  }
485
493
  c.tsw.WriteLiterally(") ")
486
494
 
487
- if err := c.WriteStmt(exp.Body); err != nil {
495
+ if err := c.writeIfBody(exp); err != nil {
488
496
  return err
489
497
  }
490
498
 
491
- if exp.Else != nil {
492
- c.tsw.WriteLiterally(" else ")
493
- if err := c.WriteStmt(exp.Else); err != nil {
494
- return err
495
- }
496
- }
497
-
498
499
  c.tsw.Indent(-1)
499
500
  c.tsw.WriteLine("}")
500
501
  return nil
@@ -507,14 +508,31 @@ func (c *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
507
508
  }
508
509
  c.tsw.WriteLiterally(") ")
509
510
 
510
- if err := c.WriteStmt(exp.Body); err != nil {
511
+ return c.writeIfBody(exp)
512
+ }
513
+
514
+ // writeIfBody writes the if body and optional else clause, handling newline suppression.
515
+ func (c *GoToTSCompiler) writeIfBody(exp *ast.IfStmt) error {
516
+ hasElse := exp.Else != nil
517
+ if err := c.WriteStmtBlock(exp.Body, hasElse); err != nil {
511
518
  return err
512
519
  }
513
520
 
514
- if exp.Else != nil {
521
+ if hasElse {
515
522
  c.tsw.WriteLiterally(" else ")
516
- if err := c.WriteStmt(exp.Else); err != nil {
517
- return err
523
+ switch elseStmt := exp.Else.(type) {
524
+ case *ast.BlockStmt:
525
+ if err := c.WriteStmtBlock(elseStmt, false); err != nil {
526
+ return err
527
+ }
528
+ case *ast.IfStmt:
529
+ if err := c.WriteStmtIf(elseStmt); err != nil {
530
+ return err
531
+ }
532
+ default:
533
+ if err := c.WriteStmt(exp.Else); err != nil {
534
+ return err
535
+ }
518
536
  }
519
537
  }
520
538
 
@@ -26,11 +26,9 @@ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
26
26
  underlying := typ.Underlying()
27
27
  switch t := underlying.(type) {
28
28
  case *types.Basic:
29
- tsTypeName, _ := GoBuiltinToTypescript(t.Name())
30
- if tsTypeName == "" {
31
- tsTypeName = t.Name() // Fallback
32
- }
33
- c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", tsTypeName)
29
+ // Use Go type name (e.g., "int") not TypeScript type name (e.g., "number")
30
+ // The reflect system needs Go type names to correctly determine Kind()
31
+ c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", t.Name())
34
32
  // Note: The original 'case *types.Named:' here for 'underlying' is intentionally omitted.
35
33
  // If typ.Underlying() is *types.Named (e.g. type T1 MyInt; type T2 T1;),
36
34
  // then writeTypeInfoObject(typ.Underlying()) would be called in some contexts,
package/compiler/type.go CHANGED
@@ -718,7 +718,7 @@ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode
718
718
  func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
719
719
  var typeStr strings.Builder
720
720
  writer := NewTSCodeWriter(&typeStr)
721
- tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
721
+ tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis, c.currentFilePath)
722
722
  tempCompiler.WriteGoType(goType, GoTypeContextGeneral)
723
723
  return typeStr.String()
724
724
  }
@@ -730,7 +730,7 @@ func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
730
730
  func (c *GoToTSCompiler) getASTTypeString(astType ast.Expr, goType types.Type) string {
731
731
  var typeStr strings.Builder
732
732
  writer := NewTSCodeWriter(&typeStr)
733
- tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
733
+ tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis, c.currentFilePath)
734
734
 
735
735
  if astType != nil {
736
736
  // Use AST-based type writing to preserve qualified names
@@ -1,3 +1,43 @@
1
+ /**
2
+ * wrapSliceProxy wraps a SliceProxy in a Proxy to intercept index access
3
+ * and route it through the backing array.
4
+ */
5
+ function wrapSliceProxy(proxy) {
6
+ const handler = {
7
+ get(target, prop) {
8
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
9
+ const index = Number(prop);
10
+ if (index >= 0 && index < target.__meta__.length) {
11
+ return target.__meta__.backing[target.__meta__.offset + index];
12
+ }
13
+ throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
14
+ }
15
+ if (prop === 'length') {
16
+ return target.__meta__.length;
17
+ }
18
+ if (prop === '__meta__') {
19
+ return target.__meta__;
20
+ }
21
+ return Reflect.get(target, prop);
22
+ },
23
+ set(target, prop, value) {
24
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
25
+ const index = Number(prop);
26
+ if (index >= 0 && index < target.__meta__.length) {
27
+ target.__meta__.backing[target.__meta__.offset + index] = value;
28
+ target[index] = value; // Also update the proxy target for consistency
29
+ return true;
30
+ }
31
+ throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
32
+ }
33
+ if (prop === 'length' || prop === '__meta__') {
34
+ return false;
35
+ }
36
+ return Reflect.set(target, prop, value);
37
+ },
38
+ };
39
+ return new Proxy(proxy, handler);
40
+ }
1
41
  // asArray converts a slice to a JavaScript array.
2
42
  export function asArray(slice) {
3
43
  if (slice === null || slice === undefined) {
@@ -65,41 +105,7 @@ export const makeSlice = (length, capacity, typeHint) => {
65
105
  length: length,
66
106
  capacity: actualCapacity,
67
107
  };
68
- // Create a proper Proxy with the handler for SliceProxy behavior
69
- const handler = {
70
- get(target, prop) {
71
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
72
- const index = Number(prop);
73
- if (index >= 0 && index < target.__meta__.length) {
74
- return target.__meta__.backing[target.__meta__.offset + index];
75
- }
76
- throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
77
- }
78
- if (prop === 'length') {
79
- return target.__meta__.length;
80
- }
81
- if (prop === '__meta__') {
82
- return target.__meta__;
83
- }
84
- return Reflect.get(target, prop);
85
- },
86
- set(target, prop, value) {
87
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
88
- const index = Number(prop);
89
- if (index >= 0 && index < target.__meta__.length) {
90
- target.__meta__.backing[target.__meta__.offset + index] = value;
91
- target[index] = value; // Also update the proxy target for consistency
92
- return true;
93
- }
94
- throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
95
- }
96
- if (prop === 'length' || prop === '__meta__') {
97
- return false;
98
- }
99
- return Reflect.set(target, prop, value);
100
- },
101
- };
102
- return new Proxy(proxy, handler);
108
+ return wrapSliceProxy(proxy);
103
109
  }
104
110
  const actualCapacity = capacity === undefined ? length : capacity;
105
111
  if (length < 0 || actualCapacity < 0 || length > actualCapacity) {
@@ -581,7 +587,7 @@ export function append(slice, ...elements) {
581
587
  length: newLength,
582
588
  capacity: oldCapacity,
583
589
  };
584
- return resultProxy;
590
+ return wrapSliceProxy(resultProxy);
585
591
  }
586
592
  // Case 2: Reallocation is needed.
587
593
  let newCapacity = oldCapacity;
@@ -613,7 +619,7 @@ export function append(slice, ...elements) {
613
619
  length: newLength,
614
620
  capacity: newCapacity,
615
621
  };
616
- return resultProxy;
622
+ return wrapSliceProxy(resultProxy);
617
623
  }
618
624
  export function copy(dst, src) {
619
625
  if (dst === null) {