@voxgig/sdkgen 1.3.2 → 1.3.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 (97) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/ReadmeErrors.js +15 -15
  3. package/dist/cmp/ReadmeErrors.js.map +1 -1
  4. package/dist/cmp/ReadmeExplanation.js +37 -35
  5. package/dist/cmp/ReadmeExplanation.js.map +1 -1
  6. package/dist/cmp/ReadmeTop.js +24 -2
  7. package/dist/cmp/ReadmeTop.js.map +1 -1
  8. package/dist/helpers/naming.js +29 -2
  9. package/dist/helpers/naming.js.map +1 -1
  10. package/dist/helpers/opShape.d.ts +6 -1
  11. package/dist/helpers/opShape.js +78 -0
  12. package/dist/helpers/opShape.js.map +1 -1
  13. package/dist/sdkgen.d.ts +2 -2
  14. package/dist/sdkgen.js +3 -1
  15. package/dist/sdkgen.js.map +1 -1
  16. package/package.json +1 -1
  17. package/project/.sdk/src/cmp/go/EntityOperation_go.ts +8 -1
  18. package/project/.sdk/src/cmp/go/Entity_go.ts +20 -3
  19. package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
  20. package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +5 -5
  21. package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +23 -12
  22. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +27 -17
  23. package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +5 -5
  24. package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +5 -6
  25. package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +11 -11
  26. package/project/.sdk/src/cmp/go/fragment/EntityCreateOp.fragment.go +2 -2
  27. package/project/.sdk/src/cmp/go/fragment/EntityListOp.fragment.go +2 -2
  28. package/project/.sdk/src/cmp/go/fragment/EntityLoadOp.fragment.go +2 -2
  29. package/project/.sdk/src/cmp/go/fragment/EntityRemoveOp.fragment.go +2 -2
  30. package/project/.sdk/src/cmp/go/fragment/EntityUpdateOp.fragment.go +2 -2
  31. package/project/.sdk/src/cmp/js/Entity_js.ts +18 -2
  32. package/project/.sdk/src/cmp/js/MainEntity_js.ts +12 -2
  33. package/project/.sdk/src/cmp/js/Main_js.ts +5 -2
  34. package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +5 -5
  35. package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +37 -26
  36. package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +5 -5
  37. package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +5 -6
  38. package/project/.sdk/src/cmp/js/fragment/Entity.fragment.js +3 -3
  39. package/project/.sdk/src/cmp/lua/EntityOperation_lua.ts +6 -1
  40. package/project/.sdk/src/cmp/lua/Entity_lua.ts +20 -2
  41. package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +8 -5
  42. package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +14 -7
  43. package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +9 -6
  44. package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +5 -2
  45. package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +8 -5
  46. package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +5 -6
  47. package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +13 -13
  48. package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +1 -1
  49. package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +1 -1
  50. package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +1 -1
  51. package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +1 -1
  52. package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +1 -1
  53. package/project/.sdk/src/cmp/php/Entity_php.ts +17 -1
  54. package/project/.sdk/src/cmp/php/MainEntity_php.ts +12 -3
  55. package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +15 -8
  56. package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +5 -6
  57. package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +2 -2
  58. package/project/.sdk/src/cmp/py/Entity_py.ts +18 -1
  59. package/project/.sdk/src/cmp/py/MainEntity_py.ts +14 -4
  60. package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
  61. package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +8 -5
  62. package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +18 -8
  63. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +10 -7
  64. package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +7 -4
  65. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +9 -6
  66. package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +6 -7
  67. package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
  68. package/project/.sdk/src/cmp/rb/Entity_rb.ts +17 -1
  69. package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +11 -2
  70. package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +8 -5
  71. package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +20 -8
  72. package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +9 -6
  73. package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +5 -2
  74. package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +8 -5
  75. package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +10 -8
  76. package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +2 -2
  77. package/project/.sdk/src/cmp/ts/Entity_ts.ts +18 -2
  78. package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +12 -2
  79. package/project/.sdk/src/cmp/ts/Main_ts.ts +5 -2
  80. package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +5 -5
  81. package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +36 -26
  82. package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +4 -5
  83. package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +5 -6
  84. package/project/.sdk/src/cmp/ts/fragment/Entity.fragment.ts +4 -4
  85. package/project/.sdk/tm/go/utility/prepare_auth.go +6 -1
  86. package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +3 -1
  87. package/project/.sdk/tm/lua/utility/prepare_auth.lua +6 -1
  88. package/project/.sdk/tm/php/utility/PrepareAuth.php +3 -1
  89. package/project/.sdk/tm/py/utility/prepare_auth.py +4 -1
  90. package/project/.sdk/tm/rb/utility/prepare_auth.rb +3 -1
  91. package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +5 -1
  92. package/src/cmp/ReadmeErrors.ts +16 -17
  93. package/src/cmp/ReadmeExplanation.ts +37 -35
  94. package/src/cmp/ReadmeTop.ts +25 -2
  95. package/src/helpers/naming.ts +32 -2
  96. package/src/helpers/opShape.ts +89 -0
  97. package/src/sdkgen.ts +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/sdkgen",
