@voxgig/sdkgen 4.6.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Feature.js +5 -0
  3. package/dist/cmp/Feature.js.map +1 -1
  4. package/dist/cmp/TestControl.d.ts +4 -0
  5. package/dist/cmp/TestControl.js +51 -0
  6. package/dist/cmp/TestControl.js.map +1 -0
  7. package/dist/helpers/featureSource.d.ts +3 -1
  8. package/dist/helpers/featureSource.js +105 -0
  9. package/dist/helpers/featureSource.js.map +1 -1
  10. package/dist/helpers/opShape.js +8 -11
  11. package/dist/helpers/opShape.js.map +1 -1
  12. package/dist/helpers/pointPath.d.ts +10 -0
  13. package/dist/helpers/pointPath.js +54 -0
  14. package/dist/helpers/pointPath.js.map +1 -0
  15. package/dist/sdkgen.d.ts +6 -3
  16. package/dist/sdkgen.js +14 -4
  17. package/dist/sdkgen.js.map +1 -1
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/dist/utility.d.ts +2 -1
  20. package/dist/utility.js +78 -1
  21. package/dist/utility.js.map +1 -1
  22. package/model/sdkgen.aon +58 -0
  23. package/package.json +2 -2
  24. package/project/.sdk/model/feature/secrets.aon +82 -0
  25. package/project/.sdk/src/cmp/c/Config_c.ts +7 -8
  26. package/project/.sdk/src/cmp/c/TestDirect_c.ts +4 -3
  27. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
  28. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +4 -3
  29. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
  30. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
  31. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +34 -9
  32. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
  33. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
  34. package/project/.sdk/src/cmp/dart/Config_dart.ts +7 -8
  35. package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
  36. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +28 -10
  37. package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
  38. package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
  39. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +9 -8
  40. package/project/.sdk/src/cmp/go/Config_go.ts +1 -7
  41. package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
  42. package/project/.sdk/src/cmp/go/TestDirect_go.ts +5 -4
  43. package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
  44. package/project/.sdk/src/cmp/java/Config_java.ts +7 -7
  45. package/project/.sdk/src/cmp/java/Main_java.ts +2 -1
  46. package/project/.sdk/src/cmp/java/TestDirect_java.ts +5 -4
  47. package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
  48. package/project/.sdk/src/cmp/js/Config_js.ts +7 -8
  49. package/project/.sdk/src/cmp/js/Main_js.ts +2 -1
  50. package/project/.sdk/src/cmp/js/TestDirect_js.ts +34 -8
  51. package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
  52. package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
  53. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
  54. package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
  55. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +7 -7
  56. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
  57. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +5 -4
  58. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
  59. package/project/.sdk/src/cmp/lean/Config_lean.ts +9 -8
  60. package/project/.sdk/src/cmp/lua/Config_lua.ts +1 -7
  61. package/project/.sdk/src/cmp/lua/Main_lua.ts +2 -1
  62. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +32 -7
  63. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
  64. package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
  65. package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
  66. package/project/.sdk/src/cmp/perl/Main_perl.ts +2 -1
  67. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +29 -7
  68. package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
  69. package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
  70. package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
  71. package/project/.sdk/src/cmp/php/Config_php.ts +1 -7
  72. package/project/.sdk/src/cmp/php/Main_php.ts +2 -1
  73. package/project/.sdk/src/cmp/php/TestDirect_php.ts +28 -8
  74. package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
  75. package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
  76. package/project/.sdk/src/cmp/py/Config_py.ts +1 -7
  77. package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
  78. package/project/.sdk/src/cmp/py/TestDirect_py.ts +5 -4
  79. package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
  80. package/project/.sdk/src/cmp/rb/Config_rb.ts +1 -7
  81. package/project/.sdk/src/cmp/rb/Main_rb.ts +2 -1
  82. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +28 -8
  83. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
  84. package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
  85. package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
  86. package/project/.sdk/src/cmp/rust/Main_rust.ts +2 -1
  87. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +35 -7
  88. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
  89. package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
  90. package/project/.sdk/src/cmp/scala/Config_scala.ts +9 -7
  91. package/project/.sdk/src/cmp/scala/Main_scala.ts +2 -1
  92. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +4 -3
  93. package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
  94. package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +3 -1
  95. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +9 -5
  96. package/project/.sdk/src/cmp/ts/Config_ts.ts +57 -16
  97. package/project/.sdk/src/cmp/ts/Main_ts.ts +10 -2
  98. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +15 -7
  99. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +11 -3
  100. package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
  101. package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
  102. package/project/.sdk/tm/dart/test/utility.dart +39 -0
  103. package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
  104. package/project/.sdk/tm/js/test/utility.js +91 -1
  105. package/project/.sdk/tm/lua/test/runner.lua +40 -0
  106. package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
  107. package/project/.sdk/tm/perl/t/runner.pm +30 -0
  108. package/project/.sdk/tm/php/test/Runner.php +36 -0
  109. package/project/.sdk/tm/rb/test/runner.rb +25 -0
  110. package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
  111. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  112. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +8 -2
  113. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +29 -5
  114. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +30 -21
  115. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
  116. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
  117. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
  118. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
  119. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
  120. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
  121. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
  122. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
  123. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
  124. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
  125. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
  126. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
  127. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
  128. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
  129. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
  130. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
  131. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +15 -13
  132. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +25 -19
  133. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +1 -1
  134. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
  135. package/project/.sdk/tm/zig/core/utility.zig +1 -1
  136. package/project/sdkgen-package.json +1 -1
  137. package/src/cmp/Feature.ts +5 -0
  138. package/src/cmp/TestControl.ts +64 -0
  139. package/src/helpers/featureSource.ts +130 -0
  140. package/src/helpers/opShape.ts +9 -14
  141. package/src/helpers/pointPath.ts +88 -0
  142. package/src/sdkgen.ts +18 -0
  143. package/src/utility.ts +92 -1
  144. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +0 -1221
