@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.
- package/dist/create-sdkgen.js +0 -20
- package/dist/create-sdkgen.js.map +1 -1
- package/dist/project/standard/CreateRoot.js +1 -103
- package/dist/project/standard/CreateRoot.js.map +1 -1
- package/package.json +3 -3
- package/project/standard/.sdk/model/config.aon +0 -5
- package/project/standard/.sdk/model/sdk.fragment.aon +1 -6
- package/project/standard/.sdk/package.json +4 -4
- package/project/standard/.sdk/src/BuildSDK.ts +5 -22
- package/project/standard/.sdk/src/Root.ts +0 -22
- package/project/standard/.sdk/src/Top.ts +0 -8
- package/project/standard/.sdk/test/feature/cost.aon +0 -43
- package/project/standard/.sdk/test/primary/clean.aon +0 -7
- package/project/standard/.sdk/test/primary/featureAdd.aon +0 -11
- package/project/standard/.sdk/test/primary/featureHook.aon +0 -10
- package/project/standard/.sdk/test/primary/featureInit.aon +0 -10
- package/project/standard/.sdk/test/primary/fetcher.aon +0 -10
- package/project/standard/.sdk/test/primary/makeFetchDef.aon +0 -10
- package/project/standard/.sdk/test/primary/makePoint.aon +0 -22
- package/project/standard/.sdk/test/primary/makeResult.aon +0 -14
- package/project/standard/.sdk/test/primary/prepareAuth.aon +0 -5
- package/project/standard/.sdk/test/primary/preparePath.aon +0 -7
- package/project/standard/.sdk/test/struct/nullsem.aon +2 -95
- package/project/standard/.sdk/test/struct/select.aon +79 -96
- package/src/create-sdkgen.ts +0 -35
- package/src/project/standard/CreateRoot.ts +1 -103
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
}
|
package/src/create-sdkgen.ts
CHANGED
|
@@ -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,
|