3
- "version": "1.3.2",
3
+ "version": "1.3.5",
4
4
  "main": "dist/sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/sdkgen.d.ts",
@@ -10,7 +10,7 @@ import { formatGoMap } from './utility_go'
10
10
 
11
11
  const EntityOperation = cmp(function Operation(props: any) {
12
12
  const { model } = props.ctx$
13
- const { ff, opname, entity, entrep, gomodule } = props
13
+ const { ff, opname, entity, entrep, gomodule, cls } = props
14
14
 
15
15
  let { indent } = props
16
16
 
@@ -34,6 +34,13 @@ const EntityOperation = cmp(function Operation(props: any) {
34
34
  ProjectName: model.const.Name,
35
35
  EntityName: entity.Name,
36
36
  entityname: entity.name,
37
+
38
+ // Class receiver tokens are decoupled from the EntityName data-type token
39
+ // in the Entity<Op>Op.fragment.go files (receiver -> cls; the per-op
40
+ // EntityName<Op>Match/Data data types stay derived from EntityName).
41
+ EntyClass: cls,
42
+ NewEntyClass: 'New' + cls,
43
+
37
44
  '#Feature-Hook': ({ name, indent }: any) =>
38
45
  Content({ indent }, `
39
46
  u.FeatureHook(ctx, "${name}")
@@ -3,9 +3,15 @@ import * as Path from 'node:path'
3
3
 
4
4
  import {
5
5
  cmp, each, camelify, names,
6
- File, Content, Folder, Fragment, Line, FeatureHook, Slot
6
+ File, Content, Folder, Fragment, Line, FeatureHook, Slot,
7
+ entityClassName,
7
8
  } from '@voxgig/sdkgen'
8
9
 
10
+ import {
11
+ KIT,
12
+ getModelPath
13
+ } from '@voxgig/apidef'
14
+
9
15
  import { EntityOperation } from './EntityOperation_go'
10
16
 
11
17
 
@@ -17,6 +23,12 @@ const Entity = cmp(function Entity(props: any) {
17
23
  // Go module path == repo path on GitHub (org from model.origin).
18
24
  const gomodule = `github.com/${model.origin || 'voxgig-sdk'}/${model.name}-sdk/go`
19
25
 
26
+ // Collision-free entity CLASS name (see entityClassName): normally
27
+ // `<Name>Entity`, but disambiguated (e.g. `<Name>EntityClient`) when it would
28
+ // clash with another entity's data-type name. The DATA type stays `<Name>`.
29
+ const entityColl = getModelPath(model, `main.${KIT}.entity`)
30
+ const cls = entityClassName(entity, entityColl)
31
+
20
32
  const entrep = {
21
33
  ...stdrep,
22
34
  }
@@ -43,13 +55,13 @@ const Entity = cmp(function Entity(props: any) {
43
55
  !opnames.includes(opname) ?
44
56
  ({ indent }: any) => {
45
57
  const Method = camelify(opname)
46
- Content({ indent }, `func (e *${entity.Name}Entity) ${Method}(_ map[string]any, _ map[string]any) (any, error) {
58
+ Content({ indent }, `func (e *${cls}) ${Method}(_ map[string]any, _ map[string]any) (any, error) {
47
59
  return core.UnsupportedOp("${opname}", e.name)
48
60
  }
49
61
  `)
50
62
  } :
51
63
  ({ indent }: any) => {
52
- EntityOperation({ ff, opname, indent, entity, entrep, gomodule })
64
+ EntityOperation({ ff, opname, indent, entity, entrep, gomodule, cls })
53
65
  }, a), {}))
54
66
 
55
67
  Fragment({
@@ -62,6 +74,11 @@ const Entity = cmp(function Entity(props: any) {
62
74
  EntityName: entity.Name,
63
75
  entityname: entity.name,
64
76
 
77
+ // Class/constructor tokens are decoupled from the EntityName data-type
78
+ // token in Entity.fragment.go so the class can be renamed independently.
79
+ EntyClass: cls,
80
+ NewEntyClass: 'New' + cls,
81
+
65
82
  '#Entity-Hook': ({ name, indent }: any) =>
66
83
  Content({ indent }, `utility.FeatureHook(ctx, "${name}")`),
67
84
 
@@ -4,6 +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,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -187,7 +188,7 @@ func init() {
187
188
  // Register entity constructors
188
189
  each(entity, (ent: any) => {
189
190
  Content(` core.New${ent.Name}EntityFunc = func(client *core.${model.const.Name}SDK, entopts map[string]any) core.${model.const.Name}Entity {
190
- return entity.New${ent.Name}Entity(client, entopts)
191
+ return entity.New${entityClassName(ent, entity)}(client, entopts)
191
192
  }
192
193
  `)
193
194
  })
@@ -143,10 +143,10 @@ fmt.Println(${eVar}s) // the array of records
143
143
 
144
144
  if (opnames.includes('create')) {
145
145
  // Members come from the SAME shape that generates the op's request
146
- // data: every required member must appear — including a required id
147
- // (the /* type */ placeholders also mark the block as an illustration
148
- // for the doc gates); an all-optional create renders an empty still
149
- // valid — literal, and the compiled example is self-consuming.
146
+ // data (every required member appears, including a required id), each
147
+ // with a type-correct example VALUE via exampleValue a
148
+ // `"name": /* type */` comment is not a value and yields uncompilable
149
+ // Go, so the example must carry a real literal.
150
150
  const createItems = opRequestShape(entity, 'create').items
151
151
  .filter((it: any) => !it.optional)
152
152
  Content(`#### Example: Create
@@ -155,7 +155,7 @@ fmt.Println(${eVar}s) // the array of records
155
155
  result, err := client.${entity.Name}(nil).Create(map[string]any{
156
156
  `)
157
157
  createItems.map((it: any) => {
158
- Content(` "${it.name}": /* ${canonToType(it.type, target.name)} */,
158
+ Content(` "${it.name}": ${exampleValue(entity, entity.op && entity.op.create, it.name, 'example_' + it.name)},
159
159
  `)
160
160
  })
161
161
  Content(`}, nil)
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, entityPrimaryOp, opRequestShape } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, pickExampleEntity, opRequestShape } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -32,16 +32,16 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
32
32
  const gomodule = `github.com/${model.origin || 'voxgig-sdk'}/${model.name}-sdk/go`
33
33
 
34
34
  const entity = getModelPath(model, `main.${KIT}.entity`)
35
- const exampleEntity = Object.values(entity || {}).find((e: any) => e && e.active !== false) as any
35
+ // Pick an entity with a real op (prefer a read op) never fabricate a
36
+ // `Load` on an op-less entity like Cloudsmith's `Abort`. primaryOp is null
37
+ // only when NO entity exposes any op (a direct()-only SDK).
38
+ const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
36
39
  const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
37
40
  // camelCase Go identifier (never snake_case or flattened lowercase,
38
41
  // never a Go keyword).
39
42
  const eLower = exampleEntity ? goVarName(exampleEntity.name) : 'entity'
40
43
  // Model-driven id key: null when the entity has no id-like field.
41
44
  const idF = exampleEntity ? entityIdField(exampleEntity) : null
42
- // Drive the test-mode example off the entity's PRIMARY op — never a hardcoded
43
- // `Load` a create-only entity lacks.
44
- const primaryOp = exampleEntity ? (entityPrimaryOp(exampleEntity) || 'load') : 'load'
45
45
  const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
46
46
  let testArg = 'nil'
47
47
  if (exampleEntity && isMatchOp) {
@@ -54,6 +54,23 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
54
54
  testArg = `map[string]any{${chosen.map((it: any) => `"${it.name}": ${goLit(it.type)}`).join(', ')}}`
55
55
  }
56
56
 
57
+ // The op-driven test-mode block, shown only when the SDK has an entity op.
58
+ // A direct()-only SDK (no ops anywhere) shows a Direct() call instead — never
59
+ // a fabricated method (`cap(primaryOp)` would also fail on a null op).
60
+ const testModeExample = primaryOp
61
+ ? `${eLower}, err := client.${eName}(nil).${cap(primaryOp)}(
62
+ ${testArg}, nil,
63
+ )
64
+ if err != nil {
65
+ panic(err)
66
+ }
67
+ fmt.Println(${eLower}) // the returned mock data`
68
+ : `result, err := client.Direct(map[string]any{"path": "/api/resource", "method": "GET"})
69
+ if err != nil {
70
+ panic(err)
71
+ }
72
+ fmt.Println(result)`
73
+
57
74
  const apikeyEnvLine = isAuthActive(model)
58
75
  ? `\n${envName(model)}_APIKEY=<your-key>`
59
76
  : ''
@@ -102,13 +119,7 @@ Create a mock client for unit testing \u2014 no server required:
102
119
  \`\`\`go
103
120
  client := sdk.Test()
104
121
 
105
- ${eLower}, err := client.${eName}(nil).${cap(primaryOp)}(
106
- ${testArg}, nil,
107
- )
108
- if err != nil {
109
- panic(err)
110
- }
111
- fmt.Println(${eLower}) // the returned mock data
122
+ ${testModeExample}
112
123
  \`\`\`
113
124
 
114
125
  ### Use a custom fetch function
@@ -50,23 +50,41 @@ const ReadmeModel = cmp(function ReadmeModel(props: any) {
50
50
  ? '| `"apikey"` | `string` | API key for authentication. |\n'
51
51
  : ''
52
52
 
53
- // First published entity name, for the Result shape illustration.
54
- const firstEntityName = (entityList[0] as any)?.Name || 'Entity'
53
+ // Illustrate the Result shape with the first entity that ACTUALLY exposes an
54
+ // op never fabricate a `Load` on an op-less first entity (e.g. Cloudsmith's
55
+ // `Abort`). firstPrimaryOp is null only when NO active entity has any op (a
56
+ // direct()-only SDK), in which case the call illustration is omitted.
57
+ const firstWithOp = entityList.find((e: any) => entityPrimaryOp(e) != null)
58
+ const firstPrimaryOp = firstWithOp ? entityPrimaryOp(firstWithOp) : null
59
+ const firstEntityName = firstWithOp ? ((firstWithOp as any).Name || 'Entity') : 'Entity'
55
60
  // camelCase Go identifier (never snake_case or flattened lowercase,
56
61
  // never a Go keyword).
57
- const firstEntityVar = goVarName((entityList[0] as any)?.name || 'entity')
62
+ const firstEntityVar = goVarName((firstWithOp as any)?.name || 'entity')
58
63
  // Model-driven id key: null when the example entity has no id-like field, so
59
64
  // the Result-shape illustration passes a nil match.
60
- const firstIdF = entityIdField(entityList[0] || {})
61
- // The example entity's PRIMARY op — an op it actually exposes (never a
62
- // hardcoded `Load` a create-only entity lacks).
63
- const firstPrimaryOp = entityList[0] ? (entityPrimaryOp(entityList[0]) || 'load') : 'load'
64
- const firstPrimaryMethod = firstPrimaryOp.charAt(0).toUpperCase() + firstPrimaryOp.slice(1)
65
+ const firstIdF = firstWithOp ? entityIdField(firstWithOp) : null
66
+ const firstPrimaryMethod = firstPrimaryOp
67
+ ? firstPrimaryOp.charAt(0).toUpperCase() + firstPrimaryOp.slice(1)
68
+ : ''
65
69
  const firstIsMatchOp = 'load' === firstPrimaryOp || 'remove' === firstPrimaryOp
66
70
  const firstOpArg = firstIsMatchOp
67
71
  ? (firstIdF ? `map[string]any{"${firstIdF}": "example_id"}` : 'nil')
68
72
  : 'map[string]any{/* fields */}'
69
73
 
74
+ // The Result-shape call illustration, shown only when some entity exposes an
75
+ // op. A direct()-only SDK (no entity ops) omits it — there is no op to call.
76
+ const resultCallExample = firstPrimaryOp
77
+ ? `Check \`err\` first, then use the value directly (or the typed
78
+ \`...Typed\` variants, which return the entity's model struct and a typed
79
+ slice):
80
+
81
+ ${firstEntityVar}, err := client.${firstEntityName}(nil).${firstPrimaryMethod}(${firstOpArg}, nil)
82
+ if err != nil { /* handle */ }
83
+ // ${firstEntityVar} is the returned record
84
+
85
+ `
86
+ : ''
87
+
70
88
  Content(`### New${model.const.Name}SDK
71
89
 
72
90
  \`\`\`go
@@ -130,15 +148,7 @@ operation's data **directly** — there is no wrapper:
130
148
  | --- | --- |
131
149
  ${resultShapeRows}
132
150
 
133
- Check \`err\` first, then use the value directly (or the typed
134
- \`...Typed\` variants, which return the entity's model struct and a typed
135
- slice):
136
-
137
- ${firstEntityVar}, err := client.${firstEntityName}(nil).${firstPrimaryMethod}(${firstOpArg}, nil)
138
- if err != nil { /* handle */ }
139
- // ${firstEntityVar} is the returned record
140
-
141
- Only \`Direct()\` returns a response envelope — a \`map[string]any\` with
151
+ ${resultCallExample}Only \`Direct()\` returns a response envelope a \`map[string]any\` with
142
152
  \`"ok"\`, \`"status"\`, \`"headers"\`, and \`"data"\` keys.
143
153
 
144
154
  `)
@@ -296,17 +296,17 @@ fmt.Println(results)
296
296
  }
297
297
  else if ('create' === opname) {
298
298
  // Members come from the SAME shape that generates the op's
299
- // request data: every required member must appear — including a
300
- // required id (the /* type */ placeholders also mark the block
301
- // as an illustration for the doc gates); an all-optional create
302
- // renders an empty — still valid — literal.
299
+ // request data (every required member appears, including a
300
+ // required id), each with a type-correct example VALUE via
301
+ // exampleValue a `"name": /* type */` comment is not a value
302
+ // and yields uncompilable Go.
303
303
  const createItems = opRequestShape(ent, 'create').items
304
304
  .filter((it: any) => !it.optional)
305
305
  Content(`\`\`\`go
306
306
  result, err := client.${ent.Name}(nil).Create(map[string]any{
307
307
  `)
308
308
  createItems.map((it: any) => {
309
- Content(` "${it.name}": /* ${canonToType(it.type, target.name)} */,
309
+ Content(` "${it.name}": ${exampleValue(ent, ent.op && ent.op.create, it.name, 'example_' + it.name)},
310
310
  `)
311
311
  })
312
312
  Content(`}, nil)
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, canonKey, entityIdField, entityPrimaryOp, opRequestShape } from '@voxgig/sdkgen'
2
+ import { cmp, Content, canonKey, entityIdField, pickExampleEntity, opRequestShape } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -30,18 +30,17 @@ const ReadmeTopTest = cmp(function ReadmeTopTest(props: any) {
30
30
  // Go module path == repo path on GitHub (org from model.origin).
31
31
  const gomodule = `github.com/${model.origin || 'voxgig-sdk'}/${model.name}-sdk/go`
32
32
 
33
- const exampleEntity = Object.values(entity).find((e: any) => e.active !== false) as any
33
+ // Pick an entity with a real op (prefer a read op) never fabricate a
34
+ // `load` on an op-less entity like Cloudsmith's `Abort`.
35
+ const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
34
36
 
35
37
  Content(`\`\`\`go
36
38
  client := sdk.Test()
37
39
  `)
38
40
 
39
- if (exampleEntity) {
41
+ if (exampleEntity && primaryOp) {
40
42
  const eName = nom(exampleEntity, 'Name')
41
- // Drive the test-mode example off the entity's PRIMARY op (never a hardcoded
42
- // `Load` a create-only entity lacks).
43
43
  const idF = entityIdField(exampleEntity)
44
- const primaryOp = entityPrimaryOp(exampleEntity) || 'load'
45
44
  const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
46
45
  let arg = 'nil'
47
46
  if (isMatchOp) {
@@ -6,7 +6,7 @@ import (
6
6
  vs "github.com/voxgig/struct"
7
7
  )
8
8
 
9
- type EntityNameEntity struct {
9
+ type EntyClass struct {
10
10
  name string
11
11
  client *core.ProjectNameSDK
12
12
  utility *core.Utility
@@ -16,7 +16,7 @@ type EntityNameEntity struct {
16
16
  entctx *core.Context
17
17
  }
18
18
 
19
- func NewEntityNameEntity(client *core.ProjectNameSDK, entopts map[string]any) *EntityNameEntity {
19
+ func NewEntyClass(client *core.ProjectNameSDK, entopts map[string]any) *EntyClass {
20
20
  if entopts == nil {
21
21
  entopts = map[string]any{}
22
22
  }
@@ -28,7 +28,7 @@ func NewEntityNameEntity(client *core.ProjectNameSDK, entopts map[string]any) *E
28
28
  entopts["active"] = true
29
29
  }
30
30
 
31
- e := &EntityNameEntity{
31
+ e := &EntyClass{
32
32
  name: "entityname",
33
33
  client: client,
34
34
  utility: client.GetUtility(),
@@ -47,17 +47,17 @@ func NewEntityNameEntity(client *core.ProjectNameSDK, entopts map[string]any) *E
47
47
  return e
48
48
  }
49
49
 
50
- func (e *EntityNameEntity) GetName() string { return e.name }
50
+ func (e *EntyClass) GetName() string { return e.name }
51
51
 
52
- func (e *EntityNameEntity) Make() core.Entity {
52
+ func (e *EntyClass) Make() core.Entity {
53
53
  opts := map[string]any{}
54
54
  for k, v := range e.entopts {
55
55
  opts[k] = v
56
56
  }
57
- return NewEntityNameEntity(e.client, opts)
57
+ return NewEntyClass(e.client, opts)
58
58
  }
59
59
 
60
- func (e *EntityNameEntity) Data(args ...any) any {
60
+ func (e *EntyClass) Data(args ...any) any {
61
61
  if len(args) > 0 && args[0] != nil {
62
62
  e.data = core.ToMapAny(vs.Clone(args[0]))
63
63
  if e.data == nil {
@@ -71,7 +71,7 @@ func (e *EntityNameEntity) Data(args ...any) any {
71
71
  return out
72
72
  }
73
73
 
74
- func (e *EntityNameEntity) Match(args ...any) any {
74
+ func (e *EntyClass) Match(args ...any) any {
75
75
  if len(args) > 0 && args[0] != nil {
76
76
  e.match = core.ToMapAny(vs.Clone(args[0]))
77
77
  if e.match == nil {
@@ -89,7 +89,7 @@ func (e *EntityNameEntity) Match(args ...any) any {
89
89
  // argument it returns the current data as an EntityName; with an argument it
90
90
  // sets the data and returns the stored value. It delegates to the untyped Data
91
91
  // (identical runtime) and converts at the typed boundary.
92
- func (e *EntityNameEntity) DataTyped(data ...EntityName) EntityName {
92
+ func (e *EntyClass) DataTyped(data ...EntityName) EntityName {
93
93
  if len(data) > 0 {
94
94
  return typedFrom[EntityName](e.Data(asMap(data[0])))
95
95
  }
@@ -99,7 +99,7 @@ func (e *EntityNameEntity) DataTyped(data ...EntityName) EntityName {
99
99
  // MatchTyped mirrors DataTyped for the entity's match filter. The match is a
100
100
  // partial of the entity, so it round-trips through EntityName (all fields
101
101
  // optional at the wire level).
102
- func (e *EntityNameEntity) MatchTyped(match ...EntityName) EntityName {
102
+ func (e *EntyClass) MatchTyped(match ...EntityName) EntityName {
103
103
  if len(match) > 0 {
104
104
  return typedFrom[EntityName](e.Match(asMap(match[0])))
105
105
  }
@@ -116,7 +116,7 @@ func (e *EntityNameEntity) MatchTyped(match ...EntityName) EntityName {
116
116
 
117
117
  // #RemoveOp
118
118
 
119
- func (e *EntityNameEntity) runOp(ctx *core.Context, postDone func()) (any, error) {
119
+ func (e *EntyClass) runOp(ctx *core.Context, postDone func()) (any, error) {
120
120
  utility := e.utility
121
121
 
122
122
  // #PrePoint-Hook
@@ -10,7 +10,7 @@ type entityCreateOp struct{}
10
10
 
11
11
  // EJECT-START
12
12
 
13
- func (e *EntityNameEntity) Create(reqdata map[string]any, ctrl map[string]any) (any, error) {
13
+ func (e *EntyClass) Create(reqdata map[string]any, ctrl map[string]any) (any, error) {
14
14
  utility := e.utility
15
15
  ctx := utility.MakeContext(map[string]any{
16
16
  "opname": "create",
@@ -35,7 +35,7 @@ func (e *EntityNameEntity) Create(reqdata map[string]any, ctrl map[string]any) (
35
35
  // CreateTyped is the statically-typed variant of Create: it takes an
36
36
  // EntityNameCreateData and returns an EntityName. It delegates to the untyped
37
37
  // Create (identical runtime) and converts at the typed boundary.
38
- func (e *EntityNameEntity) CreateTyped(reqdata EntityNameCreateData, ctrl map[string]any) (EntityName, error) {
38
+ func (e *EntyClass) CreateTyped(reqdata EntityNameCreateData, ctrl map[string]any) (EntityName, error) {
39
39
  res, err := e.Create(asMap(reqdata), ctrl)
40
40
  if err != nil {
41
41
  return EntityName{}, err
@@ -4,7 +4,7 @@ type entityListOp struct{}
4
4
 
5
5
  // EJECT-START
6
6
 
7
- func (e *EntityNameEntity) List(reqmatch map[string]any, ctrl map[string]any) (any, error) {
7
+ func (e *EntyClass) List(reqmatch map[string]any, ctrl map[string]any) (any, error) {
8
8
  utility := e.utility
9
9
  ctx := utility.MakeContext(map[string]any{
10
10
  "opname": "list",
@@ -26,7 +26,7 @@ func (e *EntityNameEntity) List(reqmatch map[string]any, ctrl map[string]any) (a
26
26
  // ListTyped is the statically-typed variant of List: it takes an
27
27
  // EntityNameListMatch and returns []EntityName. It delegates to the untyped
28
28
  // List (identical runtime) and converts at the typed boundary.
29
- func (e *EntityNameEntity) ListTyped(reqmatch EntityNameListMatch, ctrl map[string]any) ([]EntityName, error) {
29
+ func (e *EntyClass) ListTyped(reqmatch EntityNameListMatch, ctrl map[string]any) ([]EntityName, error) {
30
30
  res, err := e.List(asMap(reqmatch), ctrl)
31
31
  if err != nil {
32
32
  return nil, err
@@ -10,7 +10,7 @@ type entityLoadOp struct{}
10
10
 
11
11
  // EJECT-START
12
12
 
13
- func (e *EntityNameEntity) Load(reqmatch map[string]any, ctrl map[string]any) (any, error) {
13
+ func (e *EntyClass) Load(reqmatch map[string]any, ctrl map[string]any) (any, error) {
14
14
  utility := e.utility
15
15
  ctx := utility.MakeContext(map[string]any{
16
16
  "opname": "load",
@@ -38,7 +38,7 @@ func (e *EntityNameEntity) Load(reqmatch map[string]any, ctrl map[string]any) (a
38
38
  // LoadTyped is the statically-typed variant of Load: it takes an
39
39
  // EntityNameLoadMatch and returns an EntityName. It delegates to the untyped
40
40
  // Load (identical runtime) and converts at the typed boundary.
41
- func (e *EntityNameEntity) LoadTyped(reqmatch EntityNameLoadMatch, ctrl map[string]any) (EntityName, error) {
41
+ func (e *EntyClass) LoadTyped(reqmatch EntityNameLoadMatch, ctrl map[string]any) (EntityName, error) {
42
42
  res, err := e.Load(asMap(reqmatch), ctrl)
43
43
  if err != nil {
44
44
  return EntityName{}, err
@@ -10,7 +10,7 @@ type entityRemoveOp struct{}
10
10
 
11
11
  // EJECT-START
12
12
 
13
- func (e *EntityNameEntity) Remove(reqmatch map[string]any, ctrl map[string]any) (any, error) {
13
+ func (e *EntyClass) Remove(reqmatch map[string]any, ctrl map[string]any) (any, error) {
14
14
  utility := e.utility
15
15
  ctx := utility.MakeContext(map[string]any{
16
16
  "opname": "remove",
@@ -38,7 +38,7 @@ func (e *EntityNameEntity) Remove(reqmatch map[string]any, ctrl map[string]any)
38
38
  // RemoveTyped is the statically-typed variant of Remove: it takes an
39
39
  // EntityNameRemoveMatch and returns an EntityName. It delegates to the untyped
40
40
  // Remove (identical runtime) and converts at the typed boundary.
41
- func (e *EntityNameEntity) RemoveTyped(reqmatch EntityNameRemoveMatch, ctrl map[string]any) (EntityName, error) {
41
+ func (e *EntyClass) RemoveTyped(reqmatch EntityNameRemoveMatch, ctrl map[string]any) (EntityName, error) {
42
42
  res, err := e.Remove(asMap(reqmatch), ctrl)
43
43
  if err != nil {
44
44
  return EntityName{}, err
@@ -10,7 +10,7 @@ type entityUpdateOp struct{}
10
10
 
11
11
  // EJECT-START
12
12
 
13
- func (e *EntityNameEntity) Update(reqdata map[string]any, ctrl map[string]any) (any, error) {
13
+ func (e *EntyClass) Update(reqdata map[string]any, ctrl map[string]any) (any, error) {
14
14
  utility := e.utility
15
15
  ctx := utility.MakeContext(map[string]any{
16
16
  "opname": "update",
@@ -38,7 +38,7 @@ func (e *EntityNameEntity) Update(reqdata map[string]any, ctrl map[string]any) (
38
38
  // UpdateTyped is the statically-typed variant of Update: it takes an
39
39
  // EntityNameUpdateData and returns an EntityName. It delegates to the untyped
40
40
  // Update (identical runtime) and converts at the typed boundary.
41
- func (e *EntityNameEntity) UpdateTyped(reqdata EntityNameUpdateData, ctrl map[string]any) (EntityName, error) {
41
+ func (e *EntyClass) UpdateTyped(reqdata EntityNameUpdateData, ctrl map[string]any) (EntityName, error) {
42
42
  res, err := e.Update(asMap(reqdata), ctrl)
43
43
  if err != nil {
44
44
  return EntityName{}, err
@@ -3,9 +3,15 @@ import * as Path from 'node:path'
3
3
 
4
4
  import {
5
5
  cmp, each, camelify, names,
6
- File, Content, Folder, Fragment, Line, FeatureHook, Slot
6
+ File, Content, Folder, Fragment, Line, FeatureHook, Slot,
7
+ entityClassName,
7
8
  } from '@voxgig/sdkgen'
8
9
 
10
+ import {
11
+ KIT,
12
+ getModelPath
13
+ } from '@voxgig/apidef'
14
+
9
15
  import { EntityOperation } from './EntityOperation_js'
10
16
 
11
17
 
@@ -13,6 +19,12 @@ const Entity = cmp(function Entity(props: any) {
13
19
  const { model, stdrep } = props.ctx$
14
20
  const { target, entity } = props
15
21
 
22
+ // Collision-free entity CLASS name (see entityClassName): normally
23
+ // `<Name>Entity`, disambiguated when it would clash with another entity's
24
+ // data-type name. The class file name and the Main require path both use this.
25
+ const entityColl = getModelPath(model, `main.${KIT}.entity`)
26
+ const cls = entityClassName(entity, entityColl)
27
+
16
28
  const entrep = {
17
29
  ...stdrep,
18
30
  }
@@ -23,7 +35,7 @@ const Entity = cmp(function Entity(props: any) {
23
35
 
24
36
  Folder({ name: 'src/entity' }, () => {
25
37
 
26
- File({ name: entity.Name + 'Entity.' + target.name }, () => {
38
+ File({ name: cls + '.' + target.name }, () => {
27
39
 
28
40
  const opnames = Object.keys(entity.op)
29
41
 
@@ -43,6 +55,10 @@ const Entity = cmp(function Entity(props: any) {
43
55
  SdkName: model.const.Name,
44
56
  EntityName: entity.Name,
45
57
 
58
+ // Class token decoupled from the EntityName data-type token in
59
+ // Entity.fragment.js so the class can be renamed independently.
60
+ EntyClass: cls,
61
+
46
62
  '#Feature-Hook': ({ name, indent }: any) =>
47
63
  Content({ indent }, `
48
64
  fres = featureHook(ctx, '${name}')
@@ -1,16 +1,26 @@
1
1
 
2
2
 
3
- import { cmp, Content } from '@voxgig/sdkgen'
3
+ import { cmp, Content, entityClassName } from '@voxgig/sdkgen'
4
+
5
+ import {
6
+ KIT,
7
+ getModelPath
8
+ } from '@voxgig/apidef'
4
9
 
5
10
 
6
11
  const MainEntity = cmp(async function MainEntity(props: any) {
7
12
  const { entity } = props
13
+ const { model } = props.ctx$
14
+
15
+ // Return the collision-free class (entityClassName); the accessor METHOD name
16
+ // (entity.Name) is unchanged so callers still write client.<Name>().
17
+ const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
8
18
 
9
19
  Content(`
10
20
  // Entity access: \`client.${entity.Name}().list()\` / \`client.${entity.Name}().load({ id })\`.
11
21
  ${entity.Name}(data) {
12
22
  const self = this
13
- return new ${entity.Name}Entity(self,data)
23
+ return new ${cls}(self,data)
14
24
  }
15
25
 
16
26
  `)
@@ -4,6 +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,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -55,8 +56,10 @@ const Main = cmp(async function Main(props: any) {
55
56
 
56
57
  Line(`// ${model.const.Name} ${target.Name} SDK\n`)
57
58
 
58
- List({ item: entity }, ({ item }: any) =>
59
- Line(`const { ${item.Name}Entity } = require('./entity/${item.Name}Entity')`))
59
+ List({ item: entity }, ({ item }: any) => {
60
+ const cls = entityClassName(item, entity)
61
+ return Line(`const { ${cls} } = require('./entity/${cls}')`)
62
+ })
60
63
 
61
64
  Fragment(
62
65
  {
@@ -132,10 +132,10 @@ const ${eVar}s = await client.${entity.Name}().list()
132
132
 
133
133
  if (opnames.includes('create')) {
134
134
  // Members come from the SAME shape the runtime validates
135
- // (opRequestShape): every required member must appear — including a
136
- // required id and parent keys like page_id (the /* type */
137
- // placeholders mark the block as an illustration); an all-optional
138
- // create renders an empty still valid — literal.
135
+ // (opRequestShape): every required member appears — including a
136
+ // required id and parent keys like page_id each with a type-correct
137
+ // example VALUE via exampleValue a `name: /* type */` comment is not
138
+ // a value and yields invalid code.
139
139
  const createItems = opRequestShape(entity, 'create').items
140
140
  .filter((it: any) => !it.optional)
141
141
  Content(`#### Example: Create
@@ -144,7 +144,7 @@ const ${eVar}s = await client.${entity.Name}().list()
144
144
  const ${eVar} = await client.${entity.Name}().create({
145
145
  `)
146
146
  createItems.map((it: any) => {
147
- Content(` ${it.name}: /* ${canonToType(it.type, target.name)} */,
147
+ Content(` ${it.name}: ${exampleValue(entity, entity.op && entity.op.create, it.name, 'example_' + it.name)},
148
148
  `)
149
149
  })
150
150
  Content(`})