@voxgig/sdkgen 4.5.2 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/action/check.js +15 -0
  3. package/dist/action/check.js.map +1 -1
  4. package/dist/cmp/AgentGuide.js +1 -1
  5. package/dist/cmp/AgentGuide.js.map +1 -1
  6. package/dist/cmp/AgentGuideContent.d.ts +1 -1
  7. package/dist/cmp/AgentGuideContent.js +8 -2
  8. package/dist/cmp/AgentGuideContent.js.map +1 -1
  9. package/dist/cmp/Feature.js +17 -0
  10. package/dist/cmp/Feature.js.map +1 -1
  11. package/dist/cmp/FeatureDocs.d.ts +1 -1
  12. package/dist/cmp/FeatureDocs.js +6 -1
  13. package/dist/cmp/FeatureDocs.js.map +1 -1
  14. package/dist/cmp/ReadmeExplanation.js +3 -1
  15. package/dist/cmp/ReadmeExplanation.js.map +1 -1
  16. package/dist/cmp/ReadmeFeatures.js +1 -1
  17. package/dist/cmp/ReadmeFeatures.js.map +1 -1
  18. package/dist/cmp/ReadmeRefFeatures.js +1 -1
  19. package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
  20. package/dist/cmp/ReadmeTop.js +1 -1
  21. package/dist/helpers/applicability.d.ts +6 -0
  22. package/dist/helpers/applicability.js +103 -0
  23. package/dist/helpers/applicability.js.map +1 -0
  24. package/dist/helpers/collectDeps.js +4 -2
  25. package/dist/helpers/collectDeps.js.map +1 -1
  26. package/dist/helpers/serverVars.d.ts +2 -1
  27. package/dist/helpers/serverVars.js +17 -0
  28. package/dist/helpers/serverVars.js.map +1 -1
  29. package/dist/sdkgen.d.ts +3 -2
  30. package/dist/sdkgen.js +9 -2
  31. package/dist/sdkgen.js.map +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/dist/utility.js +7 -1
  34. package/dist/utility.js.map +1 -1
  35. package/model/sdkgen.aon +39 -0
  36. package/package.json +1 -1
  37. package/project/.sdk/model/feature/feature-index.aon +1 -0
  38. package/project/.sdk/model/feature/secrets.aon +118 -0
  39. package/project/.sdk/model/target/ts.aon +8 -1
  40. package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
  41. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
  42. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
  43. package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
  44. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
  45. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
  46. package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
  47. package/project/.sdk/src/cmp/go/Main_go.ts +6 -2
  48. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
  49. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
  50. package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
  51. package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
  52. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
  53. package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
  54. package/project/.sdk/src/cmp/java/Main_java.ts +5 -1
  55. package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
  56. package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
  57. package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
  58. package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
  59. package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
  60. package/project/.sdk/src/cmp/js/Main_js.ts +5 -1
  61. package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
  62. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
  63. package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
  64. package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
  65. package/project/.sdk/src/cmp/lua/Main_lua.ts +5 -1
  66. package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
  67. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
  68. package/project/.sdk/src/cmp/perl/Main_perl.ts +5 -1
  69. package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
  70. package/project/.sdk/src/cmp/php/Main_php.ts +5 -1
  71. package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
  72. package/project/.sdk/src/cmp/py/Main_py.ts +5 -1
  73. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
  74. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
  75. package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
  76. package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
  77. package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
  78. package/project/.sdk/src/cmp/rb/Main_rb.ts +5 -1
  79. package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
  80. package/project/.sdk/src/cmp/rust/Main_rust.ts +5 -1
  81. package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
  82. package/project/.sdk/src/cmp/scala/Main_scala.ts +5 -1
  83. package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
  84. package/project/.sdk/src/cmp/ts/Config_ts.ts +5 -1
  85. package/project/.sdk/src/cmp/ts/Main_ts.ts +57 -1
  86. package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
  87. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
  88. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
  89. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
  90. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +67 -7
  91. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +27 -4
  92. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
  93. package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
  94. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
  95. package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
  96. package/project/.sdk/tm/c/utility/make_options.c +22 -0
  97. package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
  98. package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
  99. package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
  100. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
  101. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
  102. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
  103. package/project/.sdk/tm/go/core/context.go +1 -1
  104. package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
  105. package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
  106. package/project/.sdk/tm/go/feature/test_feature.go +12 -5
  107. package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
  108. package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
  109. package/project/.sdk/tm/go/test/runner_test.go +46 -0
  110. package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
  111. package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
  112. package/project/.sdk/tm/go/utility/fetcher.go +2 -2
  113. package/project/.sdk/tm/go/utility/make_options.go +24 -5
  114. package/project/.sdk/tm/go/utility/make_point.go +1 -1
  115. package/project/.sdk/tm/go/utility/make_spec.go +17 -1
  116. package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
  117. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
  118. package/project/.sdk/tm/go/utility/transform_request.go +29 -1
  119. package/project/.sdk/tm/go/utility/transform_response.go +20 -1
  120. package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
  121. package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
  122. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
  123. package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
  124. package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
  125. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
  126. package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
  127. package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
  128. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
  129. package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
  130. package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
  131. package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
  132. package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
  133. package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
  134. package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
  135. package/project/.sdk/tm/py/test/runner.py +32 -0
  136. package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
  137. package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
  138. package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
  139. package/project/.sdk/tm/rust/utility/make_options.rs +25 -0
  140. package/project/.sdk/tm/scala/utility/Make.scala +17 -0
  141. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
  142. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
  143. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +1221 -0
  144. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +381 -0
  145. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +151 -0
  146. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +42 -0
  147. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
  148. package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
  149. package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
  150. package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
  151. package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
  152. package/project/.sdk/tm/ts/test/omni.ts +86 -0
  153. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
  154. package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
  155. package/project/.sdk/tm/ts/test/utility.ts +122 -0
  156. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +692 -0
  157. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +238 -0
  158. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
  159. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
  160. package/project/.sdk/tm/zig/core/utility.zig +23 -0
  161. package/project/sdkgen-package.json +2 -1
  162. package/src/action/check.ts +18 -0
  163. package/src/cmp/AgentGuide.ts +1 -1
  164. package/src/cmp/AgentGuideContent.ts +9 -2
  165. package/src/cmp/Feature.ts +19 -0
  166. package/src/cmp/FeatureDocs.ts +7 -1
  167. package/src/cmp/ReadmeExplanation.ts +4 -1
  168. package/src/cmp/ReadmeFeatures.ts +1 -1
  169. package/src/cmp/ReadmeRefFeatures.ts +1 -1
  170. package/src/cmp/ReadmeTop.ts +1 -1
  171. package/src/helpers/applicability.ts +123 -0
  172. package/src/helpers/collectDeps.ts +5 -1
  173. package/src/helpers/serverVars.ts +19 -0
  174. package/src/sdkgen.ts +9 -1
  175. package/src/utility.ts +10 -1
  176. package/project/.sdk/tm/ts/test/runner.ts +0 -425
@@ -1,3 +1,6 @@
1
+ // VENDORED: @voxgig/struct 0.1.3 (go/voxgigstruct.go)
2
+ // Source: https://github.com/voxgig/struct @ 94409354d0a60fda8098c83e290583b7dee76c84
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
1
4
  /* Copyright (c) 2025 Voxgig Ltd. MIT LICENSE. */
2
5
 
