@voxgig/sdkgen 0.39.1 → 0.40.2

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 (50) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/action/target.js +7 -1
  3. package/dist/action/target.js.map +1 -1
  4. package/dist/helpers/buildIdNames.d.ts +11 -0
  5. package/dist/helpers/buildIdNames.js +56 -0
  6. package/dist/helpers/buildIdNames.js.map +1 -0
  7. package/dist/helpers/collectDeps.d.ts +9 -0
  8. package/dist/helpers/collectDeps.js +50 -0
  9. package/dist/helpers/collectDeps.js.map +1 -0
  10. package/dist/helpers/getMatchEntries.d.ts +2 -0
  11. package/dist/helpers/getMatchEntries.js +13 -0
  12. package/dist/helpers/getMatchEntries.js.map +1 -0
  13. package/dist/sdkgen.d.ts +6 -2
  14. package/dist/sdkgen.js +17 -2
  15. package/dist/sdkgen.js.map +1 -1
  16. package/dist/tsconfig.tsbuildinfo +1 -1
  17. package/package.json +5 -5
  18. package/project/.sdk/model/target/lua.jsonic +1 -1
  19. package/project/.sdk/model/target/php.jsonic +2 -3
  20. package/project/.sdk/model/target/py.jsonic +1 -1
  21. package/project/.sdk/src/cmp/go/Entity_go.ts +15 -3
  22. package/project/.sdk/src/cmp/go/Main_go.ts +3 -2
  23. package/project/.sdk/src/cmp/go/Package_go.ts +8 -29
  24. package/project/.sdk/src/cmp/go/TestDirect_go.ts +14 -7
  25. package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -30
  26. package/project/.sdk/src/cmp/js/TestDirect_js.ts +6 -3
  27. package/project/.sdk/src/cmp/lua/Package_lua.ts +5 -27
  28. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +6 -3
  29. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +5 -17
  30. package/project/.sdk/src/cmp/php/Config_php.ts +21 -0
  31. package/project/.sdk/src/cmp/php/MainEntity_php.ts +11 -1
  32. package/project/.sdk/src/cmp/php/Package_php.ts +5 -31
  33. package/project/.sdk/src/cmp/php/TestDirect_php.ts +6 -3
  34. package/project/.sdk/src/cmp/php/TestEntity_php.ts +25 -29
  35. package/project/.sdk/src/cmp/py/Main_py.ts +5 -5
  36. package/project/.sdk/src/cmp/py/Package_py.ts +11 -28
  37. package/project/.sdk/src/cmp/py/TestDirect_py.ts +9 -6
  38. package/project/.sdk/src/cmp/py/TestEntity_py.ts +7 -19
  39. package/project/.sdk/src/cmp/rb/Package_rb.ts +8 -47
  40. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +6 -3
  41. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +5 -17
  42. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +6 -3
  43. package/project/.sdk/tm/go/core/helpers.go +10 -0
  44. package/project/.sdk/tm/lua/Makefile +1 -1
  45. package/project/.sdk/tm/php/Makefile +1 -1
  46. package/src/action/target.ts +7 -1
  47. package/src/helpers/buildIdNames.ts +70 -0
  48. package/src/helpers/collectDeps.ts +70 -0
  49. package/src/helpers/getMatchEntries.ts +14 -0
  50. package/src/sdkgen.ts +22 -1
@@ -3,14 +3,12 @@ import {
3
3
  Content,
4
4
  File,
5
5
  cmp,
6
- each,
6
+ collectDeps,
7
7
  } from '@voxgig/sdkgen'
8
8
 
9
9
 
