@voxgig/create-sdkgen 0.24.0 → 0.25.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.
@@ -1,14 +1,4 @@
1
1
 
2
- # PENDING — this section is deliberately empty.
3
- #
4
- # featureInit(ctx, f) calls f.init(ctx, fopts). `init` is a function, and a
5
- # fixture compiles to JSON, so the subject of the test cannot be expressed
6
- # here at all. Covered per-language by the feature suites.
7
- #
8
- # An empty `set` runs ZERO cases. Runners must not treat that as a pass by
9
- # accident: `basic: pending` below is the MACHINE-READABLE form of this note,
10
- # so the guard travels into test.json with the fixture instead of living only
11
- # in a comment the consumer never sees.
12
2
 
13
3
  basic: pending: 'featureInit calls f.init(ctx, fopts); init is a function, and a fixture compiles to JSON, so the subject cannot be expressed here.'
14
4
  basic: set: []
@@ -1,14 +1,4 @@
1
1
 
2
- # PENDING — this section is deliberately empty.
3
- #
4
- # Needs a live or mocked transport and a client instance; the shared runner
5
- # has no transport-injection hook. Covered per-language by the netsim and
6
- # feature suites until this fixture can express a transport.
7
- #
8
- # An empty `set` runs ZERO cases. Runners must not treat that as a pass by
9
- # accident: `basic: pending` below is the MACHINE-READABLE form of this note,
10
- # so the guard travels into test.json with the fixture instead of living only
11
- # in a comment the consumer never sees.
12
2
 
13
3
  basic: pending: 'Needs a live or mocked transport and a client instance; the shared runner has no transport-injection hook.'
14
4
  basic: set: []
@@ -1,14 +1,4 @@
1
1
 
2
- # PENDING — this section is deliberately empty.
3
- #
4
- # Depends on a fully built ctx (spec + point + options) that the shared
5
- # runner cannot assemble from a literal map. Covered indirectly by
6
- # makeSpec/makeUrl, and by the per-language pipeline suites.
7
- #
8
- # An empty `set` runs ZERO cases. Runners must not treat that as a pass by
9
- # accident: `basic: pending` below is the MACHINE-READABLE form of this note,
10
- # so the guard travels into test.json with the fixture instead of living only
11
- # in a comment the consumer never sees.
12
2
 
13
3
  basic: pending: 'Depends on a fully built ctx (spec + point + options) that the runner cannot assemble from a literal map.'
14
4
  basic: set: []
@@ -1,22 +1,4 @@
1
1
 
2
- # makePoint chooses which endpoint definition ("point") an operation call
3
- # should use, and records it on ctx.point.
4
- #
5
- # The whole selection is driven by DATA: `op` is rebuilt by Context from
6
- # `opname` + `entity.name` + `config.entity.<name>.op.<opname>.points`, and
7
- # `options.allow.op` gates the call. So every branch below is expressible as
8
- # a literal ctx, contrary to the deferral note this fixture used to carry,
9
- # which claimed it needed a real client.
10
- #
11
- # Errors are RETURNED, not thrown, and their JSON embeds a random ctx id, so
12
- # the error cases assert `match: out: code` rather than a whole-value `out`.
13
- # Go returns the error as a second result instead, and its runner normalises
14
- # it to the same `{code}` shape.
15
- #
16
- # `allow.op` is a COMMA-SEPARATED STRING, not a list — that is what the client
17
- # actually carries. Go reads it with strings.Contains and type-asserts to
18
- # string, so a list here silently denies every operation; TS's .includes()
19
- # accepts either and would have hidden the mismatch.
20
2
 