3
6
  /* Voxgig Struct
@@ -53,10 +56,10 @@ package voxgigstruct
53
56
 
54
57
  import (
55
58
  "encoding/json"
59
+ "errors"
56
60
  "fmt"
57
61
  "math"
58
62
  "math/bits"
59
- "net/url"
60
63
  "reflect"
61
64
  "regexp"
62
65
  "sort"
@@ -135,8 +138,8 @@ const (
135
138
  T_map = 1 << 13
136
139
  T_instance = 1 << 12
137
140
  // 4 bits reserved
138
- T_scalar = 1 << 7
139
- T_node = 1 << 6
141
+ T_scalar = 1 << 7
142
+ T_node = 1 << 6
140
143
  )
141
144
 
142
145
  // TYPENAME maps bit position (via leading zeros count) to type name string.
@@ -167,6 +170,20 @@ type _sentinel struct{ name string }
167
170
  var SKIP = &_sentinel{"SKIP"}
168
171
  var DELETE = &_sentinel{"DELETE"}
169
172
 
173
+ // NOVAL is Go's no-value: the third state canonical spells `undefined`.
174
+ //
175
+ // Go's value domain has nil and it has real values, and nothing between,
176
+ // so `Typify(nil)` is T_scalar|T_null and T_noval was unreachable - this
177
+ // port could not express `typify()` as distinct from `typify(null)`, which
178
+ // the corpus pins as two different results (1073741824 vs 4194432). Every
179
+ // other port models the distinction, natively or with a sentinel of its
180
+ // own; NOVAL is Go's.
181
+ //
182
+ // It is recognised AHEAD of the reflect dispatch, in Typify, IsEmpty and
183
+ // Clone. That ordering is the whole trick: a Go sentinel is a struct
184
+ // pointer, so reflect would otherwise class it as a node.
185
+ var NOVAL = &_sentinel{"NOVAL"}
186
+
170
187
  // Regex matching integer keys (including negative).
171
188
  var reIntegerKey = regexp.MustCompile(`^[-0-9]+$`)
172
189
 
@@ -364,6 +381,10 @@ func (inj *Injection) String(prefix ...string) string {
364
381
  }
365
382
 
366
383
  // Function applied to each node and leaf when walking a node structure depth first.
384
+ // The `path` argument is a single mutable slice per depth, shared across all
385
+ // callback invocations for the lifetime of a top-level Walk call. Callbacks
386
+ // that need to retain the path MUST clone it (e.g. `append([]string(nil), path...)`);
387
+ // otherwise its contents will be overwritten by subsequent visits.
367
388
  type WalkApply func(
368
389
  // Map keys are strings, list keys are numbers, top key is nil
369
390
  key *string,
@@ -418,7 +439,26 @@ func IsKey(val any) bool {
418
439
  }
419
440
 
420
441
  // Check for an "empty" value - nil, empty string, array, object.
442
+
443
+ // denoval collapses NOVAL to nil. Canonical distinguishes undefined from
444
+ // null in exactly one place - typify - and treats them alike everywhere
445
+ // else (`null == val` in JavaScript is true for both). The functions below
446
+ // follow canonical, so they take a value that has already been collapsed.
447
+ //
448
+ // The Group A readers - GetDef, GetProp, GetElem and (through GetProp)
449
+ // HasKey - collapse it on the way in AND on the way out, so a NOVAL reached
450
+ // as a container, a key, or a stored value reads as absent and yields the
451
+ // alt. That is canonical: `getprop({a: undefined}, 'a', alt)` is alt, and
452
+ // `haskey` is false, because `undefined == null` in JavaScript.
453
+ func denoval(val any) any {
454
+ if NOVAL == val {
455
+ return nil
456
+ }
457
+ return val
458
+ }
459
+
421
460
  func IsEmpty(val any) bool {
461
+ val = denoval(val)
422
462
  if val == nil {
423
463
  return true
424
464
  }
@@ -440,8 +480,9 @@ func IsFunc(val any) bool {
440
480
  return reflect.ValueOf(val).Kind() == reflect.Func
441
481
  }
442
482
 
443
- // Get a defined value. Returns alt if val is nil.
483
+ // Get a defined value. Returns alt if val is nil or NOVAL.
444
484
  func GetDef(val any, alt any) any {
485
+ val = denoval(val)
445
486
  if nil == val {
446
487
  return alt
447
488
  }
@@ -456,6 +497,12 @@ func Typify(value any) int {
456
497
  return T_scalar | T_null
457
498
  }
458
499
 
500
+ // Before the reflect dispatch: NOVAL is a struct pointer, and reflect
501
+ // would class it as a node.
502
+ if NOVAL == value {
503
+ return T_noval
504
+ }
505
+
459
506
  if _, ok := value.(*ListRef[any]); ok {
460
507
  return T_node | T_list
461
508
  }
@@ -507,7 +554,7 @@ func Typename(t int) string {
507
554
  if t <= 0 {
508
555
  return S_any
509
556
  }
510
- idx := bits.LeadingZeros32(uint32(t))
557
+ idx := bits.LeadingZeros32(uint32(t)) // #nosec G115 -- t is a small type bitcode (well under 2^31).
511
558
  if idx < len(TYPENAME) && TYPENAME[idx] != "" {
512
559
  return TYPENAME[idx]
513
560
  }
@@ -701,6 +748,9 @@ func GetElem(val any, key any, alts ...any) any {
701
748
  alt = alts[0]
702
749
  }
703
750
 
751
+ val = denoval(val)
752
+ key = denoval(key)
753
+
704
754
  if nil == val || nil == key {
705
755
  return alt
706
756
  }
@@ -726,7 +776,7 @@ func GetElem(val any, key any, alts ...any) any {
726
776
  }
727
777
  }
728
778
 
729
- if nil == out {
779
+ if nil == denoval(out) {
730
780
  if 0 < (T_function & Typify(alt)) {
731
781
  fn := reflect.ValueOf(alt)
732
782
  results := fn.Call(nil)
@@ -751,6 +801,9 @@ func GetProp(val any, key any, alts ...any) any {
751
801
  alt = alts[0]
752
802
  }
753
803
 
804
+ val = denoval(val)
805
+ key = denoval(key)
806
+
754
807
  if nil == val || nil == key {
755
808
  return alt
756
809
  }
@@ -805,7 +858,7 @@ func GetProp(val any, key any, alts ...any) any {
805
858
 
806
859
  } else {
807
860
  valRef := reflect.ValueOf(val)
808
- if valRef.Kind() == reflect.Ptr {
861
+ if valRef.Kind() == reflect.Pointer {
809
862
  valRef = valRef.Elem()
810
863
  }
811
864
 
@@ -822,7 +875,7 @@ func GetProp(val any, key any, alts ...any) any {
822
875
  }
823
876
  }
824
877
 
825
- if nil == out {
878
+ if nil == denoval(out) {
826
879
  return alt
827
880
  }
828
881
 
@@ -858,20 +911,18 @@ func KeysOf(val any) []string {
858
911
  return make([]string, 0)
859
912
  }
860
913
 
861
-
862
914
  // Value of property with name key in node val is defined.
863
915
  func HasKey(val any, key any) bool {
864
916
  return nil != GetProp(val, key)
865
917
  }
866
918
 
867
-
868
919
  // List the sorted keys of a map or list as an array of tuples of the form [key, value].
869
920
  func Items(val any) [][2]any {
870
921
  if IsMap(val) {
871
922
  m := val.(map[string]any)
872
923
  out := make([][2]any, 0, len(m))
873
924
 
874
- keys := KeysOf(val)
925
+ keys := KeysOf(val)
875
926
  // keys := make([]string, 0, len(m))
876
927
  // for k := range m {
877
928
  // keys = append(keys, k)
@@ -895,7 +946,7 @@ func Items(val any) [][2]any {
895
946
  return out
896
947
  }
897
948
 
898
- return make([][2]any, 0, 0)
949
+ return make([][2]any, 0)
899
950
  }
900
951
 
901
952
  // List items with an optional apply callback that maps over each [key, value] tuple.
@@ -955,7 +1006,6 @@ func Filter(val any, check func([2]any) bool) []any {
955
1006
  return out
956
1007
  }
957
1008
 
958
-
959
1009
  // Escape regular expression.
960
1010
  func EscRe(s string) string {
961
1011
  if s == "" {
@@ -965,9 +1015,78 @@ func EscRe(s string) string {
965
1015
  return re.ReplaceAllString(s, `\${0}`)
966
1016
  }
967
1017
 
968
- // Escape URLs.
1018
+ // ---------------------------------------------------------------------------
1019
+ // Regex utility — uniform Re* API (see /REGEX_API.md). Go's stdlib `regexp`
1020
+ // IS the RE2 reference implementation, so the wrappers are direct passthroughs.
1021
+ // ---------------------------------------------------------------------------
1022
+
1023
+ // ReCompile compiles a pattern. Accepts a string or an already-compiled *regexp.Regexp.
1024
+ func ReCompile(pattern string) *regexp.Regexp {
1025
+ return regexp.MustCompile(pattern)
1026
+ }
1027
+
1028
+ // ReFind returns [whole, capture1, ...] for the first match, or nil.
1029
+ func ReFind(pattern, input string) []string {
1030
+ return regexp.MustCompile(pattern).FindStringSubmatch(input)
1031
+ }
1032
+
1033
+ // ReFindRe is the *regexp.Regexp form.
1034
+ func ReFindRe(re *regexp.Regexp, input string) []string {
1035
+ return re.FindStringSubmatch(input)
1036
+ }
1037
+
1038
+ // ReFindAll returns all non-overlapping match-group arrays.
1039
+ func ReFindAll(pattern, input string) [][]string {
1040
+ return regexp.MustCompile(pattern).FindAllStringSubmatch(input, -1)
1041
+ }
1042
+
1043
+ // ReReplace replaces every match. The replacement supports Go's $0..$N
1044
+ // reference syntax (functionally equivalent to JS $&..$N).
1045
+ //
1046
+ // Note: Go's `regexp` (RE2) suppresses an empty match immediately
1047
+ // following a non-empty match at the same offset. This is RE2's
1048
+ // chosen convention and differs from ECMAScript / Python / Java etc:
1049
+ // `re_replace("a*", "abc", "X")` returns "XbXcX" here, "XXbXcX" on
1050
+ // PCRE/ECMA engines. The variance is inherent to the host regex
1051
+ // package; see REGEX_PATHOLOGICAL.md.
1052
+ func ReReplace(pattern, input, replacement string) string {
1053
+ return regexp.MustCompile(pattern).ReplaceAllString(input, replacement)
1054
+ }
1055
+
1056
+ // ReReplaceFunc replaces every match via the callback.
1057
+ func ReReplaceFunc(pattern, input string, fn func([]string) string) string {
1058
+ re := regexp.MustCompile(pattern)
1059
+ return re.ReplaceAllStringFunc(input, func(m string) string {
1060
+ groups := re.FindStringSubmatch(m)
1061
+ return fn(groups)
1062
+ })
1063
+ }
1064
+
1065
+ // ReTest reports whether the input matches.
1066
+ func ReTest(pattern, input string) bool {
1067
+ return regexp.MustCompile(pattern).MatchString(input)
1068
+ }
1069
+
1070
+ // ReEscape is an alias for EscRe.
1071
+ func ReEscape(s string) string { return EscRe(s) }
1072
+
1073
+ // Escape URLs. Mirrors the canonical encodeURIComponent: every byte is
1074
+ // percent-encoded except the unreserved set A-Za-z0-9 and -_.!~*'().
1075
+ // In particular a space encodes as %20 (url.QueryEscape would emit '+').
969
1076
  func EscUrl(s string) string {
970
- return url.QueryEscape(s)
1077
+ var b strings.Builder
1078
+ for i := 0; i < len(s); i++ {
1079
+ c := s[i]
1080
+ if (c >= 'A' && c <= 'Z') ||
1081
+ (c >= 'a' && c <= 'z') ||
1082
+ (c >= '0' && c <= '9') ||
1083
+ strings.IndexByte("-_.!~*'()", c) >= 0 {
1084
+ b.WriteByte(c)
1085
+ } else {
1086
+ fmt.Fprintf(&b, "%%%02X", c)
1087
+ }
1088
+ }
1089
+ return b.String()
971
1090
  }
972
1091
 
973
1092
  var (
@@ -980,7 +1099,7 @@ var (
980
1099
  func JoinUrl(parts []any) string {
981
1100
  var filtered []string
982
1101
  for _, p := range parts {
983
- if "" != p && nil != p {
1102
+ if p != "" && p != nil {
984
1103
  ps, ok := p.(string)
985
1104
  if !ok {
986
1105
  ps = Stringify(p)
@@ -1012,7 +1131,6 @@ func JoinUrl(parts []any) string {
1012
1131
  return strings.Join(finalParts, "/")
1013
1132
  }
1014
1133
 
1015
-
1016
1134
  // Concatenate string array elements, merging separator chars as needed.
1017
1135
  // Optional args: sep (string, default ","), url (bool, default false).
1018
1136
  func Join(arr []any, args ...any) string {
@@ -1033,7 +1151,7 @@ func Join(arr []any, args ...any) string {
1033
1151
  }
1034
1152
 
1035
1153
  var sepre string
1036
- if 1 == len(sep) {
1154
+ if len(sep) == 1 {
1037
1155
  sepre = EscRe(sep)
1038
1156
  }
1039
1157
 
@@ -1065,7 +1183,7 @@ func Join(arr []any, args ...any) string {
1065
1183
  reLeading := regexp.MustCompile(`^` + sepre + `+`)
1066
1184
  reInternal := regexp.MustCompile(`([^` + sepre + `])` + sepre + `+([^` + sepre + `])`)
1067
1185
 
1068
- if urlMode && 0 == idx {
1186
+ if urlMode && idx == 0 {
1069
1187
  s = reTrailing.ReplaceAllString(s, S_MT)
1070
1188
  } else {
1071
1189
  if 0 < idx {
@@ -1088,7 +1206,6 @@ func Join(arr []any, args ...any) string {
1088
1206
  return strings.Join(parts, sep)
1089
1207
  }
1090
1208
 
1091
-
1092
1209
  // Output JSON in a "standard" format, with 2 space indents, each property on a new line,
1093
1210
  // and spaces after {[: and before ]}. Any "weird" values (NaN, etc) are output as null.
1094
1211
  // In general, the behavior of JavaScript's JSON.stringify(val,null,2) is followed.
@@ -1112,9 +1229,17 @@ func Jsonify(val any, flags ...map[string]any) string {
1112
1229
  }
1113
1230
 
1114
1231
  if nil != val {
1115
- indentStr := strings.Repeat(" ", indent)
1116
- offsetStr := strings.Repeat(" ", offset)
1117
- b, err := json.MarshalIndent(val, offsetStr, indentStr)
1232
+ // Canonical: indent==0 compact JSON (json.Marshal). Otherwise
1233
+ // pretty-print with the given indent and prefix.
1234
+ var b []byte
1235
+ var err error
1236
+ if indent == 0 {
1237
+ b, err = json.Marshal(val)
1238
+ } else {
1239
+ indentStr := strings.Repeat(" ", indent)
1240
+ offsetStr := strings.Repeat(" ", offset)
1241
+ b, err = json.MarshalIndent(val, offsetStr, indentStr)
1242
+ }
1118
1243
  if err != nil {
1119
1244
  str = S_null
1120
1245
  } else {
@@ -1126,9 +1251,13 @@ func Jsonify(val any, flags ...map[string]any) string {
1126
1251
  }
1127
1252
 
1128
1253
  // Safely stringify a value for humans (NOT JSON!).
1254
+ //
1255
+ // In Go, nil represents a JSON null (there is no separate undefined),
1256
+ // so it stringifies to the literal "null" — matching the canonical
1257
+ // JSON.stringify(null) === "null".
1129
1258
  func Stringify(val any, maxlen ...int) string {
1130
1259
  if nil == val {
1131
- return S_MT
1260
+ return S_null
1132
1261
  }
1133
1262
 
1134
1263
  if lr, ok := val.(*ListRef[any]); ok {
@@ -1285,6 +1414,7 @@ func Clone(val any) any {
1285
1414
  }
1286
1415
 
1287
1416
  func CloneFlags(val any, flags map[string]bool) any {
1417
+ val = denoval(val)
1288
1418
  if val == nil {
1289
1419
  return nil
1290
1420
  }
@@ -1335,9 +1465,9 @@ func CloneFlags(val any, flags map[string]bool) any {
1335
1465
  }
1336
1466
  }
1337
1467
 
1338
- // Define a JSON Object from alternating key-value arguments.
1339
- // jo("a", 1, "b", 2) => {"a": 1, "b": 2}
1340
- func Jo(kv ...any) map[string]any {
1468
+ // Jm defines a JSON object from alternating key/value arguments.
1469
+ // Jm("a", 1, "b", 2) => {"a": 1, "b": 2}
1470
+ func Jm(kv ...any) map[string]any {
1341
1471
  o := make(map[string]any)
1342
1472
  kvsize := len(kv)
1343
1473
  for i := 0; i < kvsize; i += 2 {
@@ -1351,9 +1481,9 @@ func Jo(kv ...any) map[string]any {
1351
1481
  return o
1352
1482
  }
1353
1483
 
1354
- // Define a JSON Array from arguments.
1355
- // ja(1, "x", true) => [1, "x", true]
1356
- func Ja(v ...any) []any {
1484
+ // Jt defines a JSON array (tuple) from positional arguments.
1485
+ // Jt(1, "x", true) => [1, "x", true]
1486
+ func Jt(v ...any) []any {
1357
1487
  a := make([]any, len(v))
1358
1488
  for i := 0; i < len(v); i++ {
1359
1489
  a[i] = GetProp(v, i)
@@ -1379,8 +1509,6 @@ func DelProp(parent any, key any) any {
1379
1509
  if err != nil {
1380
1510
  return parent
1381
1511
  }
1382
- ki = int(math.Floor(float64(ki)))
1383
-
1384
1512
  if lr, isLR := parent.(*ListRef[any]); isLR {
1385
1513
  psize := len(lr.List)
1386
1514
  if 0 <= ki && ki < psize {
@@ -1545,13 +1673,18 @@ func SetProp(parent any, key any, newval any) any {
1545
1673
  // Walk(val, before, after, maxdepth) - with maximum recursion depth.
1546
1674
  // Pass nil for before or after to skip that callback.
1547
1675
  // For backward compatibility, Walk(val, apply) applies the callback after children (post-order).
1676
+ //
1677
+ // The `path` passed to callbacks is a single mutable slice per depth, reused
1678
+ // across recursive calls. Callbacks must clone it (see WalkApply) if they want
1679
+ // to retain it beyond the invocation.
1548
1680
  func Walk(
1549
1681
  val any,
1550
1682
  apply WalkApply,
1551
1683
  opts ...any,
1552
1684
  ) any {
1685
+ val = denoval(val)
1553
1686
  var after WalkApply
1554
- var maxdepth int = 32
1687
+ maxdepth := 32
1555
1688
 
1556
1689
  if len(opts) > 0 {
1557
1690
  if opts[0] != nil {
@@ -1573,17 +1706,25 @@ func Walk(
1573
1706
  }
1574
1707
  }
1575
1708
 
1709
+ // Allocate the path-pool once per top-level Walk. pool[n] is a backing
1710
+ // array of length n that holds the current path for depth n; it is
1711
+ // grown on demand and mutated in place by _walkDescend.
1712
+ pool := [][]string{nil}
1713
+
1576
1714
  if after != nil {
1577
1715
  // Two-callback mode: apply is before, after is after.
1578
- return _walkDescend(val, apply, after, maxdepth, nil, nil, nil)
1716
+ return _walkDescend(val, apply, after, maxdepth, nil, nil, nil, pool)
1579
1717
  }
1580
1718
 
1581
1719
  // Single-callback mode: apply is called before children (pre-order),
1582
1720
  // matching the TS implementation where walk(val, before) is pre-order.
1583
- return _walkDescend(val, apply, nil, maxdepth, nil, nil, nil)
1721
+ return _walkDescend(val, apply, nil, maxdepth, nil, nil, nil, pool)
1584
1722
  }
1585
1723
 
1586
-
1724
+ // WalkDescend performs a post-order walk from an arbitrary start point
1725
+ // (with explicit key, parent, and path). Unlike Walk it does not share a
1726
+ // pool across recursive calls: it allocates per-call path arrays. Intended
1727
+ // for ad-hoc recursive descents from a non-root position.
1587
1728
  func WalkDescend(
1588
1729
  val any,
1589
1730
  apply WalkApply,
@@ -1591,10 +1732,14 @@ func WalkDescend(
1591
1732
  parent any,
1592
1733
  path []string,
1593
1734
  ) any {
1594
- return _walkDescend(val, nil, apply, 32, key, parent, path)
1735
+ return _walkDescendAlloc(val, nil, apply, 32, key, parent, path)
1595
1736
  }
1596
1737
 
1597
-
1738
+ // _walkDescend is the pool-threaded core used by Walk. The `path` slice it
1739
+ // hands to callbacks is reused across sibling visits: each recursive call
1740
+ // reuses pool[depth+1] (growing the pool on demand), writes its own key in
1741
+ // the last slot, and passes the slice down. Callbacks must clone it if they
1742
+ // want to retain it.
1598
1743
  func _walkDescend(
1599
1744
  val any,
1600
1745
  before WalkApply,
@@ -1603,8 +1748,14 @@ func _walkDescend(
1603
1748
  key *string,
1604
1749
  parent any,
1605
1750
  path []string,
1751
+ pool [][]string,
1606
1752
  ) any {
1607
1753
 
1754
+ if path == nil {
1755
+ path = pool[0]
1756
+ }
1757
+ depth := len(path)
1758
+
1608
1759
  out := val
1609
1760
 
1610
1761
  // Apply before callback.
@@ -1613,6 +1764,69 @@ func _walkDescend(
1613
1764
  }
1614
1765
 
1615
1766
  // Check depth limit.
1767
+ if 0 == maxdepth || (0 < maxdepth && maxdepth <= depth) {
1768
+ return out
1769
+ }
1770
+
1771
+ if IsNode(out) {
1772
+ childDepth := depth + 1
1773
+ // Grow pool on demand.
1774
+ for len(pool) <= childDepth {
1775
+ pool = append(pool, nil)
1776
+ }
1777
+ childPath := pool[childDepth]
1778
+ if childPath == nil {
1779
+ childPath = make([]string, childDepth)
1780
+ pool[childDepth] = childPath
1781
+ }
1782
+ // Sync prefix [0..depth-1] from the current path. Only needed
1783
+ // once per parent: siblings share the same prefix and will each
1784
+ // overwrite slot [depth] below.
1785
+ for i := 0; i < depth; i++ {
1786
+ childPath[i] = path[i]
1787
+ }
1788
+
1789
+ for _, kv := range Items(out) {
1790
+ ckey := kv[0]
1791
+ child := kv[1]
1792
+ ckeyStr := StrKey(ckey)
1793
+ childPath[depth] = ckeyStr
1794
+ newChild := _walkDescend(child, before, after, maxdepth, &ckeyStr, out, childPath, pool)
1795
+ out = SetProp(out, ckey, newChild)
1796
+ }
1797
+
1798
+ if nil != parent && nil != key {
1799
+ SetProp(parent, *key, out)
1800
+ }
1801
+ }
1802
+
1803
+ // Apply after callback.
1804
+ if nil != after {
1805
+ out = after(key, out, parent, path)
1806
+ }
1807
+
1808
+ return out
1809
+ }
1810
+
1811
+ // _walkDescendAlloc is the legacy per-call-allocating descent used by
1812
+ // WalkDescend, preserved for callers that enter the recursion at an
1813
+ // arbitrary point without a shared pool.
1814
+ func _walkDescendAlloc(
1815
+ val any,
1816
+ before WalkApply,
1817
+ after WalkApply,
1818
+ maxdepth int,
1819
+ key *string,
1820
+ parent any,
1821
+ path []string,
1822
+ ) any {
1823
+
1824
+ out := val
1825
+
1826
+ if nil != before {
1827
+ out = before(key, out, parent, path)
1828
+ }
1829
+
1616
1830
  if 0 == maxdepth || (nil != path && 0 < maxdepth && maxdepth <= len(path)) {
1617
1831
  return out
1618
1832
  }
@@ -1625,7 +1839,7 @@ func _walkDescend(
1625
1839
  newPath := make([]string, len(path)+1)
1626
1840
  copy(newPath, path)
1627
1841
  newPath[len(path)] = ckeyStr
1628
- newChild := _walkDescend(child, before, after, maxdepth, &ckeyStr, out, newPath)
1842
+ newChild := _walkDescendAlloc(child, before, after, maxdepth, &ckeyStr, out, newPath)
1629
1843
  out = SetProp(out, ckey, newChild)
1630
1844
  }
1631
1845
 
@@ -1634,7 +1848,6 @@ func _walkDescend(
1634
1848
  }
1635
1849
  }
1636
1850
 
1637
- // Apply after callback.
1638
1851
  if nil != after {
1639
1852
  out = after(key, out, parent, path)
1640
1853
  }
@@ -1648,6 +1861,7 @@ func _walkDescend(
1648
1861
  // modified.
1649
1862
  // Optional maxdepth parameter limits recursion depth.
1650
1863
  func Merge(val any, maxdepths ...int) any {
1864
+ val = denoval(val)
1651
1865
  md := 32
1652
1866
  if len(maxdepths) > 0 {
1653
1867
  if maxdepths[0] < 0 {
@@ -1657,7 +1871,7 @@ func Merge(val any, maxdepths ...int) any {
1657
1871
  }
1658
1872
  }
1659
1873
 
1660
- var out any = nil
1874
+ var out any
1661
1875
 
1662
1876
  if !IsList(val) {
1663
1877
  return val
@@ -1780,7 +1994,7 @@ func Merge(val any, maxdepths ...int) any {
1780
1994
  // resolved against the `current` argument, if defined. Integer path
1781
1995
  // parts are used as array indexes. The inj argument allows for
1782
1996
  // custom handling when called from `inject` or `transform`.
1783
- func GetPath(path any, store any, injdefs ...*Injection) any {
1997
+ func GetPath(store any, path any, injdefs ...*Injection) any {
1784
1998
  var inj *Injection
1785
1999
  if len(injdefs) > 0 {
1786
2000
  inj = injdefs[0]
@@ -1856,20 +2070,20 @@ func GetPath(path any, store any, injdefs ...*Injection) any {
1856
2070
  } else if inj != nil && strings.HasPrefix(part, "$GET:") {
1857
2071
  // $GET:path$ -> get store value, use as path part
1858
2072
  subpath := part[5 : len(part)-1]
1859
- result := GetPath(subpath, src)
2073
+ result := GetPath(src, subpath)
1860
2074
  part = Stringify(result)
1861
2075
  } else if inj != nil && strings.HasPrefix(part, "$REF:") {
1862
2076
  // $REF:refpath$ -> get spec value, use as path part
1863
2077
  subpath := part[5 : len(part)-1]
1864
2078
  specVal := GetProp(store, S_DSPEC)
1865
2079
  if specVal != nil {
1866
- result := GetPath(subpath, specVal)
2080
+ result := GetPath(specVal, subpath)
1867
2081
  part = Stringify(result)
1868
2082
  }
1869
2083
  } else if inj != nil && strings.HasPrefix(part, "$META:") {
1870
2084
  // $META:metapath$ -> get meta value, use as path part
1871
2085
  subpath := part[6 : len(part)-1]
1872
- result := GetPath(subpath, inj.Meta)
2086
+ result := GetPath(inj.Meta, subpath)
1873
2087
  part = Stringify(result)
1874
2088
  }
1875
2089
 
@@ -1903,7 +2117,7 @@ func GetPath(path any, store any, injdefs ...*Injection) any {
1903
2117
  }
1904
2118
 
1905
2119
  if ascends <= len(dpath) {
1906
- val = GetPath(fullpath, store)
2120
+ val = GetPath(store, fullpath)
1907
2121
  } else {
1908
2122
  val = nil
1909
2123
  }
@@ -2030,7 +2244,7 @@ func _injectStr(
2030
2244
  }
2031
2245
 
2032
2246
  // Get the extracted path reference.
2033
- out := GetPath(pathref, store, inj)
2247
+ out := GetPath(store, pathref, inj)
2034
2248
 
2035
2249
  return out
2036
2250
  }
@@ -2048,7 +2262,7 @@ func _injectStr(
2048
2262
  if nil != inj {
2049
2263
  inj.Full = false
2050
2264
  }
2051
- found := GetPath(ref, store, inj)
2265
+ found := GetPath(store, ref, inj)
2052
2266
 
2053
2267
  if nil == found {
2054
2268
  return S_MT
@@ -2195,11 +2409,6 @@ func Inject(
2195
2409
  // Perform the val mode injection on the child value.
2196
2410
  Inject(childval, store, childinj)
2197
2411
 
2198
- // The injection may modify child processing.
2199
- nkI = childinj.KeyI
2200
- nodekeys = childinj.Keys.List
2201
- val = childinj.Parent
2202
-
2203
2412
  // Perform the key:post mode injection on the child key.
2204
2413
  childinj.Mode = M_KEYPOST
2205
2414
  _injectStr(nodekey, store, childinj)
@@ -2394,7 +2603,7 @@ var Transform_MERGE Injector = func(
2394
2603
  }
2395
2604
 
2396
2605
  // Remove the $MERGE command from a parent map.
2397
- DelProp(inj.Parent, inj.Key)
2606
+ DelProp(inj.Parent, inj.Key)
2398
2607
 
2399
2608
  list, ok := _asList(args)
2400
2609
  if !ok {
@@ -2416,7 +2625,6 @@ var Transform_MERGE Injector = func(
2416
2625
  return nil
2417
2626
  }
2418
2627
 
2419
-
2420
2628
  // Convert a node to a list.
2421
2629
  // Format: ['`$EACH`', '`source-path-of-node`', child-template]
2422
2630
  var Transform_EACH Injector = func(
@@ -2453,7 +2661,7 @@ var Transform_EACH Injector = func(
2453
2661
 
2454
2662
  // Source data.
2455
2663
  srcstore := GetProp(store, inj.Base, store)
2456
- src := GetPath(srcpath, srcstore, inj)
2664
+ src := GetPath(srcstore, srcpath, inj)
2457
2665
  srctype := Typify(src)
2458
2666
 
2459
2667
  // Create parallel data structures
@@ -2518,9 +2726,7 @@ var Transform_EACH Injector = func(
2518
2726
  copy(tpath, inj.Path.List[:len(inj.Path.List)-1])
2519
2727
 
2520
2728
  dpath := []string{S_DTOP}
2521
- for _, p := range strings.Split(srcpath, S_DT) {
2522
- dpath = append(dpath, p)
2523
- }
2729
+ dpath = append(dpath, strings.Split(srcpath, S_DT)...)
2524
2730
  dpath = append(dpath, "$:"+ckey)
2525
2731
 
2526
2732
  // Parent structure.
@@ -2562,7 +2768,6 @@ var Transform_EACH Injector = func(
2562
2768
  return nil
2563
2769
  }
2564
2770
 
2565
-
2566
2771
  // transform_PACK => `$PACK`
2567
2772
  var Transform_PACK Injector = func(
2568
2773
  inj *Injection,
@@ -2604,7 +2809,7 @@ var Transform_PACK Injector = func(
2604
2809
 
2605
2810
  // Source data
2606
2811
  srcstore := GetProp(store, inj.Base, store)
2607
- src := GetPath(srcpath, srcstore, inj)
2812
+ src := GetPath(srcstore, srcpath, inj)
2608
2813
 
2609
2814
  // Prepare source as a list.
2610
2815
  if !IsList(src) {
@@ -2654,7 +2859,7 @@ var Transform_PACK Injector = func(
2654
2859
  return ks
2655
2860
  }
2656
2861
  } else {
2657
- kval := GetPath(keypathStr, srcItem, inj)
2862
+ kval := GetPath(srcItem, keypathStr, inj)
2658
2863
  if ks, ok := kval.(string); ok {
2659
2864
  return ks
2660
2865
  }
@@ -2706,9 +2911,7 @@ var Transform_PACK Injector = func(
2706
2911
  }
2707
2912
 
2708
2913
  dpath := []string{S_DTOP}
2709
- for _, p := range strings.Split(srcpath, S_DT) {
2710
- dpath = append(dpath, p)
2711
- }
2914
+ dpath = append(dpath, strings.Split(srcpath, S_DT)...)
2712
2915
  dpath = append(dpath, "$:"+ckey)
2713
2916
 
2714
2917
  tcur := map[string]any{ckey: tsrc}
@@ -2780,9 +2983,9 @@ var Transform_REF Injector = func(
2780
2983
  if len(inj.Path.List) > 1 {
2781
2984
  dpath = append(dpath, inj.Path.List[1:]...)
2782
2985
  }
2783
- refResult := GetPath(refpath, spec, &Injection{
2986
+ refResult := GetPath(spec, refpath, &Injection{
2784
2987
  Dpath: dpath,
2785
- Dparent: GetPath(dpath, spec),
2988
+ Dparent: GetPath(spec, dpath),
2786
2989
  })
2787
2990
 
2788
2991
  hasSubRef := false
@@ -2807,8 +3010,8 @@ var Transform_REF Injector = func(
2807
3010
  }
2808
3011
  tpath = tpath[:len(tpath)-1]
2809
3012
 
2810
- tcur := GetPath(cpath, store)
2811
- tval := GetPath(tpath, store)
3013
+ tcur := GetPath(store, cpath)
3014
+ tval := GetPath(store, tpath)
2812
3015
 
2813
3016
  var rval any
2814
3017
 
@@ -2935,7 +3138,6 @@ var Transform_APPLY Injector = func(
2935
3138
  return out
2936
3139
  }
2937
3140
 
2938
-
2939
3141
  // transform_FORMAT => `$FORMAT`
2940
3142
  // injectChild resolves a child value via injection, going up the injection chain
2941
3143
  // to get the correct data context.
@@ -3120,8 +3322,7 @@ var Transform_FORMAT Injector = func(
3120
3322
  return nil
3121
3323
  }
3122
3324
 
3123
- var out any
3124
- out = Walk(resolved, formatter)
3325
+ out := Walk(resolved, formatter)
3125
3326
 
3126
3327
  // Set on parent output
3127
3328
  if target != nil {
@@ -3131,28 +3332,71 @@ var Transform_FORMAT Injector = func(
3131
3332
  return out
3132
3333
  }
3133
3334
 
3134
-
3135
3335
  // ---------------------------------------------------------------------
3136
3336
  // Transform function: top-level
3137
3337
 
3338
+ // Transform returns an error when the transform collected any - an unknown
3339
+ // `$FORMAT`, say - and the caller did not supply their own error collector.
3340
+ // Canonical TypeScript throws at exactly that point (StructUtility.ts:
3341
+ // `const generr = 0 < size(errs) && !collect`), and Validate in this port
3342
+ // already surfaces it the same way. Transform did not, so every error a
3343
+ // transform collected was silently dropped: `Transform(nil, ["`$FORMAT`",
3344
+ // "not-a-format", "a"])` returned nil rather than reporting the unknown
3345
+ // format. The corpus pins that behaviour in transform/format, a group this
3346
+ // port's own test runner used to skip in full.
3138
3347
  func Transform(
3139
3348
  data any, // source data
3140
3349
  spec any, // transform specification
3141
3350
  injdefs ...*Injection,
3142
- ) any {
3351
+ ) (any, error) {
3143
3352
  if len(injdefs) > 0 && injdefs[0] != nil {
3144
3353
  injdef := injdefs[0]
3145
- return TransformModifyHandler(
3354
+
3355
+ // A caller-supplied collector means the errors are theirs to
3356
+ // inspect, and canonical generates nothing.
3357
+ collecterrs := injdef.Errs
3358
+ errs := collecterrs
3359
+ if nil == errs {
3360
+ errs = ListRefCreate[any]()
3361
+ }
3362
+
3363
+ out := TransformModifyHandler(
3146
3364
  data,
3147
3365
  spec,
3148
3366
  injdef.Extra,
3149
3367
  injdef.Modify,
3150
3368
  injdef.Handler,
3151
- injdef.Errs,
3369
+ errs,
3152
3370
  injdef.Meta,
3153
3371
  )
3372
+
3373
+ if nil != collecterrs {
3374
+ return out, nil
3375
+ }
3376
+ return out, generatederr(errs)
3154
3377
  }
3155
- return TransformModify(data, spec, nil, nil)
3378
+
3379
+ out, errs := transformModifyCore(data, spec, nil, nil)
3380
+ return out, generatederr(errs)
3381
+ }
3382
+
3383
+ // generatederr joins collected error strings into the single error canonical
3384
+ // throws. Nil when nothing was collected.
3385
+ func generatederr(errs *ListRef[any]) error {
3386
+ if nil == errs || 0 == len(errs.List) {
3387
+ return nil
3388
+ }
3389
+
3390
+ errmsgs := make([]string, len(errs.List))
3391
+ for i, e := range errs.List {
3392
+ if s, ok := e.(string); ok {
3393
+ errmsgs[i] = s
3394
+ } else {
3395
+ errmsgs[i] = fmt.Sprintf("%v", e)
3396
+ }
3397
+ }
3398
+
3399
+ return errors.New(strings.Join(errmsgs, " | "))
3156
3400
  }
3157
3401
 
3158
3402
  // TransformModifyHandler is like TransformModify but allows a custom handler and injection inj.
@@ -3187,10 +3431,6 @@ func TransformModifyHandler(
3187
3431
  }
3188
3432
  }
3189
3433
 
3190
- if extraData == nil {
3191
- extraData = map[string]any{}
3192
- }
3193
-
3194
3434
  var dataClone any
3195
3435
  if data == nil {
3196
3436
  dataClone = nil
@@ -3205,10 +3445,10 @@ func TransformModifyHandler(
3205
3445
  _ = origspec
3206
3446
 
3207
3447
  store := map[string]any{
3208
- S_DTOP: dataClone,
3448
+ S_DTOP: dataClone,
3209
3449
  S_DSPEC: func() any { return origspec },
3210
- "$BT": func() any { return S_BT },
3211
- "$DS": func() any { return S_DS },
3450
+ "$BT": func() any { return S_BT },
3451
+ "$DS": func() any { return S_DS },
3212
3452
  "$WHEN": func() any {
3213
3453
  return time.Now().UTC().Format(time.RFC3339)
3214
3454
  },
@@ -3280,10 +3520,7 @@ func transformModifyCore(
3280
3520
  }
3281
3521
  }
3282
3522
 
3283
- // Create empty maps if nil
3284
- if extraData == nil {
3285
- extraData = map[string]any{}
3286
- }
3523
+ // Create empty map if nil
3287
3524
  if data == nil {
3288
3525
  data = map[string]any{}
3289
3526
  }
@@ -3396,42 +3633,6 @@ var validate_STRING Injector = func(
3396
3633
  return out
3397
3634
  }
3398
3635
 
3399
- var validate_NUMBER Injector = func(
3400
- inj *Injection,
3401
- _val any,
3402
- ref *string,
3403
- store any,
3404
- ) any {
3405
- out := GetProp(inj.Dparent, inj.Key)
3406
-
3407
- t := Typify(out)
3408
- if 0 == (T_number & t) {
3409
- msg := _invalidTypeMsg(inj.Path.List, S_number, Typename(t), out)
3410
- inj.Errs.Append(msg)
3411
- return nil
3412
- }
3413
-
3414
- return out
3415
- }
3416
-
3417
- var validate_BOOLEAN Injector = func(
3418
- inj *Injection,
3419
- _val any,
3420
- ref *string,
3421
- store any,
3422
- ) any {
3423
- out := GetProp(inj.Dparent, inj.Key)
3424
-
3425
- t := Typify(out)
3426
- if 0 == (T_boolean & t) {
3427
- msg := _invalidTypeMsg(inj.Path.List, S_boolean, Typename(t), out)
3428
- inj.Errs.Append(msg)
3429
- return nil
3430
- }
3431
-
3432
- return out
3433
- }
3434
-
3435
3636
  var validate_OBJECT Injector = func(
3436
3637
  inj *Injection,
3437
3638
  _val any,
@@ -3446,7 +3647,7 @@ var validate_OBJECT Injector = func(
3446
3647
  msg := _invalidTypeMsg(inj.Path.List, S_object, Typename(t), out)
3447
3648
  inj.Errs.Append(msg)
3448
3649
 
3449
- return nil
3650
+ return nil
3450
3651
  }
3451
3652
 
3452
3653
  return out
@@ -3470,24 +3671,6 @@ var validate_ARRAY Injector = func(
3470
3671
  return out
3471
3672
  }
3472
3673
 
3473
- var validate_FUNCTION Injector = func(
3474
- inj *Injection,
3475
- _val any,
3476
- ref *string,
3477
- store any,
3478
- ) any {
3479
- out := GetProp(inj.Dparent, inj.Key)
3480
-
3481
- t := Typify(out)
3482
- if 0 == (T_function & t) {
3483
- msg := _invalidTypeMsg(inj.Path.List, S_function, Typename(t), out)
3484
- inj.Errs.Append(msg)
3485
- return nil
3486
- }
3487
-
3488
- return out
3489
- }
3490
-
3491
3674
  var validate_ANY Injector = func(
3492
3675
  inj *Injection,
3493
3676
  _val any,
@@ -3632,10 +3815,20 @@ var validate_CHILD Injector = func(
3632
3815
  inj.Parent = newParent
3633
3816
  }
3634
3817
 
3635
- inj.KeyI = 0
3818
+ // NOTE: modifying inj! This extends the child value loop in inject
3819
+ // to cover every cloned child.
3820
+ for ckeyI := len(inj.Keys.List); ckeyI < length; ckeyI++ {
3821
+ inj.Keys.Append(StrKey(ckeyI))
3822
+ }
3636
3823
 
3637
- out := GetProp(dparent, 0)
3638
- return out
3824
+ // Restart the child value loop at the first element (the loop
3825
+ // increments KeyI on resume) so that the first element is also
3826
+ // validated against the child template.
3827
+ inj.KeyI = -1
3828
+
3829
+ // SKIP leaves the cloned child template in place at the first
3830
+ // element so the resumed loop can validate it.
3831
+ return SKIP
3639
3832
  }
3640
3833
 
3641
3834
  return nil
@@ -3680,7 +3873,7 @@ func init_validate_ONE() {
3680
3873
 
3681
3874
  // Clean up structure by replacing [$ONE, ...] with current value
3682
3875
  SetProp(grandparent, grandkey, inj.Dparent)
3683
- inj.Parent = inj.Dparent
3876
+ inj.Parent = inj.Dparent
3684
3877
 
3685
3878
  // Adjust the path
3686
3879
  inj.Path.List = inj.Path.List[:len(inj.Path.List)-1]
@@ -3786,7 +3979,7 @@ func init_validate_EXACT() {
3786
3979
 
3787
3980
  // Clean up structure by replacing [$EXACT, ...] with current value
3788
3981
  SetProp(grandparent, grandkey, inj.Dparent)
3789
- inj.Parent = inj.Dparent
3982
+ inj.Parent = inj.Dparent
3790
3983
 
3791
3984
  // Adjust the path
3792
3985
  inj.Path.List = inj.Path.List[:len(inj.Path.List)-1]
@@ -3806,21 +3999,21 @@ func init_validate_EXACT() {
3806
3999
  // See if we can find an exact value match
3807
4000
  var currentStr *string
3808
4001
  for _, tval := range tvals {
3809
- exactMatch := false
3810
-
3811
- if !exactMatch {
3812
- // Unwrap ListRefs for comparison since data and spec may have
3813
- // different wrapping levels.
3814
- unwrapFlags := map[string]bool{"unwrap": true}
3815
- utval := CloneFlags(tval, unwrapFlags)
3816
- ucurrent := CloneFlags(inj.Dparent, unwrapFlags)
3817
- exactMatch = reflect.DeepEqual(utval, ucurrent)
3818
- }
3819
-
4002
+ exactMatch := false
4003
+
4004
+ if !exactMatch {
4005
+ // Unwrap ListRefs for comparison since data and spec may have
4006
+ // different wrapping levels.
4007
+ unwrapFlags := map[string]bool{"unwrap": true}
4008
+ utval := CloneFlags(tval, unwrapFlags)
4009
+ ucurrent := CloneFlags(inj.Dparent, unwrapFlags)
4010
+ exactMatch = reflect.DeepEqual(utval, ucurrent)
4011
+ }
4012
+
3820
4013
  if !exactMatch && IsNode(tval) {
3821
4014
  if nil == currentStr {
3822
4015
  tmpstr := Stringify(inj.Dparent)
3823
- currentStr = &tmpstr
4016
+ currentStr = &tmpstr
3824
4017
  }
3825
4018
  tvalStr := Stringify(tval)
3826
4019
  exactMatch = tvalStr == *currentStr
@@ -3900,7 +4093,7 @@ func makeValidation(exact bool) Modify {
3900
4093
  ptype := Typify(pval)
3901
4094
 
3902
4095
  // Delete any special commands remaining.
3903
- if 0 < (T_string & ptype) && pval != nil {
4096
+ if 0 < (T_string&ptype) && pval != nil {
3904
4097
  if strVal, ok := pval.(string); ok && strings.Contains(strVal, S_DS) {
3905
4098
  return
3906
4099
  }
@@ -3931,6 +4124,10 @@ func makeValidation(exact bool) Modify {
3931
4124
 
3932
4125
  // Empty spec object {} means object can be open (any keys).
3933
4126
  if len(pkeys) > 0 && GetProp(pval, "`$OPEN`") != true {
4127
+ // Literal presence: the shape DECLARES ckey even when its value
4128
+ // is nil. Canonical uses `NONE === _lookup`; HasKey is Group A
4129
+ // (value-based) and would drop records with a nil field from an
4130
+ // open ($AND) select. Test map key presence on the shape (pval).
3934
4131
  badkeys := []string{}
3935
4132
  pvalMap, pvalIsMap := pval.(map[string]any)
3936
4133
  for _, ckey := range ckeys {
@@ -3987,8 +4184,6 @@ func makeValidation(exact bool) Modify {
3987
4184
  // Spec value was a default, copy over data
3988
4185
  SetProp(parent, key, cval)
3989
4186
  }
3990
-
3991
- return
3992
4187
  }
3993
4188
  }
3994
4189
 
@@ -4002,7 +4197,7 @@ var _validatehandler Injector = func(
4002
4197
  ref *string,
4003
4198
  store any,
4004
4199
  ) any {
4005
- out := val
4200
+ var out any
4006
4201
 
4007
4202
  refStr := ""
4008
4203
  if ref != nil {
@@ -4046,7 +4241,6 @@ func Validate(
4046
4241
  errs = ListRefCreate[any]()
4047
4242
  }
4048
4243
 
4049
-
4050
4244
  // Initialize validate_ONE if not already initialized.
4051
4245
  // This avoids a circular reference error, validate_ONE calls Validate.
4052
4246
  if validate_ONE == nil {
@@ -4062,7 +4256,7 @@ func Validate(
4062
4256
  store := map[string]any{
4063
4257
  // Remove the transform commands
4064
4258
  "$DELETE": nil,
4065
- "$COPY": nil,
4259
+ "$COPY": nil,
4066
4260
  "$KEY": nil,
4067
4261
  "$META": nil,
4068
4262
  "$MERGE": nil,
@@ -4093,10 +4287,8 @@ func Validate(
4093
4287
  }
4094
4288
 
4095
4289
  // Add any extra validation commands
4096
- if extra != nil {
4097
- for k, fn := range extra {
4098
- store[k] = fn
4099
- }
4290
+ for k, fn := range extra {
4291
+ store[k] = fn
4100
4292
  }
4101
4293
 
4102
4294
  // A special top level value to collect errors
@@ -4126,7 +4318,7 @@ func Validate(
4126
4318
  // Run the transformation with validation and _validatehandler
4127
4319
  out := TransformModifyHandler(data, spec, store, validationFn, _validatehandler, errs, meta)
4128
4320
 
4129
- // Generate an error if we collected any errors and the caller didn't provide
4321
+ // Generate an error if we collected any errors and the caller didn't provide
4130
4322
  // their own error collection
4131
4323
  var err error
4132
4324
  generr := 0 < len(errs.List) && collecterrs == nil
@@ -4146,7 +4338,6 @@ func Validate(
4146
4338
  return out, err
4147
4339
  }
4148
4340
 
4149
-
4150
4341
  // Mode names for injection modes.
4151
4342
  var MODENAME = map[int]string{
4152
4343
  M_VAL: "val",
@@ -4207,7 +4398,6 @@ func InjectorArgs(argTypes []int, args []any) []any {
4207
4398
  return found
4208
4399
  }
4209
4400
 
4210
-
4211
4401
  // Select helpers - internal injectors for query matching.
4212
4402
 
4213
4403
  var select_AND Injector = func(
@@ -4221,7 +4411,7 @@ var select_AND Injector = func(
4221
4411
 
4222
4412
  pathList := inj.Path.List
4223
4413
  ppath := pathList[:len(pathList)-1]
4224
- point := GetPath(ppath, store)
4414
+ point := GetPath(store, ppath)
4225
4415
 
4226
4416
  vstore := Merge([]any{map[string]any{}, store})
4227
4417
  SetProp(vstore, S_DTOP, point)
@@ -4257,7 +4447,7 @@ var select_OR Injector = func(
4257
4447
 
4258
4448
  pathList := inj.Path.List
4259
4449
  ppath := pathList[:len(pathList)-1]
4260
- point := GetPath(ppath, store)
4450
+ point := GetPath(store, ppath)
4261
4451
 
4262
4452
  vstore := Merge([]any{map[string]any{}, store})
4263
4453
  SetProp(vstore, S_DTOP, point)
@@ -4294,7 +4484,7 @@ var select_NOT Injector = func(
4294
4484
 
4295
4485
  pathList := inj.Path.List
4296
4486
  ppath := pathList[:len(pathList)-1]
4297
- point := GetPath(ppath, store)
4487
+ point := GetPath(store, ppath)
4298
4488
 
4299
4489
  vstore := Merge([]any{map[string]any{}, store})
4300
4490
  SetProp(vstore, S_DTOP, point)
@@ -4328,7 +4518,7 @@ var select_CMP Injector = func(
4328
4518
 
4329
4519
  pathList := inj.Path.List
4330
4520
  ppath := pathList[:len(pathList)-1]
4331
- point := GetPath(ppath, store)
4521
+ point := GetPath(store, ppath)
4332
4522
 
4333
4523
  pass := false
4334
4524
  refStr := ""
@@ -4379,7 +4569,6 @@ var select_CMP Injector = func(
4379
4569
  return nil
4380
4570
  }
4381
4571
 
4382
-
4383
4572
  // Internal exact-mode validation for Select.
4384
4573
  // Like Validate but uses exact scalar comparison.
4385
4574
  func validateCollectExact(
@@ -4431,10 +4620,8 @@ func validateCollectExact(
4431
4620
  "$EXACT": validate_EXACT,
4432
4621
  }
4433
4622
 
4434
- if extra != nil {
4435
- for k, fn := range extra {
4436
- store[k] = fn
4437
- }
4623
+ for k, fn := range extra {
4624
+ store[k] = fn
4438
4625
  }
4439
4626
 
4440
4627
  store["$ERRS"] = errs
@@ -4443,7 +4630,6 @@ func validateCollectExact(
4443
4630
  TransformModifyHandler(data, spec, store, makeValidation(true), _validatehandler, errs, meta)
4444
4631
  }
4445
4632
 
4446
-
4447
4633
  // Select children from a node that match a query.
4448
4634
  // Uses validate internally with query operators ($AND, $OR, $NOT,
4449
4635
  // $GT, $LT, $GTE, $LTE, $LIKE).
@@ -4515,7 +4701,6 @@ func Select(children any, query any) []any {
4515
4701
  return results
4516
4702
  }
4517
4703
 
4518
-
4519
4704
  // Internal utilities
4520
4705
  // ==================
4521
4706
 
@@ -4529,25 +4714,14 @@ func ListRefCreate[T any]() *ListRef[T] {
4529
4714
  }
4530
4715
  }
4531
4716
 
4532
-
4533
4717
  func (l *ListRef[T]) Append(elem T) {
4534
4718
  l.List = append(l.List, elem)
4535
4719
  }
4536
4720
 
4537
-
4538
4721
  func (l *ListRef[T]) Prepend(elem T) {
4539
4722
  l.List = append([]T{elem}, l.List...)
4540
4723
  }
4541
4724
 
4542
- func _join(vals []any, sep string) string {
4543
- strVals := make([]string, len(vals))
4544
- for i, v := range vals {
4545
- strVals[i] = fmt.Sprint(v)
4546
- }
4547
- return strings.Join(strVals, sep)
4548
- }
4549
-
4550
-
4551
4725
  func _invalidTypeMsg(path []string, needtype string, vt string, v any, whence ...string) string {
4552
4726
  vs := "no value"
4553
4727
  if v != nil {
@@ -4575,14 +4749,6 @@ func _invalidTypeMsg(path []string, needtype string, vt string, v any, whence ..
4575
4749
  return message + "."
4576
4750
  }
4577
4751
 
4578
- func _getType(v any) string {
4579
- if nil == v {
4580
- return "nil"
4581
- }
4582
- return reflect.TypeOf(v).String()
4583
- }
4584
-
4585
-
4586
4752
  // StrKey converts different types of keys to string representation.
4587
4753
  // String keys are returned as is.
4588
4754
  // Number keys are converted to strings.
@@ -4620,7 +4786,6 @@ func StrKey(key any) string {
4620
4786
  }
4621
4787
  }
4622
4788
 
4623
-
4624
4789
  func _resolveStrings(input []any) []string {
4625
4790
  var result []string
4626
4791
 
@@ -4635,7 +4800,6 @@ func _resolveStrings(input []any) []string {
4635
4800
  return result
4636
4801
  }
4637
4802
 
4638
-
4639
4803
  // Extract a bare []any from either a []any or a *ListRef[any].
4640
4804
  // Recursively unwrap *ListRef[any] to []any for JSON marshaling.
4641
4805
  func _unwrapListRefs(val any) any {
@@ -4680,7 +4844,6 @@ func _asList(val any) ([]any, bool) {
4680
4844
  return nil, false
4681
4845
  }
4682
4846
 
4683
-
4684
4847
  func _listify(src any) []any {
4685
4848
  if lr, ok := src.(*ListRef[any]); ok {
4686
4849
  return lr.List
@@ -4708,7 +4871,6 @@ func _listify(src any) []any {
4708
4871
  return nil
4709
4872
  }
4710
4873
 
4711
-
4712
4874
  // toFloat64 helps unify numeric types for floor conversion.
4713
4875
  func _toFloat64(val any) (float64, error) {
4714
4876
  switch n := val.(type) {
@@ -4742,12 +4904,11 @@ func _toFloat64(val any) (float64, error) {
4742
4904
  }
4743
4905
  }
4744
4906
 
4745
-
4746
4907
  // _parseInt is a helper to convert a string to int safely.
4747
4908
  func _parseInt(s string) (int, error) {
4748
4909
  // We'll do a very simple parse:
4749
4910
  var x int
4750
- var sign int = 1
4911
+ sign := 1
4751
4912
  for i, c := range s {
4752
4913
  if c == '-' && i == 0 {
4753
4914
  sign = -1
@@ -4761,15 +4922,12 @@ func _parseInt(s string) (int, error) {
4761
4922
  return x * sign, nil
4762
4923
  }
4763
4924
 
4764
-
4765
4925
  type ParseIntError struct{ input string }
4766
4926
 
4767
-
4768
4927
  func (e *ParseIntError) Error() string {
4769
4928
  return "cannot parse int from: " + e.input
4770
4929
  }
4771
4930
 
4772
-
4773
4931
  func _makeArrayType(values []any, target any) any {
4774
4932
  targetElem := reflect.TypeOf(target).Elem()
4775
4933
  out := reflect.MakeSlice(reflect.SliceOf(targetElem), len(values), len(values))
@@ -4786,7 +4944,6 @@ func _makeArrayType(values []any, target any) any {
4786
4944
  return out.Interface()
4787
4945
  }
4788
4946
 
4789
-
4790
4947
  func _stringifyValue(v any) string {
4791
4948
  switch vv := v.(type) {
4792
4949
  case string:
@@ -4797,97 +4954,3 @@ func _stringifyValue(v any) string {
4797
4954
  return Stringify(v)
4798
4955
  }
4799
4956
  }
4800
-
4801
-
4802
-
4803
-
4804
- // DEBUG
4805
-
4806
- func fdt(data any) string {
4807
- return fdti(data, "")
4808
- }
4809
-
4810
- func fdti(data any, indent string) string {
4811
- result := ""
4812
-
4813
- if data == nil {
4814
- return indent + "nil\n"
4815
- }
4816
-
4817
- // Get a pointer for memory address
4818
- memoryAddr := "0x???"
4819
- val := reflect.ValueOf(data)
4820
-
4821
- // For non-pointer types that are addressable, get their pointer
4822
- if val.Kind() != reflect.Ptr && val.CanAddr() {
4823
- ptr := val.Addr()
4824
- memoryAddr = fmt.Sprintf("0x%x", ptr.Pointer())
4825
- } else if val.Kind() == reflect.Ptr {
4826
- // For pointer types, use the pointer value directly
4827
- memoryAddr = fmt.Sprintf("0x%x", val.Pointer())
4828
- } else if val.Kind() == reflect.Map || val.Kind() == reflect.Slice {
4829
- // For maps and slices, use the pointer to internal data
4830
- memoryAddr = fmt.Sprintf("0x%x", val.Pointer())
4831
- }
4832
-
4833
- switch v := data.(type) {
4834
- case map[string]any:
4835
- result += indent + fmt.Sprintf("{ @%s\n", memoryAddr)
4836
- for key, value := range v {
4837
- result += fmt.Sprintf("%s \"%s\": %s", indent, key, fdti(value, indent+" "))
4838
- }
4839
- result += indent + "}\n"
4840
-
4841
- case []any:
4842
- result += indent + fmt.Sprintf("[ @%s\n", memoryAddr)
4843
- for _, value := range v {
4844
- result += fmt.Sprintf("%s - %s", indent, fdti(value, indent+" "))
4845
- }
4846
- result += indent + "]\n"
4847
-
4848
- default:
4849
- // Check if it's a struct using reflection
4850
- typ := val.Type()
4851
-
4852
- // Handle pointers by dereferencing
4853
- isPtr := false
4854
- if val.Kind() == reflect.Ptr {
4855
- isPtr = true
4856
- if val.IsNil() {
4857
- return indent + "nil\n"
4858
- }
4859
- val = val.Elem()
4860
- typ = val.Type()
4861
- }
4862
-
4863
- if val.Kind() == reflect.Struct {
4864
- structName := typ.Name()
4865
- if isPtr {
4866
- structName = "*" + structName
4867
- }
4868
- result += indent + fmt.Sprintf("struct %s @%s {\n", structName, memoryAddr)
4869
-
4870
- // Iterate over all fields of the struct
4871
- for i := 0; i < val.NumField(); i++ {
4872
- field := val.Field(i)
4873
- fieldType := typ.Field(i)
4874
-
4875
- // Skip unexported fields (lowercase field names)
4876
- if !fieldType.IsExported() {
4877
- continue
4878
- }
4879
-
4880
- fieldName := fieldType.Name
4881
- fieldValue := field.Interface()
4882
-
4883
- result += fmt.Sprintf("%s %s: %s", indent, fieldName, fdti(fieldValue, indent+" "))
4884
- }
4885
- result += indent + "}\n"
4886
- } else {
4887
- // For non-struct types, just format value with its type
4888
- result += fmt.Sprintf("%v (%s) @%s\n", v, reflect.TypeOf(v), memoryAddr)
4889
- }
4890
- }
4891
-
4892
- return result
4893
- }