@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.
Files changed (35) hide show
  1. package/dist/cmp/ReadmeTop.js +5 -1
  2. package/dist/cmp/ReadmeTop.js.map +1 -1
  3. package/dist/helpers/opExample.js +10 -1
  4. package/dist/helpers/opExample.js.map +1 -1
  5. package/dist/sdkgen.d.ts +2 -2
  6. package/dist/sdkgen.js +6 -2
  7. package/dist/sdkgen.js.map +1 -1
  8. package/dist/tsconfig.tsbuildinfo +1 -1
  9. package/dist/utility.d.ts +8 -1
  10. package/dist/utility.js +144 -0
  11. package/dist/utility.js.map +1 -1
  12. package/package.json +1 -1
  13. package/project/.sdk/src/cmp/c/Config_c.ts +45 -0
  14. package/project/.sdk/src/cmp/c/utility_c.ts +36 -0
  15. package/project/.sdk/src/cmp/dart/Config_dart.ts +15 -0
  16. package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +1 -1
  17. package/project/.sdk/src/cmp/go/Config_go.ts +9 -52
  18. package/project/.sdk/src/cmp/js/Config_js.ts +49 -1
  19. package/project/.sdk/src/cmp/js/fragment/Config.data.fragment.js +50 -0
  20. package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +1 -1
  21. package/project/.sdk/src/cmp/lua/Config_lua.ts +51 -1
  22. package/project/.sdk/src/cmp/lua/utility_lua.ts +21 -0
  23. package/project/.sdk/src/cmp/php/Config_php.ts +86 -0
  24. package/project/.sdk/src/cmp/py/Config_py.ts +47 -1
  25. package/project/.sdk/src/cmp/rb/Config_rb.ts +46 -2
  26. package/project/.sdk/src/cmp/rust/Config_rust.ts +50 -35
  27. package/project/.sdk/src/cmp/rust/utility_rust.ts +17 -0
  28. package/project/.sdk/src/cmp/ts/Config_ts.ts +10 -50
  29. package/project/.sdk/src/cmp/zig/Config_zig.ts +47 -40
  30. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +28 -17
  31. package/project/.sdk/tm/zig/test/gotcha_test.zig +8 -8
  32. package/src/cmp/ReadmeTop.ts +6 -1
  33. package/src/helpers/opExample.ts +11 -1
  34. package/src/sdkgen.ts +6 -1
  35. package/src/utility.ts +157 -1
@@ -127,7 +127,28 @@ function clean(o: any, dropDefaults?: boolean): any {
127
127
  }
128
128
 
129
129
 
