@voxgig/sdkgen 4.2.3 → 4.2.5

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 (81) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/AgentGuideTop.js +15 -7
  3. package/dist/cmp/AgentGuideTop.js.map +1 -1
  4. package/dist/cmp/ReadmeStation.js +88 -12
  5. package/dist/cmp/ReadmeStation.js.map +1 -1
  6. package/dist/helpers/station.d.ts +3 -0
  7. package/dist/helpers/station.js +67 -0
  8. package/dist/helpers/station.js.map +1 -0
  9. package/dist/sdkgen.d.ts +2 -1
  10. package/dist/sdkgen.js +41 -15
  11. package/dist/sdkgen.js.map +1 -1
  12. package/dist/tsconfig.tsbuildinfo +1 -1
  13. package/dist/types.d.ts +2 -0
  14. package/dist/utility.js +15 -1
  15. package/dist/utility.js.map +1 -1
  16. package/model/sdkgen.aon +20 -0
  17. package/package.json +1 -1
  18. package/project/.sdk/model/feature/audit.aon +1 -0
  19. package/project/.sdk/model/feature/cache.aon +1 -0
  20. package/project/.sdk/model/feature/clienttrack.aon +1 -0
  21. package/project/.sdk/model/feature/debug.aon +1 -0
  22. package/project/.sdk/model/feature/idempotency.aon +1 -0
  23. package/project/.sdk/model/feature/log.aon +1 -0
  24. package/project/.sdk/model/feature/metrics.aon +1 -0
  25. package/project/.sdk/model/feature/netsim.aon +1 -0
  26. package/project/.sdk/model/feature/paging.aon +1 -0
  27. package/project/.sdk/model/feature/proxy.aon +1 -0
  28. package/project/.sdk/model/feature/ratelimit.aon +1 -0
  29. package/project/.sdk/model/feature/rbac.aon +1 -0
  30. package/project/.sdk/model/feature/retry.aon +1 -0
  31. package/project/.sdk/model/feature/streaming.aon +1 -0
  32. package/project/.sdk/model/feature/telemetry.aon +1 -0
  33. package/project/.sdk/model/feature/test.aon +1 -0
  34. package/project/.sdk/model/feature/timeout.aon +1 -0
  35. package/project/.sdk/src/cmp/c/Config_c.ts +13 -10
  36. package/project/.sdk/src/cmp/clojure/Config_clojure.ts +4 -1
  37. package/project/.sdk/src/cmp/dart/Config_dart.ts +4 -1
  38. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +4 -1
  39. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +21 -2
  40. package/project/.sdk/src/cmp/go/Config_go.ts +4 -1
  41. package/project/.sdk/src/cmp/java/Config_java.ts +11 -8
  42. package/project/.sdk/src/cmp/js/Config_js.ts +4 -1
  43. package/project/.sdk/src/cmp/js/Main_js.ts +20 -1
  44. package/project/.sdk/src/cmp/js/fragment/MainStation.fragment.js +36 -0
  45. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +11 -7
  46. package/project/.sdk/src/cmp/lua/Config_lua.ts +4 -1
  47. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +3 -1
  48. package/project/.sdk/src/cmp/php/Config_php.ts +4 -1
  49. package/project/.sdk/src/cmp/py/Config_py.ts +4 -1
  50. package/project/.sdk/src/cmp/rb/Config_rb.ts +4 -1
  51. package/project/.sdk/src/cmp/rust/ReadmeEntity_rust.ts +2 -2
  52. package/project/.sdk/src/cmp/rust/ReadmeHowto_rust.ts +2 -2
  53. package/project/.sdk/src/cmp/rust/ReadmeIntro_rust.ts +2 -2
  54. package/project/.sdk/src/cmp/rust/ReadmeModel_rust.ts +2 -2
  55. package/project/.sdk/src/cmp/rust/ReadmeQuick_rust.ts +3 -3
  56. package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +3 -3
  57. package/project/.sdk/src/cmp/rust/ReadmeTopQuick_rust.ts +2 -2
  58. package/project/.sdk/src/cmp/rust/ReadmeTopTest_rust.ts +2 -2
  59. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +7 -7
  60. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +2 -2
  61. package/project/.sdk/src/cmp/swift/Entity_swift.ts +3 -1
  62. package/project/.sdk/src/cmp/swift/Main_swift.ts +37 -2
  63. package/project/.sdk/src/cmp/swift/Package_swift.ts +9 -6
  64. package/project/.sdk/src/cmp/swift/ReadmeExamplesTest_swift.ts +1 -1
  65. package/project/.sdk/src/cmp/swift/ReadmeExplanation_swift.ts +5 -3
  66. package/project/.sdk/src/cmp/swift/Test_swift.ts +3 -1
  67. package/project/.sdk/src/cmp/swift/utility_swift.ts +16 -0
  68. package/project/.sdk/src/cmp/ts/Config_ts.ts +4 -1
  69. package/project/.sdk/src/cmp/ts/Main_ts.ts +20 -1
  70. package/project/.sdk/src/cmp/ts/fragment/MainStation.fragment.ts +36 -0
  71. package/project/.sdk/src/cmp/zig/Config_zig.ts +4 -2
  72. package/project/.sdk/tm/elixir/lib/utility/struct/voxgig_struct.ex +66 -12
  73. package/project/.sdk/tm/zig/Makefile +20 -0
  74. package/project/.sdk/tm/zig/feature/test.zig +1 -1
  75. package/project/sdkgen-package.json +1 -1
  76. package/src/cmp/AgentGuideTop.ts +15 -7
  77. package/src/cmp/ReadmeStation.ts +93 -13
  78. package/src/helpers/station.ts +80 -0
  79. package/src/sdkgen.ts +52 -15
  80. package/src/types.ts +6 -0
  81. package/src/utility.ts +15 -1
