@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
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content, canonToType, canonKey, entityIdField, opRequestShape } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, canonToType, canonKey, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -56,6 +56,9 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
56
56
  const fields = entity.fields || []
57
57
  // Model-driven id key: null when this entity has no id-like field.
58
58
  const idF = entityIdField(entity)
59
+ // Sanitise the local variable name — an entity whose lowercased name is a
60
+ // Ruby keyword (e.g. `self`) would otherwise emit uncompilable code.
61
+ const eVar = safeVarName(entity.name, 'rb')
59
62
 
60
63
  Content(`
61
64
  ### ${entity.Name}
@@ -68,7 +71,7 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
68
71
  `)
69
72
  }
70
73
 
71
- Content(`Create an instance: \`${entity.name} = client.${entity.Name}\`
74
+ Content(`Create an instance: \`${eVar} = client.${entity.Name}\`
72
75
 
73
76
  `)
74
77
 
@@ -124,7 +127,7 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
124
127
 
125
128
  \`\`\`ruby
126
129
  # load returns the bare ${entity.Name} record (raises on error).
127
- ${entity.name} = client.${entity.Name}.load(${loadArg})
130
+ ${eVar} = client.${entity.Name}.load(${loadArg})
128
131
  \`\`\`
129
132
 
130
133
  `)
@@ -135,7 +138,7 @@ ${entity.name} = client.${entity.Name}.load(${loadArg})
135
138
 
136
139
  \`\`\`ruby
137
140
  # list returns an Array of ${entity.Name} records (raises on error).
138
- ${entity.name}s = client.${entity.Name}.list
141
+ ${eVar}s = client.${entity.Name}.list
139
142
  \`\`\`
140
143
 
141
144
  `)
@@ -152,7 +155,7 @@ ${entity.name}s = client.${entity.Name}.list
152
155
  Content(`#### Example: Create
153
156
 
154
157
  \`\`\`ruby
155
- ${entity.name} = client.${entity.Name}.create({
158
+ ${eVar} = client.${entity.Name}.create({
156
159
  `)