130
+
131
+ // A Lua LONG-BRACKET string holding `s` verbatim.
132
+ //
133
+ // Lua's quoted strings process escapes, so the JSON's own `\n` and `\uXXXX`
134
+ // would be consumed by the Lua lexer before the JSON decoder ever saw them -
135
+ // turning an escaped newline into a real one inside a JSON string (invalid
136
+ // JSON), and failing outright on `\uXXXX`, which Lua 5.1/5.2 do not accept at
137
+ // all. A long bracket processes nothing, so the JSON text survives byte for
138
+ // byte. The level is raised until its terminator does not occur in the text.
139
+ function luaLongString(s: string): string {
140
+ let level = 0
141
+ while (s.includes(']' + '='.repeat(level) + ']')) {
142
+ level++
143
+ }
144
+ const eq = '='.repeat(level)
145
+ // A long bracket swallows an immediately following newline, so start the
146
+ // content on the same line as the opener.
147
+ return '[' + eq + '[' + s + ']' + eq + ']'
148
+ }
149
+
130
150
  export {
151
+ luaLongString,
131
152
  clean,
132
153
  formatLuaTable,
133
154
  formatLuaValue,
@@ -8,8 +8,12 @@ import {
8
8
  Fragment,
9
9
  Line,
10
10
  cmp,
11
+ configDefinition,
12
+ configReprSetting,
11
13
  each,
12
14
  isAuthActive,
15
+ isConfigData,
16
+ rawStringLiteral,
13
17
  resolveAuthPrefix,
14
18
  serverVariables,
15
19
  } from '@voxgig/sdkgen'
@@ -63,6 +67,13 @@ const Config = cmp(async function Config(props: any) {
63
67
  ],\n`
64
68
  : ''
65
69
 
70
+ // The same config as an OBJECT, built by the shared helper so this target's
71
+ // literal and the data that replaces it above the threshold are the same
72
+ // config by construction. The JSON is what the threshold is measured on -
73
+ // emitted source size varies by language, the model does not.
74
+ const { json: configJson } = configDefinition(model)
75
+ const asData = isConfigData(configJson, configReprSetting(model))
76
+
66
77
  File({ name: 'config.' + target.ext }, () => {
67
78
 
68
79
  Content(`<?php
@@ -91,7 +102,79 @@ class ${model.const.Name}Config
91
102
  return self::$shared_config;
92
103
  }
93
104
 
105
+ `)
106
+
107
+ // ABOVE THE THRESHOLD: emit the model as DATA.
108
+ //
109
+ // An array literal is compiled opcode by opcode and held in the opcache
110
+ // entry for this file; a string constant is one token, and `json_decode`
111
+ // (C) builds the array far faster than the equivalent literal.
112
+ //
113
+ // PHP cannot tell an empty list from an empty map, so `{}` and `[]` decode
114
+ // to the same value and the two representations have to AGREE about which
115
+ // one the literal would have produced. `formatPhpArray` emits `[]` for
116
+ // every empty map, and the literal branch hand-writes `(object)[]` in
117
+ // exactly two places - `entity` and `options.entity`, and only when the
118
+ // model declares no entities at all, because the SDK runtime validator
119
+ // wants a map there. This reproduces that rule rather than improving on
120
+ // it: an emission wart is not something the data path gets to fix
121
+ // unilaterally, or the two branches stop being interchangeable.
122
+ //
123
+ // A SINGLE-quoted literal, so the JSON survives verbatim: a double-quoted
124
+ // PHP string would interpolate any `$name` the model contains - and the
125
+ // model is full of them (`$STRING`, `$action`).
126
+ if (asData) {
127
+ Content(` /**
128
+ * THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1).
129
+ *
130
+ * Emitted only above a size threshold, or when \`main.kit.config.repr\`
131
+ * pins it: for a small model the array literal is smaller and far easier
132
+ * to read when debugging.
133
+ */
134
+ private const CONFIG_DATA = ${rawStringLiteral(configJson)};
135
+
136
+ /**
137
+ * Decoded JSON in the shape the literal branch produces: every map
138
+ * becomes an array, including an empty one.
139
+ */
140
+ private static function config_decode(mixed $v): mixed
141
+ {
142
+ if ($v instanceof \\stdClass) {
143
+ $out = [];
144
+ foreach (get_object_vars($v) as $k => $c) {
145
+ $out[$k] = self::config_decode($c);
146
+ }
147
+ return $out;
148
+ }
149
+ if (is_array($v)) {
150
+ return array_map([self::class, 'config_decode'], $v);
151
+ }
152
+ return $v;
153
+ }
154
+
94
155
  /**
156
+ * Parse a fresh, fully materialised config array. Every call re-parses,
157
+ * so prefer shared_config unless you need a private copy.
158
+ */
159
+ public static function make_config(): array
160
+ {
161
+ /** @var array<string,mixed> $out */
162
+ $out = self::config_decode(json_decode(self::CONFIG_DATA));
163
+
164
+ // The two map-shape exceptions the literal branch makes by hand.
165
+ if (count($out["entity"]) === 0) {
166
+ $out["entity"] = (object)[];
167
+ }
168
+ if (count($out["options"]["entity"]) === 0) {
169
+ $out["options"]["entity"] = (object)[];
170
+ }
171
+ return $out;
172
+ }
173
+ `)
174
+ }
175
+ else {
176
+
177
+ Content(` /**
95
178
  * Build a fresh, fully materialised config array. Every call rebuilds the
96
179
  * whole structure, so prefer shared_config unless you need a private copy.
97
180
  */
@@ -151,7 +234,10 @@ ${serverBlock}${authBlock} "headers" => ${formatPhpArray(headers,
151
234
  }
152
235
 
153
236
  Content(` }
237
+ `)
238
+ }
154
239
 
240
+ Content(`
155
241
 
156
242
  public static function make_feature(string $name)
157
243
  {
@@ -8,8 +8,11 @@ import {
8
8
  Fragment,
9
9
  Line,
10
10
  cmp,
11
+ configDefinition,
12
+ configReprSetting,
11
13
  each,
12
14
  isAuthActive,
15
+ isConfigData,
13
16
  resolveAuthPrefix,
14
17
  serverVariables,
15
18
  } from '@voxgig/sdkgen'
@@ -61,10 +64,17 @@ const Config = cmp(async function Config(props: any) {
61
64
  },\n`
62
65
  : ''
63
66
 
67
+ // The same config as an OBJECT, built by the shared helper so this target's
68
+ // literal and the data that replaces it above the threshold are the same
69
+ // config by construction. The JSON is what the threshold is measured on -
70
+ // emitted source size varies by language, the model does not.
71
+ const { json: configJson } = configDefinition(model)
72
+ const asData = isConfigData(configJson, configReprSetting(model))
73
+
64
74
  File({ name: 'config.' + target.ext }, () => {
65
75
 
66
76
  Content(`# ${model.const.Name} SDK configuration
67
-
77
+ ${asData ? '\nimport json\n' : ''}
68
78
 
69
79
  _shared_config = None
70
80
 
@@ -84,7 +94,43 @@ def shared_config():
84
94
  return _shared_config
85
95
 
86
96
 
97
+ `)
98
+
99
+ // ABOVE THE THRESHOLD: emit the model as DATA.
100
+ //
101
+ // A dict literal makes CPython build the whole structure opcode by opcode
102
+ // at import, and the compiler hold the entire literal in memory to produce
103
+ // that bytecode. A string constant is one object, and `json.loads` (the C
104
+ // scanner) builds the dict far faster than the equivalent literal.
105
+ //
106
+ // `json.loads` yields exactly what the literal did - str keys, int for
107
+ // whole numbers, True/False/None - so make_config's result is unchanged.
108
+ //
109
+ // JSON.stringify output is a valid Python string literal: every escape it
110
+ // emits (\\", \\\\, \\n, \\uXXXX) means the same thing in Python, it never emits
111
+ // \\/ (which Python would not treat as an escape), and Python 3 source is
112
+ // UTF-8 so non-ASCII needs no escaping.
113
+ if (asData) {
114
+ Content(`# THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1).
115
+ #
116
+ # Emitted only above a size threshold, or when \`main.kit.config.repr\` pins it:
117
+ # for a small model the dict literal is smaller and far easier to read when
118
+ # debugging.
119
+ _CONFIG_DATA = ${JSON.stringify(configJson)}
120
+
121
+
87
122
  def make_config():
123
+ """Parse a fresh, fully materialised config dict.
124
+
125
+ Every call re-parses, so prefer shared_config unless you need a private
126
+ copy you intend to mutate.
127
+ """
128
+ return json.loads(_CONFIG_DATA)
129
+ `)
130
+ return
131
+ }
132
+
133
+ Content(`def make_config():
88
134
  """Build a fresh, fully materialised config dict.
89
135
 
90
136
  Every call rebuilds the whole structure, so prefer shared_config unless
@@ -8,8 +8,12 @@ import {
8
8
  Fragment,
9
9
  Line,
10
10
  cmp,
11
+ configDefinition,
12
+ configReprSetting,
11
13
  each,
12
14
  isAuthActive,
15
+ isConfigData,
16
+ rawStringLiteral,
13
17
  resolveAuthPrefix,
14
18
  serverVariables,
15
19
  } from '@voxgig/sdkgen'
@@ -63,10 +67,17 @@ const Config = cmp(async function Config(props: any) {
63
67
  },\n`
64
68
  : ''
65
69
 
70
+ // The same config as an OBJECT, built by the shared helper so this target's
71
+ // literal and the data that replaces it above the threshold are the same
72
+ // config by construction. The JSON is what the threshold is measured on -
73
+ // emitted source size varies by language, the model does not.
74
+ const { json: configJson } = configDefinition(model)
75
+ const asData = isConfigData(configJson, configReprSetting(model))
76
+
66
77
  File({ name: 'config.' + target.ext }, () => {
67
78
 
68
79
  Content(`# ${model.const.Name} SDK configuration
69
-
80
+ ${asData ? "\nrequire 'json'\n" : ''}
70
81
  module ${model.const.Name}Config
71
82
  # Return the process-wide config, built once on first use. The SDK reads
72
83
  # the config on every request and never writes to it, so one instance is
@@ -79,7 +90,37 @@ module ${model.const.Name}Config
79
90
  end
80
91
 
81
92
 
82
- # Build a fresh, fully materialised config hash. Every call rebuilds the
93
+ `)
94
+
95
+ // ABOVE THE THRESHOLD: emit the model as DATA.
96
+ //
97
+ // A hash literal makes the Ruby parser build a node per entry and the VM
98
+ // execute an instruction per entry on every load. A string constant is one
99
+ // token, and `JSON.parse` (a C extension) builds the hash far faster.
100
+ //
101
+ // `JSON.parse` yields exactly what the literal did - String keys, Integer
102
+ // for whole numbers, true/false/nil - so make_config's result is unchanged.
103
+ //
104
+ // A SINGLE-quoted literal, so the JSON survives verbatim: a double-quoted
105
+ // Ruby string would interpolate any `#{` the model happens to contain.
106
+ if (asData) {
107
+ Content(` # 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 hash literal is smaller and far easier to read
111
+ # when debugging.
112
+ CONFIG_DATA = ${rawStringLiteral(configJson)}.freeze
113
+
114
+ # Parse a fresh, fully materialised config hash. Every call re-parses, so
115
+ # prefer shared_config unless you need a private copy you intend to mutate.
116
+ def self.make_config
117
+ JSON.parse(CONFIG_DATA)
118
+ end
119
+ `)
120
+ }
121
+ else {
122
+
123
+ Content(` # Build a fresh, fully materialised config hash. Every call rebuilds the
83
124
  # whole structure, so prefer shared_config unless you need a private copy
84
125
  # you intend to mutate.
85
126
  def self.make_config
@@ -119,7 +160,10 @@ ${serverBlock}${authBlock} "headers" => ${formatRubyHash(headers, 4)},
119
160
  }, true), a), {}), 3)},
120
161
  }
121
162
  end
163
+ `)
164
+ }
122
165
 
166
+ Content(`
123
167
 
124
168
  def self.make_feature(name)
125
169
  require_relative 'features'
@@ -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,8 +20,8 @@ import {
17
20
 
18
21
 
19
22
  import {
20
- clean,
21
23
  formatRustValue,
24
+ rustRawString,
22
25
  } from './utility_rust'
23
26
 
24
27
 
@@ -43,43 +46,54 @@ const Config = cmp(async function Config(props: any) {
43
46
  let baseUrl = ''
44
47
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
45
48
 
46
- // Assemble the whole config as a JSON-shaped object, then render it once
47
- // via formatRustValue (byte-stable; each() sorted-key iteration).
48
- const featureConfig: any = {}
49
- each(feature, (f: any) => {
50
- featureConfig[f.name] = f.config || {}
51
- })
49
+ // The canonical config OBJECT and its JSON, from the shared helper. Both
50
+ // representations render from the same `def`, so they cannot describe
51
+ // different configs - and this target picks up `options.server` (the
52
+ // OpenAPI server-variable defaults), which the hand-rolled build here
53
+ // omitted entirely.
54
+ const { def: config, json: configJson } = configDefinition(model)
55
+ const asData = isConfigData(configJson, configReprSetting(model))
52
56
 
53
- const entityOptions: any = {}
54
- each(entity, (ent: any) => {
55
- entityOptions[ent.name] = {}
56
- })
57
+ File({ name: 'config.' + target.ext }, () => {
57
58
 
58
- const options: any = {
59
- base: baseUrl,
60
- headers,
61
- entity: entityOptions,
62
- }
63
- if (authActive) {
64
- options.auth = { prefix: authPrefix }
65
- }
66
-
67
- const entityConfig = Object.values(entity || {}).reduce((a: any, n: any) => (
68
- a[n.name] = clean({
69
- fields: n.fields,
70
- name: n.name,
71
- op: n.op,
72
- relations: n.relations,
73
- }, true), a), {})
74
-
75
- const config = {
76
- main: { name: model.const.Name },
77
- feature: featureConfig,
78
- options,
79
- entity: entityConfig,
80
- }
59
+ // ABOVE THE THRESHOLD: emit the model as DATA.
60
+ //
61
+ // The literal is one deeply nested expression. rustc type-checks and
62
+ // monomorphises it as a single item, so compile time and memory grow with
63
+ // the whole model at once; a string constant is one token, and json_parse
64
+ // builds the same Value tree at runtime.
65
+ //
66
+ // No number-type question here, unlike Go: `Value::Num` is f64 in both
67
+ // representations, so there is nothing for the two paths to disagree about.
68
+ if (asData) {
69
+ Content(`// Generated API configuration (mirrors go core/config.go).
81
70
 
82
- File({ name: 'config.' + target.ext }, () => {
71
+ use std::cell::RefCell;
72
+ use std::rc::Rc;
73
+
74
+ use crate::core::types::FeatureRef;
75
+ use crate::utility::jsonparse::json_parse;
76
+ use crate::utility::voxgigstruct::Value;
77
+
78
+ /// THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1).
79
+ ///
80
+ /// Emitted only above a size threshold, or when \`main.kit.config.repr\` pins
81
+ /// it: for a small model the literal is smaller and far easier to read when
82
+ /// debugging.
83
+ const CONFIG_DATA: &str = ${rustRawString(configJson)};
84
+
85
+ pub fn make_config() -> Value {
86
+ // Unreachable on error: the constant is generated by a JSON serialiser and
87
+ // never edited by hand. Panicking beats returning an empty config, which
88
+ // would fail far from the cause.
89
+ json_parse(CONFIG_DATA).expect("${model.const.Name}: embedded config is not valid JSON")
90
+ }
91
+
92
+ pub fn make_feature(name: &str) -> FeatureRef {
93
+ match name {
94
+ `)
95
+ }
96
+ else {
83
97
 
84
98
  Content(`// Generated API configuration (mirrors go core/config.go).
85
99
 
@@ -96,6 +110,7 @@ pub fn make_config() -> Value {
96
110
  pub fn make_feature(name: &str) -> FeatureRef {
97
111
  match name {
98
112
  `)
113
+ }
99
114
 
100
115
  each(feature, (f: any) => {
101
116
  const fname = f.name.charAt(0).toUpperCase() + f.name.slice(1)
@@ -146,7 +146,24 @@ function clean(o: any, dropDefaults?: boolean): any {
146
146
  }
147
147
 
148
148
 
149
+
150
+ // The JSON as a Rust RAW string literal.
151
+ //
152
+ // A raw string processes no escapes, so the JSON's own `\n` and `\uXXXX`
153
+ // survive byte for byte and reach the JSON parser as written - which a normal
154
+ // Rust string would not, having consumed them itself. The hash level is raised
155
+ // until its terminator does not occur in the text.
156
+ function rustRawString(s: string): string {
157
+ let level = 0
158
+ while (s.includes('"' + '#'.repeat(level))) {
159
+ level++
160
+ }
161
+ const hashes = '#'.repeat(level)
162
+ return 'r' + hashes + '"' + s + '"' + hashes
163
+ }
164
+
149
165
  export {
166
+ rustRawString,
150
167
  clean,
151
168
  crateIdent,
152
169
  crateName,
@@ -8,6 +8,9 @@ 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,
@@ -26,7 +29,6 @@ import {
26
29
 
27
30
 
28
31
  import {
29
- clean,
30
32
  formatJson,
31
33
  } from './utility_ts'
32
34
 
@@ -75,58 +77,16 @@ const Config = cmp(async function Config(props: any) {
75
77
  baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`)
76
78
  } catch (_e) { }
77
79
 
78
- // The same config as an OBJECT, so it can be measured and, above the
79
- // threshold, emitted as data instead of as class field literals. Mirrors the
80
- // literal branch below exactly - feature config is NOT cleaned here, because
81
- // it is not cleaned there either.
82
- const entityDefs: any = {}
83
- each(entity, (e: any) => {
84
- entityDefs[e.name] = clean({
85
- fields: e.fields,
86
- name: e.name,
87
- op: e.op,
88
- relations: e.relations,
89
- }, true)
90
- })
91
-
92
- const featureDefs: any = {}
93
- each(feature, (f: any) => {
94
- featureDefs[f.name] = f.config
95
- })
96
-
97
- const entityStubs: any = {}
98
- each(entity, (e: any) => {
99
- entityStubs[e.name] = {}
100
- })
101
-
102
- const optionsDef: any = { base: baseUrl }
103
- if (0 < svars.length) {
104
- optionsDef.server = svars.reduce(
105
- (a: any, v: any) => (a[v.name] = v.dflt, a), {})
106
- }
107
- if (authActive) {
108
- optionsDef.auth = { prefix: authPrefix }
109
- }
110
- optionsDef.headers = headers
111
- optionsDef.entity = entityStubs
112
-
113
- const configDef = {
114
- main: { name: model.const.Name },
115
- feature: featureDefs,
116
- options: optionsDef,
117
- entity: entityDefs,
118
- }
119
- const configJson = JSON.stringify(configDef)
120
-
121
- // `auto` decides by size; 'data'/'literal' pin it for this SDK.
122
- let configReprSetting = 'auto'
123
- try {
124
- configReprSetting = getModelPath(model, `main.${KIT}.config.repr`) || 'auto'
125
- } catch (_e) { }
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))
126
86
 
127
87
  File({ name: 'Config.' + target.ext }, () => {
128
88
 
129
- if (isConfigData(configJson, configReprSetting)) {
89
+ if (asData) {
130
90
  Fragment({
131
91
  from: ff + 'Config.data.fragment.ts',
132
92
 
@@ -3,9 +3,11 @@ import {
3
3
  Content,
4
4
  File,
5
5
  cmp,
6
+ configDefinition,
7
+ configReprSetting,
6
8
  each,
7
9
  isAuthActive,
8
- resolveAuthPrefix,
10
+ isConfigData,
9
11
  } from '@voxgig/sdkgen'
10
12
 
11
13
 
@@ -17,7 +19,6 @@ import {
17
19
 
18
20
 
19
21
  import {
20
- clean,
21
22
  formatZigValue,
22
23
  } from './utility_zig'
23
24
 
@@ -31,52 +32,57 @@ const Config = cmp(async function Config(props: any) {
31
32
 
32
33
  const model: Model = ctx$.model
33
34
 
34
- const entity = getModelPath(model, `main.${KIT}.entity`)
35
35
  const feature = getModelPath(model, `main.${KIT}.feature`)
36
36
 
37
- const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
37
+ // The canonical config OBJECT and its JSON, from the shared helper. Both
38
+ // representations render from the same `def`, so they cannot describe
39
+ // different configs - and this target picks up `options.server` (the
40
+ // OpenAPI server-variable defaults), which the hand-rolled build here
41
+ // omitted entirely.
42
+ const { def: config, json: configJson } = configDefinition(model)
43
+ const asData = isConfigData(configJson, configReprSetting(model))
38
44
 
39
- const authActive = isAuthActive(model)
40
- const authPrefix = resolveAuthPrefix(model)
45
+ File({ name: 'config.' + target.ext }, () => {
41
46
 
42
- let baseUrl = ''
43
- try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
47
+ // ABOVE THE THRESHOLD: emit the model as DATA.
48
+ //
49
+ // The literal is one nested expression that Zig's comptime evaluator has
50
+ // to walk in full at every build; a string constant is one token, and
51
+ // `json_parse` (std.json at the boundary, then fromStdJson) builds the
52
+ // same Value at runtime.
53
+ //
54
+ // The escaping is JSON.stringify's, which is valid Zig: it escapes every
55
+ // backslash, so the JSON's own `\uXXXX` reaches the file as `\\uXXXX` and
56
+ // no Zig escape sequence is ever formed from it.
57
+ if (asData) {
58
+ Content(`// Generated API configuration (mirrors go/rust core/config).
44
59
 
45
- const featureConfig: any = {}
46
- each(feature, (f: any) => {
47
- featureConfig[f.name] = f.config || {}
48
- })
60
+ const std = @import("std");
61
+ const h = @import("helpers.zig");
62
+ const types = @import("types.zig");
63
+ const jsonparse = @import("../utility/jsonparse.zig");
64
+ const Value = h.Value;
65
+ const Feature = types.Feature;
49
66
 
50
- const entityOptions: any = {}
51
- each(entity, (ent: any) => {
52
- entityOptions[ent.name] = {}
53
- })
67
+ /// THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1).
68
+ ///
69
+ /// Emitted only above a size threshold, or when \`main.kit.config.repr\` pins
70
+ /// it: for a small model the literal is smaller and far easier to read when
71
+ /// debugging.
72
+ const CONFIG_DATA: []const u8 = ${JSON.stringify(configJson)};
54
73
 
55
- const options: any = {
56
- base: baseUrl,
57
- headers,
58
- entity: entityOptions,
59
- }
60
- if (authActive) {
61
- options.auth = { prefix: authPrefix }
62
- }
63
-
64
- const entityConfig = Object.values(entity || {}).reduce((a: any, n: any) => (
65
- a[n.name] = clean({
66
- fields: n.fields,
67
- name: n.name,
68
- op: n.op,
69
- relations: n.relations,
70
- }, true), a), {})
71
-
72
- const config = {
73
- main: { name: model.const.Name },
74
- feature: featureConfig,
75
- options,
76
- entity: entityConfig,
77
- }
74
+ pub fn make_config() Value {
75
+ // Unreachable on error: the constant is generated by a JSON serialiser and
76
+ // never edited by hand. Panicking beats returning an empty config, which
77
+ // would fail far from the cause.
78
+ return jsonparse.json_parse(CONFIG_DATA) catch
79
+ @panic("${model.const.Name}: embedded config is not valid JSON");
80
+ }
78
81
 
79
- File({ name: 'config.' + target.ext }, () => {
82
+ pub fn make_feature(name: []const u8) Feature {
83
+ `)
84
+ }
85
+ else {
80
86
 
81
87
  Content(`// Generated API configuration (mirrors go/rust core/config).
82
88
 
@@ -92,6 +98,7 @@ pub fn make_config() Value {
92
98
 
93
99
  pub fn make_feature(name: []const u8) Feature {
94
100
  `)
101
+ }
95
102
 
96
103
  // The factory must be able to instantiate any built-in feature by name,
97
104
  // not just the ones the current API model configures — a caller can enable