package/model/sdkgen.aon CHANGED
@@ -270,6 +270,15 @@ main: kit: target: &: {
270
270
  output: {
271
271
  path: *'' | string
272
272
 
273
+ # Whether generation may create a destination folder that does not yet
274
+ # exist. true preserves the ordinary behaviour: jostraca creates it and
275
+ # any missing parents. false keeps the target active but skips its
276
+ # out-of-tree generation pass until the destination folder exists — for
277
+ # example when a separate provider fleet is only checked out on some
278
+ # machines. An existing destination still goes through every ownership
279
+ # and overwrite-safety check below.
280
+ create: *true | boolean
281
+
273
282
  # Write into a destination that already holds content this generator did
274
283
  # not write.
275
284
  #
@@ -411,6 +420,16 @@ main: kit: feature: &: {
411
420
  # TODO: version '0.0.1' | semver # where semver is a string format type as per semver.org
412
421
  version: *'0.0.1' | string
413
422
 
423
+ # Transport role (station design §8.4): how the feature relates to the
424
+ # transport slot. 'base' REPLACES ctx.utility.fetcher outright (only the
425
+ # `test` feature); 'wrap' wraps it in init() (netsim, retry, cache,
426
+ # ratelimit, timeout, proxy - and station's own feature); 'none' is
427
+ # hook-only. Carried into the embedded config by configDefinition so
428
+ # station can validate the resolved feature order. Declared, never
429
+ # inferred: the obvious signal, an empty `hook: {}`, is wrong for a
430
+ # feature that both wraps AND dispatches hooks (station does).
431
+ transport: *'none' | 'base' | 'wrap'
432
+
414
433
  hook: &: {
415
434
  active: *false | boolean
416
435
  await: *false | boolean
@@ -480,6 +499,7 @@ main: kit: docs: &: {
480
499
  output: {
481
500
  path: *'' | string
482
501
  repo: *'' | string
502
+ create: *true | boolean
483
503
  adopt: *false | boolean
484
504
  sdkrel: *'' | string
485
505
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/sdkgen",
3
- "version": "4.2.3",
3
+ "version": "4.2.5",
4
4
  "main": "dist/sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "engines": {
@@ -6,6 +6,7 @@ main: kit: feature: audit: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: cache: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'wrap'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: clienttrack: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: debug: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: idempotency: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: log: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
 
11
12
  config: {
@@ -6,6 +6,7 @@ main: kit: feature: metrics: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: netsim: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'wrap'
9
10
 
10
11
  config: {
11
12
  # Test-support feature: enable per test via
@@ -6,6 +6,7 @@ main: kit: feature: paging: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: proxy: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'wrap'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: ratelimit: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'wrap'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: rbac: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: retry: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'wrap'
9
10
 
10
11
  config: {
11
12
  # Opt-in: enable per SDK via options.feature.retry.active = true
@@ -6,6 +6,7 @@ main: kit: feature: streaming: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: telemetry: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'none'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -6,6 +6,7 @@ main: kit: feature: test: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'base'
9
10
 
10
11
  config: {
11
12
  # Test mode is not active by default, unlike most features
@@ -6,6 +6,7 @@ main: kit: feature: timeout: {
6
6
  version: '0.0.1'
7
7
  active: true
8
8
  base: 'BASE'
9
+ transport: 'wrap'
9
10
 
10
11
  config: {
11
12
  options: {
@@ -46,9 +46,21 @@ const Config = cmp(async function Config(props: any) {
46
46
  let baseUrl = ''
47
47
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
48
48
 
49
+ // The same config as an OBJECT, built by the shared helper so this target's
50
+ // literal and the data that replaces it above the threshold are the same
51
+ // config by construction. The JSON is what the threshold is measured on -
52
+ // emitted source size varies by language, the model does not. Passing
53
+ // target.name opts this target into the main slug/version/target identity
54
+ // fields (read by station's descriptor - see configDefinition).
55
+ const { def: configDef, json: configJson } = configDefinition(model, target.name)
56
+ const asData = isConfigData(configJson, configReprSetting(model))
57
+
58
+ // The feature block comes from configDefinition's def, not from f.config,
59
+ // so the literal carries each feature's `transport` role (station design
60
+ // §8.4) beside its options and cannot drift from the data rep.
49
61
  const featureConfig: any = {}
50
62
  each(feature, (f: any) => {
51
- featureConfig[f.name] = f.config || {}
63
+ featureConfig[f.name] = configDef.feature[f.name] || {}
52
64
  })
53
65
 
54
66
  const entityOptions: any = {}
@@ -73,15 +85,6 @@ const Config = cmp(async function Config(props: any) {
73
85
  relations: n.relations,
74
86
  }, true), a), {})
75
87
 
76
- // The same config as an OBJECT, built by the shared helper so this target's
77
- // literal and the data that replaces it above the threshold are the same
78
- // config by construction. The JSON is what the threshold is measured on -
79
- // emitted source size varies by language, the model does not. Passing
80
- // target.name opts this target into the main slug/version/target identity
81
- // fields (read by station's descriptor - see configDefinition).
82
- const { def: configDef, json: configJson } = configDefinition(model, target.name)
83
- const asData = isConfigData(configJson, configReprSetting(model))
84
-
85
88
  const config = {
86
89
  // main from configDefinition's def, not re-derived here, so the literal
87
90
  // rep and the data rep cannot disagree on identity (the Config_ts
@@ -32,6 +32,7 @@ import {
32
32
  // N-feature-safe: an empty feature/entity map renders as (vs/jm).
33
33
  const Config = cmp(async function Config(props: any) {
34
34
  const ctx$ = props.ctx$
35
+ const target = props.target
35
36
 
36
37
  const model: Model = ctx$.model
37
38
 
@@ -39,7 +40,9 @@ const Config = cmp(async function Config(props: any) {
39
40
  // representations render from the same `def`, so they cannot describe
40
41
  // different configs - and this target picks up `options.server` (the OpenAPI
41
42
  // server-variable defaults), which the hand-rolled build here omitted.
42
- const { def: config, json: configJson } = configDefinition(model)
43
+ // Passing target.name opts this target into the main slug/version/target
44
+ // identity fields (read by station's descriptor - see configDefinition).
45
+ const { def: config, json: configJson } = configDefinition(model, target.name)
43
46
  const asData = isConfigData(configJson, configReprSetting(model))
44
47
 
45
48
  File({ name: 'config.clj' }, () => {
@@ -143,8 +143,11 @@ const Config = cmp(async function Config(props: any) {
143
143
  Line(` '${f.name}': () => ${nom(f, 'Name')}Feature(),`)
144
144
  }),
145
145
 
146
+ // Rendered from configDefinition's def, not from f.config, so the
147
+ // literal carries the feature's `transport` role (station design
148
+ // §8.4) beside its options and cannot drift from the data rep.
146
149
  '// #FeatureConfigs': () => each(feature, (f: any) => {
147
- Line(` '${f.name}': ${dartValue(f.config, 2)},`)
150
+ Line(` '${f.name}': ${dartValue(configDef.feature[f.name], 2)},`)
148
151
  }),
149
152
 
150
153
 
@@ -174,7 +174,10 @@ defmodule ${Name}.Config do
174
174
  `)
175
175
 
176
176
  each(feature, (f: any) => {
177
- Content(` ${elixirString(f.name)} => ${formatElixir(f.config || {}, 4)},
177
+ // From configDefinition's def, not f.config, so the literal carries
178
+ // the feature's `transport` role (station design §8.4) beside its
179
+ // options and cannot drift from the data rep.
180
+ Content(` ${elixirString(f.name)} => ${formatElixir(configDef.feature[f.name] || {}, 4)},
178
181
  `)
179
182
  })
180
183
 
@@ -48,15 +48,34 @@ const Main = cmp(async function Main(props: any) {
48
48
  // Generate the documented typespec module (lib/<app>_types.ex).
49
49
  EntityTypes({ target })
50
50
 
51
- // Copy tm/elixir verbatim (with ProjectName/projectname substitution).
51
+ // Copy tm/elixir (with ProjectName/projectname substitution).
52
+ //
53
+ // THE RUNTIME DIRECTORY HAS TO CARRY THE APP NAME. Copy substitutes file
54
+ // CONTENTS, never path components, so a blanket copy shipped the runtime as
55
+ // lib/projectname/ in every generated SDK. Nothing broke — Elixir resolves
56
+ // modules by their `defmodule`, not by path, and those were substituted
57
+ // correctly — so it compiled, tested green, and published looking wrong.
58
+ //
59
+ // Copy's `to` prop names the destination, so lib/projectname is copied
60
+ // explicitly under the app's own name and excluded from the blanket copy.
52
61
  Copy({
53
62
  from: 'tm/' + target.name,
54
- exclude: [/src\//],
63
+ exclude: [/src\//, /lib\/projectname\//],
55
64
  replace: {
56
65
  ...stdrep,
57
66
  }
58
67
  })
59
68
 
69
+ Folder({ name: 'lib' }, () => {
70
+ Copy({
71
+ from: 'tm/' + target.name + '/lib/projectname',
72
+ to: model.const.name,
73
+ replace: {
74
+ ...stdrep,
75
+ }
76
+ })
77
+ })
78
+
60
79
  Folder({ name: 'lib' }, () => {
61
80
 
62
81
  // Main SDK module (entity factory fns injected at the slot).
@@ -173,7 +173,10 @@ func MakeConfig() map[string]any {
173
173
  `)
174
174
 
175
175
  each(feature, (f: any) => {
176
- const fconfig = f.config || {}
176
+ // From configDefinition's def, not f.config, so the literal carries
177
+ // the feature's `transport` role (station design §8.4) beside its
178
+ // options and cannot drift from the data rep.
179
+ const fconfig = configDef.feature[f.name] || {}
177
180
  Content(` "${f.name}": ${formatGoMap(fconfig, 3)},
178
181
  `)
179
182
  })
@@ -48,10 +48,19 @@ const Config = cmp(async function Config(props: any) {
48
48
  let baseUrl = ''
49
49
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
50
50
 
51
- // Assemble the config shape (mirrors Config_go's emitted map).
51
+ // Identity comes from configDefinition's def, not re-derived here, so
52
+ // this target cannot disagree with the shared emitter on main.slug /
53
+ // main.version / main.target (the three station descriptor fields,
54
+ // station design §4) — passing target.name is what opts this target in.
55
+ const { def: configDef } = configDefinition(model, target.name)
56
+
57
+ // Assemble the config shape (mirrors Config_go's emitted map). The
58
+ // feature block comes from configDefinition's def, not from f.config,
59
+ // so it carries each feature's `transport` role (station design §8.4)
60
+ // beside its options and cannot drift from the shared emitter.
52
61
  const featureConfig: Record<string, any> = {}
53
62
  each(feature, (f: any) => {
54
- featureConfig[f.name] = cleanModel(f.config || {})
63
+ featureConfig[f.name] = cleanModel(configDef.feature[f.name] || {})
55
64
  })
56
65
 
57
66
  const optionsEntity: Record<string, any> = {}
@@ -76,12 +85,6 @@ const Config = cmp(async function Config(props: any) {
76
85
  relations: n.relations,
77
86
  }, true), a), {})
78
87
 
79
- // Identity comes from configDefinition's def, not re-derived here, so
80
- // this target cannot disagree with the shared emitter on main.slug /
81
- // main.version / main.target (the three station descriptor fields,
82
- // station design §4) — passing target.name is what opts this target in.
83
- const { def: configDef } = configDefinition(model, target.name)
84
-
85
88
  const config = {
86
89
  main: configDef.main,
87
90
  feature: featureConfig,
@@ -142,8 +142,11 @@ const Config = cmp(async function Config(props: any) {
142
142
  Line(` ${f.name}: ${nom(f, 'Name')}Feature,`)
143
143
  }),
144
144
 
145
+ // Rendered from configDefinition's def, not from f.config, so the
146
+ // literal carries the feature's `transport` role (station design
147
+ // §8.4) beside its options and cannot drift from the data rep.
145
148
  '// #FeatureConfigs': () => each(feature, (f: any) => {
146
- Line(` ${f.name}: ${formatJson(f.config, { margin: 4 })},`)
149
+ Line(` ${f.name}: ${formatJson(configDef.feature[f.name], { margin: 4 })},`)
147
150
  }),
148
151
 
149
152
 
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each, names, cmap,
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
- entityClassName, entityCollection, srcFeatureExcludes,
7
+ entityClassName, entityCollection, srcFeatureExcludes, stationLibrary,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
10
 
@@ -102,6 +102,25 @@ if (fres instanceof Promise) { await fres }
102
102
  MainEntity(entprops)
103
103
  })
104
104
  })
105
+
106
+ // Station self-registration (station design §6.2 path 1;
107
+ // station-declarative-config §11 item 2): emitted ONLY when the model
108
+ // carries an ACTIVE station feature (installed via
109
+ // `package add @voxgig/sdkgen-station`). The library package name
110
+ // comes from the feature model's own `deps.js` block — the same entry
111
+ // collectDeps flows into package.json — so the manifest dependency
112
+ // and the emitted require cannot disagree.
113
+ const stationPkg = stationLibrary(model, target.name)
114
+ if (null != stationPkg) {
115
+ Fragment({
116
+ from: Path.normalize(
117
+ __dirname + '/../../../src/cmp/js/fragment/MainStation.fragment.js'),
118
+ replace: {
119
+ ...props.ctx$.stdrep,
120
+ "'STATIONPKG'": JSON.stringify(stationPkg),
121
+ }
122
+ })
123
+ }
105
124
  })
106
125
 
107
126
  Config({ target })
@@ -0,0 +1,36 @@
1
+
2
+
3
+ // Station self-registration (voxgig/station design §6.2, path 1): linking
4
+ // this package is the whole bootstrap. When the station library is
5
+ // installed beside this SDK, the {construct, config} factory pair is
6
+ // registered under the api slug (config.main.slug, the descriptor slug) at
7
+ // module init, so `station.sdk('<name>')` needs no imports in application
8
+ // code. Registration is SOFT — the station library is a peer dependency of
9
+ // the station feature, and a project without it skips registration and
10
+ // gets exactly the SDK it always had. Only a genuine module-not-found is
11
+ // swallowed: a station that RESOLVES but throws while loading must
12
+ // propagate (a silently empty factory table has no visible cause), and so
13
+ // must station_factory_conflict — two different builds of one SDK in one
14
+ // process is not a thing to pick between quietly.
15
+ let stationInstalled = true
16
+ try { require.resolve('STATIONPKG') }
17
+ catch (err) {
18
+ // MODULE_NOT_FOUND is the absent case. Anything else - an invalid
19
+ // package config, a root entry the package does not export - is a
20
+ // real failure to load a station that IS installed, and swallowing
21
+ // it would leave an empty factory table with no visible cause.
22
+ if ('MODULE_NOT_FOUND' !== (err && err.code)) { throw err }
23
+ stationInstalled = false
24
+ }
25
+
26
+ if (stationInstalled) {
27
+ const { provide } = require('STATIONPKG')
28
+ // A station library predating the factory table has no provide() —
29
+ // there is nothing to register with, which is not this SDK's error.
30
+ if ('function' === typeof provide) {
31
+ provide(config.main.slug, {
32
+ construct: (options) => new ProjectNameSDK(options),
33
+ config,
34
+ })
35
+ }
36
+ }
@@ -46,9 +46,19 @@ const Config = cmp(async function Config(props: any) {
46
46
  let baseUrl = ''
47
47
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
48
48
 
49
+ // Identity comes from configDefinition's def, not re-derived here, so
50
+ // this target cannot disagree with the shared emitter on main.slug /
51
+ // main.version / main.target (the three station descriptor fields,
52
+ // station design §4) — passing target.name is what opts this target in.
53
+ const { def: configDef } = configDefinition(model, target.name)
54
+
55
+ // The feature block comes from configDefinition's def, not from
56
+ // f.config, so it carries each feature's `transport` role (station
57
+ // design §8.4) beside its options and cannot drift from the shared
58
+ // emitter.
49
59
  const featureConfig: Record<string, any> = {}
50
60
  each(feature, (f: any) => {
51
- featureConfig[f.name] = cleanModel(f.config || {})
61
+ featureConfig[f.name] = cleanModel(configDef.feature[f.name] || {})
52
62
  })
53
63
 
54
64
  const optionsEntity: Record<string, any> = {}
@@ -73,12 +83,6 @@ const Config = cmp(async function Config(props: any) {
73
83
  relations: n.relations,
74
84
  }, true), a), {})
75
85
 
76
- // Identity comes from configDefinition's def, not re-derived here, so
77
- // this target cannot disagree with the shared emitter on main.slug /
78
- // main.version / main.target (the three station descriptor fields,
79
- // station design §4) — passing target.name is what opts this target in.
80
- const { def: configDef } = configDefinition(model, target.name)
81
-
82
86
  const config = {
83
87
  main: configDef.main,
84
88
  feature: featureConfig,
@@ -133,7 +133,10 @@ local function make_config()
133
133
  `)
134
134
 
135
135
  each(feature, (f: any) => {
136
- const fconfig = f.config || {}
136
+ // From configDefinition's def, not f.config, so the literal carries
137
+ // the feature's `transport` role (station design §8.4) beside its
138
+ // options and cannot drift from the data rep.
139
+ const fconfig = configDef.feature[f.name] || {}
137
140
  Content(` ["${f.name}"] = ${formatLuaTable(fconfig, 3)},
138
141
  `)
139
142
  })
@@ -40,7 +40,9 @@ const Config = cmp(async function Config(props: any) {
40
40
  // representations render from the same `def`, so they cannot describe
41
41
  // different configs - and this target picks up `options.server` (the OpenAPI
42
42
  // server-variable defaults), which the hand-rolled build here omitted.
43
- const { def: config, json: configJson } = configDefinition(model)
43
+ // Passing target.name opts this target into the main slug/version/target
44
+ // identity fields (read by station's descriptor - see configDefinition).
45
+ const { def: config, json: configJson } = configDefinition(model, target.name)
44
46
  const asData = isConfigData(configJson, configReprSetting(model))
45
47
 
46
48
  File({ name: 'sdk_config.' + target.ext }, () => {
@@ -197,7 +197,10 @@ class ${model.const.Name}Config
197
197
  `)
198
198
 
199
199
  each(feature, (f: any) => {
200
- const fconfig = f.config || {}
200
+ // From configDefinition's def, not f.config, so the literal carries
201
+ // the feature's `transport` role (station design §8.4) beside its
202
+ // options and cannot drift from the data rep.
203
+ const fconfig = configDef.feature[f.name] || {}
201
204
  Content(` "${f.name}" => ${formatPhpArray(fconfig, 4)},
202
205
  `)
203
206
  })
@@ -153,7 +153,10 @@ def make_config():
153
153
  `)
154
154
 
155
155
  each(feature, (f: any) => {
156
- const fconfig = f.config || {}
156
+ // From configDefinition's def, not f.config, so the literal carries
157
+ // the feature's `transport` role (station design §8.4) beside its
158
+ // options and cannot drift from the data rep.
159
+ const fconfig = configDef.feature[f.name] || {}
157
160
  Content(` "${f.name}": ${formatPyDict(fconfig, 3)},
158
161
  `)
159
162
  })
@@ -138,7 +138,10 @@ module ${model.const.Name}Config
138
138
  `)
139
139
 
140
140
  each(feature, (f: any) => {
141
- const fconfig = f.config || {}
141
+ // From configDefinition's def, not f.config, so the literal carries
142
+ // the feature's `transport` role (station design §8.4) beside its
143
+ // options and cannot drift from the data rep.
144
+ const fconfig = configDef.feature[f.name] || {}
142
145
  Content(` "${f.name}" => ${formatRubyHash(fconfig, 4)},
143
146
  `)
144
147
  })
@@ -6,7 +6,7 @@ import {
6
6
  getModelPath,
7
7
  } from '@voxgig/apidef'
8
8
 
9
- import { rustVarName } from './utility_rust'
9
+ import { rustVarName, rustMethodName } from './utility_rust'
10
10
 
11
11
 
12
12
  // Type names come from the shared canonToType 'rust' column (single source of truth).
@@ -56,7 +56,7 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
56
56
  const fields = entity.fields || []
57
57
  const idF = entityIdField(entity)
58
58
  const eVar = rustVarName(entity.name)
59
- const method = rustVarName(entity.name)
59
+ const method = rustMethodName(entity.name)
60
60
 
61
61
  Content(`
62
62
  ### ${entity.Name}
@@ -7,7 +7,7 @@ import {
7
7
  nom,
8
8
  } from '@voxgig/apidef'
9
9
 
10
- import { crateIdent, rustVarName } from './utility_rust'
10
+ import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
11
11
 
12
12
 
13
13
  // A type-correct rust expression constructing a voxgig struct Value.
@@ -31,7 +31,7 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
31
31
  const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
32
32
  const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
33
33
  const eVar = exampleEntity ? rustVarName(exampleEntity.name) : 'entity'
34
- const method = exampleEntity ? rustVarName(exampleEntity.name) : 'entity'
34
+ const method = exampleEntity ? rustMethodName(exampleEntity.name) : 'entity'
35
35
  const idF = exampleEntity ? entityIdField(exampleEntity) : null
36
36
  const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
37
37
  let testArg = 'Value::Noval'
@@ -6,7 +6,7 @@ import {
6
6
  getModelPath,
7
7
  } from '@voxgig/apidef'
8
8
 
9
- import { rustVarName } from './utility_rust'
9
+ import { rustVarName, rustMethodName } from './utility_rust'
10
10
 
11
11
 
12
12
  const ReadmeIntro = cmp(function ReadmeIntro(props: any) {
@@ -30,7 +30,7 @@ The ${target.title} SDK for the ${model.Name} API — an entity-oriented client
30
30
  const exampleEntity = Object.values(entity || {})
31
31
  .find((e: any) => e.active !== false) as any
32
32
  const example = exampleEntity
33
- ? `for example \`client.${rustVarName(exampleEntity.name)}(Value::Noval)\` — each`
33
+ ? `for example \`client.${rustMethodName(exampleEntity.name)}(Value::Noval)\` — each`
34
34
  : 'each'
35
35
 
36
36
  // Model-driven op list — only the operations the active entities actually
@@ -6,7 +6,7 @@ import {
6
6
  getModelPath,
7
7
  } from '@voxgig/apidef'
8
8
 
9
- import { crateIdent, rustVarName } from './utility_rust'
9
+ import { crateIdent, rustVarName, rustMethodName } from './utility_rust'
10
10
 
11
11
 
12
12
  const ReadmeModel = cmp(function ReadmeModel(props: any) {
@@ -77,7 +77,7 @@ Creates a test-mode client with mock transport. Both arguments may be
77
77
 
78
78
  each(entityList, (ent: any) => {
79
79
  const article = /^[aeiou]/i.test(ent.Name) ? 'an' : 'a'
80
- Content(`| \`${rustVarName(ent.name)}\` | \`(entopts: Value) -> Rc<${ent.Name}Entity>\` | Create ${article} ${ent.Name} entity instance. |
80
+ Content(`| \`${rustMethodName(ent.name)}\` | \`(entopts: Value) -> Rc<${ent.Name}Entity>\` | Create ${article} ${ent.Name} entity instance. |
81
81
  `)
82
82
  })
83
83