21
3
  basic: set: [
22
4
 
@@ -59,8 +41,6 @@ basic: set: [
59
41
  match: { out: { code: 'point_no_points' } }
60
42
  }
61
43
 
62
- # Two points: `select.exist` picks the one whose required keys are present
63
- # in the match. `id` is supplied, so the by-id point wins.
64
44
  {
65
45
  ctx: {
66
46
  opname: 'list'
@@ -75,8 +55,6 @@ basic: set: [
75
55
  out: { method: 'GET', path: ['byid'], select: { exist: ['id'] } }
76
56
  }
77
57
 
78
- # Same two points, but `id` is absent — the exist check fails and selection
79
- # falls through to the unconstrained point.
80
58
  {
81
59
  ctx: {
82
60
  opname: 'list'
@@ -1,18 +1,4 @@
1
1
 
2
- # PENDING — this section is deliberately empty.
3
- #
4
- # makeResult needs a ctx carrying a constructed Spec AND Result, built via the
5
- # SDK's own NewSpec/NewResult constructors, plus the absent-error path for
6
- # each. The runner builds ctx from a literal map and has no constructor hook,
7
- # so these cannot be expressed here yet — covered per-language today (e.g.
8
- # go's makeResult-basic / -no-spec / -no-result), which is exactly the drift
9
- # this corpus exists to remove. Promoting it needs a runner change, not just
10
- # fixture data.
11
- #
12
- # An empty `set` runs ZERO cases. Runners must not treat that as a pass by
13
- # accident: `basic: pending` below is the MACHINE-READABLE form of this note,
14
- # so the guard travels into test.json with the fixture instead of living only
15
- # in a comment the consumer never sees.
16
2
 
17
3
  basic: pending: 'Needs a ctx carrying a Spec AND Result built via the SDK constructors; the runner builds ctx from a literal map and has no constructor hook.'
18
4
  basic: set: []
@@ -1,10 +1,5 @@
1
1
 
2
2
  DEF: {
3
- # auth pinned explicitly (not left to a spec's own derived prefix/scheme):
4
- # this corpus tests the raw single-token mechanics in isolation, so it
5
- # must not silently inherit whatever a given SDK's real spec declares
6
- # (e.g. genuine HTTP Basic Auth, a non-empty prefix, two credentials) -
7
- # that has its own dedicated coverage elsewhere.
8
3
  setup: a: { apikey: 'APIKEY01', auth: { prefix: '', basic: false } }
9
4
  }
10
5
 
@@ -1,11 +1,4 @@
1
1
 
2
- # preparePath: join ctx.point.parts with '/', dropping empty segments.
3
- #
4
- # Path templating is the highest-risk request-shaping step, and this fixture
5
- # was an empty `set: []` — so every language "passed" it while running zero
6
- # cases. The cases below were lifted from the per-language hand-written tests
7
- # (go/py) that had drifted out of the shared corpus, plus the empty/blank
8
- # segment edges that `join(..., true)` is specifically responsible for.
9
2
 
10
3
  basic: set: [
11
4
  # Multi-segment path, placeholder left intact (params are substituted later,
@@ -1,102 +1,21 @@
1
1
 
2
- # STORED-NULL SEMANTICS.
3
- #
4
- # One question, asked of every reader: when a key is PRESENT and holds a JSON
5
- # null, is that "no value" (the same as an absent key) or is it a value?
6
- #
7
- # Nothing in the corpus asked it before. `minor.getprop` and `minor.haskey`
8
- # have ~50 cases each and not one stores a null: every entry is an absent key
9
- # or a non-null value. So the ports diverged silently, and they HAVE:
10
- #
11
- # port getprop({x:null},'x','ALT') haskey({x:null},'x')
12
- # ──── ─────────────────────────── ────────────────────
13
- # go 'ALT' false
14
- # py 'ALT' false
15
- # perl 'ALT' false
16
- # rb null true
17
- # php null true
18
- # js null true
19
- #
20
- # That is not academic. It is the direct cause of the `auth: null` bug family:
21
- # `auth: null` is the documented way to suppress auth outright, and where a
22
- # stored null reads as "no value" the optspec `auth` default fires, the
23
- # suppression becomes "use the default auth", and the credential the caller
24
- # withheld goes out on the wire.
25
- #
26
- # 0.3.2 answers "no value" — the top three rows. This section pins that.
27
- #
28
- #
29
- # THIS SECTION IS OPT-IN, AND DELIBERATELY SO.
30
- #
31
- # Half the tree still vendors a struct that answers the other way. Putting
32
- # these cases in `minor` would red those targets today and gate the whole
33
- # corpus on finishing the struct migration everywhere first. Instead a target
34
- # wires this section up as it migrates:
35
- #
36
- # runsetflags(nullsemSpec["getprop"], {"null": False}, getprop_wrapper)
37
- #
38
- # so the section becomes that target's null-semantics gate, and a target that
39
- # has not migrated simply does not reference it yet.
40
- #
41
- #
42
- # `null: false` IS NOT OPTIONAL HERE.
43
- #
44
- # The runner's default fixJSON rewrites every JSON null - on BOTH the `in` and
45
- # `out` sides - to the string '__NULL__' before the subject ever sees it. Run
46
- # without the flag, every case below would pass the STRING '__NULL__' as the
47
- # stored value, which is not a null and is not the question. The section would
48
- # then pass on every port while asserting nothing about null at all - the
49
- # exact "green while checking nothing" failure the corpus guards exist to
50
- # prevent. `minor.getelem` and `minor.clone` already run with the flag for the
51
- # same reason.
52
- #
53
- # With the flag, an entry with NO `out` expects a null result, and `out:` with
54
- # a value expects that value.
55
- #
56
- #
57
- # VERIFIED AGAINST THE REFERENCE, NOT GUESSED.
58
- #
59
- # All 33 cases below were run against upstream @voxgig/struct 0.3.2
60
- # (typescript/src/StructUtility.ts, the canonical port) and all 33 pass. They
61
- # are what the reference DOES, not what it was assumed to do.
62
- #
63
- # That mattered on the first run: `getprop([null], 0, 'ALT')` was assumed to
64
- # be a formality and turned out to be a live defect in the py port's vendored
65
- # copy. Canonical getprop tests `isnode(val)` - map AND list - and then
66
- # applies the null rule once to whatever it read. The py copy branches on
67
- # ismap/islist and its list branch does `return val[key]`, an early return
68
- # that skips the null check entirely, so it hands back the stored null where
69
- # every other reader would substitute the alt. Its map path is correct, which
70
- # is why nothing noticed.
71
- #
72
- # So py cannot opt in to this section until its vendored struct is resynced.
73
- # That is the section working as intended: it says "not yet" in a test result
74
- # instead of in a comment.
75
-
76
-
77
- # Does the ALT fire for a stored null, as it does for an absent key?
2
+
3
+
78
4
  getprop: {
79
5
  set: [
80
- # THE CASE. Absent and stored-null must answer alike.
81
6
  { in: { val: {x:null}, key: x, alt: 'ALT' }, out: 'ALT' }
82
7
  { in: { val: {}, key: x, alt: 'ALT' }, out: 'ALT' }
83
8
 
84
- # No alt: a stored null reads back as null either way, so this pins that
85
- # the rule does not turn a null into something else.
86
9
  { in: { val: {x:null}, key: x } }
87
10
 
88
11
  # A stored null must not leak past its own key.
89
12
  { in: { val: {x:null, y:1}, key: y, alt: 'ALT' }, out: 1 }
90
13
  { in: { val: {x:1, y:null}, key: x, alt: 'ALT' }, out: 1 }
91
14
 
92
- # false, 0 and '' are VALUES, not absence. A port that collapses "nullish"
93
- # to "falsy" passes every case above and fails these.
94
15
  { in: { val: {x:false}, key: x, alt: 'ALT' }, out: false }
95
16
  { in: { val: {x:0}, key: x, alt: 'ALT' }, out: 0 }
96
17
  { in: { val: {x:''}, key: x, alt: 'ALT' }, out: '' }
97
18
 
98
- # Lists too: index 0 holding a null is the list-shaped form of the same
99
- # question, and a port can easily fix one and not the other.
100
19
  { in: { val: [null], key: 0, alt: 'ALT' }, out: 'ALT' }
101
20
  { in: { val: [null, 1], key: 1, alt: 'ALT' }, out: 1 }
102
21
  ]
@@ -111,8 +30,6 @@ getelem: {
111
30
  { in: { val: [10, null, 30], key: 1, alt: 'ALT' }, out: 'ALT' }
112
31
  { in: { val: [10, null, 30], key: 2, alt: 'ALT' }, out: 30 }
113
32
 
114
- # Negative indices are getelem's whole reason for existing; a stored null
115
- # at a negative index is the case a port is most likely to miss.
116
33
  { in: { val: [10, null], key: -1, alt: 'ALT' }, out: 'ALT' }
117
34
  { in: { val: [null, 20], key: -1, alt: 'ALT' }, out: 20 }
118
35
 
@@ -137,15 +54,12 @@ getpath: {
137
54
  }
138
55
 
139
56
 
140
- # Presence, asked directly. This is the reader the auth-null fix needs in
141
- # every port, and the one whose answer splits the tree.
142
57
  haskey: {
143
58
  set: [
144
59
  { in: { src: {x:null}, key: x }, out: false }
145
60
  { in: { src: {}, key: x }, out: false }
146
61
  { in: { src: {x:1}, key: x }, out: true }
147
62
 
148
- # Again: falsy is not absent.
149
63
  { in: { src: {x:false}, key: x }, out: true }
150
64
  { in: { src: {x:0}, key: x }, out: true }
151
65
  { in: { src: {x:''}, key: x }, out: true }
@@ -156,13 +70,6 @@ haskey: {
156
70
  }
157
71
 
158
72
 
159
- # The other direction, and the reason `auth: null` is expressible at all:
160
- # presence is STRUCTURAL. keysof lists a null-valued key, because the key IS
161
- # there - it is only the READERS above that treat its value as no value.
162
- #
163
- # A port that made haskey false by DELETING the key would pass every case
164
- # above and fail here, and it would also make the suppression unwritable:
165
- # there would be nothing left for makeOptions to detect.
166
73
  keysof: {
167
74
  set: [
168
75
  { in: {a:null}, out: [a] }
@@ -2,41 +2,39 @@
2
2
  basic: {
3
3
  set: [
4
4
  # Basic equality matching
5
- { in: { query: {age:30}, obj: {a:{name:'Alice',age:30}, b:{name:'Bob',age:25}} },
5
+ { in: { query: {age:30}, obj: {a:{name:'Alice',age:30}, b:{name:'Bob',age:25}} },
6
6
  out: [{name:'Alice',age:30,'$KEY':'a'}] }
7
7
 
8
- { in: { query: {name:'Bob'}, obj: {a:{name:'Alice',age:30}, b:{name:'Bob',age:25}} },
8
+ { in: { query: {name:'Bob'}, obj: {a:{name:'Alice',age:30}, b:{name:'Bob',age:25}} },
9
9
  out: [{name:'Bob',age:25,'$KEY':'b'}] }
10
-
10
+
11
11
  # Multiple field equality
12
- { in: { query: {age:30,city:'NYC'}, obj: {a:{age:30,city:'NYC'}, b:{age:30,city:'LA'}} },
12
+ { in: { query: {age:30,city:'NYC'}, obj: {a:{age:30,city:'NYC'}, b:{age:30,city:'LA'}} },
13
13
  out: [{age:30,city:'NYC','$KEY':'a'}] }
14
-
15
- # Array children
14
+
16
15
  { in: { query: {type:'user'},
17
- obj: [{id:1,type:'user'}, {id:2,type:'admin'}, {id:3,type:'user'}] },
16
+ obj: [{id:1,type:'user'}, {id:2,type:'admin'}, {id:3,type:'user'}] },
18
17
  out: [{id:1,type:'user','$KEY':0}, {id:3,type:'user','$KEY':2}] }
19
-
20
- # No matches
21
- { in: { query: {age:40}, obj: {a:{age:30}, b:{age:25}} },
18
+
19
+ { in: { query: {age:40}, obj: {a:{age:30}, b:{age:25}} },
22
20
  out: [] }
23
-
21
+
24
22
  # Empty query matches all
25
- { in: { query: {}, obj: {a:{x:1}, b:{x:2}} },
23
+ { in: { query: {}, obj: {a:{x:1}, b:{x:2}} },
26
24
  out: [{x:1,'$KEY':'a'}, {x:2,'$KEY':'b'}] }
27
-
25
+
28
26
  # Non-object input
29
27
  { in: { query: {x:1}, obj: 'hello' }, out: [] }
30
28
  { in: { query: {x:1}, obj: 42 }, out: [] }
31
29
  { in: { query: {x:1}, obj: null }, out: [] }
32
30
  { in: { query: {x:1}, obj: undefined }, out: [] }
33
-
31
+
34
32
  # Deep equality for objects
35
- { in: { query: {data:{x:1,y:2}}, obj: {a:{data:{x:1,y:2}}, b:{data:{x:1,y:3}}} },
33
+ { in: { query: {data:{x:1,y:2}}, obj: {a:{data:{x:1,y:2}}, b:{data:{x:1,y:3}}} },
36
34
  out: [{data:{x:1,y:2},'$KEY':'a'}] }
37
-
35
+
38
36
  # Deep equality for arrays
39
- { in: { query: {tags:['a','b']}, obj: {a:{tags:['a','b']}, b:{tags:['a','c']}} },
37
+ { in: { query: {tags:['a','b']}, obj: {a:{tags:['a','b']}, b:{tags:['a','c']}} },
40
38
  out: [{tags:['a','b'],'$KEY':'a'}] }
41
39
  ]
42
40
  }
@@ -44,156 +42,141 @@ basic: {
44
42
  operators: {
45
43
  set: [
46
44
  # `$AND` operator
47
- { in: { query: {'`$AND`':[{age:30}, {city:'NYC'}]},
48
- obj: {a:{age:30,city:'NYC'}, b:{age:30,city:'LA'}, c:{age:25,city:'NYC'}} },
45
+ { in: { query: {'`$AND`':[{age:30}, {city:'NYC'}]},
46
+ obj: {a:{age:30,city:'NYC'}, b:{age:30,city:'LA'}, c:{age:25,city:'NYC'}} },
49
47
  out: [{age:30,city:'NYC','$KEY':'a'}] }
50
-
48
+
51
49
  # `$AND` with no matches
52
- { in: { query: {'`$AND`':[{age:30}, {city:'Boston'}]},
53
- obj: {a:{age:30,city:'NYC'}, b:{age:30,city:'LA'}} },
50
+ { in: { query: {'`$AND`':[{age:30}, {city:'Boston'}]},
51
+ obj: {a:{age:30,city:'NYC'}, b:{age:30,city:'LA'}} },
54
52
  out: [] }
55
-
56
- # `$OR` operator
57
- { in: { query: {'`$OR`':['x', 'y']},
58
- obj: {a:'x', b:'z', c:'y'} },
53
+
54
+ { in: { query: {'`$OR`':['x', 'y']},
55
+ obj: {a:'x', b:'z', c:'y'} },
59
56
  out: ['x', 'y'] }
60
57
 
61
58
 
62
- # TODO: review
63
-
64
- # { in: { query: {'$KEY':{'`$OR`':['b', 'c']}},
65
- # obj: {a:'x', b:'z', c:'y'} },
66
- # out: ['z', 'y'] }
67
59
 
68
- # { in: { query: {'`$OR`':[{'$KEY':'b'}, {'$KEY':'c'}]},
69
- # obj: {a:'x', b:'z', c:'y'} },
70
- # out: ['z', 'y'] }
71
60
 
72
-
73
- { in: { query: {'`$OR`':[{age:25}, {age:35}]},
74
- obj: {a:{age:30}, b:{age:25}, c:{age:35}} },
61
+
62
+
63
+ { in: { query: {'`$OR`':[{age:25}, {age:35}]},
64
+ obj: {a:{age:30}, b:{age:25}, c:{age:35}} },
75
65
  out: [{age:25,'$KEY':'b'}, {age:35,'$KEY':'c'}] }
76
-
66
+
77
67
  # `$OR` with all matches
78
- { in: { query: {'`$OR`':[{type:'user'}, {type:'admin'}]},
79
- obj: [{type:'user'}, {type:'admin'}, {type:'guest'}] },
68
+ { in: { query: {'`$OR`':[{type:'user'}, {type:'admin'}]},
69
+ obj: [{type:'user'}, {type:'admin'}, {type:'guest'}] },
80
70
  out: [{type:'user','$KEY':0}, {type:'admin','$KEY':1}] }
81
-
71
+
82
72
  # Nested `$AND` within `$OR`
83
- { in: { query: {'`$OR`':[{'`$AND`':[{role:'user'}, {active:true}]}, {'`$AND`':[{role:'admin'}, {age:30}]}]},
84
- obj: {a:{role:'admin',age:30,active:true}, b:{role:'user',age:25,active:true},
85
- c:{role:'user',age:30,active:false}, d:{role:'admin',age:30,active:false}} },
86
- out: [{role:'admin',age:30,active:true,'$KEY':'a'}, {role:'user',age:25,active:true,'$KEY':'b'},
73
+ { in: { query: {'`$OR`':[{'`$AND`':[{role:'user'}, {active:true}]}, {'`$AND`':[{role:'admin'}, {age:30}]}]},
74
+ obj: {a:{role:'admin',age:30,active:true}, b:{role:'user',age:25,active:true},
75
+ c:{role:'user',age:30,active:false}, d:{role:'admin',age:30,active:false}} },
76
+ out: [{role:'admin',age:30,active:true,'$KEY':'a'}, {role:'user',age:25,active:true,'$KEY':'b'},
87
77
  {role:'admin',age:30,active:false,'$KEY':'d'}] }
88
-
78
+
89
79
  # Complex nested operators
90
- { in: { query: {'`$AND`':[{'`$OR`':[{status:'active'}, {status:'pending'}]}, {priority:'high'}]},
91
- obj: {a:{status:'active',priority:'high'}, b:{status:'active',priority:'low'},
92
- c:{status:'pending',priority:'high'}, d:{status:'done',priority:'high'}} },
80
+ { in: { query: {'`$AND`':[{'`$OR`':[{status:'active'}, {status:'pending'}]}, {priority:'high'}]},
81
+ obj: {a:{status:'active',priority:'high'}, b:{status:'active',priority:'low'},
82
+ c:{status:'pending',priority:'high'}, d:{status:'done',priority:'high'}} },
93
83
  out: [{status:'active',priority:'high','$KEY':'a'}, {status:'pending',priority:'high','$KEY':'c'}] }
94
-
84
+
95
85
  # Empty `$AND` array (should match all)
96
- { in: { query: {'`$AND`':[]}, obj: {a:{x:1}, b:{x:2}} },
86
+ { in: { query: {'`$AND`':[]}, obj: {a:{x:1}, b:{x:2}} },
97
87
  out: [{x:1,'$KEY':'a'}, {x:2,'$KEY':'b'}] }
98
-
88
+
99
89
  # Empty `$OR` array (should match none)
100
- { in: { query: {'`$OR`':[]}, obj: {a:{x:1}, b:{x:2}} },
90
+ { in: { query: {'`$OR`':[]}, obj: {a:{x:1}, b:{x:2}} },
101
91
  out: [] }
102
92
 
103
93
 
104
- { in: { query: {a:{'`$GT`':10}}, obj: [{a:9},{a:10},{a:11},{a:12}] },
94
+ { in: { query: {a:{'`$GT`':10}}, obj: [{a:9},{a:10},{a:11},{a:12}] },
105
95
  out: [{a:11,'$KEY':2},{a:12,'$KEY':3}] }
106
96
 
107
- { in: { query: {a:{'`$GTE`':10}}, obj: [{a:9},{a:10},{a:11},{a:12}] },
97
+ { in: { query: {a:{'`$GTE`':10}}, obj: [{a:9},{a:10},{a:11},{a:12}] },
108
98
  out: [{a:10,'$KEY':1},{a:11,'$KEY':2},{a:12,'$KEY':3}] }
109
99
 
110
-
111
- { in: { query: {b:{'`$LT`':10}}, obj: [{b:9},{b:10},{b:11},{b:12}] },
100
+
101
+ { in: { query: {b:{'`$LT`':10}}, obj: [{b:9},{b:10},{b:11},{b:12}] },
112
102
  out: [{b:9,'$KEY':0}] }
113
103
 
114
- { in: { query: {b:{'`$LTE`':10}}, obj: [{b:9},{b:10},{b:11},{b:12}] },
104
+ { in: { query: {b:{'`$LTE`':10}}, obj: [{b:9},{b:10},{b:11},{b:12}] },
115
105
  out: [{b:9,'$KEY':0},{b:10,'$KEY':1}] }
116
106
 
117
-
118
- { in: { query: {a:b:'`$LT`':10}, obj: [{a:b:9},{a:b:10},{a:b:11},{a:b:12}] },
107
+
108
+ { in: { query: {a:b:'`$LT`':10}, obj: [{a:b:9},{a:b:10},{a:b:11},{a:b:12}] },
119
109
  out: [{a:b:9,'$KEY':0}] }
120
110
 
121
-
122
- { in: { query: {x:{y:20}}, obj: [{x:{y:20,z:220}},{x:{y:21,z:221}}] },
111
+
112
+ { in: { query: {x:{y:20}}, obj: [{x:{y:20,z:220}},{x:{y:21,z:221}}] },
123
113
  out: [{x:{y:20,z:220},'$KEY':0}] }
124
114
 
125
- { in: { query: {s0:'`$LIKE`':'[aA][bB][cC]'}, obj: [{s0:'DEf'},{s0:'ABc'}] },
115
+ { in: { query: {s0:'`$LIKE`':'[aA][bB][cC]'}, obj: [{s0:'DEf'},{s0:'ABc'}] },
126
116
  out: [{s0:'ABc','$KEY':1}] }
127
117
 
128
118
 
129
- { in: { query: {'`$NOT`':10}, obj: [9,10,11] },
119
+ { in: { query: {'`$NOT`':10}, obj: [9,10,11] },
130
120
  out: [9,11] }
131
121
 
132
- { in: { query: {'`$NOT`':{n0:'x'}}, obj: [{n0:x},{n0:y},{n0:z}] },
122
+ { in: { query: {'`$NOT`':{n0:'x'}}, obj: [{n0:x},{n0:y},{n0:z}] },
133
123
  out: [{n0:y,'$KEY':1},{n0:z,'$KEY':2}] }
134
124
 
135
125
  { in: { query: {'`$NOT`':{'`$OR`':[{x:1},{y:2}]}},
136
- obj: [{x:1,y:3},{x:2,y:2},{x:3,y:1}] },
126
+ obj: [{x:1,y:3},{x:2,y:2},{x:3,y:1}] },
137
127
  out: [{x:3,y:1,'$KEY':2}] }
138
-
128
+
139
129
  ]
140
130
  }
141
131
 
142
132
  edge: {
143
133
  set: [
144
134
  # Mixed equality and operators
145
- { in: { query: {type:'user', '`$OR`':[{status:'active'}, {admin:true}]},
146
- obj: [{type:'user',status:'active'}, {type:'user',status:'inactive',admin:true},
147
- {type:'guest',status:'active'}] },
135
+ { in: { query: {type:'user', '`$OR`':[{status:'active'}, {admin:true}]},
136
+ obj: [{type:'user',status:'active'}, {type:'user',status:'inactive',admin:true},
137
+ {type:'guest',status:'active'}] },
148
138
  out: [{type:'user',status:'active','$KEY':0}, {type:'user',status:'inactive',admin:true,'$KEY':1}] }
149
-
150
-
151
- { in: { query: {value:q}, obj: {a:{value:q}, b:{value:p},c:{}} },
139
+
140
+
141
+ { in: { query: {value:q}, obj: {a:{value:q}, b:{value:p},c:{}} },
152
142
  out: [{value:q,'$KEY':'a'}] }
153
143
 
154
- { in: { query: {value:null}, obj: {a:{value:null}, b:{value:0}, c:{}} },
144
+ { in: { query: {value:null}, obj: {a:{value:null}, b:{value:0}, c:{}} },
155
145
  out: [{value:null,'$KEY':'a'}] }
156
-
157
- { in: { query: {active:true}, obj: {a:{active:true}, b:{active:false}, c:{active:1}} },
146
+
147
+ { in: { query: {active:true}, obj: {a:{active:true}, b:{active:false}, c:{active:1}} },
158
148
  out: [{active:true,'$KEY':'a'}] }
159
-
149
+
160
150
  # Number comparison (exact match)
161
- { in: { query: {count:0}, obj: {a:{count:0}, b:{count:false}, c:{count:'0'}} },
151
+ { in: { query: {count:0}, obj: {a:{count:0}, b:{count:false}, c:{count:'0'}} },
162
152
  out: [{count:0,'$KEY':'a'}] }
163
153
 
164
-
165
- { in: { query: {'`$OR`':[{a:1},{a:2}]}, obj: [{a:0}, {a:1}, {a:2}, {a:3}] },
154
+
155
+ { in: { query: {'`$OR`':[{a:1},{a:2}]}, obj: [{a:0}, {a:1}, {a:2}, {a:3}] },
166
156
  out: [{a:1,'$KEY':1}, {a:2,'$KEY':2}] }
167
157
 
168
- { in: { query: {a:{'`$OR`':[10,20]}}, obj: [{a:0}, {a:10}, {a:20}, {a:30}] },
158
+ { in: { query: {a:{'`$OR`':[10,20]}}, obj: [{a:0}, {a:10}, {a:20}, {a:30}] },
169
159
  out: [{a:10,'$KEY':1}, {a:20,'$KEY':2}] }
170
160
 
171
161
  { in: { query: {a2:{'`$OR`':[{b2:1},{b2:2}]}},
172
- obj: [{a2:{b2:0}}, {a2:{b2:1}}, {a2:{b2:2}}, {a2:{b2:3}}] },
162
+ obj: [{a2:{b2:0}}, {a2:{b2:1}}, {a2:{b2:2}}, {a2:{b2:3}}] },
173
163
  out: [{a2:{b2:1},'$KEY':1}, {a2:{b2:2},'$KEY':2}] }
174
164
 
175
- { in: { query: {'`$OR`':[{a3:{'`$OR`':[1]}}]}, obj: [{a3:0}, {a3:1}, {a3:2}] },
165
+ { in: { query: {'`$OR`':[{a3:{'`$OR`':[1]}}]}, obj: [{a3:0}, {a3:1}, {a3:2}] },
176
166
  out: [{a3:1,'$KEY':1}] }
177
167
 
178
168
 
179
169
  { in: { query: {c0:{'`$AND`':[{x:1},{y:2}]}},
180
- obj: [{c0:{x:1,y:3,z:0}}, {c0:{x:1,y:2,z:1}}] },
170
+ obj: [{c0:{x:1,y:3,z:0}}, {c0:{x:1,y:2,z:1}}] },
181
171
  out: [{c0:{x:1,y:2,z:1},'$KEY':1}] }
182
172
 
183
173
  { in: { query: {c0:{'`$AND`':[{x:1},{y:{'`$OR`':[2,3]}}]}},
184
- obj: [{c0:{x:1,y:3,z:0}}, {c0:{x:1,y:2,z:1}}, {c0:{x:1,y:1,z:2}}] },
185
- out: [{c0:{x:1,y:3,z:0},'$KEY':0}, {c0:{x:1,y:2,z:1},'$KEY':1}] }
174
+ obj: [{c0:{x:1,y:3,z:0}}, {c0:{x:1,y:2,z:1}}, {c0:{x:1,y:1,z:2}}] },
175
+ out: [{c0:{x:1,y:3,z:0},'$KEY':0}, {c0:{x:1,y:2,z:1},'$KEY':1}] }
186
176
  ]
187
177
  }
188
178
 
189
179
 
190
- # path alternates selection
191
- # NOTES FOR SDK:
192
- # build query by union of all select keys, getting values from operation spec, match
193
- # need a deterministic sort of alts so multiple selects (should not happen) have at least
194
- # a stable list of returned alts order. also $path as get out of jail
195
- # normalize: /a/{x}/b/ -> path$:'/a/{}/b'
196
- # if no alt found, use select to indicate missing select options
197
180
  alts: {
198
181
  data: obj0: [
199
182
  { select:foo_id:true, x:1 }
@@ -211,7 +194,7 @@ alts: {
211
194
  { select:{'$action':foo,zed_id:true}, x:31 }
212
195
  { select:{'$action':bar,zed_id:true}, x:32 }
213
196
  ]
214
-
197
+
215
198
  set: [
216
199
  { in: { query: select: {
217
200
  foo_id:true # boolean true means value exists (incl. JSON null)
@@ -236,12 +219,12 @@ alts: {
236
219
  obj: $.struct.select.alts.data.obj2 }
237
220
  out: [{ '$KEY': 0, select: { query:q0:v0 }, x: 21} ]
238
221
  }
239
-
222
+
240
223
  { in: { query: select: { '$action':bar, zed_id:true }
241
224
  obj: $.struct.select.alts.data.obj3 }
242
225
  out: [{ '$KEY': 1, select: {'$action':bar,zed_id:true}, x: 32} ]
243
226
  }
244
-
227
+
245
228
 
246
229
  ]
247
230
  }
@@ -45,8 +45,6 @@ const { names, Jostraca } = JostracaModule
45
45
  const SDK_FOLDER = '.sdk'
46
46
 
47
47
 
48
- // TODO: CreateSdkGen opts and generate opts should be mostly the same, and
49
- // generate should override
50
48
  function CreateSdkGen(opts: FullCreateSdkGenOptions) {
51
49
  const fs = opts.fs || Fs
52
50
 
@@ -107,12 +105,6 @@ function CreateSdkGen(opts: FullCreateSdkGenOptions) {
107
105
  folder,
108
106
  log: log.child({ cmp: 'jostraca' }),
109
107
  meta: { spec },
110
- // Overwrite the scaffolded project (.sdk components, templates, build
111
- // config) rather than 3-way merge. The scaffold is toolchain-derived and
112
- // not hand-edited; merging against a drifting .jostraca base keeps STALE
113
- // components/templates on a toolchain bump (so a fix never propagates) and
114
- // can inject <<<<<<< markers. See @voxgig/sdkgen
115
- // docs/explanation/regeneration-overwrite.md.
116
108
  existing: {
117
109
  txt: {
118
110
  write: true,
@@ -166,7 +158,6 @@ function CreateSdkGen(opts: FullCreateSdkGenOptions) {
166
158
 
167
159
  projectFolder = Path.resolve(Path.join(__dirname, 'project', spec.project))
168
160
 
169
- // TODO: support auto install project npm package (specific version) in a special cache folder
170
161
 
171
162
  if (!fs.existsSync(projectFolder)) {
172
163
  projectFolder = spec.project
@@ -275,17 +266,6 @@ async function installNpm(spec: GenerateSpec, opts: any, model: any) {
275
266
  await runNpm(['install'], spawn_opts)
276
267
  }
277
268
 
278
- // `-t` / `-f` are INDEPENDENT of `--no-install`: the documented contract is
279
- // that --no-install skips `npm install`, not that it drops the requested
280
- // targets and features. Adding them is still valid without an install when
281
- // the project already has its toolchain (re-scaffolding an existing folder
282
- // is a supported flow — "if run against an existing folder generated files
283
- // will be overwritten").
284
- //
285
- // They ARE npm run scripts that resolve @voxgig/sdkgen from node_modules, so
286
- // without one they cannot work. Fail with an actionable message rather than
287
- // the raw module-resolution error — and rather than silently skipping, which
288
- // reports success while leaving config.aon without the requested entries.
289
269
  const wanted = [
290
270
  (spec.target ?? []).length ? '--target' : '',
291
271
  (spec.feature ?? []).length ? '--feature' : '',
@@ -342,21 +322,6 @@ async function installFeatures(spec: GenerateSpec, opts: any, model: any, spawn_
342
322
  }
343
323
 
344
324
 
345
- /*
346
- function logfiles(info: any, log: ReturnType<typeof Pino>) {
347
- const cwd = process.cwd()
348
-
349
- ; Object.keys(info.files).map(action => {
350
- let entries = info.files[action]
351
- if (0 < entries.length) {
352
- log.debug({
353
- point: 'file-' + action, entries,
354
- note: '\n' + entries.map((n: any) => n.replace(cwd, '.')).join('\n')
355
- })
356
- }
357
- })
358
- }
359
- */
360
325
 
361
326
  export type {
362
327
  CreateSdkGenOptions,