@voxgig/sdkgen 4.23.0 → 4.24.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 +18 -2
- package/dist/action/doctor.d.ts +1 -1
- package/dist/action/doctor.js +12 -10
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/edition.js +4 -0
- package/dist/action/edition.js.map +1 -1
- package/dist/action/feature.js +5 -1
- package/dist/action/feature.js.map +1 -1
- package/dist/action/remove.d.ts +16 -0
- package/dist/action/remove.js +345 -0
- package/dist/action/remove.js.map +1 -0
- package/dist/action/target.js +6 -1
- package/dist/action/target.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/.sdk/tm/c/core/context.c +3 -1
- package/project/.sdk/tm/c/feature/paging.c +37 -3
- package/project/.sdk/tm/c/feature/test.c +51 -27
- package/project/.sdk/tm/c/utility/prepare_query.c +2 -1
- package/project/.sdk/tm/c/utility/transform_request.c +25 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +21 -7
- package/project/.sdk/tm/clojure/src/sdk/features.clj +68 -18
- package/project/.sdk/tm/cpp/core/types.hpp +1 -1
- package/project/.sdk/tm/cpp/feature/paging.hpp +36 -3
- package/project/.sdk/tm/cpp/feature/test.hpp +41 -13
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +16 -4
- package/project/.sdk/tm/csharp/core/Context.cs +1 -1
- package/project/.sdk/tm/csharp/feature/PagingFeature.cs +58 -4
- package/project/.sdk/tm/csharp/feature/TestFeature.cs +13 -21
- package/project/.sdk/tm/csharp/utility/Fetcher.cs +19 -12
- package/project/.sdk/tm/csharp/utility/PrepareQuery.cs +1 -1
- package/project/.sdk/tm/csharp/utility/TransformRequest.cs +23 -3
- package/project/.sdk/tm/elixir/lib/projectname/context.ex +1 -1
- package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +52 -10
- package/project/.sdk/tm/elixir/lib/projectname/feature/test.ex +47 -17
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +30 -9
- package/project/.sdk/tm/go/core/context.go +13 -13
- package/project/.sdk/tm/go/feature/paging_feature.go +40 -2
- package/project/.sdk/tm/go/feature/test_feature.go +3 -15
- package/project/.sdk/tm/go/test/feature_corpus_test.go +1 -0
- package/project/.sdk/tm/go/test/feature_test.go +54 -0
- package/project/.sdk/tm/go/utility/fetcher.go +41 -20
- package/project/.sdk/tm/go/utility/prepare_query.go +1 -1
- package/project/.sdk/tm/go/utility/transform_request.go +22 -3
- package/project/.sdk/tm/java/core/Context.java +2 -1
- package/project/.sdk/tm/java/feature/PagingFeature.java +46 -4
- package/project/.sdk/tm/java/feature/TestFeature.java +10 -13
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +3 -0
- package/project/.sdk/tm/java/test/FeatureTest.java +42 -0
- package/project/.sdk/tm/java/utility/Fetcher.java +36 -28
- package/project/.sdk/tm/java/utility/PrepareQuery.java +1 -1
- package/project/.sdk/tm/java/utility/TransformRequest.java +18 -3
- package/project/.sdk/tm/js/src/Context.js +7 -2
- package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +24 -6
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +7 -5
- package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +1 -1
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +4 -0
- package/project/.sdk/tm/kotlin/core/Context.kt +1 -1
- package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +36 -4
- package/project/.sdk/tm/kotlin/feature/TestFeature.kt +11 -14
- package/project/.sdk/tm/kotlin/utility/Fetcher.kt +33 -24
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +1 -1
- package/project/.sdk/tm/kotlin/utility/ResultTransform.kt +18 -3
- package/project/.sdk/tm/lua/core/context.lua +8 -1
- package/project/.sdk/tm/lua/core/control.lua +3 -0
- package/project/.sdk/tm/lua/feature/paging_feature.lua +44 -5
- package/project/.sdk/tm/lua/feature/test_feature.lua +32 -14
- package/project/.sdk/tm/lua/utility/prepare_query.lua +2 -1
- package/project/.sdk/tm/lua/utility/transform_request.lua +19 -3
- package/project/.sdk/tm/ocaml/sdk_features.ml +72 -21
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +23 -8
- package/project/.sdk/tm/perl/core/context.pm +2 -1
- package/project/.sdk/tm/perl/feature/paging_feature.pm +31 -5
- package/project/.sdk/tm/perl/feature/test_feature.pm +46 -16
- package/project/.sdk/tm/perl/feature/timeout_feature.pm +17 -13
- package/project/.sdk/tm/perl/t/feature_corpus.t +14 -1
- package/project/.sdk/tm/perl/utility/fetcher.pm +15 -1
- package/project/.sdk/tm/perl/utility/prepare_query.pm +1 -0
- package/project/.sdk/tm/perl/utility/transform_request.pm +13 -3
- package/project/.sdk/tm/php/core/Context.php +5 -1
- package/project/.sdk/tm/php/core/Control.php +5 -0
- package/project/.sdk/tm/php/feature/PagingFeature.php +26 -5
- package/project/.sdk/tm/php/feature/TestFeature.php +27 -42
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +17 -1
- package/project/.sdk/tm/php/test/FeatureTest.php +44 -0
- package/project/.sdk/tm/php/utility/Fetcher.php +17 -3
- package/project/.sdk/tm/php/utility/PrepareQuery.php +1 -1
- package/project/.sdk/tm/php/utility/TransformRequest.php +15 -3
- package/project/.sdk/tm/py/pkg/core/context.py +2 -1
- package/project/.sdk/tm/py/pkg/feature/paging_feature.py +29 -4
- package/project/.sdk/tm/py/pkg/feature/test_feature.py +29 -12
- package/project/.sdk/tm/py/pkg/utility/fetcher.py +63 -65
- package/project/.sdk/tm/py/pkg/utility/prepare_query.py +2 -1
- package/project/.sdk/tm/py/pkg/utility/transform_request.py +11 -3
- package/project/.sdk/tm/py/test/test_feature.py +27 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +11 -1
- package/project/.sdk/tm/rb/core/context.rb +1 -1
- package/project/.sdk/tm/rb/feature/paging_feature.rb +27 -5
- package/project/.sdk/tm/rb/feature/test_feature.rb +33 -7
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +12 -1
- package/project/.sdk/tm/rb/test/feature_test.rb +38 -0
- package/project/.sdk/tm/rb/utility/fetcher.rb +49 -3
- package/project/.sdk/tm/rb/utility/prepare_query.rb +1 -1
- package/project/.sdk/tm/rb/utility/transform_request.rb +10 -3
- package/project/.sdk/tm/rust/core/context.rs +2 -6
- package/project/.sdk/tm/rust/feature/paging.rs +44 -3
- package/project/.sdk/tm/rust/feature/secrets.rs +1 -1
- package/project/.sdk/tm/rust/feature/test.rs +66 -33
- package/project/.sdk/tm/rust/utility/fetcher.rs +41 -14
- package/project/.sdk/tm/rust/utility/prepare_query.rs +1 -1
- package/project/.sdk/tm/rust/utility/transform_request.rs +31 -5
- package/project/.sdk/tm/scala/core/Context.scala +2 -1
- package/project/.sdk/tm/scala/feature/PagingFeature.scala +41 -4
- package/project/.sdk/tm/scala/feature/TestFeature.scala +11 -12
- package/project/.sdk/tm/scala/utility/Fetcher.scala +34 -13
- package/project/.sdk/tm/scala/utility/Prepare.scala +1 -1
- package/project/.sdk/tm/scala/utility/Transform.scala +19 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +31 -4
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +12 -9
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Fetcher.swift +22 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +14 -3
- package/project/.sdk/tm/ts/src/Context.ts +7 -2
- package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +19 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +3 -0
- package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +1 -1
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +1 -0
- package/project/.sdk/tm/zig/core/context.zig +1 -1
- package/project/.sdk/tm/zig/core/utility.zig +19 -5
- package/project/.sdk/tm/zig/feature/paging.zig +34 -4
- package/project/.sdk/tm/zig/feature/test.zig +54 -26
- package/project/sdkgen-package.json +1 -1
- package/src/action/doctor.ts +12 -9
- package/src/action/edition.ts +8 -0
- package/src/action/feature.ts +7 -1
- package/src/action/remove.ts +492 -0
- package/src/action/target.ts +8 -1
|
@@ -22,7 +22,7 @@ class PagingFeature extends BaseFeature {
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
PreRequest(this: any, ctx: any) {
|
|
25
|
-
if (!this._isList(ctx)) {
|
|
25
|
+
if (!this.active || !this._isList(ctx)) {
|
|
26
26
|
return
|
|
27
27
|
}
|
|
28
28
|
const spec = ctx.spec
|
|
@@ -51,7 +51,10 @@ class PagingFeature extends BaseFeature {
|
|
|
51
51
|
spec.query[cursorParam] = paging.cursor
|
|
52
52
|
}
|
|
53
53
|
else if (null == spec.query[pageParam]) {
|
|
54
|
-
|
|
54
|
+
// A record written back by PreResult holds the page just fetched as
|
|
55
|
+
// `page` and the one to fetch as `nextPage`, so nextPage wins.
|
|
56
|
+
spec.query[pageParam] = null != paging.nextPage ? paging.nextPage :
|
|
57
|
+
null != paging.page ? paging.page : (this._options.startPage || 1)
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
if (null != this._options.limit && null == spec.query[limitParam]) {
|
|
@@ -61,7 +64,7 @@ class PagingFeature extends BaseFeature {
|
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
PreResult(this: any, ctx: any) {
|
|
64
|
-
if (!this._isList(ctx)) {
|
|
67
|
+
if (!this.active || !this._isList(ctx)) {
|
|
65
68
|
return
|
|
66
69
|
}
|
|
67
70
|
const result = ctx.result
|
|
@@ -118,8 +121,17 @@ class PagingFeature extends BaseFeature {
|
|
|
118
121
|
|
|
119
122
|
if (body && 'object' === typeof body) {
|
|
120
123
|
if (null != body.next) { paging.next = paging.next || body.next }
|
|
124
|
+
if (null != body.next_cursor) { paging.cursor = body.next_cursor }
|
|
121
125
|
if (null != body.cursor) { paging.cursor = body.cursor }
|
|
122
126
|
if (null != body.nextCursor) { paging.cursor = body.nextCursor }
|
|
127
|
+
if (null == paging.nextPage) {
|
|
128
|
+
const np = null != body.nextPage ? body.nextPage : body.next_page
|
|
129
|
+
if ('number' === typeof np || 'string' === typeof np) { paging.nextPage = np }
|
|
130
|
+
}
|
|
131
|
+
if ('boolean' === typeof body.has_more) {
|
|
132
|
+
paging.hasMore = body.has_more
|
|
133
|
+
explicitMore = true
|
|
134
|
+
}
|
|
123
135
|
if ('boolean' === typeof body.hasMore) {
|
|
124
136
|
paging.hasMore = body.hasMore
|
|
125
137
|
explicitMore = true
|
|
@@ -138,6 +150,10 @@ class PagingFeature extends BaseFeature {
|
|
|
138
150
|
|
|
139
151
|
result.paging = paging
|
|
140
152
|
|
|
153
|
+
if (null != ctx.ctrl) {
|
|
154
|
+
ctx.ctrl.paging = paging
|
|
155
|
+
}
|
|
156
|
+
|
|
141
157
|
const client: any = this._client
|
|
142
158
|
client._paging = { last: paging }
|
|
143
159
|
}
|
|
@@ -193,6 +193,7 @@ class TestFeature extends BaseFeature {
|
|
|
193
193
|
const found = select(entmap, args)
|
|
194
194
|
const ent = getelem(found, 0)
|
|
195
195
|
if (null == ent) {
|
|
196
|
+
// update miss: 404, never another record
|
|
196
197
|
return respond(404, undefined, { statusText: S_NOT_FOUND })
|
|
197
198
|
}
|
|
198
199
|
else {
|
|
@@ -237,6 +238,8 @@ class TestFeature extends BaseFeature {
|
|
|
237
238
|
const out = clone(ent)
|
|
238
239
|
return respond(200, out)
|
|
239
240
|
}
|
|
241
|
+
|
|
242
|
+
return respond(404, undefined, { statusText: 'Unknown operation' })
|
|
240
243
|
}
|
|
241
244
|
|
|
242
245
|
// Optional network behaviour simulation over the mock transport. Enable
|
|
@@ -206,6 +206,7 @@ describe('FeatureCorpus', () => {
|
|
|
206
206
|
|
|
207
207
|
const probe: any = makeClient({})
|
|
208
208
|
if (!probe._rootctx.config.hasFeature(name)) {
|
|
209
|
+
t.diagnostic(`feature.${name}: inert (this SDK does not generate the feature)`)
|
|
209
210
|
return t.skip(`this SDK was generated without the ${name} feature`)
|
|
210
211
|
}
|
|
211
212
|
|
|
@@ -100,7 +100,7 @@ pub const Context = struct {
|
|
|
100
100
|
ctrl = c;
|
|
101
101
|
} else if (ctxspec.ctrl_obj) |co| {
|
|
102
102
|
ctrl = co;
|
|
103
|
-
} else if (basectx) |b| {
|
|
103
|
+
} else if (if (ctxspec.opname == null) basectx else null) |b| {
|
|
104
104
|
ctrl = b.ctrl;
|
|
105
105
|
} else {
|
|
106
106
|
ctrl = Control.make();
|
|
@@ -1120,7 +1120,7 @@ pub fn prepare_query_util(ctx: *Context) Value {
|
|
|
1120
1120
|
}
|
|
1121
1121
|
}
|
|
1122
1122
|
}
|
|
1123
|
-
if (!h.is_noval(val) and !contained) h.setp(out, key, val);
|
|
1123
|
+
if (!h.is_noval(val) and !std.mem.eql(u8, key, "$action") and !contained) h.setp(out, key, val);
|
|
1124
1124
|
}
|
|
1125
1125
|
}
|
|
1126
1126
|
return out;
|
|
@@ -1375,6 +1375,20 @@ pub fn result_body_util(ctx: *Context) ?*SdkResult {
|
|
|
1375
1375
|
return result;
|
|
1376
1376
|
}
|
|
1377
1377
|
|
|
1378
|
+
// `$action` selects the point (see make_point_util); it is never an API
|
|
1379
|
+
// field, so the body is a copy without it. The caller's map is left untouched.
|
|
1380
|
+
fn strip_action(reqdata: Value) Value {
|
|
1381
|
+
if (reqdata != .object) return reqdata;
|
|
1382
|
+
if (reqdata.object.get("$action") == null) return reqdata;
|
|
1383
|
+
const body = h.omap();
|
|
1384
|
+
var it = reqdata.object.iterator();
|
|
1385
|
+
while (it.next()) |kv| {
|
|
1386
|
+
const key = kv.key_ptr.*;
|
|
1387
|
+
if (!std.mem.eql(u8, key, "$action")) h.setp(body, key, kv.value_ptr.*);
|
|
1388
|
+
}
|
|
1389
|
+
return body;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1378
1392
|
pub fn transform_request_util(ctx: *Context) Value {
|
|
1379
1393
|
const spec = ctx.spec;
|
|
1380
1394
|
const point = ctx.point;
|
|
@@ -1382,16 +1396,16 @@ pub fn transform_request_util(ctx: *Context) Value {
|
|
|
1382
1396
|
if (spec) |sp| sp.step = "reqform";
|
|
1383
1397
|
|
|
1384
1398
|
const transform = h.to_map(h.getp(point, "transform"));
|
|
1385
|
-
if (h.is_noval(transform)) return ctx.reqdata;
|
|
1399
|
+
if (h.is_noval(transform)) return strip_action(ctx.reqdata);
|
|
1386
1400
|
|
|
1387
1401
|
const reqform = h.getp(transform, "req");
|
|
1388
|
-
if (h.is_noval(reqform)) return ctx.reqdata;
|
|
1402
|
+
if (h.is_noval(reqform)) return strip_action(ctx.reqdata);
|
|
1389
1403
|
|
|
1390
1404
|
const store = h.jo(&.{.{ "reqdata", ctx.reqdata }});
|
|
1391
1405
|
// transform now reports collected injection errors beside the value; .out
|
|
1392
1406
|
// is what it used to return on its own, errors or not.
|
|
1393
|
-
const tres = vs.transform(h.A(), store, reqform) catch return ctx.reqdata;
|
|
1394
|
-
return tres.out;
|
|
1407
|
+
const tres = vs.transform(h.A(), store, reqform) catch return strip_action(ctx.reqdata);
|
|
1408
|
+
return strip_action(tres.out);
|
|
1395
1409
|
}
|
|
1396
1410
|
|
|
1397
1411
|
pub fn transform_response_util(ctx: *Context) Value {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// (or a cursor) into the request query; on the way back (PreResult) it reads
|
|
4
4
|
// the server's pagination signals — a `Link: rel="next"` header,
|
|
5
5
|
// `X-Page`/`X-Next-Page`/`X-Total-Count` headers, or
|
|
6
|
-
// `next`/`cursor`/`nextCursor`/`hasMore` fields in the body
|
|
7
|
-
// on `result.paging
|
|
6
|
+
// `next`/`cursor`/`nextCursor`/`hasMore` fields in the body (snake_case forms
|
|
7
|
+
// too) — and records them on `result.paging` and the caller's ctrl paging. A per-call cursor/page from ctrl takes priority (used by
|
|
8
8
|
// auto-iteration). Parameter names (`pageParam`, `limitParam`, `cursorParam`),
|
|
9
9
|
// the page size (`limit`) and the start page (`startPage`, default 1) are
|
|
10
10
|
// configurable.
|
|
@@ -114,7 +114,10 @@ pub const PagingFeature = struct {
|
|
|
114
114
|
if (!h.is_noval(cursor)) {
|
|
115
115
|
h.setp(query, cursor_param, cursor);
|
|
116
116
|
} else if (h.is_noval(h.getp(query, page_param))) {
|
|
117
|
-
|
|
117
|
+
// A record written back by pre_result holds the page just fetched
|
|
118
|
+
// as "page" and the one to fetch as "nextPage", so nextPage wins.
|
|
119
|
+
var page = h.getp(paging, "nextPage");
|
|
120
|
+
if (h.is_noval(page)) page = h.getp(paging, "page");
|
|
118
121
|
if (!h.is_noval(page)) {
|
|
119
122
|
h.setp(query, page_param, page);
|
|
120
123
|
} else {
|
|
@@ -257,12 +260,16 @@ pub const PagingFeature = struct {
|
|
|
257
260
|
}
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
// Body-level
|
|
263
|
+
// Body-level signals; snake_case forms are read first so camelCase wins.
|
|
261
264
|
if (body == .object) {
|
|
262
265
|
const next = h.getp(body, "next");
|
|
263
266
|
if (!h.is_noval(next) and h.is_noval(h.getp(paging, "next"))) {
|
|
264
267
|
h.setp(paging, "next", next);
|
|
265
268
|
}
|
|
269
|
+
const snake_cursor = h.getp(body, "next_cursor");
|
|
270
|
+
if (!h.is_noval(snake_cursor)) {
|
|
271
|
+
h.setp(paging, "cursor", snake_cursor);
|
|
272
|
+
}
|
|
266
273
|
const cursor = h.getp(body, "cursor");
|
|
267
274
|
if (!h.is_noval(cursor)) {
|
|
268
275
|
h.setp(paging, "cursor", cursor);
|
|
@@ -271,6 +278,18 @@ pub const PagingFeature = struct {
|
|
|
271
278
|
if (!h.is_noval(next_cursor)) {
|
|
272
279
|
h.setp(paging, "cursor", next_cursor);
|
|
273
280
|
}
|
|
281
|
+
if (h.is_noval(h.getp(paging, "nextPage"))) {
|
|
282
|
+
var np = h.getp(body, "nextPage");
|
|
283
|
+
if (h.is_noval(np)) np = h.getp(body, "next_page");
|
|
284
|
+
switch (np) {
|
|
285
|
+
.integer, .float, .string => h.setp(paging, "nextPage", np),
|
|
286
|
+
else => {},
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (h.get_bool(body, "has_more")) |has_more| {
|
|
290
|
+
h.setp(paging, "hasMore", h.vbool(has_more));
|
|
291
|
+
explicit_more = true;
|
|
292
|
+
}
|
|
274
293
|
if (h.get_bool(body, "hasMore")) |has_more| {
|
|
275
294
|
h.setp(paging, "hasMore", h.vbool(has_more));
|
|
276
295
|
explicit_more = true;
|
|
@@ -292,6 +311,17 @@ pub const PagingFeature = struct {
|
|
|
292
311
|
|
|
293
312
|
result.paging = paging;
|
|
294
313
|
self.last = paging;
|
|
314
|
+
|
|
315
|
+
// `.object` is a *MapRef, so the caller's map is refilled in place; a
|
|
316
|
+
// fresh map on ctrl never reaches the caller.
|
|
317
|
+
if (ctx.ctrl.paging == .object) {
|
|
318
|
+
const shared = ctx.ctrl.paging.object;
|
|
319
|
+
shared.data.clearRetainingCapacity();
|
|
320
|
+
var it = paging.object.iterator();
|
|
321
|
+
while (it.next()) |kv| h.setp(ctx.ctrl.paging, kv.key_ptr.*, kv.value_ptr.*);
|
|
322
|
+
} else {
|
|
323
|
+
ctx.ctrl.paging = paging;
|
|
324
|
+
}
|
|
295
325
|
}
|
|
296
326
|
|
|
297
327
|
fn vname(p: *anyopaque) []const u8 {
|
|
@@ -150,26 +150,62 @@ fn resolve_match(ctx: *Context, explicit: Value) Value {
|
|
|
150
150
|
return h.omap();
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
fn point_terminal(p: Value) bool {
|
|
154
|
+
const parts = h.getp(p, "parts");
|
|
155
|
+
if (parts != .array) return false;
|
|
156
|
+
const items = parts.array.data.items;
|
|
157
|
+
if (items.len == 0) return false;
|
|
158
|
+
const last = items[items.len - 1];
|
|
159
|
+
return last == .string and std.mem.startsWith(u8, last.string, "{");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn point_depth(p: Value) usize {
|
|
163
|
+
const parts = h.getp(p, "parts");
|
|
164
|
+
return if (parts == .array) parts.array.data.items.len else 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// The entity's own endpoint: a terminal `{param}` marks a record route, and
|
|
168
|
+
// among equals the shallower path wins (the same rule as make_point).
|
|
169
|
+
fn pick_point(points: Value) Value {
|
|
170
|
+
if (points != .array) return h.vnull();
|
|
171
|
+
const items = points.array.data.items;
|
|
172
|
+
if (items.len == 0) return h.vnull();
|
|
173
|
+
var point = items[0];
|
|
174
|
+
for (items[1..]) |cand| {
|
|
175
|
+
if (point_terminal(cand) != point_terminal(point)) {
|
|
176
|
+
if (point_terminal(cand)) point = cand;
|
|
177
|
+
} else if (point_depth(cand) < point_depth(point)) {
|
|
178
|
+
point = cand;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return point;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
fn reqd_names(point: Value, kind: []const u8) Value {
|
|
185
|
+
const args_path = h.getpath(&.{ "args", kind }, point);
|
|
186
|
+
const reqd_args = vs.select(h.A(), args_path, h.jo(&.{.{ "reqd", h.vbool(true) }})) catch return h.olist();
|
|
187
|
+
// transform reports collected injection errors beside the value; .out is
|
|
188
|
+
// the value itself, errors or not.
|
|
189
|
+
const tres = vs.transform(h.A(), reqd_args, h.ja(&.{
|
|
190
|
+
h.vstr("`$EACH`"),
|
|
191
|
+
h.vstr(""),
|
|
192
|
+
h.vstr("`$KEY.name`"),
|
|
193
|
+
})) catch return h.olist();
|
|
194
|
+
return tres.out;
|
|
195
|
+
}
|
|
196
|
+
|
|
153
197
|
fn build_args(ctx: *Context, args: Value) Value {
|
|
154
198
|
const op = ctx.op;
|
|
155
199
|
const opname = op.name;
|
|
156
200
|
const entname: []const u8 = if (ctx.entity) |e| e.get_name() else op.entity;
|
|
157
201
|
|
|
158
202
|
const points = h.getpath(&.{ "entity", entname, "op", opname, "points" }, ctx.config);
|
|
159
|
-
const point =
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
// .out is what it used to return on its own, errors or not.
|
|
166
|
-
const tres = vs.transform(h.A(), reqd_params, h.ja(&.{
|
|
167
|
-
h.vstr("`$EACH`"),
|
|
168
|
-
h.vstr(""),
|
|
169
|
-
h.vstr("`$KEY.name`"),
|
|
170
|
-
})) catch break :blk h.olist();
|
|
171
|
-
break :blk tres.out;
|
|
172
|
-
};
|
|
203
|
+
const point = pick_point(points);
|
|
204
|
+
|
|
205
|
+
// Path AND query: a path-only read misses a query-addressed record
|
|
206
|
+
// (e.g. GET /result?trace_id=), which has no path param at all.
|
|
207
|
+
const reqd_params = reqd_names(point, "params");
|
|
208
|
+
const reqd_query = reqd_names(point, "query");
|
|
173
209
|
|
|
174
210
|
const qand = h.olist();
|
|
175
211
|
const q = h.jo(&.{.{ "`$AND`", qand }});
|
|
@@ -178,8 +214,8 @@ fn build_args(ctx: *Context, args: Value) Value {
|
|
|
178
214
|
const keys = h.keysof_vec(args);
|
|
179
215
|
for (keys) |key| {
|
|
180
216
|
const is_id = std.mem.eql(u8, key, "id");
|
|
181
|
-
const
|
|
182
|
-
|
|
217
|
+
const is_reqd = !h.is_empty(vs.select(h.A(), reqd_params, h.vstr(key)) catch h.olist()) or
|
|
218
|
+
!h.is_empty(vs.select(h.A(), reqd_query, h.vstr(key)) catch h.olist());
|
|
183
219
|
|
|
184
220
|
if (is_id or is_reqd) {
|
|
185
221
|
const v = ctx.util().param(ctx, h.vstr(key));
|
|
@@ -251,20 +287,12 @@ fn test_fetch(entity: Value, ctx: *Context, _: []const u8, _: Value) err.E!Value
|
|
|
251
287
|
const args = build_args(ctx, update_match);
|
|
252
288
|
const found = vs.select(h.A(), entmap, args) catch h.olist();
|
|
253
289
|
var ent = h.get_elem(found, h.vnum(0), h.vnull());
|
|
254
|
-
if (h.is_noval(ent) and h.sizeOf(entmap) > 0) {
|
|
255
|
-
for (h.items_vec(entmap)) |kv| {
|
|
256
|
-
if (kv.v == .object) {
|
|
257
|
-
ent = kv.v;
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
290
|
if (h.is_noval(ent)) {
|
|
291
|
+
// update miss: 404, never another record
|
|
263
292
|
return respond(ctx, 404, h.vnull(), &.{.{ "statusText", h.vstr("Not found") }});
|
|
264
293
|
}
|
|
265
294
|
if (ent == .object and reqdata == .object) {
|
|
266
|
-
|
|
267
|
-
while (it.next()) |kv| h.setp(ent, kv.key_ptr.*, kv.value_ptr.*);
|
|
295
|
+
ent = h.merge(h.ja(&.{ ent, reqdata }));
|
|
268
296
|
}
|
|
269
297
|
h.del_prop(ent, h.vstr("$KEY"));
|
|
270
298
|
return respond(ctx, 200, h.clone(ent), &.{});
|
package/src/action/doctor.ts
CHANGED
|
@@ -230,7 +230,7 @@ function orphanModelFiles(actx: ActionContext): string[] {
|
|
|
230
230
|
|
|
231
231
|
|
|
232
232
|
async function doctor(
|
|
233
|
-
actx: ActionContext, scope?: DoctorScope,
|
|
233
|
+
actx: ActionContext, scope?: DoctorScope, selected?: string[],
|
|
234
234
|
): Promise<ActionResult> {
|
|
235
235
|
const log = actx.log
|
|
236
236
|
const fs = actx.fs()
|
|
@@ -283,17 +283,18 @@ async function doctor(
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
if ('target' === kind) {
|
|
286
|
-
checkTarget(actx, source, report)
|
|
286
|
+
checkTarget(actx, source, report, selected)
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
if ('edition' === kind) {
|
|
290
290
|
checkEdition(actx, source, report)
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
// Only an ACTIVE feature has source copied out;
|
|
294
|
-
//
|
|
293
|
+
// Only an ACTIVE feature has source copied out; an inactive one's
|
|
294
|
+
// leftovers are stale. `selected` overrides that for the caller's own.
|
|
295
295
|
if ('feature' === kind &&
|
|
296
|
-
false !== (model as any)?.main?.[KIT]?.feature?.[name]?.active
|
|
296
|
+
(false !== (model as any)?.main?.[KIT]?.feature?.[name]?.active ||
|
|
297
|
+
true === selected?.includes(name))) {
|
|
297
298
|
checkFeatureSource(actx, source, targets, report)
|
|
298
299
|
}
|
|
299
300
|
|
|
@@ -409,6 +410,7 @@ function resolveDeclared(
|
|
|
409
410
|
|
|
410
411
|
function checkTarget(
|
|
411
412
|
actx: ActionContext, resolved: Source, report: DoctorReport,
|
|
413
|
+
selected?: string[],
|
|
412
414
|
) {
|
|
413
415
|
const tname = resolved.name
|
|
414
416
|
const tfolder = resolved.folder
|
|
@@ -442,14 +444,15 @@ function checkTarget(
|
|
|
442
444
|
},
|
|
443
445
|
]
|
|
444
446
|
|
|
445
|
-
// The feature set `target add` would select right now
|
|
446
|
-
//
|
|
447
|
-
//
|
|
448
|
-
//
|
|
447
|
+
// The feature set `target add` would select right now, plus any the CALLER
|
|
448
|
+
// is acting on (`selected`, see COMMENT-NOTES.md). A project that added its
|
|
449
|
+
// targets before feature trimming existed carries source for features its
|
|
450
|
+
// model never declared — expected here as STALE, which is what it is.
|
|
449
451
|
const featuremodel: any = model?.main?.[KIT]?.feature ?? {}
|
|
450
452
|
const features = Array.from(new Set([
|
|
451
453
|
'test',
|
|
452
454
|
...Object.keys(featuremodel).filter((n: string) => false !== featuremodel[n]?.active),
|
|
455
|
+
...(selected ?? []),
|
|
453
456
|
]))
|
|
454
457
|
|
|
455
458
|
// `folder` and `model` matter: the trim catalogue is resolved consumer-side
|
package/src/action/edition.ts
CHANGED
|
@@ -39,6 +39,7 @@ function cmpBase(): string {
|
|
|
39
39
|
|
|
40
40
|
const CMD_MAP: any = Object.assign(Object.create(null), {
|
|
41
41
|
add: cmd_edition_add,
|
|
42
|
+
remove: cmd_edition_remove,
|
|
42
43
|
})
|
|
43
44
|
|
|
44
45
|
|
|
@@ -65,6 +66,13 @@ async function cmd_edition_add(
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
|
|
69
|
+
async function cmd_edition_remove(
|
|
70
|
+
args: string[], actx: ActionContext,
|
|
71
|
+
): Promise<ActionResult> {
|
|
72
|
+
return require('./remove').kind_remove('edition', parseAddNames(args), actx)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
68
76
|
async function edition_add(
|
|
69
77
|
edition: string[], actx: ActionContext,
|
|
70
78
|
): Promise<ActionResult> {
|
package/src/action/feature.ts
CHANGED
|
@@ -42,7 +42,8 @@ import {
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
const CMD_MAP: any = {
|
|
45
|
-
add: cmd_feature_add
|
|
45
|
+
add: cmd_feature_add,
|
|
46
|
+
remove: cmd_feature_remove,
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const BASE = 'node_modules/@voxgig/sdkgen'
|
|
@@ -74,6 +75,11 @@ async function cmd_feature_add(args: string[], actx: ActionContext): Promise<Act
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
|
|
78
|
+
async function cmd_feature_remove(args: string[], actx: ActionContext): Promise<ActionResult> {
|
|
79
|
+
return require('./remove').kind_remove('feature', parseAddNames(args), actx)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
77
83
|
async function feature_add(features: string[], actx: ActionContext): Promise<ActionResult> {
|
|
78
84
|
|
|
79
85
|
// Reuse the caller's Jostraca instance so feature generation honours the
|