157
160
  createItems.map((it: any) => {
158
161
  Content(` "${it.name}" => ${rbLit(it.type, 'example_' + it.name)}, # ${canonToType(it.type, target.name)}
@@ -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, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -23,15 +23,20 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
23
23
  const { target, ctx$: { model } } = props
24
24
 
25
25
  const entity = getModelPath(model, `main.${KIT}.entity`)
26
- const exampleEntity = Object.values(entity || {}).find((e: any) => e && e.active !== false) as any
26
+ // Pick an entity with a real op (prefer a read op) never fabricate a
27
+ // `load` on an op-less entity like Cloudsmith's `Abort`. primaryOp is null
28
+ // only when NO entity exposes any op (a direct()-only SDK).
29
+ const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
27
30
  const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
31
+ // Sanitise the local variable name — an entity whose lowercased name is a
32
+ // Ruby keyword (e.g. `self`) would otherwise emit uncompilable code. The
33
+ // fixture KEY (`"${eName.toLowerCase()}"`) stays raw — it must match the
34
+ // entity's registered name for the mock lookup to resolve.
35
+ const eVar = safeVarName(eName.toLowerCase(), 'rb')
28
36
  // Model-driven id key: null when the entity has no id-like field (a
29
37
  // response-wrapped spec). When null the fixture seeds no id and a match op
30
38
  // takes no argument.
31
39
  const idF = exampleEntity ? entityIdField(exampleEntity) : null
32
- // Drive the test-mode example off the entity's PRIMARY op — an op it actually
33
- // exposes — never a hardcoded `load` a create-only entity lacks.
34
- const primaryOp = exampleEntity ? (entityPrimaryOp(exampleEntity) || 'load') : 'load'
35
40
  const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
36
41
  const seedSentence = idF
37
42
  ? '. Seed fixture\ndata via the `entity` option so offline calls resolve without a live server'
@@ -52,6 +57,15 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
52
57
  testCallArg = `{ ${chosen.map((it: any) => `"${it.name}" => ${rbLit(it.type)}`).join(', ')} }`
53
58
  }
54
59
 
60
+ // The op-driven test-mode line, shown only when the SDK has an entity op.
61
+ // A direct()-only SDK (no ops anywhere) shows a direct() call instead.
62
+ const testModeExample = primaryOp
63
+ ? `# Entity ops return the bare mock record (raises on error).
64
+ ${eVar} = client.${eName}.${primaryOp}(${testCallArg})
65
+ puts ${eVar}`
66
+ : `result = client.direct({ "path" => "/api/resource", "method" => "GET" })
67
+ puts result`
68
+
55
69
  const apikeyEnvLine = isAuthActive(model)
56
70
  ? `\n${envName(model)}_APIKEY=<your-key>`
57
71
  : ''
@@ -101,9 +115,7 @@ Create a mock client for unit testing — no server required${seedSentence}:
101
115
  \`\`\`ruby
102
116
  client = ${testCtor}
103
117
 
104
- # Entity ops return the bare mock record (raises on error).
105
- ${eName.toLowerCase()} = client.${eName}.${primaryOp}(${testCallArg})
106
- puts ${eName.toLowerCase()}
118
+ ${testModeExample}
107
119
  \`\`\`
108
120
 
109
121
  ### Use a custom fetch function
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content, isAuthActive, envName, canonKey, opRequestShape, entityIdField, entityDataIdField, entityOps } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, isAuthActive, envName, canonKey, opRequestShape, entityIdField, entityDataIdField, entityOps, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -43,6 +43,9 @@ client = ${ctor}
43
43
  if (exampleEntity) {
44
44
  const eName = nom(exampleEntity, 'Name')
45
45
  const article = /^[aeiou]/i.test(eName) ? "an" : "a"
46
+ // Sanitise the local variable name — an entity whose lowercased name is a
47
+ // Ruby keyword (e.g. `self`) would otherwise emit uncompilable code.
48
+ const eVar = safeVarName(eName.toLowerCase(), 'rb')
46
49
  const opnames = entityOps(exampleEntity)
47
50
  // Model-driven id keys: `idF` is the load-MATCH key (null when the entity
48
51
  // has no id-like field — a response-wrapped spec); when null, load/remove
@@ -84,8 +87,8 @@ client = ${ctor}
84
87
  \`\`\`ruby
85
88
  begin
86
89
  # list returns an Array of ${eName} records — iterate directly.
87
- ${eName.toLowerCase()}s = client.${eName}.list
88
- ${eName.toLowerCase()}s.each do |item|
90
+ ${eVar}s = client.${eName}.list
91
+ ${eVar}s.each do |item|
89
92
  puts "${itemPrint}"
90
93
  end
91
94
  rescue => err
@@ -99,7 +102,7 @@ end
99
102
  if (nestedEntity) {
100
103
  const neName = nom(nestedEntity, 'Name')
101
104
  const neArticle = /^[aeiou]/i.test(neName) ? "an" : "a"
102
- const neVar = neName.toLowerCase()
105
+ const neVar = safeVarName(neName.toLowerCase(), 'rb')
103
106
 
104
107
  // Model-driven match: every REQUIRED load-match key — the same shape
105
108
  // the runtime resolves path params from, so the example always works.
@@ -151,8 +154,8 @@ end
151
154
  \`\`\`ruby
152
155
  begin
153
156
  # load returns the bare ${eName} record (raises on error).
154
- ${eName.toLowerCase()} = client.${eName}.load(${loadArg})
155
- puts ${eName.toLowerCase()}
157
+ ${eVar} = client.${eName}.load(${loadArg})
158
+ puts ${eVar}
156
159
  rescue => err
157
160
  warn "load failed: #{err}"
158
161
  end
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content, canonToType, canonKey, File, isAuthActive, entityIdField, opRequestShape } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, canonToType, canonKey, File, isAuthActive, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -172,6 +172,9 @@ same parameters as \`direct()\`. Raises on error.
172
172
  // Model-driven id key: null when this entity has no id-like field, in
173
173
  // which case load/remove match on no argument and update omits the id.
174
174
  const idF = entityIdField(ent)
175
+ // Sanitise the local variable name — an entity whose lowercased name is
176
+ // a Ruby keyword (e.g. `self`) would otherwise emit uncompilable code.
177
+ const eVar = safeVarName(ent.name, 'rb')
175
178
 
176
179
  Content(`
177
180
  ---
@@ -187,7 +190,7 @@ same parameters as \`direct()\`. Raises on error.
187
190
  }
188
191
 
189
192
  Content(`\`\`\`ruby
190
- ${ent.name} = client.${ent.Name}
193
+ ${eVar} = client.${ent.Name}
191
194
  \`\`\`
192
195
 
193
196
  `)
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, opRequestShape } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -43,6 +43,9 @@ client = ${ctor}
43
43
 
44
44
  if (exampleEntity) {
45
45
  const eName = nom(exampleEntity, 'Name')
46
+ // Sanitise the local variable name — an entity whose lowercased name is a
47
+ // Ruby keyword (e.g. `self`) would otherwise emit uncompilable code.
48
+ const eVar = safeVarName(eName.toLowerCase(), 'rb')
46
49
  const opnames = Object.keys(exampleEntity.op || {})
47
50
  // Model-driven id key: null when the entity has no id-like field, in which
48
51
  // case the load example takes no match argument.
@@ -52,8 +55,8 @@ client = ${ctor}
52
55
 
53
56
  if (opnames.includes('list')) {
54
57
  Content(`# List all ${eName.toLowerCase()}s (returns an Array; raises on error)
55
- ${eName.toLowerCase()}s = client.${eName}.list
56
- puts ${eName.toLowerCase()}s
58
+ ${eVar}s = client.${eName}.list
59
+ puts ${eVar}s
57
60
  `)
58
61
  hasCall = true
59
62
  }
@@ -73,8 +76,8 @@ puts ${eName.toLowerCase()}s
73
76
  : ''
74
77
  Content(`
75
78
  # Load a specific ${eName.toLowerCase()} (returns the bare record; raises on error)
76
- ${eName.toLowerCase()} = client.${eName}.load(${loadArg})
77
- puts ${eName.toLowerCase()}
79
+ ${eVar} = client.${eName}.load(${loadArg})
80
+ puts ${eVar}
78
81
  `)
79
82
  hasCall = true
80
83
  }
@@ -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, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -24,17 +24,16 @@ const ReadmeTopTest = cmp(function ReadmeTopTest(props: any) {
24
24
 
25
25
  const entity = getModelPath(model, `main.${KIT}.entity`)
26
26
 
27
- const exampleEntity = Object.values(entity).find((e: any) => e.active !== false) as any
27
+ // Pick an entity with a real op (prefer a read op) never fabricate a
28
+ // `load` on an op-less entity like Cloudsmith's `Abort`.
29
+ const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
28
30
 
29
- if (exampleEntity) {
31
+ if (exampleEntity && primaryOp) {
30
32
  const eName = nom(exampleEntity, 'Name')
31
33
  const ename = eName.toLowerCase()
32
34
  // Model-driven id key: null when the entity has no id-like field, so the
33
35
  // seeded record carries no id and a match op takes no argument.
34
36
  const idF = entityIdField(exampleEntity)
35
- // Drive the test-mode example off the entity's PRIMARY op — never a
36
- // hardcoded `load` a create-only entity lacks.
37
- const primaryOp = entityPrimaryOp(exampleEntity) || 'load'
38
37
  const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
39
38
  const recBody = idF ? `{ "${idF}" => "test01" }` : '{}'
40
39
  let callArg = ''
@@ -56,8 +55,11 @@ const ReadmeTopTest = cmp(function ReadmeTopTest(props: any) {
56
55
  const chosen = required.length ? required : items.slice(0, 3)
57
56
  callArg = `{ ${chosen.map((it: any) => `"${it.name}" => ${rbLit(it.type)}`).join(', ')} }`
58
57
  }
59
- // A list result is an Array — name the variable accordingly.
60
- const eVar = ename + ('list' === primaryOp ? 's' : '')
58
+ // A list result is an Array — name the variable accordingly. Sanitise the
59
+ // base name an entity whose lowercased name is a Ruby keyword (e.g.
60
+ // `self`) would otherwise emit uncompilable code. The fixture KEY (`ename`)
61
+ // stays raw so the mock lookup resolves.
62
+ const eVar = safeVarName(ename, 'rb') + ('list' === primaryOp ? 's' : '')
61
63
  Content(`\`\`\`ruby
62
64
  # Seed fixture data so offline calls resolve without a live server.
63
65
  client = ${model.const.Name}SDK.test({
@@ -3,7 +3,7 @@
3
3
  require_relative '../utility/struct/voxgig_struct'
4
4
  require_relative '../core/helpers'
5
5
 
6
- class EntityNameEntity
6
+ class EntyClass
7
7
  def initialize(client, entopts = nil)
8
8
  entopts ||= {}
9
9
  if entopts["active"].nil?
@@ -35,7 +35,7 @@ class EntityNameEntity
35
35
 
36
36
  def make
37
37
  opts = @_entopts.dup
38
- EntityNameEntity.new(@_client, opts)
38
+ EntyClass.new(@_client, opts)
39
39
  end
40
40
 
41
41
  def data_set(args)
@@ -4,9 +4,14 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each, camelify, names,
6
6
  File, Content, Folder, Fragment, Line, FeatureHook, Slot,
7
- opTypeName,
7
+ opTypeName, entityClassName,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
+ import {
11
+ KIT,
12
+ getModelPath
13
+ } from '@voxgig/apidef'
14
+
10
15
  import { EntityOperation } from './EntityOperation_ts'
11
16
  // import { EntityTest } from './EntityTest_ts'
12
17
 
@@ -15,6 +20,13 @@ const Entity = cmp(function Entity(props: any) {
15
20
  const { model, stdrep } = props.ctx$
16
21
  const { target, entity } = props
17
22
 
23
+ // Collision-free entity CLASS name (see entityClassName): normally
24
+ // `<Name>Entity`, disambiguated when it would clash with another entity's
25
+ // data-type name. The DATA type stays `<Name>`. The class file name and the
26
+ // Main import path both use this, so they always agree.
27
+ const entityColl = getModelPath(model, `main.${KIT}.entity`)
28
+ const cls = entityClassName(entity, entityColl)
29
+
18
30
  const entrep = {
19
31
  ...stdrep,
20
32
  }
@@ -38,7 +50,7 @@ const Entity = cmp(function Entity(props: any) {
38
50
 
39
51
  Folder({ name: 'src/entity' }, () => {
40
52
 
41
- File({ name: entity.Name + 'Entity.' + target.name }, () => {
53
+ File({ name: cls + '.' + target.name }, () => {
42
54
 
43
55
  const opnames = Object.keys(entity.op)
44
56
 
@@ -58,6 +70,10 @@ const Entity = cmp(function Entity(props: any) {
58
70
  SdkName: model.const.Name,
59
71
  EntityName: entity.Name,
60
72
 
73
+ // Class token decoupled from the EntityName data-type token in
74
+ // Entity.fragment.ts so the class can be renamed independently.
75
+ EntyClass: cls,
76
+
61
77
  '#TypeImports': ({ indent }: any) => Content({ indent }, typeImport),
62
78
 
63
79
  '#Feature-Hook': ({ name, indent }: any) =>
@@ -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 TYPE (entityClassName); the accessor METHOD
16
+ // name (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?: any) {
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(`import { ${item.Name}Entity } from './entity/${item.Name}Entity'`))
59
+ List({ item: entity }, ({ item }: any) => {
60
+ const cls = entityClassName(item, entity)
61
+ return Line(`import { ${cls} } from './entity/${cls}'`)
62
+ })
60
63
 
61
64
  // Re-export the generated typed models so consumers can
62
65
  // `import { Advice, AdviceLoadMatch } from '<pkg>'`.
@@ -131,10 +131,10 @@ const ${eVar}s = await client.${entity.Name}().list()
131
131
  }
132
132
 
133
133
  if (opnames.includes('create')) {
134
- // Members come from the SAME shape that generates <Name>CreateData:
135
- // every required member must appear (the /* type */ placeholders also
136
- // mark the block as an illustration for the doc gates); an
137
- // all-optional create renders an empty still assignable literal.
134
+ // Members come from the SAME shape that generates <Name>CreateData
135
+ // (every required member appears), with a type-correct example VALUE
136
+ // via exampleValue a `name: /* type */` comment is not a value and
137
+ // yields invalid TS (TS1109), so the example must carry a real literal.
138
138
  const createItems = opRequestShape(entity, 'create').items
139
139
  .filter((it: any) => !it.optional)
140
140
  Content(`#### Example: Create
@@ -143,7 +143,7 @@ const ${eVar}s = await client.${entity.Name}().list()
143
143
  const ${eVar} = await client.${entity.Name}().create({
144
144
  `)
145
145
  createItems.map((it: any) => {
146
- Content(` ${it.name}: /* ${canonToType(it.type, target.name)} */,
146
+ Content(` ${it.name}: ${exampleValue(entity, entity.op && entity.op.create, it.name, 'example_' + it.name)},
147
147
  `)
148
148
  })
149
149
  Content(`})
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isAuthActive, envName, entityIdField, entityDataIdField, entityPrimaryOp, opRequestShape, safeVarName } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive, envName, entityIdField, entityDataIdField, pickExampleEntity, opRequestShape, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -14,15 +14,15 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
14
14
  const { target, ctx$: { model } } = props
15
15
 
16
16
  const entity = getModelPath(model, `main.${KIT}.entity`)
17
- const exampleEntity = Object.values(entity || {}).find((e: any) => e && e.active !== false) as any
17
+ // Pick an entity with a real op (prefer a read op) never fabricate a
18
+ // `load` on an op-less entity like Cloudsmith's `Abort`. primaryOp is null
19
+ // only when NO entity exposes any op (a direct()-only SDK).
20
+ const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
18
21
  const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
19
22
  // Variable-safe lowercase name (a `Delete` entity must not bind `delete`).
20
23
  const eVar = safeVarName(eName.toLowerCase(), 'ts')
21
24
 
22
- // Drive the test-mode / stateful examples off the entity's PRIMARY op an op
23
- // it actually exposes — never a hardcoded `load` a create-only entity lacks.
24
- const primaryOp = exampleEntity ? (entityPrimaryOp(exampleEntity) || 'load') : 'load'
25
- const primaryOpDef = exampleEntity && exampleEntity.op && exampleEntity.op[primaryOp]
25
+ const primaryOpDef = exampleEntity && primaryOp && exampleEntity.op && exampleEntity.op[primaryOp]
26
26
  const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
27
27
  // Model-driven id key: `idF` is the entity's id-like MATCH field name, or null
28
28
  // when it has none. `dataIdF` is the id on the RETURNED record's data type —
@@ -32,7 +32,7 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
32
32
 
33
33
  // A type-correct, language-idiomatic argument for the primary op call.
34
34
  const primaryArg = (idPlaceholder: string): string => {
35
- if (!exampleEntity) return ''
35
+ if (!exampleEntity || !primaryOp) return ''
36
36
  if ('list' === primaryOp) return ''
37
37
  if (isMatchOp) {
38
38
  return idF ? `{ ${idF}: ${exampleValue(exampleEntity, primaryOpDef, idF, idPlaceholder)} }` : ''
@@ -53,6 +53,33 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
53
53
  ? `console.log(data.${dataIdF})`
54
54
  : `console.log(data)`
55
55
 
56
+ // The op-driven example lines, shown only when the SDK has an entity op.
57
+ // A direct()-only SDK (no ops anywhere) shows a direct() test call instead.
58
+ const testModeExample = primaryOp
59
+ ? `const ${eVar} = await client.${eName}().${primaryOp}(${testCallArg})
60
+ // ${eVar} is a bare entity populated with mock response data
61
+ console.log(${eVar})`
62
+ : `const result = await client.direct({ path: '/api/resource', method: 'GET' })
63
+ console.log(result)`
64
+ const stateSection = primaryOp
65
+ ? `### Retain entity state across calls
66
+
67
+ Entity instances remember their last match and data:
68
+
69
+ \`\`\`ts
70
+ const entity = client.${eName}()
71
+
72
+ // First call runs the operation and stores its result
73
+ await entity.${primaryOp}(${stateCallArg})
74
+
75
+ // Subsequent calls reuse the stored state
76
+ const data = entity.data()
77
+ ${stateDataLine}
78
+ \`\`\`
79
+
80
+ `
81
+ : ''
82
+
56
83
  const authActive = isAuthActive(model)
57
84
  const apikeyTesterCtor = authActive
58
85
  ? `new ${model.const.Name}SDK({ apikey: '...' })`
@@ -102,9 +129,7 @@ Create a mock client for unit testing \u2014 no server required:
102
129
  \`\`\`ts
103
130
  const client = ${model.const.Name}SDK.test()
104
131
 
105
- const ${eVar} = await client.${eName}().${primaryOp}(${testCallArg})
106
- // ${eVar} is a bare entity populated with mock response data
107
- console.log(${eVar})
132
+ ${testModeExample}
108
133
  \`\`\`
109
134
 
110
135
  You can also use the instance method:
@@ -114,22 +139,7 @@ const client = ${apikeyTesterCtor}
114
139
  const testClient = client.tester()
115
140
  \`\`\`
116
141
 
117
- ### Retain entity state across calls
118
-
119
- Entity instances remember their last match and data:
120
-
121
- \`\`\`ts
122
- const entity = client.${eName}()
123
-
124
- // First call runs the operation and stores its result
125
- await entity.${primaryOp}(${stateCallArg})
126
-
127
- // Subsequent calls reuse the stored state
128
- const data = entity.data()
129
- ${stateDataLine}
130
- \`\`\`
131
-
132
- ### Add custom middleware
142
+ ${stateSection}### Add custom middleware
133
143
 
134
144
  Pass features via the \`extend\` option:
135
145
 
@@ -299,17 +299,16 @@ const results = await client.${ent.Name}().${opname}()
299
299
  }
300
300
  else if ('create' === opname) {
301
301
  // Members come from the SAME shape that generates
302
- // <Name>CreateData: every required member must appear (the
303
- // /* type */ placeholders also mark the block as an illustration
304
- // for the doc gates); an all-optional create renders an empty —
305
- // still assignable — literal.
302
+ // <Name>CreateData (every required member appears), each with a
303
+ // type-correct example VALUE via exampleValue a `name: /* type */`
304
+ // comment is not a value and yields invalid TS (TS1109).
306
305
  const createItems = opRequestShape(ent, 'create').items
307
306
  .filter((it: any) => !it.optional)
308
307
  Content(`\`\`\`ts
309
308
  const result = await client.${ent.Name}().create({
310
309
  `)
311
310
  createItems.map((it: any) => {
312
- Content(` ${it.name}: /* ${canonToType(it.type, target.name)} */,
311
+ Content(` ${it.name}: ${exampleValue(ent, ent.op && ent.op.create, it.name, 'example_' + it.name)},
313
312
  `)
314
313
  })
315
314
  Content(`})
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, entityIdField, entityPrimaryOp, opRequestShape, safeVarName } from '@voxgig/sdkgen'
2
+ import { cmp, Content, entityIdField, pickExampleEntity, opRequestShape, safeVarName } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -14,17 +14,16 @@ const ReadmeTopTest = cmp(function ReadmeTopTest(props: any) {
14
14
  const { target, ctx$: { model } } = props
15
15
 
16
16
  const entity = getModelPath(model, `main.${KIT}.entity`)
17
- const exampleEntity = Object.values(entity).find((e: any) => e.active !== false) as any
17
+ // Pick an entity with a real op (prefer a read op) never fabricate a
18
+ // `load` on an op-less entity like Cloudsmith's `Abort`.
19
+ const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
18
20
 
19
21
  Content(`\`\`\`ts
20
22
  const client = ${model.const.Name}SDK.test()
21
23
  `)
22
24
 
23
- if (exampleEntity) {
25
+ if (exampleEntity && primaryOp) {
24
26
  const eName = nom(exampleEntity, 'Name')
25
- // Drive the test-mode example off the entity's PRIMARY op (never a
26
- // hardcoded `load` a create-only entity lacks).
27
- const primaryOp = entityPrimaryOp(exampleEntity) || 'load'
28
27
  // A list() result is an array — name the variable accordingly.
29
28
  const eVar = safeVarName(eName.toLowerCase(), 'ts') +
30
29
  ('list' === primaryOp ? 's' : '')
@@ -18,7 +18,7 @@ import type {
18
18
 
19
19
 
20
20
  // TODO: needs Entity superclass
21
- class EntityNameEntity extends ProjectNameEntityBase<EntityName> {
21
+ class EntyClass extends ProjectNameEntityBase<EntityName> {
22
22
 
23
23
  constructor(client: ProjectNameSDK, entopts: any) {
24
24
  super(client, entopts)
@@ -28,8 +28,8 @@ class EntityNameEntity extends ProjectNameEntityBase<EntityName> {
28
28
  }
29
29
 
30
30
 
31
- make(this: EntityNameEntity) {
32
- return new EntityNameEntity(this._client, this.entopts())
31
+ make(this: EntyClass) {
32
+ return new EntyClass(this._client, this.entopts())
33
33
  }
34
34
 
35
35
 
@@ -47,5 +47,5 @@ class EntityNameEntity extends ProjectNameEntityBase<EntityName> {
47
47
 
48
48
 
49
49
  export {
50
- EntityNameEntity
50
+ EntyClass
51
51
  }
@@ -47,7 +47,12 @@ func prepareAuthUtil(ctx *core.Context) (*core.Spec, error) {
47
47
  if av, ok := apikey.(string); ok {
48
48
  apikeyVal = av
49
49
  }
50
- headers[headerAuth] = authPrefix + " " + apikeyVal
50
+ // Empty prefix (raw apiKey credential) must not add a leading space.
51
+ if authPrefix == "" {
52
+ headers[headerAuth] = apikeyVal
53
+ } else {
54
+ headers[headerAuth] = authPrefix + " " + apikeyVal
55
+ }
51
56
  }
52
57
 
53
58
  return spec, nil
@@ -36,7 +36,9 @@ function prepareAuth(ctx) {
36
36
  delprop(headers, HEADER_auth)
37
37
  }
38
38
  else {
39
- setprop(headers, HEADER_auth, options.auth.prefix + ' ' + apikey)
39
+ // Empty prefix (raw apiKey credential) must not add a leading space.
40
+ setprop(headers, HEADER_auth,
41
+ options.auth.prefix ? options.auth.prefix + ' ' + apikey : apikey)
40
42
  }
41
43
 
42
44
  return spec
@@ -38,7 +38,12 @@ local function prepare_auth_util(ctx)
38
38
  if type(apikey) == "string" then
39
39
  apikey_val = apikey
40
40
  end
41
- headers[HEADER_AUTH] = auth_prefix .. " " .. apikey_val
41
+ -- Empty prefix (raw apiKey credential) must not add a leading space.
42
+ if auth_prefix == "" then
43
+ headers[HEADER_AUTH] = apikey_val
44
+ else
45
+ headers[HEADER_AUTH] = auth_prefix .. " " .. apikey_val
46
+ end
42
47
  end
43
48
 
44
49
  return spec, nil
@@ -35,7 +35,9 @@ class ProjectNamePrepareAuth
35
35
  } else {
36
36
  $auth_prefix = \Voxgig\Struct\Struct::getpath($options, 'auth.prefix') ?? '';
37
37
  $apikey_val = is_string($apikey) ? $apikey : '';
38
- $headers[self::HEADER_AUTH] = "{$auth_prefix} {$apikey_val}";
38
+ // Empty prefix (raw apiKey credential) must not add a leading space.
39
+ $headers[self::HEADER_AUTH] = $auth_prefix === ''
40
+ ? $apikey_val : "{$auth_prefix} {$apikey_val}";
39
41
  }
40
42
 
41
43
  return [$spec, null];