10
- import {
11
- KIT,
10
+ import type {
12
11
  Model,
13
- getModelPath,
14
12
  } from '@voxgig/apidef'
15
13
 
16
14
 
@@ -20,12 +18,10 @@ const Package = cmp(async function Package(props: any) {
20
18
 
21
19
  const model: Model = ctx$.model
22
20
 
23
- const feature = getModelPath(model, `main.${KIT}.feature`)
24
-
25
21
  File({ name: 'pyproject.toml' }, () => {
26
22
  Content(`[build-system]
27
23
  requires = ["setuptools>=61.0"]
28
- build-backend = "setuptools.backends._legacy:_Backend"
24
+ build-backend = "setuptools.build_meta"
29
25
 
30
26
  [project]
31
27
  name = "${model.name}-sdk"
@@ -37,34 +33,21 @@ dependencies = [
37
33
  "requests>=2.33",
38
34
  `)
39
35
 
40
- // Collect dependencies from features
41
- each(feature, (f: any) => {
42
- const pyDeps = f.deps?.py
43
- if (pyDeps) {
44
- each(pyDeps, (dep: any) => {
45
- if (dep.active) {
46
- Content(` "${dep.key$}>=${dep.version}",
36
+ for (const d of collectDeps(model, target.name, target.deps)) {
37
+ const v = d.source === 'target' ? (d.version || '0.0') : d.version
38
+ Content(` "${d.name}>=${v}",
47
39
  `)
48
- }
49
- })
50
- }
51
- })
52
-
53
- // Add target-level deps
54
- const targetDeps = target.deps
55
- if (targetDeps) {
56
- each(targetDeps, (dep: any) => {
57
- if (dep.active !== false) {
58
- Content(` "${dep.key$}>=${dep.version || '0.0'}",
59
- `)
60
- }
61
- })
62
40
  }
63
41
 
64
42
  Content(`]
65
43
 
66
44
  [project.urls]
67
45
  Homepage = "https://github.com/voxgig/${model.name}-sdk"
46
+
47
+ # Explicit package list — setuptools auto-discovery refuses to pick when
48
+ # multiple top-level dirs (core/entity/feature/utility) are present.
49
+ [tool.setuptools.packages.find]
50
+ include = ["core*", "entity*", "feature*", "utility*"]
68
51
  `)
69
52
  })
70
53
  })
@@ -20,10 +20,13 @@ function normalizePathParams(
20
20
  return part.replace(/\{([^}]+)\}/g, (match: string, rawName: string) => {
21
21
  const snaked = snakify(rawName)
22
22
  const depluralized = depluralize(snaked)
23
+ // Prefer exact name match — orig matches can collide when one param's
24
+ // original name was renamed to another param's current name (e.g. badge
25
+ // load: param 'group_id' has orig 'id', and another param has name 'id').
23
26
  const param = params.find((p: any) =>
24
- p.orig === snaked || p.name === snaked ||
25
- p.orig === depluralized || p.name === depluralized
26
- )
27
+ p.name === snaked || p.name === depluralized) ||
28
+ params.find((p: any) =>
29
+ p.orig === snaked || p.orig === depluralized)
27
30
  if (param) return '{' + param.name + '}'
28
31
 
29
32
  if (rename) {
@@ -95,7 +98,7 @@ class Test${entity.Name}Direct:
95
98
 
96
99
  if (hasList && listPoint) {
97
100
  Content(` def test_should_direct_list_${entity.name}(self):
98
- setup = ${entity.name}_direct_setup([
101
+ setup = _${entity.name}_direct_setup([
99
102
  {"id": "direct01"},
100
103
  {"id": "direct02"},
101
104
  ])
@@ -147,7 +150,7 @@ class Test${entity.Name}Direct:
147
150
 
148
151
  if (hasLoad && loadPoint) {
149
152
  Content(` def test_should_direct_load_${entity.name}(self):
150
- setup = ${entity.name}_direct_setup({"id": "direct01"})
153
+ setup = _${entity.name}_direct_setup({"id": "direct01"})
151
154
  client = setup["client"]
152
155
 
153
156
  `)
@@ -190,7 +193,7 @@ class Test${entity.Name}Direct:
190
193
 
191
194
  Content(`
192
195
 
193
- def ${entity.name}_direct_setup(mockres):
196
+ def _${entity.name}_direct_setup(mockres):
194
197
  runner.load_env_local()
195
198
 
196
199
  calls = []
@@ -16,6 +16,8 @@ import {
16
16
  File,
17
17
  cmp,
18
18
  each,
19
+ buildIdNames,
20
+ getMatchEntries,
19
21
  } from '@voxgig/sdkgen'
20
22
 
21
23
 
@@ -45,15 +47,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
45
47
 
46
48
  const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
47
49
 
48
- const ancestors = (entity.relations?.ancestors || []).flat()
49
-
50
- // Build idmap names
51
- const idnames: string[] = []
52
- for (let i = 1; i <= 3; i++) idnames.push(`${entity.name}0${i}`)
53
- for (const anc of ancestors) {
54
- for (let i = 1; i <= 3; i++) idnames.push(`${anc}0${i}`)
55
- }
56
-
50
+ const idnames = buildIdNames(entity, basicflow)
57
51
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
58
52
 
59
53
  const allSteps = Object.values(basicflow.step) as any[]
@@ -91,7 +85,7 @@ class Test${entity.Name}Entity:
91
85
  assert ent is not None
92
86
 
93
87
  def test_should_run_basic_flow(self):
94
- setup = ${entity.name}_basic_setup(None)
88
+ setup = _${entity.name}_basic_setup(None)
95
89
  client = setup["client"]
96
90
 
97
91
  `)
@@ -101,7 +95,7 @@ class Test${entity.Name}Entity:
101
95
  if (!flowHasCreate) {
102
96
  Content(` # Bootstrap entity data from existing test data.
103
97
  ${entity.name}_ref01_data_raw = vs.items(helpers.to_map(
104
- vs.getprop(setup["data"], "existing.${entity.name}")))
98
+ vs.getpath(setup["data"], "existing.${entity.name}")))
105
99
  ${entity.name}_ref01_data = None
106
100
  if len(${entity.name}_ref01_data_raw) > 0:
107
101
  ${entity.name}_ref01_data = helpers.to_map(${entity.name}_ref01_data_raw[0][1])
@@ -120,7 +114,7 @@ class Test${entity.Name}Entity:
120
114
 
121
115
  Content(`
122
116
 
123
- def ${entity.name}_basic_setup(extra):
117
+ def _${entity.name}_basic_setup(extra):
124
118
  runner.load_env_local()
125
119
 
126
120
  entity_data_file = os.path.join(_TEST_DIR, "../../.sdk/test/entity/${entity.name}/${entity.Name}TestData.json")
@@ -193,12 +187,6 @@ def ${entity.name}_basic_setup(extra):
193
187
  })
194
188
 
195
189
 
196
- function getMatchEntries(step: any): [string, any][] {
197
- if (!step?.match) return []
198
- return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
199
- }
200
-
201
-
202
190
  const generateCreate: OpGen = (ctx, step, index) => {
203
191
  const { entity, flow } = ctx
204
192
  const ref = step.input?.ref ?? entity.name + '_ref01'
@@ -419,7 +407,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
419
407
  }
420
408
  if (!hasSrcData) {
421
409
  Content(` ${srcdatavar}_raw = vs.items(helpers.to_map(
422
- vs.getprop(setup["data"], "existing.${entity.name}")))
410
+ vs.getpath(setup["data"], "existing.${entity.name}")))
423
411
  ${srcdatavar} = None
424
412
  if len(${srcdatavar}_raw) > 0:
425
413
  ${srcdatavar} = helpers.to_map(${srcdatavar}_raw[0][1])
@@ -3,14 +3,12 @@ import {
3
3
  Content,
4
4
  File,
5
5
  cmp,
6
- each,
6
+ collectDeps,
7
7
  } from '@voxgig/sdkgen'
8
8
 
9
9
 
10
- import {
11
- KIT,
10
+ import type {
12
11
  Model,
13
- getModelPath,
14
12
  } from '@voxgig/apidef'
15
13
 
16
14
 
@@ -20,7 +18,8 @@ const Package = cmp(async function Package(props: any) {
20
18
 
21
19
  const model: Model = ctx$.model
22
20
 
23
- const feature = getModelPath(model, `main.${KIT}.feature`)
21
+ const versionOf = (d: { version: string; source: 'feature' | 'target' }) =>
22
+ d.source === 'target' ? (d.version || '0.0') : d.version
24
23
 
25
24
  // Generate Gemfile
26
25
  File({ name: 'Gemfile' }, () => {
@@ -30,28 +29,9 @@ gemspec
30
29
 
31
30
  `)
32
31
 
33
- // Collect dependencies from features
34
- each(feature, (f: any) => {
35
- const rbDeps = f.deps?.rb
36
- if (rbDeps) {
37
- each(rbDeps, (dep: any) => {
38
- if (dep.active) {
39
- Content(`gem "${dep.key$}", "~> ${dep.version}"
40
- `)
41
- }
42
- })
43
- }
44
- })
45
-
46
- // Add target-level deps
47
- const targetDeps = target.deps
48
- if (targetDeps) {
49
- each(targetDeps, (dep: any) => {
50
- if (dep.active !== false) {
51
- Content(`gem "${dep.key$}", "~> ${dep.version || '0.0'}"
32
+ for (const d of collectDeps(model, target.name, target.deps)) {
33
+ Content(`gem "${d.name}", "~> ${versionOf(d)}"
52
34
  `)
53
- }
54
- })
55
35
  }
56
36
  })
57
37
 
@@ -73,28 +53,9 @@ gemspec
73
53
  spec.add_dependency "json"
74
54
  `)
75
55
 
76
- // Collect dependencies from features
77
- each(feature, (f: any) => {
78
- const rbDeps = f.deps?.rb
79
- if (rbDeps) {
80
- each(rbDeps, (dep: any) => {
81
- if (dep.active) {
82
- Content(` spec.add_dependency "${dep.key$}", "~> ${dep.version}"
83
- `)
84
- }
85
- })
86
- }
87
- })
88
-
89
- // Add target-level deps
90
- const targetDeps = target.deps
91
- if (targetDeps) {
92
- each(targetDeps, (dep: any) => {
93
- if (dep.active !== false) {
94
- Content(` spec.add_dependency "${dep.key$}", "~> ${dep.version || '0.0'}"
56
+ for (const d of collectDeps(model, target.name, target.deps)) {
57
+ Content(` spec.add_dependency "${d.name}", "~> ${versionOf(d)}"
95
58
  `)
96
- }
97
- })
98
59
  }
99
60
 
100
61
  Content(`
@@ -20,10 +20,13 @@ function normalizePathParams(
20
20
  return part.replace(/\{([^}]+)\}/g, (match: string, rawName: string) => {
21
21
  const snaked = snakify(rawName)
22
22
  const depluralized = depluralize(snaked)
23
+ // Prefer exact name match — orig matches can collide when one param's
24
+ // original name was renamed to another param's current name (e.g. badge
25
+ // load: param 'group_id' has orig 'id', and another param has name 'id').
23
26
  const param = params.find((p: any) =>
24
- p.orig === snaked || p.name === snaked ||
25
- p.orig === depluralized || p.name === depluralized
26
- )
27
+ p.name === snaked || p.name === depluralized) ||
28
+ params.find((p: any) =>
29
+ p.orig === snaked || p.orig === depluralized)
27
30
  if (param) return '{' + param.name + '}'
28
31
 
29
32
  if (rename) {
@@ -16,6 +16,8 @@ import {
16
16
  File,
17
17
  cmp,
18
18
  each,
19
+ buildIdNames,
20
+ getMatchEntries,
19
21
  } from '@voxgig/sdkgen'
20
22
 
21
23
 
@@ -45,15 +47,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
45
47
 
46
48
  const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
47
49
 
48
- const ancestors = (entity.relations?.ancestors || []).flat()
49
-
50
- // Build idmap names
51
- const idnames: string[] = []
52
- for (let i = 1; i <= 3; i++) idnames.push(`${entity.name}0${i}`)
53
- for (const anc of ancestors) {
54
- for (let i = 1; i <= 3; i++) idnames.push(`${anc}0${i}`)
55
- }
56
-
50
+ const idnames = buildIdNames(entity, basicflow)
57
51
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
58
52
 
59
53
  const allSteps = Object.values(basicflow.step) as any[]
@@ -92,7 +86,7 @@ class ${entity.Name}EntityTest < Minitest::Test
92
86
  if (!flowHasCreate) {
93
87
  Content(` # Bootstrap entity data from existing test data.
94
88
  ${entity.name}_ref01_data_raw = Vs.items(Helpers.to_map(
95
- Vs.getprop(setup[:data], "existing.${entity.name}")))
89
+ Vs.getpath(setup[:data], "existing.${entity.name}")))
96
90
  ${entity.name}_ref01_data = nil
97
91
  if ${entity.name}_ref01_data_raw.length > 0
98
92
  ${entity.name}_ref01_data = Helpers.to_map(${entity.name}_ref01_data_raw[0][1])
@@ -191,12 +185,6 @@ end
191
185
  })
192
186
 
193
187
 
194
- function getMatchEntries(step: any): [string, any][] {
195
- if (!step?.match) return []
196
- return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
197
- }
198
-
199
-
200
188
  const generateCreate: OpGen = (ctx, step, index) => {
201
189
  const { entity, flow } = ctx
202
190
  const ref = step.input?.ref ?? entity.name + '_ref01'
@@ -417,7 +405,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
417
405
  }
418
406
  if (!hasSrcData) {
419
407
  Content(` ${srcdatavar}_raw = Vs.items(Helpers.to_map(
420
- Vs.getprop(setup[:data], "existing.${entity.name}")))
408
+ Vs.getpath(setup[:data], "existing.${entity.name}")))
421
409
  ${srcdatavar} = nil
422
410
  if ${srcdatavar}_raw.length > 0
423
411
  ${srcdatavar} = Helpers.to_map(${srcdatavar}_raw[0][1])
@@ -302,10 +302,13 @@ function normalizePathParams(
302
302
  return part.replace(/\{([^}]+)\}/g, (match: string, rawName: string) => {
303
303
  const snaked = snakify(rawName)
304
304
  const depluralized = depluralize(snaked)
305
+ // Prefer exact name match — orig matches can collide when one param's
306
+ // original name was renamed to another param's current name (e.g. badge
307
+ // load: param 'group_id' has orig 'id', and another param has name 'id').
305
308
  const param = params.find((p: any) =>
306
- p.orig === snaked || p.name === snaked ||
307
- p.orig === depluralized || p.name === depluralized
308
- )
309
+ p.name === snaked || p.name === depluralized) ||
310
+ params.find((p: any) =>
311
+ p.orig === snaked || p.orig === depluralized)
309
312
  if (param) return '{' + param.name + '}'
310
313
 
311
314
  // Reverse-lookup through rename mapping: if rawName is a renamed value
@@ -1,5 +1,15 @@
1
1
  package core
2
2
 
3
+ import "fmt"
4
+
5
+ // UnsupportedOp is returned by entity stub methods for operations the
6
+ // underlying API spec doesn't define. The static ProjectNameEntity interface
7
+ // requires every CRUD method on every entity, so absent ops must still be
8
+ // callable — they error at runtime instead of failing to compile.
9
+ func UnsupportedOp(opname, entityname string) (any, error) {
10
+ return nil, fmt.Errorf("operation '%s' not supported by entity '%s'", opname, entityname)
11
+ }
12
+
3
13
  func ToMapAny(v any) map[string]any {
4
14
  if v == nil {
5
15
  return nil
@@ -1,7 +1,7 @@
1
1
  .PHONY: test build clean
2
2
 
3
3
  test:
4
- busted test/
4
+ busted -p _test test/
5
5
 
6
6
  build:
7
7
  @echo "No build step for Lua"
@@ -1,7 +1,7 @@
1
1
  .PHONY: test build clean
2
2
 
3
3
  test:
4
- ./vendor/bin/phpunit
4
+ ./vendor/bin/phpunit test
5
5
 
6
6
  build:
7
7
  @echo "No build step for PHP"
@@ -91,7 +91,13 @@ async function target_add(targets: string[], actx: ActionContext): Promise<Actio
91
91
 
92
92
  showChanges(opts.log, 'target-result', jres)
93
93
 
94
- const features = Object.keys(actx.model.main[KIT].feature)
94
+ // The `test` feature is required by every generated target (SDK.test()
95
+ // depends on it), so ensure it is added even if the model does not yet
96
+ // declare it.
97
+ const features = Array.from(new Set([
98
+ 'test',
99
+ ...Object.keys(actx.model.main[KIT].feature),
100
+ ]))
95
101
  await feature_add(features, actx)
96
102
 
97
103
  opts.log.info({
@@ -0,0 +1,70 @@
1
+ // Build the list of placeholder ID names that the test setup populates into
2
+ // `setup.idmap`. The set is the union of:
3
+ // - the entity's own ids (`<entity>01..03`)
4
+ // - every ancestor entity's ids (`<anc>01..03`)
5
+ // - every literal string value referenced by `step.match` across the flow
6
+ // (e.g. path-parameter aliases like `year01` for `/{year}/domain` — apidef
7
+ // doesn't always populate `relations.ancestors` when the parent in the
8
+ // path is a bare parameter rather than an entity, so we have to mine the
9
+ // flow steps directly to avoid KeyError-ing the idmap from generated test
10
+ // code)
11
+ //
12
+ // Identical helper was previously inlined in TestEntity_{go,py,lua,rb,php}.ts.
13
+
14
+ const COUNT = 3 // 3 ids per name: <name>01, <name>02, <name>03
15
+
16
+ type FlowLike = {
17
+ step?: Record<string, any> | any[]
18
+ }
19
+
20
+ type EntityLike = {
21
+ name: string
22
+ relations?: {
23
+ ancestors?: any
24
+ }
25
+ }
26
+
27
+ function buildIdNames(entity: EntityLike, flow: FlowLike): string[] {
28
+ const idnames: string[] = []
29
+ const seen = new Set<string>()
30
+ const push = (n: string) => {
31
+ if (!seen.has(n)) {
32
+ seen.add(n)
33
+ idnames.push(n)
34
+ }
35
+ }
36
+
37
+ for (let i = 1; i <= COUNT; i++) push(`${entity.name}0${i}`)
38
+
39
+ const ancestors: string[] = (entity.relations?.ancestors || []).flat()
40
+ for (const anc of ancestors) {
41
+ for (let i = 1; i <= COUNT; i++) push(`${anc}0${i}`)
42
+ }
43
+
44
+ const steps = Array.isArray(flow?.step)
45
+ ? flow.step
46
+ : Object.values(flow?.step || {})
47
+ for (const step of steps) {
48
+ if (step?.match) {
49
+ for (const v of Object.values(step.match)) {
50
+ if (typeof v === 'string' && v && !v.endsWith('$')) push(v)
51
+ }
52
+ }
53
+ // step.data values can also be aliased via setup (e.g. update step.data
54
+ // = {data_type_id: 'data_type01'} → setup adds idmap[data_type_id] =
55
+ // idmap[data_type01]). The right-hand side `data_type01` must be in the
56
+ // idmap or the alias resolves to undefined.
57
+ if (step?.data) {
58
+ for (const v of Object.values(step.data)) {
59
+ if (typeof v === 'string' && v && !v.endsWith('$')) push(v)
60
+ }
61
+ }
62
+ }
63
+
64
+ return idnames
65
+ }
66
+
67
+
68
+ export {
69
+ buildIdNames,
70
+ }
@@ -0,0 +1,70 @@
1
+ // Collect target-language dependencies from features and from the target's
2
+ // own `deps` block, applying the active-flag semantics that every Package_*.ts
3
+ // template was hand-rolling identically:
4
+ //
5
+ // - feature deps : included when `dep.active === true` (default off)
6
+ // - target deps : included when `dep.active !== false` (default on)
7
+ //
8
+ // The two sources are kept distinct via the `source` field so callers can
9
+ // apply their own version defaults / formatting (e.g. go uses `v0.0.0`,
10
+ // python `0.0`). The original dep object is exposed as `raw` for callers that
11
+ // need extra fields like `dep.replace` (go module replace directives) or
12
+ // `dep.kind` (prod/dev/peer).
13
+
14
+ import { each } from 'jostraca'
15
+ import { KIT, getModelPath } from '@voxgig/apidef'
16
+
17
+ type DepEntry = {
18
+ name: string
19
+ version: string
20
+ source: 'feature' | 'target'
21
+ raw: any
22
+ }
23
+
24
+ function collectDeps(
25
+ model: any,
26
+ targetName: string,
27
+ targetDeps: any,
28
+ ): DepEntry[] {
29
+ const out: DepEntry[] = []
30
+ const feature = getModelPath(model, `main.${KIT}.feature`)
31
+
32
+ each(feature, (f: any) => {
33
+ const langDeps = f?.deps?.[targetName]
34
+ if (!langDeps) return
35
+ each(langDeps, (dep: any) => {
36
+ if (dep?.active) {
37
+ out.push({
38
+ name: dep.key$,
39
+ version: dep.version,
40
+ source: 'feature',
41
+ raw: dep,
42
+ })
43
+ }
44
+ })
45
+ })
46
+
47
+ if (targetDeps) {
48
+ each(targetDeps, (dep: any) => {
49
+ if (dep?.active !== false) {
50
+ out.push({
51
+ name: dep.key$,
52
+ version: dep.version,
53
+ source: 'target',
54
+ raw: dep,
55
+ })
56
+ }
57
+ })
58
+ }
59
+
60
+ return out
61
+ }
62
+
63
+
64
+ export type {
65
+ DepEntry,
66
+ }
67
+
68
+ export {
69
+ collectDeps,
70
+ }
@@ -0,0 +1,14 @@
1
+ // Return the user-facing entries of a flow step's `match` object. Keys ending
2
+ // in `$` are jostraca/aontu metadata sentinels and are skipped.
3
+ //
4
+ // Identical helper was previously inlined in TestEntity_*.ts and TestDirect_*.ts.
5
+
6
+ function getMatchEntries(step: any): [string, any][] {
7
+ if (!step?.match) return []
8
+ return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
9
+ }
10
+
11
+
12
+ export {
13
+ getMatchEntries,
14
+ }
package/src/sdkgen.ts CHANGED
@@ -38,6 +38,11 @@ import { ReadmeExplanation } from './cmp/ReadmeExplanation'
38
38
  import { ReadmeRef } from './cmp/ReadmeRef'
39
39
  import { FeatureHook } from './cmp/FeatureHook'
40
40
 
41
+ import { buildIdNames } from './helpers/buildIdNames'
42
+ import { getMatchEntries } from './helpers/getMatchEntries'
43
+ import { collectDeps } from './helpers/collectDeps'
44
+ import type { DepEntry } from './helpers/collectDeps'
45
+
41
46
 
42
47
  import {
43
48
  action_target,
@@ -90,7 +95,7 @@ const ACTION_MAP: any = {
90
95
  const dlog = getdlog('sdkgen', __filename)
91
96
 
92
97
 
93
- const aontu = new Aontu()
98
+ let aontu: any = null
94
99
 
95
100
 
96
101
  function SdkGen(opts: SdkGenOptions) {
@@ -195,6 +200,17 @@ function SdkGen(opts: SdkGenOptions) {
195
200
  function resolveModel() {
196
201
  const path = './model/sdk.jsonic'
197
202
  const errs: any[] = []
203
+
204
+ if (null == aontu) {
205
+ aontu = new Aontu({
206
+ preload: {
207
+ folders: ['./model'],
208
+ ext: ['.jsonic', '.json'],
209
+ recursive: true,
210
+ }
211
+ })
212
+ }
213
+
198
214
  const aopts = { path, errs }
199
215
  const src = fs.readFileSync(path, 'utf8')
200
216
 
@@ -319,6 +335,7 @@ function clear(path: string) {
319
335
 
320
336
  export type {
321
337
  SdkGenOptions,
338
+ DepEntry,
322
339
  }
323
340
 
324
341
 
@@ -378,4 +395,8 @@ export {
378
395
  SdkGen,
379
396
 
380
397
  requirePath,
398
+
399
+ buildIdNames,
400
+ getMatchEntries,
401
+ collectDeps,
381
402
  }