@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.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/action/check.js +15 -0
- package/dist/action/check.js.map +1 -1
- package/dist/cmp/AgentGuide.js +1 -1
- package/dist/cmp/AgentGuide.js.map +1 -1
- package/dist/cmp/AgentGuideContent.d.ts +1 -1
- package/dist/cmp/AgentGuideContent.js +8 -2
- package/dist/cmp/AgentGuideContent.js.map +1 -1
- package/dist/cmp/Feature.js +17 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/cmp/FeatureDocs.d.ts +1 -1
- package/dist/cmp/FeatureDocs.js +6 -1
- package/dist/cmp/FeatureDocs.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.js +3 -1
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeFeatures.js +1 -1
- package/dist/cmp/ReadmeFeatures.js.map +1 -1
- package/dist/cmp/ReadmeRefFeatures.js +1 -1
- package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
- package/dist/cmp/ReadmeTop.js +1 -1
- package/dist/helpers/applicability.d.ts +6 -0
- package/dist/helpers/applicability.js +103 -0
- package/dist/helpers/applicability.js.map +1 -0
- package/dist/helpers/collectDeps.js +4 -2
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/serverVars.d.ts +2 -1
- package/dist/helpers/serverVars.js +17 -0
- package/dist/helpers/serverVars.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -2
- package/dist/sdkgen.js +9 -2
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.js +7 -1
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +39 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aon +1 -0
- package/project/.sdk/model/feature/secrets.aon +118 -0
- package/project/.sdk/model/target/ts.aon +8 -1
- package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
- package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +6 -2
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +5 -1
- package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
- package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
- package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +5 -1
- package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
- package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +5 -1
- package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
- package/project/.sdk/src/cmp/perl/Main_perl.ts +5 -1
- package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +5 -1
- package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +5 -1
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
- package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +5 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +5 -1
- package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +5 -1
- package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +5 -1
- package/project/.sdk/src/cmp/ts/Main_ts.ts +57 -1
- package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +67 -7
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +27 -4
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
- package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
- package/project/.sdk/tm/c/utility/make_options.c +22 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
- package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
- package/project/.sdk/tm/go/core/context.go +1 -1
- package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
- package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
- package/project/.sdk/tm/go/feature/test_feature.go +12 -5
- package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
- package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
- package/project/.sdk/tm/go/test/runner_test.go +46 -0
- package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
- package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
- package/project/.sdk/tm/go/utility/fetcher.go +2 -2
- package/project/.sdk/tm/go/utility/make_options.go +24 -5
- package/project/.sdk/tm/go/utility/make_point.go +1 -1
- package/project/.sdk/tm/go/utility/make_spec.go +17 -1
- package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
- package/project/.sdk/tm/go/utility/transform_request.go +29 -1
- package/project/.sdk/tm/go/utility/transform_response.go +20 -1
- package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
- package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
- package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
- package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
- package/project/.sdk/tm/py/test/runner.py +32 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
- package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +25 -0
- package/project/.sdk/tm/scala/utility/Make.scala +17 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +1221 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +381 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +151 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +42 -0
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
- package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
- package/project/.sdk/tm/ts/test/omni.ts +86 -0
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
- package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
- package/project/.sdk/tm/ts/test/utility.ts +122 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +692 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +238 -0
- package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
- package/project/.sdk/tm/zig/core/utility.zig +23 -0
- package/project/sdkgen-package.json +2 -1
- package/src/action/check.ts +18 -0
- package/src/cmp/AgentGuide.ts +1 -1
- package/src/cmp/AgentGuideContent.ts +9 -2
- package/src/cmp/Feature.ts +19 -0
- package/src/cmp/FeatureDocs.ts +7 -1
- package/src/cmp/ReadmeExplanation.ts +4 -1
- package/src/cmp/ReadmeFeatures.ts +1 -1
- package/src/cmp/ReadmeRefFeatures.ts +1 -1
- package/src/cmp/ReadmeTop.ts +1 -1
- package/src/helpers/applicability.ts +123 -0
- package/src/helpers/collectDeps.ts +5 -1
- package/src/helpers/serverVars.ts +19 -0
- package/src/sdkgen.ts +9 -1
- package/src/utility.ts +10 -1
- 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
|
|
139
|
-
T_node
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
|
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 &&
|
|
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
|
-
|
|
1116
|
-
|
|
1117
|
-
b
|
|
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
|
|
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
|
-
//
|
|
1339
|
-
//
|
|
1340
|
-
func
|
|
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
|
-
//
|
|
1355
|
-
//
|
|
1356
|
-
func
|
|
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
|
-
|
|
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
|
|
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 :=
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
2986
|
+
refResult := GetPath(spec, refpath, &Injection{
|
|
2784
2987
|
Dpath: dpath,
|
|
2785
|
-
Dparent: GetPath(
|
|
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(
|
|
2811
|
-
tval := GetPath(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
3448
|
+
S_DTOP: dataClone,
|
|
3209
3449
|
S_DSPEC: func() any { return origspec },
|
|
3210
|
-
"$BT":
|
|
3211
|
-
"$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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
3638
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
4097
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
4435
|
-
|
|
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
|
-
|
|
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
|
-
}
|