@voxgig/sdkgen 3.4.3 → 3.4.6
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 +10 -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 +10 -2
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +12 -1
- package/dist/utility.js +206 -1
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +11 -0
- 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/go/Config_go.ts +86 -2
- 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 +49 -1
- package/project/.sdk/src/cmp/ts/fragment/Config.data.fragment.ts +50 -0
- package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +1 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +47 -40
- package/src/cmp/ReadmeTop.ts +10 -1
- package/src/helpers/opExample.ts +11 -1
- package/src/sdkgen.ts +11 -1
- package/src/utility.ts +226 -1
package/dist/utility.js
CHANGED
|
@@ -3,13 +3,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SdkGenError = void 0;
|
|
6
|
+
exports.CONFIG_REPR_VALUES = exports.CONFIG_DATA_THRESHOLD = exports.SdkGenError = void 0;
|
|
7
7
|
exports.resolvePath = resolvePath;
|
|
8
8
|
exports.requirePath = requirePath;
|
|
9
9
|
exports.isAuthActive = isAuthActive;
|
|
10
10
|
exports.resolveAuthPrefix = resolveAuthPrefix;
|
|
11
|
+
exports.isConfigData = isConfigData;
|
|
12
|
+
exports.configRepr = configRepr;
|
|
13
|
+
exports.configReprSetting = configReprSetting;
|
|
14
|
+
exports.configDefinition = configDefinition;
|
|
15
|
+
exports.clean = clean;
|
|
16
|
+
exports.rawStringLiteral = rawStringLiteral;
|
|
11
17
|
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const jostraca_1 = require("jostraca");
|
|
12
19
|
const apidef_1 = require("@voxgig/apidef");
|
|
20
|
+
const serverVars_1 = require("./helpers/serverVars");
|
|
13
21
|
// Where a per-target component is loaded from: `<project>/.sdk/dist/<path>`.
|
|
14
22
|
//
|
|
15
23
|
// `ctx$.folder` is jostraca's OUTPUT folder, which is the project for an
|
|
@@ -79,4 +87,201 @@ class SdkGenError extends Error {
|
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
exports.SdkGenError = SdkGenError;
|
|
90
|
+
// CONFIG REPRESENTATION (design rung L1, threshold from design Q7).
|
|
91
|
+
//
|
|
92
|
+
// Above a size threshold the API model is emitted as DATA - a JSON string
|
|
93
|
+
// constant parsed once - rather than as a composite literal. Below it the
|
|
94
|
+
// literal stays, because for a small model the literal is smaller, simpler,
|
|
95
|
+
// faster to load and far easier to debug, and a symbol table would be pure
|
|
96
|
+
// complexity.
|
|
97
|
+
//
|
|
98
|
+
// The threshold is on the JSON, not the emitted source, because the emitted
|
|
99
|
+
// source size varies by language while the model does not. It is measured in
|
|
100
|
+
// UTF-8 BYTES rather than string length: `.length` counts UTF-16 code units,
|
|
101
|
+
// so a CJK-heavy model would read as roughly a third of its real size and
|
|
102
|
+
// stay on the expensive literal path well past the point where it hurts.
|
|
103
|
+
//
|
|
104
|
+
// Measured on the real gitlab model (923.5 KB of JSON), Go, cold cache,
|
|
105
|
+
// recompiling only the config package:
|
|
106
|
+
//
|
|
107
|
+
// composite literal JSON string constant
|
|
108
|
+
// compile+link wall 30.80 s 0.34 s 91x faster
|
|
109
|
+
// peak compiler RSS 2.49 GB 0.06 GB 39x less
|
|
110
|
+
// binary 7.44 MB 3.51 MB 2.1x smaller
|
|
111
|
+
//
|
|
112
|
+
// The reader side is unchanged either way: make_config returns the same map,
|
|
113
|
+
// so nothing downstream can tell which representation it got.
|
|
114
|
+
const CONFIG_DATA_THRESHOLD = 256 * 1024;
|
|
115
|
+
exports.CONFIG_DATA_THRESHOLD = CONFIG_DATA_THRESHOLD;
|
|
116
|
+
// Should this model be emitted as data rather than as a literal?
|
|
117
|
+
//
|
|
118
|
+
// `repr` is the per-SDK override from `main.kit.config.repr`: 'auto' (the
|
|
119
|
+
// default) decides by size, 'data' and 'literal' pin it. The override is what
|
|
120
|
+
// lets a small fixture exercise the data path - by size alone no test model
|
|
121
|
+
// comes near the threshold, so the branch every large SDK depends on would
|
|
122
|
+
// never be generated, compiled or run in CI.
|
|
123
|
+
const CONFIG_REPR_VALUES = ['auto', 'data', 'literal'];
|
|
124
|
+
exports.CONFIG_REPR_VALUES = CONFIG_REPR_VALUES;
|
|
125
|
+
function isConfigData(configJson, repr) {
|
|
126
|
+
// An unknown value is REJECTED, not ignored. The aontu declaration
|
|
127
|
+
// documents the closed set but does not enforce it here, and silently
|
|
128
|
+
// treating `repr: 'date'` as `auto` would quietly restore the compile cost
|
|
129
|
+
// this exists to remove - the failure mode being a slow build nobody
|
|
130
|
+
// connects to a typo.
|
|
131
|
+
if (null != repr && '' !== repr && !CONFIG_REPR_VALUES.includes(repr)) {
|
|
132
|
+
throw new SdkGenError('sdkgen: main.kit.config.repr must be one of ' +
|
|
133
|
+
CONFIG_REPR_VALUES.join(', ') + ' (got: ' + repr + ')', {});
|
|
134
|
+
}
|
|
135
|
+
if ('data' === repr) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
if ('literal' === repr) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
return CONFIG_DATA_THRESHOLD < Buffer.byteLength(configJson, 'utf8');
|
|
142
|
+
}
|
|
143
|
+
// The chosen representation, as a word - for generation logs and for the
|
|
144
|
+
// per-SDK reporting the fleet regen needs, so a model crossing the threshold
|
|
145
|
+
// is visible rather than showing up as an unexplained whole-file diff.
|
|
146
|
+
function configRepr(configJson, repr) {
|
|
147
|
+
return isConfigData(configJson, repr) ? 'data' : 'literal';
|
|
148
|
+
}
|
|
149
|
+
// The per-SDK override, or 'auto'. `main.kit.config.repr` is optional, and
|
|
150
|
+
// getModelPath throws rather than returning undefined for an absent path.
|
|
151
|
+
function configReprSetting(model) {
|
|
152
|
+
try {
|
|
153
|
+
return (0, apidef_1.getModelPath)(model, `main.${apidef_1.KIT}.config.repr`) || 'auto';
|
|
154
|
+
}
|
|
155
|
+
catch (_e) {
|
|
156
|
+
return 'auto';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// L0 NORMALISATION: strip what the emitted config must not carry.
|
|
160
|
+
//
|
|
161
|
+
// Three kinds of noise, and the distinction between them matters:
|
|
162
|
+
//
|
|
163
|
+
// MODEL_META jostraca's `each` injects index$/key$/val$ into every node
|
|
164
|
+
// it iterates. Pure bookkeeping, and it leaked into every
|
|
165
|
+
// generated SDK for years (5,231 occurrences in gitlab
|
|
166
|
+
// alone) because this helper deleted keys during a walk that
|
|
167
|
+
// assigned them straight back.
|
|
168
|
+
//
|
|
169
|
+
// CONFIG_DEFAULT a key whose value equals the default the reader already
|
|
170
|
+
// applies. Emitting it is pure bulk. Only these three names
|
|
171
|
+
// are dropped, and only at their default value - `entity$`
|
|
172
|
+
// is real Seneca data, so no blanket suffix rule.
|
|
173
|
+
//
|
|
174
|
+
// PAYLOAD_KEYS the boundary. Under `default`/`example`/`examples` the
|
|
175
|
+
// value is API DATA, not config, and an example that happens
|
|
176
|
+
// to contain `active: true` must survive intact. Below one of
|
|
177
|
+
// these keys default-dropping stops; metadata stripping does
|
|
178
|
+
// not, because jostraca's bookkeeping is never payload.
|
|
179
|
+
const MODEL_META = ['index$', 'key$', 'val$'];
|
|
180
|
+
const CONFIG_DEFAULT = {
|
|
181
|
+
active: true,
|
|
182
|
+
req: false,
|
|
183
|
+
reqd: false,
|
|
184
|
+
};
|
|
185
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples'];
|
|
186
|
+
function clean(o, dropDefaults) {
|
|
187
|
+
// Rebuild rather than delete in place: the caller's model is shared with
|
|
188
|
+
// every other component, and mutating it here would strip metadata a later
|
|
189
|
+
// target still needs.
|
|
190
|
+
const prune = (node, defaults) => {
|
|
191
|
+
if (Array.isArray(node))
|
|
192
|
+
return node.map((n) => prune(n, defaults));
|
|
193
|
+
if (null != node && 'object' === typeof node) {
|
|
194
|
+
const out = {};
|
|
195
|
+
for (const k of Object.keys(node)) {
|
|
196
|
+
if (MODEL_META.includes(k))
|
|
197
|
+
continue;
|
|
198
|
+
// An ABSENT optional member, dropped rather than carried as undefined.
|
|
199
|
+
//
|
|
200
|
+
// Callers build `{fields, name, op, relations}` from an entity, and
|
|
201
|
+
// `op` and `relations` are optional - so the key exists with value
|
|
202
|
+
// undefined. JSON.stringify silently omits such a key, while the
|
|
203
|
+
// literal formatters emit it as None/nil/null, and the two
|
|
204
|
+
// representations would describe different configs for any entity
|
|
205
|
+
// without an `op`. Dropping it here fixes both branches at once,
|
|
206
|
+
// because both reach the emitter through this function.
|
|
207
|
+
if (undefined === node[k])
|
|
208
|
+
continue;
|
|
209
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k])
|
|
210
|
+
continue;
|
|
211
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k));
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
215
|
+
return node;
|
|
216
|
+
};
|
|
217
|
+
return prune(o, true === dropDefaults);
|
|
218
|
+
}
|
|
219
|
+
// The JSON as a source-level string literal, for a language whose SINGLE
|
|
220
|
+
// quoted literal neither interpolates nor processes escapes beyond the quote
|
|
221
|
+
// and the backslash - Ruby, PHP, Perl, Lua.
|
|
222
|
+
//
|
|
223
|
+
// Reproducing the JSON text VERBATIM is all that is needed, because the JSON
|
|
224
|
+
// already encodes control characters and non-ASCII itself. That is why this is
|
|
225
|
+
// preferred over the double-quoted form in those languages: Ruby and PHP
|
|
226
|
+
// interpolate (`#{...}`, `$var`) and Lua does not understand `\uXXXX` at all,
|
|
227
|
+
// so a double-quoted literal would need a language-specific escape table and
|
|
228
|
+
// would get it wrong for exactly the inputs nobody tests.
|
|
229
|
+
function rawStringLiteral(s) {
|
|
230
|
+
return "'" + s.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'";
|
|
231
|
+
}
|
|
232
|
+
// THE CANONICAL CONFIG OBJECT, and the JSON the threshold is measured on.
|
|
233
|
+
//
|
|
234
|
+
// Every target builds its config from this one function, so the literal a
|
|
235
|
+
// target emits and the data that replaces it above the threshold cannot
|
|
236
|
+
// describe different configs - which is the entire promise of rung L1. Before
|
|
237
|
+
// this existed each target assembled its own, and they had already drifted:
|
|
238
|
+
// `feature.<name>` came out as `{}` in Go and as nothing at all in ts when a
|
|
239
|
+
// feature declared no config.
|
|
240
|
+
//
|
|
241
|
+
// Key order is `each`'s order, which is sorted, so the JSON is byte-stable
|
|
242
|
+
// across runs exactly like the literal it replaces.
|
|
243
|
+
function configDefinition(model) {
|
|
244
|
+
const entity = (0, apidef_1.getModelPath)(model, `main.${apidef_1.KIT}.entity`);
|
|
245
|
+
const feature = (0, apidef_1.getModelPath)(model, `main.${apidef_1.KIT}.feature`);
|
|
246
|
+
const headers = (0, apidef_1.getModelPath)(model, `main.${apidef_1.KIT}.config.headers`) || {};
|
|
247
|
+
const authActive = isAuthActive(model);
|
|
248
|
+
const authPrefix = resolveAuthPrefix(model);
|
|
249
|
+
let baseUrl = '';
|
|
250
|
+
try {
|
|
251
|
+
baseUrl = (0, apidef_1.getModelPath)(model, `main.${apidef_1.KIT}.info.servers.0.url`);
|
|
252
|
+
}
|
|
253
|
+
catch (_e) { }
|
|
254
|
+
const svars = (0, serverVars_1.serverVariables)(model);
|
|
255
|
+
const entityDefs = {};
|
|
256
|
+
const entityStubs = {};
|
|
257
|
+
(0, jostraca_1.each)(entity, (e) => {
|
|
258
|
+
entityDefs[e.name] = clean({
|
|
259
|
+
fields: e.fields,
|
|
260
|
+
name: e.name,
|
|
261
|
+
op: e.op,
|
|
262
|
+
relations: e.relations,
|
|
263
|
+
}, true);
|
|
264
|
+
entityStubs[e.name] = {};
|
|
265
|
+
});
|
|
266
|
+
const featureDefs = {};
|
|
267
|
+
(0, jostraca_1.each)(feature, (f) => {
|
|
268
|
+
featureDefs[f.name] = f.config || {};
|
|
269
|
+
});
|
|
270
|
+
const options = { base: baseUrl };
|
|
271
|
+
if (0 < svars.length) {
|
|
272
|
+
options.server = svars.reduce((a, v) => (a[v.name] = v.dflt, a), {});
|
|
273
|
+
}
|
|
274
|
+
if (authActive) {
|
|
275
|
+
options.auth = { prefix: authPrefix };
|
|
276
|
+
}
|
|
277
|
+
options.headers = headers;
|
|
278
|
+
options.entity = entityStubs;
|
|
279
|
+
const def = {
|
|
280
|
+
main: { name: model.const.Name },
|
|
281
|
+
feature: featureDefs,
|
|
282
|
+
options,
|
|
283
|
+
entity: entityDefs,
|
|
284
|
+
};
|
|
285
|
+
return { def, json: JSON.stringify(def) };
|
|
286
|
+
}
|
|
82
287
|
//# sourceMappingURL=utility.js.map
|
package/dist/utility.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utility.js","sourceRoot":"","sources":["../src/utility.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"utility.js","sourceRoot":"","sources":["../src/utility.ts"],"names":[],"mappings":"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"}
|
package/model/sdkgen.aontu
CHANGED
|
@@ -13,6 +13,17 @@ main: def: {
|
|
|
13
13
|
# `<sdk>/.sdk/model/config.aontu`.
|
|
14
14
|
main: kit: config: docs_order: *['ts','py','php','go','rb','lua'] | [&: string]
|
|
15
15
|
|
|
16
|
+
# How the API model is embedded in a generated SDK (design rung L1).
|
|
17
|
+
#
|
|
18
|
+
# auto by size - data above the threshold, literal below (the default)
|
|
19
|
+
# literal always a composite literal, whatever the size
|
|
20
|
+
# data always a parsed JSON constant, whatever the size
|
|
21
|
+
#
|
|
22
|
+
# Declared as a closed set so a typo is REJECTED at model-compile time rather
|
|
23
|
+
# than silently falling through to `auto`, which would quietly restore the
|
|
24
|
+
# compile cost this exists to remove.
|
|
25
|
+
main: kit: config: repr: *'auto' | 'literal' | 'data'
|
|
26
|
+
|
|
16
27
|
# How the generated test suites behave.
|
|
17
28
|
main: kit: test: {
|
|
18
29
|
|
package/package.json
CHANGED
|
@@ -3,8 +3,11 @@ import {
|
|
|
3
3
|
Content,
|
|
4
4
|
File,
|
|
5
5
|
cmp,
|
|
6
|
+
configDefinition,
|
|
7
|
+
configReprSetting,
|
|
6
8
|
each,
|
|
7
9
|
isAuthActive,
|
|
10
|
+
isConfigData,
|
|
8
11
|
resolveAuthPrefix,
|
|
9
12
|
} from '@voxgig/sdkgen'
|
|
10
13
|
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
import {
|
|
23
|
+
cStringLiteral,
|
|
20
24
|
clean,
|
|
21
25
|
formatCValue,
|
|
22
26
|
} from './utility_c'
|
|
@@ -75,8 +79,48 @@ const Config = cmp(async function Config(props: any) {
|
|
|
75
79
|
entity: entityConfig,
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
// The same config as an OBJECT, built by the shared helper so this target's
|
|
83
|
+
// literal and the data that replaces it above the threshold are the same
|
|
84
|
+
// config by construction. The JSON is what the threshold is measured on -
|
|
85
|
+
// emitted source size varies by language, the model does not.
|
|
86
|
+
const { json: configJson } = configDefinition(model)
|
|
87
|
+
const asData = isConfigData(configJson, configReprSetting(model))
|
|
88
|
+
|
|
78
89
|
File({ name: 'config.c' }, () => {
|
|
79
90
|
|
|
91
|
+
// ABOVE THE THRESHOLD: emit the model as DATA.
|
|
92
|
+
//
|
|
93
|
+
// The literal is a single nested `cmap(...)`/`clist(...)` expression. For a
|
|
94
|
+
// real model that is one function whose expression nests hundreds of
|
|
95
|
+
// thousands of calls deep - the shape that makes a C compiler's parser and
|
|
96
|
+
// register allocator quadratic, and the reason gcc can be seen taking
|
|
97
|
+
// minutes on a generated config. A string constant is one token, and
|
|
98
|
+
// `json_parse` (declared in sdk.h, reached through api.h) builds the same
|
|
99
|
+
// voxgig_value tree at runtime.
|
|
100
|
+
if (asData) {
|
|
101
|
+
Content(`// Generated API configuration (mirrors core/config.go).
|
|
102
|
+
|
|
103
|
+
#include "api.h"
|
|
104
|
+
|
|
105
|
+
#include <string.h>
|
|
106
|
+
|
|
107
|
+
// THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1).
|
|
108
|
+
//
|
|
109
|
+
// Emitted only above a size threshold, or when \`main.kit.config.repr\` pins
|
|
110
|
+
// it: for a small model the cmap() literal is smaller and far easier to read
|
|
111
|
+
// when debugging.
|
|
112
|
+
static const char CONFIG_DATA[] =
|
|
113
|
+
${cStringLiteral(configJson)};
|
|
114
|
+
|
|
115
|
+
voxgig_value* make_config(void) {
|
|
116
|
+
return json_parse(CONFIG_DATA);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
Feature* make_feature(const char* name) {
|
|
120
|
+
`)
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
|
|
80
124
|
Content(`// Generated API configuration (mirrors core/config.go).
|
|
81
125
|
|
|
82
126
|
#include "api.h"
|
|
@@ -89,6 +133,7 @@ voxgig_value* make_config(void) {
|
|
|
89
133
|
|
|
90
134
|
Feature* make_feature(const char* name) {
|
|
91
135
|
`)
|
|
136
|
+
}
|
|
92
137
|
|
|
93
138
|
// Dispatch to the features the MODEL declares, and only those.
|
|
94
139
|
//
|
|
@@ -150,7 +150,43 @@ function clean(o: any, dropDefaults?: boolean): any {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
|
|
153
|
+
|
|
154
|
+
// The JSON as a C string literal, split into ADJACENT literals.
|
|
155
|
+
//
|
|
156
|
+
// C99 only guarantees 4095 characters in a single string literal, and a real
|
|
157
|
+
// model runs to hundreds of kilobytes. Adjacent literals are concatenated by
|
|
158
|
+
// the translation phase before that limit applies, so this stays portable
|
|
159
|
+
// rather than relying on gcc/clang having no practical limit.
|
|
160
|
+
//
|
|
161
|
+
// Only `\` and `"` need escaping: JSON.stringify output contains no raw
|
|
162
|
+
// control characters (it writes them as the six TEXT characters \u00XX), and
|
|
163
|
+
// C string literals take UTF-8 bytes verbatim. Escaping the backslash also
|
|
164
|
+
// means no `\uXXXX` ever reaches the C lexer, which matters - a universal
|
|
165
|
+
// character name below 0xA0 is ill-formed C, so the JSON's own escapes would
|
|
166
|
+
// have been rejected if they had been passed through.
|
|
167
|
+
//
|
|
168
|
+
// Chunks are cut from the RAW text and escaped afterwards, so a cut can never
|
|
169
|
+
// land inside an escape sequence. Surrogate pairs are kept whole, or the cut
|
|
170
|
+
// would emit a lone surrogate as invalid UTF-8.
|
|
171
|
+
function cStringLiteral(json: string, chunkSize: number = 2000): string {
|
|
172
|
+
const parts: string[] = []
|
|
173
|
+
let i = 0
|
|
174
|
+
while (i < json.length) {
|
|
175
|
+
let end = Math.min(i + chunkSize, json.length)
|
|
176
|
+
const code = json.charCodeAt(end - 1)
|
|
177
|
+
if (end < json.length && 0xd800 <= code && code <= 0xdbff) {
|
|
178
|
+
end++
|
|
179
|
+
}
|
|
180
|
+
parts.push(json.slice(i, end))
|
|
181
|
+
i = end
|
|
182
|
+
}
|
|
183
|
+
return parts
|
|
184
|
+
.map((p) => ' "' + p.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"')
|
|
185
|
+
.join('\n')
|
|
186
|
+
}
|
|
187
|
+
|
|
153
188
|
export {
|
|
189
|
+
cStringLiteral,
|
|
154
190
|
clean,
|
|
155
191
|
cIdent,
|
|
156
192
|
cName,
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
Line,
|
|
10
10
|
cmp,
|
|
11
11
|
each,
|
|
12
|
+
clean,
|
|
13
|
+
configDefinition,
|
|
14
|
+
configReprSetting,
|
|
12
15
|
isAuthActive,
|
|
16
|
+
isConfigData,
|
|
13
17
|
resolveAuthPrefix,
|
|
14
18
|
serverVariables,
|
|
15
19
|
} from '@voxgig/sdkgen'
|
|
@@ -24,7 +28,6 @@ import {
|
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
import {
|
|
27
|
-
clean,
|
|
28
31
|
formatGoMap,
|
|
29
32
|
goFeatureName,
|
|
30
33
|
} from './utility_go'
|
|
@@ -62,9 +65,87 @@ const Config = cmp(async function Config(props: any) {
|
|
|
62
65
|
},\n`
|
|
63
66
|
: ''
|
|
64
67
|
|
|
65
|
-
//
|
|
68
|
+
// The same config as an OBJECT, built by the shared helper so this target's
|
|
69
|
+
// literal and the data that replaces it above the threshold are the same
|
|
70
|
+
// config by construction. The JSON is what the threshold is measured on -
|
|
71
|
+
// emitted source size varies by language, the model does not.
|
|
72
|
+
const { json: configJson } = configDefinition(model)
|
|
73
|
+
const asData = isConfigData(configJson, configReprSetting(model))
|
|
74
|
+
|
|
66
75
|
File({ name: 'config.' + target.ext }, () => {
|
|
67
76
|
|
|
77
|
+
// ABOVE THE THRESHOLD: emit the model as DATA.
|
|
78
|
+
//
|
|
79
|
+
// A composite literal makes the compiler walk every node of the model;
|
|
80
|
+
// a string constant is one token. On the real gitlab model that is 30.8 s
|
|
81
|
+
// and 2.49 GB of compiler memory versus 0.34 s and 0.06 GB, and a binary
|
|
82
|
+
// 2.1x smaller. MakeConfig still returns the same map, so nothing
|
|
83
|
+
// downstream can tell which representation it got.
|
|
84
|
+
//
|
|
85
|
+
// JSON.stringify output is a valid Go interpreted string literal: JSON
|
|
86
|
+
// escapes are a subset of Go's, and Go source is UTF-8 so non-ASCII needs
|
|
87
|
+
// no escaping. A raw (backtick) literal could NOT be used - the model
|
|
88
|
+
// contains backticks in values like `$STRING`.
|
|
89
|
+
if (asData) {
|
|
90
|
+
Content(`package core
|
|
91
|
+
|
|
92
|
+
import (
|
|
93
|
+
"encoding/json"
|
|
94
|
+
"math"
|
|
95
|
+
"sync"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// The API model, emitted as data rather than as a composite literal: see
|
|
99
|
+
// sdkgen rung L1. Parsed by MakeConfig, and parsed once by SharedConfig.
|
|
100
|
+
const configJSON = ${JSON.stringify(configJson)}
|
|
101
|
+
|
|
102
|
+
// json.Unmarshal decodes EVERY JSON number as float64, but the literal
|
|
103
|
+
// representation emits an integer token as an untyped constant that lands in
|
|
104
|
+
// map[string]any as an int. MakeConfig is public API and consumers type-assert
|
|
105
|
+
// against it, so the two representations must not disagree about the type of
|
|
106
|
+
// a whole number just because the model crossed a size threshold.
|
|
107
|
+
//
|
|
108
|
+
// Whole values become int; anything fractional, or too large to be exact in a
|
|
109
|
+
// float64, stays float64 - which is what the literal branch does too.
|
|
110
|
+
func configNormalise(val any) any {
|
|
111
|
+
switch v := val.(type) {
|
|
112
|
+
case map[string]any:
|
|
113
|
+
for k, c := range v {
|
|
114
|
+
v[k] = configNormalise(c)
|
|
115
|
+
}
|
|
116
|
+
return v
|
|
117
|
+
case []any:
|
|
118
|
+
for i, c := range v {
|
|
119
|
+
v[i] = configNormalise(c)
|
|
120
|
+
}
|
|
121
|
+
return v
|
|
122
|
+
case float64:
|
|
123
|
+
if v == math.Trunc(v) && math.Abs(v) <= 1<<53 {
|
|
124
|
+
return int(v)
|
|
125
|
+
}
|
|
126
|
+
return v
|
|
127
|
+
}
|
|
128
|
+
return val
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// MakeConfig parses a fresh, fully materialised config map. Every call
|
|
132
|
+
// re-parses, so prefer SharedConfig unless you need a private copy you
|
|
133
|
+
// intend to mutate.
|
|
134
|
+
func MakeConfig() map[string]any {
|
|
135
|
+
var out map[string]any
|
|
136
|
+
if err := json.Unmarshal([]byte(configJSON), &out); err != nil {
|
|
137
|
+
// Unreachable: the constant is generated by json.Marshal's
|
|
138
|
+
// counterpart and never edited by hand. Panic rather than return a
|
|
139
|
+
// silently empty config, which would fail far from the cause.
|
|
140
|
+
panic("${model.const.Name}: embedded config is not valid JSON: " + err.Error())
|
|
141
|
+
}
|
|
142
|
+
out, _ = configNormalise(out).(map[string]any)
|
|
143
|
+
return out
|
|
144
|
+
}
|
|
145
|
+
`)
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
|
|
68
149
|
Content(`package core
|
|
69
150
|
|
|
70
151
|
import (
|
|
@@ -113,7 +194,10 @@ ${serverBlock}${authBlock} "headers": ${formatGoMap(headers, 3)},
|
|
|
113
194
|
}, true), a), {}), 2)},
|
|
114
195
|
}
|
|
115
196
|
}
|
|
197
|
+
`)
|
|
198
|
+
}
|
|
116
199
|
|
|
200
|
+
Content(`
|
|
117
201
|
var (
|
|
118
202
|
sharedConfigOnce sync.Once
|
|
119
203
|
sharedConfigVal map[string]any
|
|
@@ -8,9 +8,13 @@ import {
|
|
|
8
8
|
Fragment,
|
|
9
9
|
Line,
|
|
10
10
|
cmp,
|
|
11
|
+
clean,
|
|
12
|
+
configDefinition,
|
|
13
|
+
configReprSetting,
|
|
11
14
|
each,
|
|
12
15
|
indent,
|
|
13
16
|
isAuthActive,
|
|
17
|
+
isConfigData,
|
|
14
18
|
resolveAuthPrefix,
|
|
15
19
|
serverVariables,
|
|
16
20
|
} from '@voxgig/sdkgen'
|
|
@@ -25,7 +29,6 @@ import {
|
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
import {
|
|
28
|
-
clean,
|
|
29
32
|
formatJson,
|
|
30
33
|
} from './utility_js'
|
|
31
34
|
|
|
@@ -62,13 +65,58 @@ const Config = cmp(async function Config(props: any) {
|
|
|
62
65
|
svars.map((v: any) => ` ${JSON.stringify(v.name)}: ${JSON.stringify(v.dflt)},\n`).join('') +
|
|
63
66
|
' },\n\n '
|
|
64
67
|
|
|
68
|
+
// Read the base URL here rather than leaving it to a `$$...$$` stdrep
|
|
69
|
+
// placeholder in the fragment. stdrep can only substitute a path the model
|
|
70
|
+
// actually has: a model with no `info.servers` left the placeholder itself in
|
|
71
|
+
// the generated source, so `options.base` came out as the literal string
|
|
72
|
+
// '$main.kit.info.servers.0.url$'. Reading it explicitly yields '' in that
|
|
73
|
+
// case, which is what every other target already emits, and is identical to
|
|
74
|
+
// the old output whenever the model does define a server.
|
|
75
|
+
let baseUrl = ''
|
|
76
|
+
try {
|
|
77
|
+
baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`)
|
|
78
|
+
} catch (_e) { }
|
|
79
|
+
|
|
80
|
+
// The same config as an OBJECT, built by the shared helper so this target's
|
|
81
|
+
// literal and the data that replaces it above the threshold are the same
|
|
82
|
+
// config by construction. The JSON is what the threshold is measured on -
|
|
83
|
+
// emitted source size varies by language, the model does not.
|
|
84
|
+
const { json: configJson } = configDefinition(model)
|
|
85
|
+
const asData = isConfigData(configJson, configReprSetting(model))
|
|
86
|
+
|
|
65
87
|
File({ name: 'Config.' + target.ext }, () => {
|
|
66
88
|
|
|
89
|
+
if (asData) {
|
|
90
|
+
Fragment({
|
|
91
|
+
from: ff + 'Config.data.fragment.js',
|
|
92
|
+
|
|
93
|
+
replace: {
|
|
94
|
+
|
|
95
|
+
'// #ImportFeatures': () => each(feature, (f: any) => {
|
|
96
|
+
Line(`const { ${nom(f, 'Name')}Feature } = ` +
|
|
97
|
+
`require('./feature/${f.name}/${nom(f, 'Name')}Feature')`)
|
|
98
|
+
}),
|
|
99
|
+
|
|
100
|
+
'// #FeatureClasses': () => each(feature, (f: any) => {
|
|
101
|
+
Line(` ${f.name}: ${nom(f, 'Name')}Feature,`)
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
// A JS string literal, so the JSON survives verbatim. JSON.stringify
|
|
105
|
+
// escapes the quotes and backslashes the model contains (values like
|
|
106
|
+
// `$STRING` carry backticks, which a template literal could not).
|
|
107
|
+
"'CONFIGJSON'": JSON.stringify(configJson),
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
67
113
|
Fragment({
|
|
68
114
|
from: ff + 'Config.fragment.js',
|
|
69
115
|
|
|
70
116
|
replace: {
|
|
71
117
|
|
|
118
|
+
"'BASEURL'": JSON.stringify(baseUrl),
|
|
119
|
+
|
|
72
120
|
"'SERVERBLOCK'": serverBlock,
|
|
73
121
|
|
|
74
122
|
"'AUTHBLOCK'": authBlock,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
const { BaseFeature } = require('./feature/base/BaseFeature')
|
|
3
|
+
// #ImportFeatures
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const FEATURE_CLASS = {
|
|
7
|
+
// #FeatureClasses
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1).
|
|
12
|
+
//
|
|
13
|
+
// The literal form of this file declares the whole model as nested object
|
|
14
|
+
// literals on the class. For a large API that is megabytes of source the
|
|
15
|
+
// JavaScript engine must build node by node on every module load, and that
|
|
16
|
+
// every tool reading this file must parse.
|
|
17
|
+
//
|
|
18
|
+
// As a single string constant it is one token, and V8's JSON parser builds the
|
|
19
|
+
// object far faster than the equivalent literal - JSON.parse is the well-worn
|
|
20
|
+
// trick for exactly this shape of data.
|
|
21
|
+
//
|
|
22
|
+
// Emitted only above a size threshold, or when `main.kit.config.repr` pins it:
|
|
23
|
+
// for a small model the literal is smaller, loads no slower, and is far easier
|
|
24
|
+
// to read when debugging.
|
|
25
|
+
const CONFIG_DATA = 'CONFIGJSON'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Config {
|
|
29
|
+
|
|
30
|
+
makeFeature(fn) {
|
|
31
|
+
const fc = FEATURE_CLASS[fn]
|
|
32
|
+
const fi = new fc()
|
|
33
|
+
// TODO: errors etc
|
|
34
|
+
return fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// Parsed ONCE, at module load, exactly like the literal form was built once.
|
|
41
|
+
//
|
|
42
|
+
// The parsed data is assigned onto an instance rather than replacing it, so
|
|
43
|
+
// `config.main` / `.feature` / `.options` / `.entity` read exactly as they did
|
|
44
|
+
// when they were field initialisers, and `makeFeature` stays where it was on
|
|
45
|
+
// the prototype. Callers cannot tell which representation they were given.
|
|
46
|
+
const config = Object.assign(new Config(), JSON.parse(CONFIG_DATA))
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
config
|
|
50
|
+
}
|