@voxgig/sdkgen 3.4.5 → 3.4.7
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/cmp/ReadmeTop.js +5 -1
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/helpers/opExample.js +10 -1
- package/dist/helpers/opExample.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +6 -2
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +8 -1
- package/dist/utility.js +144 -0
- package/dist/utility.js.map +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/c/Config_c.ts +45 -0
- package/project/.sdk/src/cmp/c/utility_c.ts +36 -0
- package/project/.sdk/src/cmp/dart/Config_dart.ts +15 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +1 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +9 -52
- package/project/.sdk/src/cmp/js/Config_js.ts +49 -1
- package/project/.sdk/src/cmp/js/fragment/Config.data.fragment.js +50 -0
- package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +1 -1
- package/project/.sdk/src/cmp/lua/Config_lua.ts +51 -1
- package/project/.sdk/src/cmp/lua/utility_lua.ts +21 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +86 -0
- package/project/.sdk/src/cmp/py/Config_py.ts +47 -1
- package/project/.sdk/src/cmp/rb/Config_rb.ts +46 -2
- package/project/.sdk/src/cmp/rust/Config_rust.ts +50 -35
- package/project/.sdk/src/cmp/rust/utility_rust.ts +17 -0
- package/project/.sdk/src/cmp/ts/Config_ts.ts +10 -50
- package/project/.sdk/src/cmp/zig/Config_zig.ts +47 -40
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +28 -17
- package/project/.sdk/tm/zig/test/gotcha_test.zig +8 -8
- package/src/cmp/ReadmeTop.ts +6 -1
- package/src/helpers/opExample.ts +11 -1
- package/src/sdkgen.ts +6 -1
- package/src/utility.ts +157 -1
|
@@ -22,7 +22,20 @@ const Spec = spec_mod.Spec;
|
|
|
22
22
|
pub const ProjectNameSDK = struct {
|
|
23
23
|
mode: []const u8 = "live",
|
|
24
24
|
options: Value = .{ .null = {} },
|
|
25
|
-
utility
|
|
25
|
+
// NOT named `utility`, and NOT a name `zigVarName` can produce.
|
|
26
|
+
//
|
|
27
|
+
// Entity accessors are generated as methods on this same struct
|
|
28
|
+
// (MainEntity_zig), so an API with an entity called `utility` collided
|
|
29
|
+
// twice: Zig rejects a local binding that shadows the declaration, and
|
|
30
|
+
// `sdk.utility` resolves to the FIELD, which made the generated accessor
|
|
31
|
+
// unreachable.
|
|
32
|
+
//
|
|
33
|
+
// `zigVarName` lowercases every name it is given, so a field spelling with
|
|
34
|
+
// an uppercase letter is one no entity name can ever reach - which is why
|
|
35
|
+
// this is `sdkUtility` and not, say, `util_rt`, a name an entity called
|
|
36
|
+
// `util-rt` would map straight onto. The public reader is `get_utility()`,
|
|
37
|
+
// so this field is internal and free to be spelled this way.
|
|
38
|
+
sdkUtility: *Utility,
|
|
26
39
|
features: std.ArrayList(Feature),
|
|
27
40
|
rootctx: ?*Context = null,
|
|
28
41
|
|
|
@@ -31,22 +44,22 @@ pub const ProjectNameSDK = struct {
|
|
|
31
44
|
sdk.* = .{
|
|
32
45
|
.mode = "live",
|
|
33
46
|
.options = h.vnull(),
|
|
34
|
-
.
|
|
47
|
+
.sdkUtility = Utility.new(),
|
|
35
48
|
.features = std.ArrayList(Feature).init(h.A()),
|
|
36
49
|
.rootctx = null,
|
|
37
50
|
};
|
|
38
51
|
|
|
39
52
|
const cfg = config.make_config();
|
|
40
53
|
|
|
41
|
-
const rootctx = sdk.
|
|
54
|
+
const rootctx = sdk.sdkUtility.make_context(CtxSpec{
|
|
42
55
|
.client = sdk,
|
|
43
|
-
.utility = sdk.
|
|
56
|
+
.utility = sdk.sdkUtility,
|
|
44
57
|
.config = cfg,
|
|
45
58
|
.options = options,
|
|
46
59
|
.shared = h.omap(),
|
|
47
60
|
}, null);
|
|
48
61
|
|
|
49
|
-
const opts = sdk.
|
|
62
|
+
const opts = sdk.sdkUtility.make_options(rootctx);
|
|
50
63
|
sdk.options = opts;
|
|
51
64
|
|
|
52
65
|
if (h.veq(h.getpath(&.{ "feature", "test", "active" }, opts), h.vbool(true))) {
|
|
@@ -71,7 +84,7 @@ pub const ProjectNameSDK = struct {
|
|
|
71
84
|
const fopts = h.getp(feature_opts, fname);
|
|
72
85
|
if (fopts == .object) {
|
|
73
86
|
if (h.get_bool(fopts, "active") orelse false) {
|
|
74
|
-
sdk.
|
|
87
|
+
sdk.sdkUtility.feature_add(rootctx, config.make_feature(fname));
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
}
|
|
@@ -80,9 +93,9 @@ pub const ProjectNameSDK = struct {
|
|
|
80
93
|
// Initialize features.
|
|
81
94
|
var snap = std.ArrayList(Feature).init(h.A());
|
|
82
95
|
for (sdk.features.items) |f| snap.append(f) catch {};
|
|
83
|
-
for (snap.items) |f| sdk.
|
|
96
|
+
for (snap.items) |f| sdk.sdkUtility.feature_init(rootctx, f);
|
|
84
97
|
|
|
85
|
-
sdk.
|
|
98
|
+
sdk.sdkUtility.feature_hook(rootctx, "PostConstruct");
|
|
86
99
|
|
|
87
100
|
return sdk;
|
|
88
101
|
}
|
|
@@ -92,7 +105,7 @@ pub const ProjectNameSDK = struct {
|
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
pub fn get_utility(self: *ProjectNameSDK) *Utility {
|
|
95
|
-
return Utility.copy(self.
|
|
108
|
+
return Utility.copy(self.sdkUtility);
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
pub fn get_root_ctx(self: *ProjectNameSDK) *Context {
|
|
@@ -100,7 +113,6 @@ pub const ProjectNameSDK = struct {
|
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
pub fn prepare(self: *ProjectNameSDK, fetchargs_in: Value) E!Value {
|
|
103
|
-
const utility = self.utility;
|
|
104
116
|
|
|
105
117
|
const fetchargs: Value = switch (fetchargs_in) {
|
|
106
118
|
.object => fetchargs_in,
|
|
@@ -112,7 +124,7 @@ pub const ProjectNameSDK = struct {
|
|
|
112
124
|
else => h.omap(),
|
|
113
125
|
};
|
|
114
126
|
|
|
115
|
-
const ctx =
|
|
127
|
+
const ctx = self.sdkUtility.make_context(CtxSpec{
|
|
116
128
|
.opname = "prepare",
|
|
117
129
|
.ctrl = ctrl,
|
|
118
130
|
}, self.get_root_ctx());
|
|
@@ -134,7 +146,7 @@ pub const ProjectNameSDK = struct {
|
|
|
134
146
|
else => h.omap(),
|
|
135
147
|
};
|
|
136
148
|
|
|
137
|
-
const headers =
|
|
149
|
+
const headers = self.sdkUtility.prepare_headers(ctx);
|
|
138
150
|
|
|
139
151
|
const specmap = h.jo(&.{
|
|
140
152
|
.{ "base", h.getp(options, "base") },
|
|
@@ -158,9 +170,9 @@ pub const ProjectNameSDK = struct {
|
|
|
158
170
|
while (it.next()) |kv| h.setp(spec.headers, kv.key_ptr.*, kv.value_ptr.*);
|
|
159
171
|
}
|
|
160
172
|
|
|
161
|
-
_ = try
|
|
173
|
+
_ = try self.sdkUtility.prepare_auth(ctx);
|
|
162
174
|
|
|
163
|
-
return
|
|
175
|
+
return self.sdkUtility.make_fetch_def(ctx);
|
|
164
176
|
}
|
|
165
177
|
|
|
166
178
|
// Raw endpoint access is operator-controllable, like every entity op.
|
|
@@ -200,7 +212,6 @@ pub const ProjectNameSDK = struct {
|
|
|
200
212
|
// a caller-supplied marker would let anyone opt straight back out of the
|
|
201
213
|
// gate by passing it.
|
|
202
214
|
fn raw_request(self: *ProjectNameSDK, fetchargs_in: Value) Value {
|
|
203
|
-
const utility = self.utility;
|
|
204
215
|
|
|
205
216
|
const fetchdef = self.prepare(fetchargs_in) catch {
|
|
206
217
|
return h.jo(&.{
|
|
@@ -218,13 +229,13 @@ pub const ProjectNameSDK = struct {
|
|
|
218
229
|
else => h.omap(),
|
|
219
230
|
};
|
|
220
231
|
|
|
221
|
-
const ctx =
|
|
232
|
+
const ctx = self.sdkUtility.make_context(CtxSpec{
|
|
222
233
|
.opname = "direct",
|
|
223
234
|
.ctrl = ctrl,
|
|
224
235
|
}, self.get_root_ctx());
|
|
225
236
|
|
|
226
237
|
const url = h.get_str(fetchdef, "url") orelse "";
|
|
227
|
-
const fetched =
|
|
238
|
+
const fetched = self.sdkUtility.fetch(ctx, url, fetchdef) catch {
|
|
228
239
|
return h.jo(&.{
|
|
229
240
|
.{ "ok", h.vbool(false) },
|
|
230
241
|
.{ "err", h.vstr(if (ctx.pending_err) |e| e.msg else "fetch failed") },
|
|
@@ -100,7 +100,7 @@ test "gotcha8: custom utility callable survives" {
|
|
|
100
100
|
const fn_val = h.callable(@ptrCast(&probe_dummy), probeCall);
|
|
101
101
|
const opts = h.jo(&.{.{ "utility", h.jo(&.{.{ "probe", fn_val }}) }});
|
|
102
102
|
const client = sdk.SDK.new(opts);
|
|
103
|
-
const stored = h.getp(client.
|
|
103
|
+
const stored = h.getp(client.sdkUtility.custom, "probe");
|
|
104
104
|
try testing.expect(stored == .function);
|
|
105
105
|
const r = h.call_vfn(stored, h.vstr("x"));
|
|
106
106
|
try testing.expect(r == .string and std.mem.eql(u8, r.string, "x"));
|
|
@@ -139,10 +139,10 @@ test "gotcha3: many features" {
|
|
|
139
139
|
|
|
140
140
|
test "gotcha2: make_point surfaces out.point error" {
|
|
141
141
|
const client = sdk.new();
|
|
142
|
-
const ctx = client.
|
|
142
|
+
const ctx = client.sdkUtility.make_context(.{ .opname = "list" }, client.get_root_ctx());
|
|
143
143
|
const e = ctx.make_error("rbac_denied", "denied");
|
|
144
144
|
ctx.out_set("point", sdk.OutVal{ .err = e });
|
|
145
|
-
try testing.expectError(error.Sdk, client.
|
|
145
|
+
try testing.expectError(error.Sdk, client.sdkUtility.make_point(ctx));
|
|
146
146
|
try testing.expect(ctx.pending_err != null);
|
|
147
147
|
try testing.expect(std.mem.eql(u8, ctx.pending_err.?.code, "rbac_denied"));
|
|
148
148
|
}
|
|
@@ -152,8 +152,8 @@ test "gotcha2: make_point surfaces out.point error" {
|
|
|
152
152
|
test "gotcha4: featureAdd before ordering" {
|
|
153
153
|
const client = sdk.new();
|
|
154
154
|
const ctx = client.get_root_ctx();
|
|
155
|
-
client.
|
|
156
|
-
client.
|
|
155
|
+
client.sdkUtility.feature_add(ctx, OrderFeat.make("aaa", vnull()));
|
|
156
|
+
client.sdkUtility.feature_add(ctx, OrderFeat.make("bbb", h.jo(&.{.{ "__before__", h.vstr("aaa") }})));
|
|
157
157
|
var ai: usize = 999;
|
|
158
158
|
var bi: usize = 999;
|
|
159
159
|
for (client.features.items, 0..) |f, i| {
|
|
@@ -176,7 +176,7 @@ test "gotcha6: netsim offline error code" {
|
|
|
176
176
|
});
|
|
177
177
|
const client = sdk.SDK.new(opts);
|
|
178
178
|
const ctx = client.get_root_ctx();
|
|
179
|
-
const r = client.
|
|
179
|
+
const r = client.sdkUtility.fetch(ctx, "http://x", h.jo(&.{.{ "url", h.vstr("http://x") }}));
|
|
180
180
|
try testing.expectError(error.Sdk, r);
|
|
181
181
|
try testing.expect(std.mem.eql(u8, ctx.pending_err.?.code, "netsim_offline"));
|
|
182
182
|
}
|
|
@@ -192,7 +192,7 @@ test "gotcha6: netsim conn error code" {
|
|
|
192
192
|
});
|
|
193
193
|
const client = sdk.SDK.new(opts);
|
|
194
194
|
const ctx = client.get_root_ctx();
|
|
195
|
-
const r = client.
|
|
195
|
+
const r = client.sdkUtility.fetch(ctx, "http://x", h.jo(&.{.{ "url", h.vstr("http://x") }}));
|
|
196
196
|
try testing.expectError(error.Sdk, r);
|
|
197
197
|
try testing.expect(std.mem.eql(u8, ctx.pending_err.?.code, "netsim_conn"));
|
|
198
198
|
}
|
|
@@ -215,7 +215,7 @@ test "gotcha6: netsim seeded latency is deterministic" {
|
|
|
215
215
|
const ctx = client.get_root_ctx();
|
|
216
216
|
var i: usize = 0;
|
|
217
217
|
while (i < 4) : (i += 1) {
|
|
218
|
-
_ = client.
|
|
218
|
+
_ = client.sdkUtility.fetch(ctx, "http://x", h.jo(&.{.{ "url", h.vstr("http://x") }})) catch {};
|
|
219
219
|
}
|
|
220
220
|
return sleep_log.toOwnedSlice() catch &.{};
|
|
221
221
|
}
|
package/src/cmp/ReadmeTop.ts
CHANGED
|
@@ -261,8 +261,13 @@ ${aboutMd.trim()}
|
|
|
261
261
|
exCall = `const ${exLower} = await client.${ex}().load(${exLoadArg})`
|
|
262
262
|
} else if ('create' === primaryOp || 'update' === primaryOp) {
|
|
263
263
|
const exIdF = entityIdField(exEnt)
|
|
264
|
+
// Drop the id only when the request shape says it is OPTIONAL. It is
|
|
265
|
+
// server-assigned on a normal create, but an op whose id comes from a
|
|
266
|
+
// PATH PARAMETER requires it, and the typed CreateData then rejects a
|
|
267
|
+
// body without it. Same rule as dataArg in helpers/opExample.
|
|
264
268
|
const shapeItems = opRequestShape(exEnt, primaryOp).items
|
|
265
|
-
.filter((it: any) =>
|
|
269
|
+
.filter((it: any) =>
|
|
270
|
+
(it.name !== exIdF && it.name !== 'id') || !it.optional)
|
|
266
271
|
const required = shapeItems.filter((it: any) => !it.optional)
|
|
267
272
|
// ALL required fields must appear or the literal is not assignable to
|
|
268
273
|
// the typed CreateData/UpdateData; cap only the optional fallback.
|
package/src/helpers/opExample.ts
CHANGED
|
@@ -116,8 +116,18 @@ function matchArg(
|
|
|
116
116
|
// `key: value` pairs (capped) in the target language's object syntax. Ensures
|
|
117
117
|
// the body satisfies a typed CreateData/UpdateData (required fields present).
|
|
118
118
|
function dataArg(lang: ExampleLang, ent: any, op: string, idF: string | null): string {
|
|
119
|
+
// The id is normally server-assigned on create, so it is dropped from the
|
|
120
|
+
// example body. But it is only safe to drop when the request shape says it is
|
|
121
|
+
// OPTIONAL: an op whose id comes from a PATH PARAMETER requires it, and a
|
|
122
|
+
// typed CreateData then rejects a body without it.
|
|
123
|
+
//
|
|
124
|
+
// Conecto's `/integrations/{slug}/actions/{action}/run/` is the case — the
|
|
125
|
+
// guide renames the `action` param to `id`, so ActionCreateData is
|
|
126
|
+
// `{id, slug, ok}` and the generated snippet emitted only `{slug, ok}`,
|
|
127
|
+
// failing to compile with "Property 'id' is missing".
|
|
119
128
|
const items = opRequestShape(ent, op).items
|
|
120
|
-
.filter((it: any) =>
|
|
129
|
+
.filter((it: any) =>
|
|
130
|
+
(it.name !== idF && it.name !== 'id') || !it.optional)
|
|
121
131
|
const required = items.filter((it: any) => !it.optional)
|
|
122
132
|
// ALL required fields must appear (a typed CreateData rejects a partial); cap
|
|
123
133
|
// only the optional fallback used when the op declares no required field.
|
package/src/sdkgen.ts
CHANGED
|
@@ -20,7 +20,8 @@ import type {
|
|
|
20
20
|
} from './types'
|
|
21
21
|
|
|
22
22
|
import { SdkGenError, requirePath, isAuthActive, resolveAuthPrefix,
|
|
23
|
-
CONFIG_DATA_THRESHOLD, CONFIG_REPR_VALUES, isConfigData, configRepr
|
|
23
|
+
CONFIG_DATA_THRESHOLD, CONFIG_REPR_VALUES, isConfigData, configRepr,
|
|
24
|
+
configReprSetting, configDefinition, clean, rawStringLiteral } from './utility'
|
|
24
25
|
|
|
25
26
|
import { Main } from './cmp/Main'
|
|
26
27
|
import { ExternalTarget } from './cmp/ExternalTarget'
|
|
@@ -781,6 +782,10 @@ export {
|
|
|
781
782
|
CONFIG_REPR_VALUES,
|
|
782
783
|
isConfigData,
|
|
783
784
|
configRepr,
|
|
785
|
+
configReprSetting,
|
|
786
|
+
configDefinition,
|
|
787
|
+
clean,
|
|
788
|
+
rawStringLiteral,
|
|
784
789
|
|
|
785
790
|
// Scaffold components need this to fail a generation with an actionable
|
|
786
791
|
// message rather than a bare Error (py-data guards on its sibling `py`).
|
package/src/utility.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
import Path from 'node:path'
|
|
3
3
|
|
|
4
|
-
import { JostracaResult } from 'jostraca'
|
|
4
|
+
import { JostracaResult, each } from 'jostraca'
|
|
5
5
|
|
|
6
6
|
import { KIT, getModelPath } from '@voxgig/apidef'
|
|
7
7
|
|
|
8
|
+
import { serverVariables } from './helpers/serverVars'
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
// Where a per-target component is loaded from: `<project>/.sdk/dist/<path>`.
|
|
10
12
|
//
|
|
@@ -100,6 +102,10 @@ export {
|
|
|
100
102
|
CONFIG_REPR_VALUES,
|
|
101
103
|
isConfigData,
|
|
102
104
|
configRepr,
|
|
105
|
+
configReprSetting,
|
|
106
|
+
configDefinition,
|
|
107
|
+
clean,
|
|
108
|
+
rawStringLiteral,
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
|
|
@@ -166,3 +172,153 @@ function isConfigData(configJson: string, repr?: string): boolean {
|
|
|
166
172
|
function configRepr(configJson: string, repr?: string): string {
|
|
167
173
|
return isConfigData(configJson, repr) ? 'data' : 'literal'
|
|
168
174
|
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
// The per-SDK override, or 'auto'. `main.kit.config.repr` is optional, and
|
|
178
|
+
// getModelPath throws rather than returning undefined for an absent path.
|
|
179
|
+
function configReprSetting(model: any): string {
|
|
180
|
+
try {
|
|
181
|
+
return getModelPath(model, `main.${KIT}.config.repr`) || 'auto'
|
|
182
|
+
}
|
|
183
|
+
catch (_e) {
|
|
184
|
+
return 'auto'
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
// L0 NORMALISATION: strip what the emitted config must not carry.
|
|
190
|
+
//
|
|
191
|
+
// Three kinds of noise, and the distinction between them matters:
|
|
192
|
+
//
|
|
193
|
+
// MODEL_META jostraca's `each` injects index$/key$/val$ into every node
|
|
194
|
+
// it iterates. Pure bookkeeping, and it leaked into every
|
|
195
|
+
// generated SDK for years (5,231 occurrences in gitlab
|
|
196
|
+
// alone) because this helper deleted keys during a walk that
|
|
197
|
+
// assigned them straight back.
|
|
198
|
+
//
|
|
199
|
+
// CONFIG_DEFAULT a key whose value equals the default the reader already
|
|
200
|
+
// applies. Emitting it is pure bulk. Only these three names
|
|
201
|
+
// are dropped, and only at their default value - `entity$`
|
|
202
|
+
// is real Seneca data, so no blanket suffix rule.
|
|
203
|
+
//
|
|
204
|
+
// PAYLOAD_KEYS the boundary. Under `default`/`example`/`examples` the
|
|
205
|
+
// value is API DATA, not config, and an example that happens
|
|
206
|
+
// to contain `active: true` must survive intact. Below one of
|
|
207
|
+
// these keys default-dropping stops; metadata stripping does
|
|
208
|
+
// not, because jostraca's bookkeeping is never payload.
|
|
209
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
210
|
+
|
|
211
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
212
|
+
active: true,
|
|
213
|
+
req: false,
|
|
214
|
+
reqd: false,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
218
|
+
|
|
219
|
+
function clean(o: any, dropDefaults?: boolean): any {
|
|
220
|
+
// Rebuild rather than delete in place: the caller's model is shared with
|
|
221
|
+
// every other component, and mutating it here would strip metadata a later
|
|
222
|
+
// target still needs.
|
|
223
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
224
|
+
if (Array.isArray(node)) return node.map((n: any) => prune(n, defaults))
|
|
225
|
+
if (null != node && 'object' === typeof node) {
|
|
226
|
+
const out: any = {}
|
|
227
|
+
for (const k of Object.keys(node)) {
|
|
228
|
+
if (MODEL_META.includes(k)) continue
|
|
229
|
+
// An ABSENT optional member, dropped rather than carried as undefined.
|
|
230
|
+
//
|
|
231
|
+
// Callers build `{fields, name, op, relations}` from an entity, and
|
|
232
|
+
// `op` and `relations` are optional - so the key exists with value
|
|
233
|
+
// undefined. JSON.stringify silently omits such a key, while the
|
|
234
|
+
// literal formatters emit it as None/nil/null, and the two
|
|
235
|
+
// representations would describe different configs for any entity
|
|
236
|
+
// without an `op`. Dropping it here fixes both branches at once,
|
|
237
|
+
// because both reach the emitter through this function.
|
|
238
|
+
if (undefined === node[k]) continue
|
|
239
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) continue
|
|
240
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
241
|
+
}
|
|
242
|
+
return out
|
|
243
|
+
}
|
|
244
|
+
return node
|
|
245
|
+
}
|
|
246
|
+
return prune(o, true === dropDefaults)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
// The JSON as a source-level string literal, for a language whose SINGLE
|
|
251
|
+
// quoted literal neither interpolates nor processes escapes beyond the quote
|
|
252
|
+
// and the backslash - Ruby, PHP, Perl, Lua.
|
|
253
|
+
//
|
|
254
|
+
// Reproducing the JSON text VERBATIM is all that is needed, because the JSON
|
|
255
|
+
// already encodes control characters and non-ASCII itself. That is why this is
|
|
256
|
+
// preferred over the double-quoted form in those languages: Ruby and PHP
|
|
257
|
+
// interpolate (`#{...}`, `$var`) and Lua does not understand `\uXXXX` at all,
|
|
258
|
+
// so a double-quoted literal would need a language-specific escape table and
|
|
259
|
+
// would get it wrong for exactly the inputs nobody tests.
|
|
260
|
+
function rawStringLiteral(s: string): string {
|
|
261
|
+
return "'" + s.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
// THE CANONICAL CONFIG OBJECT, and the JSON the threshold is measured on.
|
|
266
|
+
//
|
|
267
|
+
// Every target builds its config from this one function, so the literal a
|
|
268
|
+
// target emits and the data that replaces it above the threshold cannot
|
|
269
|
+
// describe different configs - which is the entire promise of rung L1. Before
|
|
270
|
+
// this existed each target assembled its own, and they had already drifted:
|
|
271
|
+
// `feature.<name>` came out as `{}` in Go and as nothing at all in ts when a
|
|
272
|
+
// feature declared no config.
|
|
273
|
+
//
|
|
274
|
+
// Key order is `each`'s order, which is sorted, so the JSON is byte-stable
|
|
275
|
+
// across runs exactly like the literal it replaces.
|
|
276
|
+
function configDefinition(model: any): { def: any, json: string } {
|
|
277
|
+
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
278
|
+
const feature = getModelPath(model, `main.${KIT}.feature`)
|
|
279
|
+
const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
|
|
280
|
+
|
|
281
|
+
const authActive = isAuthActive(model)
|
|
282
|
+
const authPrefix = resolveAuthPrefix(model)
|
|
283
|
+
|
|
284
|
+
let baseUrl = ''
|
|
285
|
+
try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
|
|
286
|
+
|
|
287
|
+
const svars = serverVariables(model)
|
|
288
|
+
|
|
289
|
+
const entityDefs: any = {}
|
|
290
|
+
const entityStubs: any = {}
|
|
291
|
+
each(entity, (e: any) => {
|
|
292
|
+
entityDefs[e.name] = clean({
|
|
293
|
+
fields: e.fields,
|
|
294
|
+
name: e.name,
|
|
295
|
+
op: e.op,
|
|
296
|
+
relations: e.relations,
|
|
297
|
+
}, true)
|
|
298
|
+
entityStubs[e.name] = {}
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const featureDefs: any = {}
|
|
302
|
+
each(feature, (f: any) => {
|
|
303
|
+
featureDefs[f.name] = f.config || {}
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
const options: any = { base: baseUrl }
|
|
307
|
+
if (0 < svars.length) {
|
|
308
|
+
options.server = svars.reduce((a: any, v: any) => (a[v.name] = v.dflt, a), {})
|
|
309
|
+
}
|
|
310
|
+
if (authActive) {
|
|
311
|
+
options.auth = { prefix: authPrefix }
|
|
312
|
+
}
|
|
313
|
+
options.headers = headers
|
|
314
|
+
options.entity = entityStubs
|
|
315
|
+
|
|
316
|
+
const def = {
|
|
317
|
+
main: { name: model.const.Name },
|
|
318
|
+
feature: featureDefs,
|
|
319
|
+
options,
|
|
320
|
+
entity: entityDefs,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return { def, json: JSON.stringify(def) }
|
|
324
|
+
}
|