@@ -30,7 +30,11 @@ import {
30
30
  cmp,
31
31
  each,
32
32
  isAuthActive,
33
- entityDataIdField, envName, envToken
33
+ serverVarEnv,
34
+ serverVariables,
35
+ entityDataIdField, envName, envToken,
36
+ jsKey,
37
+ jsProp
34
38
  } from '@voxgig/sdkgen'
35
39
 
36
40
 
@@ -63,13 +67,32 @@ const TestEntity = cmp(function TestEntity(props: any) {
63
67
  const ENTENVNAME = envToken(entity.name)
64
68
  const authActive = isAuthActive(model)
65
69
  const apikeyEnvEntry = authActive
66
- ? `\n '${PROJENVNAME}_APIKEY': 'NONE',`
70
+ ? `\n '${PROJENVNAME}_APIKEY': '',`
67
71
  : ''
68
72
  const apikeyLiveField = authActive
69
73
  ? `
70
74
  apikey: env.${PROJENVNAME}_APIKEY,`
71
75
  : ''
72
76
 
77
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
78
+ // impossible to construct without values: makeOptions raises rather than
79
+ // request a URL with a literal `{account_id}` in it. So the live suite
80
+ // takes them from the environment the same way it takes the apikey.
81
+ //
82
+ // Keys are quoted and the env read is bracketed via jsKey/jsProp: a server
83
+ // variable name is spec-derived and need not be a JS identifier — the URL
84
+ // grammar admits a leading digit ({2fa}), and a declared-but-unreferenced
85
+ // variable ({edge-zone}) is not constrained at all. Bare `name:` and
86
+ // `env.PROJ_SERVER_EDGE-ZONE` are both syntax errors.
87
+ const svars = serverVariables(model)
88
+ const serverEnvEntry = svars
89
+ .map((v: any) => `\n '${serverVarEnv(PROJENVNAME, v.name)}': ${JSON.stringify(v.dflt)},`).join('')
90
+ const serverLiveField = 0 === svars.length ? '' : `
91
+ server: {${svars
92
+ .map((v: any) => `
93
+ ${jsKey(v.name)}: ${jsProp('env', serverVarEnv(PROJENVNAME, v.name))},`).join('')}
94
+ },`
95
+
73
96
  // TODO: should be a utility function
74
97
  const ff = projectPath('src/cmp/js/fragment/')
75
98
 
@@ -143,16 +166,23 @@ function basicSetup(extra) {
143
166
  const env = envOverride({
144
167
  '${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID': idmap,
145
168
  '${PROJENVNAME}_TEST_LIVE': 'FALSE',
146
- '${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}
169
+ '${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
147
170
  })
148
171
 
149
172
  idmap = env['${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID']
150
173
 
151
174
  if ('TRUE' === env.${PROJENVNAME}_TEST_LIVE) {
152
175
  client = new ${model.Name}SDK(merge([
153
- {${apikeyLiveField}
176
+ // FIRST, so the generated fields below win: sdk-test-control.json's
177
+ // test.client.options adds to the live client, it does not redirect it.
178
+ liveClientOptions(),
179
+ {${apikeyLiveField}${serverLiveField}
154
180
  },
155
- extra
181
+ // 'extra || {}', not a bare 'extra': struct.merge returns UNDEFINED when
182
+ // the last entry is undefined, and basicSetup is normally called with no
183
+ // argument at all - so a bare 'extra' silently discarded the apikey and
184
+ // server values above and handed the SDK undefined.
185
+ extra || {}
156
186
  ]))
157
187
  }
158
188
 
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestDirect } from './TestDirect_js'
@@ -16,6 +17,9 @@ const Test = cmp(function Test(props: any) {
16
17
 
17
18
  Folder({ name: 'test' }, () => {
18
19
 
20
+ // Write-once: a project's edited control file survives regeneration.
21
+ TestControl({ target, dir: 'test' })
22
+
19
23
  Folder({ name: 'entity' }, () => {
20
24
  const entity = each(entityCollection(model))
21
25
  .filter((e: any) => false !== e.active)
@@ -2,7 +2,7 @@
2
2
  const envlocal = __dirname + '/../../../.env.local'
3
3
  require('dotenv').config({ quiet: true, path: [envlocal] })
4
4
 
5
- const { test, describe } = require('node:test')
5
+ const { test, describe, afterEach } = require('node:test')
6
6
  const assert = require('node:assert')
7
7
 
8
8
 
@@ -10,11 +10,17 @@ const { ProjectNameSDK } = require('../../..')
10
10
 
11
11
  const {
12
12
  envOverride,
13
+ liveClientOptions,
14
+ liveDelay,
13
15
  } = require('../../utility')
14
16
 
15
17
 
16
18
  describe('EntityNameDirect', async () => {
17
19
 
20
+ // Per-test live pacing. Delay is read from sdk-test-control.json's
21
+ // `test.live.delayMs`; only sleeps when PROJECTENV_TEST_LIVE=TRUE.
22
+ afterEach(liveDelay('PROJECTENV_TEST_LIVE'))
23
+
18
24
  test('direct-exists', async () => {
19
25
  const sdk = new ProjectNameSDK({
20
26
  // Concrete base: a live construction must satisfy any server
@@ -5,7 +5,7 @@ require('dotenv').config({ quiet: true, path: [envlocal] })
5
5
  const Path = require('node:path')
6
6
  const Fs = require('node:fs')
7
7
 
8
- const { test, describe } = require('node:test')
8
+ const { test, describe, afterEach } = require('node:test')
9
9
  const assert = require('node:assert')
10
10
 
11
11
 
@@ -13,6 +13,8 @@ const { ProjectNameSDK, BaseFeature, stdutil, config } = require('../../..')
13
13
 
14
14
  const {
15
15
  envOverride,
16
+ liveClientOptions,
17
+ liveDelay,
16
18
  makeCtrl,
17
19
  makeMatch,
18
20
  makeReqdata,
@@ -23,6 +25,10 @@ const {
23
25
 
24
26
  describe('EntityNameEntity', async () => {
25
27
 
28
+ // Per-test live pacing. Delay is read from sdk-test-control.json's
29
+ // `test.live.delayMs`; only sleeps when PROJECTENV_TEST_LIVE=TRUE.
30
+ afterEach(liveDelay('PROJECTENV_TEST_LIVE'))
31
+
26
32
  test('instance', async () => {
27
33
  const testsdk = ProjectNameSDK.test()
28
34
  const ent = testsdk.EntityName()
@@ -79,13 +79,13 @@ const Config = cmp(async function Config(props: any) {
79
79
  options.headers = headers
80
80
  options.entity = optionsEntity
81
81
 
82
- const entityConfig = Object.values(entity).reduce((a: any, n: any) => (
83
- a[n.name] = cleanModel({
84
- fields: n.fields,
85
- name: n.name,
86
- op: n.op,
87
- relations: n.relations,
88
- }, true), a), {})
82
+ // configDefinition's `def.entity` verbatim, NOT rebuilt here. This reduce
83
+ // was one of fourteen copies of that function's entityDefs loop, and when
84
+ // configDefinition started reconstructing a point's `parts` from apidef's
85
+ // segment vector (its ADR-003), only the copies that read `configDef` got
86
+ // it — this target's literal config emitted paths with no parts at all
87
+ // while its data config had them. One rule, one place.
88
+ const entityConfig = configDef.entity
89
89
 
90
90
  const config = {
91
91
  main: configDef.main,
@@ -4,6 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each,
6
6
  File, Copy, Folder, Fragment,
7
+ TEST_CONTROL_EXCLUDE
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -47,7 +48,7 @@ const Main = cmp(async function Main(props: any) {
47
48
  // token used throughout the templates (package/import statements).
48
49
  Copy({
49
50
  from: 'tm/' + target.name,
50
- exclude: [/src\//],
51
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
51
52
  replace: {
52
53
  ...props.ctx$.stdrep,
53
54
  KOTLINPACKAGE: kotlinpackage,
@@ -11,7 +11,8 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive, envName, envToken,
15
+ pointParts,
15
16
  } from '@voxgig/sdkgen'
16
17
 
17
18
 
@@ -84,10 +85,10 @@ const TestDirect = cmp(function TestDirect(props: any) {
84
85
  const listOp = entity.op?.list
85
86
 
86
87
  const loadPoint = loadOp?.points?.[0]
87
- const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
88
+ const loadPath = loadPoint ? normalizePathParams(pointParts(loadPoint), loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
88
89
  const allLoadParams = loadPoint?.args?.params || []
89
90
  const _pathPlaceholders = new Set<string>()
90
- for (const part of (loadPoint?.parts || [])) {
91
+ for (const part of pointParts(loadPoint)) {
91
92
  if (typeof part === 'string' && part.startsWith('{') && part.endsWith('}')) {
92
93
  _pathPlaceholders.add(part.slice(1, -1))
93
94
  }
@@ -104,7 +105,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
104
105
  _renamedPlaceholders.has(p.name) || _renamedPlaceholders.has(p.orig))
105
106
 
106
107
  const listPoint = listOp?.points?.[0]
107
- const listPath = listPoint ? normalizePathParams(listPoint.parts || [], listPoint?.args?.params || [], listPoint?.rename?.param) : ''
108
+ const listPath = listPoint ? normalizePathParams(pointParts(listPoint), listPoint?.args?.params || [], listPoint?.rename?.param) : ''
108
109
  const listParams = listPoint?.args?.params || []
109
110
 
110
111
  const loadQuery = loadPoint?.args?.query || []
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_kotlin'
@@ -20,6 +21,9 @@ const Test = cmp(function Test(props: any) {
20
21
 
21
22
  Folder({ name: 'test' }, () => {
22
23
 
24
+ // Write-once: a project's edited control file survives regeneration.
25
+ TestControl({ target, dir: 'test' })
26
+
23
27
  // The shared test infrastructure (RunnerSupport, FeatureHarness,
24
28
  // PipelineTest, FeatureTest, NetsimTest, PrimaryUtilityTest,
25
29
  // CustomUtilityTest, StructRunner/StructCorpusTest, ExistsTest and
@@ -6,6 +6,7 @@ import {
6
6
  isAuthActive,
7
7
  resolveAuthPrefix,
8
8
  targetFeatures,
9
+ configDefinition,
9
10
  } from '@voxgig/sdkgen'
10
11
 
11
12
  import {
@@ -15,7 +16,6 @@ import {
15
16
  } from '@voxgig/apidef'
16
17
 
17
18
  import {
18
- clean,
19
19
  leanString,
20
20
  } from './utility_lean'
21
21
 
@@ -30,6 +30,7 @@ const Config = cmp(async function Config(props: any) {
30
30
  const model: Model = ctx$.model
31
31
 
32
32
  const entity = getModelPath(model, `main.${KIT}.entity`)
33
+ const { def: configDef } = configDefinition(model, target.name)
33
34
  // Gated by the applicability tags, so this target never imports or
34
35
  // registers a feature it has no source for. One rule, one place:
35
36
  // helpers/applicability.
@@ -57,13 +58,13 @@ const Config = cmp(async function Config(props: any) {
57
58
  options.auth = { prefix: authPrefix }
58
59
  }
59
60
 
60
- const entityConfig = Object.values(entity || {}).reduce((a: any, n: any) => (
61
- a[n.name] = clean({
62
- fields: n.fields,
63
- name: n.name,
64
- op: n.op,
65
- relations: n.relations,
66
- }, true), a), {})
61
+ // configDefinition's `def.entity` verbatim, NOT rebuilt here. This reduce
62
+ // was one of fourteen copies of that function's entityDefs loop, and when
63
+ // configDefinition started reconstructing a point's `parts` from apidef's
64
+ // segment vector (its ADR-003), only the copies that read `configDef` got
65
+ // it — this target's literal config emitted paths with no parts at all
66
+ // while its data config had them. One rule, one place.
67
+ const entityConfig = configDef.entity
67
68
 
68
69
  const config = {
69
70
  main: { name: model.const.Name },
@@ -28,7 +28,6 @@ import {
28
28
 
29
29
 
30
30
  import {
31
- clean,
32
31
  formatLuaTable,
33
32
  luaLongString,
34
33
  } from './utility_lua'
@@ -160,12 +159,7 @@ ${serverBlock}${authBlock} headers = ${formatLuaTable(headers, 3)},
160
159
  Content(` },
161
160
  },
162
161
  entity = ${formatLuaTable(
163
- Object.values(entity).reduce((a: any, n: any) => (a[n.name] = clean({
164
- fields: n.fields,
165
- name: n.name,
166
- op: n.op,
167
- relations: n.relations,
168
- }, true), a), {}), 2)},
162
+ configDef.entity, 2)},
169
163
  }
170
164
  end
171
165
  `)
@@ -5,6 +5,7 @@ import {
5
5
  cmp, each, names, cmap,
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
7
  targetFeatures,
8
+ TEST_CONTROL_EXCLUDE
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -44,7 +45,7 @@ const Main = cmp(async function Main(props: any) {
44
45
  // Copy tm/lua files with replacements
45
46
  Copy({
46
47
  from: 'tm/' + target.name,
47
- exclude: [/src\//],
48
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
48
49
  replace: {
49
50
  ...props.ctx$.stdrep,
50
51
  }
@@ -11,9 +11,14 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive, envName, envToken,
15
+ serverVarEnv,
16
+ serverVariables,
17
+ pointParts,
15
18
  } from '@voxgig/sdkgen'
16
19
 
20
+ import { formatLuaValue } from './utility_lua'
21
+
17
22
 
18
23
  function normalizePathParams(
19
24
  parts: string[],
@@ -64,12 +69,25 @@ const TestDirect = cmp(function TestDirect(props: any) {
64
69
 
65
70
  const authActive = isAuthActive(model)
66
71
  const apikeyEnvEntry = authActive
67
- ? `\n ["${PROJECTNAME}_APIKEY"] = "NONE",`
72
+ ? `\n ["${PROJECTNAME}_APIKEY"] = "",`
68
73
  : ''
69
74
  const apikeyLiveField = authActive
70
75
  ? `\n apikey = env["${PROJECTNAME}_APIKEY"],`
71
76
  : ''
72
77
 
78
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
79
+ // impossible to construct without values: makeOptions raises rather than
80
+ // request a URL with a literal `{account_id}` in it. So the live suite
81
+ // takes them from the environment the same way it takes the apikey.
82
+ const svars = serverVariables(model)
83
+ const serverEnvEntry = svars
84
+ .map((v: any) => `\n ["${serverVarEnv(PROJECTNAME, v.name)}"] = ${formatLuaValue(v.dflt)},`).join('')
85
+ const serverLiveField = 0 === svars.length ? '' : `
86
+ server = {${svars
87
+ .map((v: any) => `
88
+ ["${v.name}"] = env["${serverVarEnv(PROJECTNAME, v.name)}"],`).join('')}
89
+ },`
90
+
73
91
  const opnames = Object.keys(entity.op || {})
74
92
  const hasLoad = opnames.includes('load')
75
93
  const hasList = opnames.includes('list')
@@ -82,7 +100,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
82
100
  const listOp = entity.op?.list
83
101
 
84
102
  const loadPoint = loadOp?.points?.[0]
85
- const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
103
+ const loadPath = loadPoint ? normalizePathParams(pointParts(loadPoint), loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
86
104
  const allLoadParams = loadPoint?.args?.params || []
87
105
  // Some upstream OpenAPI specs declare a parameter as `in: path` even when
88
106
  // that path has no `{name}` placeholder for it. Only path params that
@@ -90,7 +108,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
90
108
  // setup and URL-substitution asserts; otherwise the SDK silently drops
91
109
  // them and the URL-includes assert fails.
92
110
  const _pathPlaceholders = new Set<string>()
93
- for (const part of (loadPoint?.parts || [])) {
111
+ for (const part of pointParts(loadPoint)) {
94
112
  if (typeof part === 'string' && part.startsWith('{') && part.endsWith('}')) {
95
113
  _pathPlaceholders.add(part.slice(1, -1))
96
114
  }
@@ -107,7 +125,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
107
125
  _renamedPlaceholders.has(p.name) || _renamedPlaceholders.has(p.orig))
108
126
 
109
127
  const listPoint = listOp?.points?.[0]
110
- const listPath = listPoint ? normalizePathParams(listPoint.parts || [], listPoint?.args?.params || [], listPoint?.rename?.param) : ''
128
+ const listPath = listPoint ? normalizePathParams(pointParts(listPoint), listPoint?.args?.params || [], listPoint?.rename?.param) : ''
111
129
  const listParams = listPoint?.args?.params || []
112
130
 
113
131
  // Required query params with spec-provided examples — needed in live mode.
@@ -350,14 +368,21 @@ function ${entity.name}_direct_setup(mockres)
350
368
 
351
369
  local env = runner.env_override({
352
370
  ["${entidEnvVar}"] = {},
353
- ["${PROJECTNAME}_TEST_LIVE"] = "FALSE",${apikeyEnvEntry}
371
+ ["${PROJECTNAME}_TEST_LIVE"] = "FALSE",${apikeyEnvEntry}${serverEnvEntry}
354
372
  })
355
373
 
356
374
  local live = env["${PROJECTNAME}_TEST_LIVE"] == "TRUE"
357
375
 
358
376
  if live then
359
- local merged_opts = {${apikeyLiveField}
377
+ local merged_opts = {${apikeyLiveField}${serverLiveField}
360
378
  }
379
+ -- sdk-test-control.json's test.client.options goes UNDER the generated
380
+ -- fields: it adds to the live client, it does not redirect it.
381
+ for _k, _v in pairs(runner.live_client_options()) do
382
+ if merged_opts[_k] == nil then
383
+ merged_opts[_k] = _v
384
+ end
385
+ end
361
386
  local client = sdk.new(merged_opts)
362
387
  return {
363
388
  client = client,
@@ -24,9 +24,13 @@ import {
24
24
  buildIdNames,
25
25
  getMatchEntries,
26
26
  isAuthActive,
27
- entityDataIdField, envName, envToken
27
+ entityDataIdField, envName, envToken,
28
+ serverVarEnv,
29
+ serverVariables
28
30
  } from '@voxgig/sdkgen'
29
31
 
32
+ import { formatLuaValue } from './utility_lua'
33
+
30
34
 
31
35
  // See TestEntity_ts.ts for the GenCtx/OpGen contract.
32
36
  type GenCtx = {
@@ -62,12 +66,25 @@ const TestEntity = cmp(function TestEntity(props: any) {
62
66
 
63
67
  const authActive = isAuthActive(model)
64
68
  const apikeyEnvEntry = authActive
65
- ? `\n ["${PROJUPPER}_APIKEY"] = "NONE",`
69
+ ? `\n ["${PROJUPPER}_APIKEY"] = "",`
66
70
  : ''
67
71
  const apikeyLiveField = authActive
68
72
  ? `\n apikey = env["${PROJUPPER}_APIKEY"],`
69
73
  : ''
70
74
 
75
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
76
+ // impossible to construct without values: makeOptions raises rather than
77
+ // request a URL with a literal `{account_id}` in it. So the live suite
78
+ // takes them from the environment the same way it takes the apikey.
79
+ const svars = serverVariables(model)
80
+ const serverEnvEntry = svars
81
+ .map((v: any) => `\n ["${serverVarEnv(PROJUPPER, v.name)}"] = ${formatLuaValue(v.dflt)},`).join('')
82
+ const serverLiveField = 0 === svars.length ? '' : `
83
+ server = {${svars
84
+ .map((v: any) => `
85
+ ["${v.name}"] = env["${serverVarEnv(PROJUPPER, v.name)}"],`).join('')}
86
+ },`
87
+
71
88
  const idnames = buildIdNames(entity, basicflow)
72
89
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
73
90
 
@@ -241,7 +258,7 @@ end)
241
258
  local env = runner.env_override({
242
259
  ["${PROJUPPER}_TEST_${envToken(entity.name)}_ENTID"] = idmap,
243
260
  ["${PROJUPPER}_TEST_LIVE"] = "FALSE",
244
- ["${PROJUPPER}_TEST_EXPLAIN"] = "FALSE",${apikeyEnvEntry}
261
+ ["${PROJUPPER}_TEST_EXPLAIN"] = "FALSE",${apikeyEnvEntry}${serverEnvEntry}
245
262
  })
246
263
 
247
264
  local idmap_resolved = helpers.to_map(
@@ -262,7 +279,10 @@ end)
262
279
  Content(`
263
280
  if env["${PROJUPPER}_TEST_LIVE"] == "TRUE" then
264
281
  local merged_opts = vs.merge({
265
- {${apikeyLiveField}
282
+ -- FIRST, so the generated fields below win: sdk-test-control.json's
283
+ -- test.client.options adds to the live client, it does not redirect it.
284
+ runner.live_client_options(),
285
+ {${apikeyLiveField}${serverLiveField}
266
286
  },
267
287
  extra or {},
268
288
  })
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, File, Content, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, File, Content, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_lua'
@@ -17,6 +18,9 @@ const Test = cmp(function Test(props: any) {
17
18
 
18
19
  Folder({ name: 'test' }, () => {
19
20
 
21
+ // Write-once: a project's edited control file survives regeneration.
22
+ TestControl({ target, dir: 'test' })
23
+
20
24
  // Generate exists test
21
25
  File({ name: 'exists_test.' + target.ext }, () => {
22
26
  Content(`-- ${model.const.Name} SDK exists test
@@ -19,6 +19,7 @@ const Gitignore = cmp(async function Gitignore(_props: any) {
19
19
  a.out
20
20
  run_sdk_test
21
21
  run_struct_corpus
22
+ run_primary_corpus
22
23
 
23
24
  # dune (unused; the build is stock ocamlc via the Makefile)
24
25
  _build/
@@ -5,6 +5,7 @@ import {
5
5
  cmp, each, names,
6
6
  File, Content, Copy, Folder, Fragment,
7
7
  targetFeatures,
8
+ TEST_CONTROL_EXCLUDE
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -49,7 +50,7 @@ const Main = cmp(async function Main(props: any) {
49
50
  // Copy tm/perl files with replacements
50
51
  Copy({
51
52
  from: 'tm/' + target.name,
52
- exclude: [/src\//],
53
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
53
54
  replace: {
54
55
  ...props.ctx$.stdrep,
55
56
  }
@@ -11,9 +11,14 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive, envName, envToken,
15
+ serverVarEnv,
16
+ serverVariables,
17
+ pointParts,
15
18
  } from '@voxgig/sdkgen'
16
19
 
20
+ import { perlStringLiteral } from './utility_perl'
21
+
17
22
 
18
23
  function normalizePathParams(
19
24
  parts: string[],
@@ -74,12 +79,25 @@ const TestDirect = cmp(function TestDirect(props: any) {
74
79
 
75
80
  const authActive = isAuthActive(model)
76
81
  const apikeyEnvEntry = authActive
77
- ? `\n '${PROJECTNAME}_APIKEY' => 'NONE',`
82
+ ? `\n '${PROJECTNAME}_APIKEY' => '',`
78
83
  : ''
79
84
  const apikeyLiveField = authActive
80
85
  ? `\n 'apikey' => $env->{'${PROJECTNAME}_APIKEY'},`
81
86
  : ''
82
87
 
88
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
89
+ // impossible to construct without values: makeOptions raises rather than
90
+ // request a URL with a literal `{account_id}` in it. So the live suite
91
+ // takes them from the environment the same way it takes the apikey.
92
+ const svars = serverVariables(model)
93
+ const serverEnvEntry = svars
94
+ .map((v: any) => `\n '${serverVarEnv(PROJECTNAME, v.name)}' => ${perlStringLiteral(v.dflt)},`).join('')
95
+ const serverLiveField = 0 === svars.length ? '' : `
96
+ 'server' => {${svars
97
+ .map((v: any) => `
98
+ '${v.name}' => $env->{'${serverVarEnv(PROJECTNAME, v.name)}'},`).join('')}
99
+ },`
100
+
83
101
  const opnames = Object.keys(entity.op || {})
84
102
  const hasLoad = opnames.includes('load')
85
103
  const hasList = opnames.includes('list')
@@ -92,12 +110,12 @@ const TestDirect = cmp(function TestDirect(props: any) {
92
110
  const listOp = entity.op?.list
93
111
 
94
112
  const loadPoint = loadOp?.points?.[0]
95
- const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
113
+ const loadPath = loadPoint ? normalizePathParams(pointParts(loadPoint), loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
96
114
  const allLoadParams = loadPoint?.args?.params || []
97
115
  // Only path params that actually appear in the URL template drive direct-
98
116
  // test path-param setup and URL-substitution asserts (see TestDirect_rb).
99
117
  const _pathPlaceholders = new Set<string>()
100
- for (const part of (loadPoint?.parts || [])) {
118
+ for (const part of pointParts(loadPoint)) {
101
119
  if (typeof part === 'string' && part.startsWith('{') && part.endsWith('}')) {
102
120
  _pathPlaceholders.add(part.slice(1, -1))
103
121
  }
@@ -114,7 +132,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
114
132
  _renamedPlaceholders.has(p.name) || _renamedPlaceholders.has(p.orig))
115
133
 
116
134
  const listPoint = listOp?.points?.[0]
117
- const listPath = listPoint ? normalizePathParams(listPoint.parts || [], listPoint?.args?.params || [], listPoint?.rename?.param) : ''
135
+ const listPath = listPoint ? normalizePathParams(pointParts(listPoint), listPoint?.args?.params || [], listPoint?.rename?.param) : ''
118
136
  const listParams = listPoint?.args?.params || []
119
137
 
120
138
  // Required query params with spec-provided examples - needed in live mode.
@@ -377,13 +395,17 @@ sub ${entity.name}_direct_setup {
377
395
 
378
396
  my $env = ${N}TestRunner::env_override({
379
397
  '${entidEnvVar}' => {},
380
- '${PROJECTNAME}_TEST_LIVE' => 'FALSE',${apikeyEnvEntry}
398
+ '${PROJECTNAME}_TEST_LIVE' => 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
381
399
  });
382
400
 
383
401
  my $live = ((($env->{'${PROJECTNAME}_TEST_LIVE'}) || '') eq 'TRUE') ? 1 : 0;
384
402
 
385
403
  if ($live) {
386
- my $client = ${N}SDK->new({${apikeyLiveField}
404
+ # live_client_options() FIRST so the generated fields below win:
405
+ # sdk-test-control.json's test.client.options adds to the live client,
406
+ # it does not redirect it (a later key wins in a Perl hash literal).
407
+ my $client = ${N}SDK->new({
408
+ %{ ${N}TestRunner::live_client_options() },${apikeyLiveField}${serverLiveField}
387
409
  });
388
410
  return {
389
411
  'client' => $client,