@voxgig/sdkgen 4.7.0 → 4.8.1

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/helpers/opShape.js +8 -11
  3. package/dist/helpers/opShape.js.map +1 -1
  4. package/dist/helpers/pointPath.d.ts +10 -0
  5. package/dist/helpers/pointPath.js +91 -0
  6. package/dist/helpers/pointPath.js.map +1 -0
  7. package/dist/sdkgen.d.ts +4 -2
  8. package/dist/sdkgen.js +8 -3
  9. package/dist/sdkgen.js.map +1 -1
  10. package/dist/tsconfig.tsbuildinfo +1 -1
  11. package/dist/utility.js +26 -1
  12. package/dist/utility.js.map +1 -1
  13. package/package.json +2 -2
  14. package/project/.sdk/src/cmp/c/Config_c.ts +7 -8
  15. package/project/.sdk/src/cmp/c/TestDirect_c.ts +4 -3
  16. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +4 -3
  17. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +5 -4
  18. package/project/.sdk/src/cmp/dart/Config_dart.ts +7 -8
  19. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +6 -5
  20. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +9 -8
  21. package/project/.sdk/src/cmp/go/Config_go.ts +1 -7
  22. package/project/.sdk/src/cmp/go/TestDirect_go.ts +5 -4
  23. package/project/.sdk/src/cmp/java/Config_java.ts +7 -7
  24. package/project/.sdk/src/cmp/java/TestDirect_java.ts +5 -4
  25. package/project/.sdk/src/cmp/js/Config_js.ts +7 -8
  26. package/project/.sdk/src/cmp/js/TestDirect_js.ts +5 -4
  27. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +7 -7
  28. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +5 -4
  29. package/project/.sdk/src/cmp/lean/Config_lean.ts +9 -8
  30. package/project/.sdk/src/cmp/lua/Config_lua.ts +1 -7
  31. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +5 -4
  32. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +5 -4
  33. package/project/.sdk/src/cmp/php/Config_php.ts +1 -7
  34. package/project/.sdk/src/cmp/php/TestDirect_php.ts +5 -4
  35. package/project/.sdk/src/cmp/py/Config_py.ts +1 -7
  36. package/project/.sdk/src/cmp/py/TestDirect_py.ts +5 -4
  37. package/project/.sdk/src/cmp/rb/Config_rb.ts +1 -7
  38. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +5 -4
  39. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +5 -4
  40. package/project/.sdk/src/cmp/scala/Config_scala.ts +9 -7
  41. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +4 -3
  42. package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +3 -1
  43. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +9 -5
  44. package/project/.sdk/src/cmp/ts/Config_ts.ts +7 -8
  45. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +6 -5
  46. package/project/sdkgen-package.json +1 -1
  47. package/src/helpers/opShape.ts +9 -14
  48. package/src/helpers/pointPath.ts +118 -0
  49. package/src/sdkgen.ts +11 -0
  50. package/src/utility.ts +31 -1
@@ -0,0 +1,118 @@
1
+ /* Copyright (c) 2024-2025 Voxgig, MIT License */
2
+
3
+ // A point's path, as apidef resolves it.
4
+ //
5
+ // apidef ADR-003: the model carries the path as a typed segment vector —
6
+ // `[{ lit: 'element' }, { var: 'id' }]` — and no brace-templated string. A
7
+ // `var` names one of the point's `args.params` directly, so a consumer walks
8
+ // the vector instead of parsing anything.
9
+ //
10
+ // The generated SDK runtimes still speak the older braced-string form
11
+ // (`['element', '{id}']`), which this file reconstructs in ONE place. Every
12
+ // generation-time consumer and the embedded config go through here, so when
13
+ // the runtimes move onto segments there is a single call site to retire —
14
+ // rather than the twenty-odd hand-written brace parsers ADR-003 is about.
15
+
16
+
17
+ import { SdkGenError } from '../utility'
18
+
19
+
20
+ type PathSegment = {
21
+ lit?: string
22
+ var?: string
23
+ }
24
+
25
+
26
+ // The segment vector, defensively. GraphQL points address the single endpoint
27
+ // and carry no path, so an empty vector is normal, not a fault.
28
+ //
29
+ // A point carrying the OLD `parts` and no `segments` is a different thing
30
+ // entirely: a model generated by apidef <= 8.1, checked in under a project's
31
+ // `.sdk/model/`, that has not been regenerated. Treating it as pathless
32
+ // would emit an SDK whose every request goes to the API ROOT — silently,
33
+ // because an empty path is indistinguishable from a GraphQL point here.
34
+ //
35
+ // THIS is where the check belongs. apidef 8.2.0 tried it in the schema
36
+ // (`parts?: null`) and that bricked the upgrade instead: the stale model is
37
+ // the INPUT to the tool that rewrites it, and voxgig-model unifies before it
38
+ // runs apidef's build, so the file blocked its own regeneration. Here the
39
+ // model unifies, apidef regenerates it, and a stale point only ever reaches
40
+ // this helper when that regeneration did NOT happen — exactly when the
41
+ // project needs to be told.
42
+ function pointSegments(point: any): PathSegment[] {
43
+ const segments = point && point.segments
44
+
45
+ if (!Array.isArray(segments) && Array.isArray(point && point.parts)) {
46
+ throw new SdkGenError(
47
+ 'model: the point for `' + ((point && point.orig) || '(unknown path)') +
48
+ '` still describes its path as `parts` (' +
49
+ JSON.stringify(point.parts) + ') and carries no `segments`. apidef ' +
50
+ 'replaced the braced-string form with the typed segment vector in ' +
51
+ '8.2.0 (its ADR-003), so this model was generated by an older apidef ' +
52
+ 'and has not been regenerated. Generating from it would build every ' +
53
+ 'URL as the API root. Run `npm run generate` in the project\'s `.sdk` ' +
54
+ 'so apidef rewrites its entity models, then generate again.')
55
+ }
56
+
57
+ return Array.isArray(segments) ? segments : []
58
+ }
59
+
60
+
61
+ // The braced-string form the SDK runtimes still consume. A `lit` is emitted
62
+ // verbatim — including one that contains braces, which apidef leaves literal
63
+ // (a compound element like `{a}.{b}` names no single parameter). That is the
64
+ // lossiness ADR-003 removed from the model, and it survives here only because
65
+ // this is the OLD representation being reconstructed on the way out.
66
+ function pointParts(point: any): string[] {
67
+ return pointSegments(point).map((seg: PathSegment) =>
68
+ null == seg.var ? String(seg.lit ?? '') : '{' + seg.var + '}')
69
+ }
70
+
71
+
72
+ // Does the path end in a parameter?
73
+ //
74
+ // DELIBERATELY asked of the reconstructed part, not of the vector, even
75
+ // though the vector states it directly and more accurately.
76
+ //
77
+ // The same rule runs at RUNTIME, in all 21 languages' makePoint template
78
+ // (`0 === last.indexOf('{')` over `parts`), to pick a fallback route when no
79
+ // point's `select.exist` matches. Those templates ship standalone, outside
80
+ // this package, so the rule is written twice on purpose and BOTH SIDES MUST
81
+ // MOVE TOGETHER — see the note in opShape.ts and in each template.
82
+ //
83
+ // Reading the vector here would break that. A path ending in a LITERAL that
84
+ // contains braces (`/reports/{id}.json`) is not a terminal parameter by the
85
+ // vector, but every runtime still says it is, because from the reconstructed
86
+ // string it cannot tell. Generation-time `ownPoint` would then pick a
87
+ // different route than the SDK picks at request time — for the same model.
88
+ //
89
+ // So this stays bug-compatible with the runtimes until they move onto
90
+ // segments, at which point this becomes `null != last.var` and all 21 change
91
+ // with it.
92
+ function pointTerminalParam(point: any): boolean {
93
+ const parts = pointParts(point)
94
+ const last = 0 < parts.length ? parts[parts.length - 1] : ''
95
+ return 0 === last.indexOf('{')
96
+ }
97
+
98
+
99
+ // Do two points describe the same route? Compares the vectors, so a literal
100
+ // containing braces cannot be mistaken for a parameter of the same spelling.
101
+ function pointPathKey(point: any): string {
102
+ return pointSegments(point)
103
+ .map((seg: PathSegment) =>
104
+ null == seg.var ? 'l:' + String(seg.lit ?? '') : 'v:' + seg.var)
105
+ .join('/')
106
+ }
107
+
108
+
109
+ export type {
110
+ PathSegment,
111
+ }
112
+
113
+ export {
114
+ pointSegments,
115
+ pointParts,
116
+ pointTerminalParam,
117
+ pointPathKey,
118
+ }
package/src/sdkgen.ts CHANGED
@@ -67,6 +67,9 @@ import { serverVariables, hasServerVariables, serverVarEnv } from './helpers/ser
67
67
  import { primaryOpCall, idLiteral, matchArg, dataArg, litFor } from './helpers/opExample'
68
68
  import type { ExampleLang } from './helpers/opExample'
69
69
  import { liveStrict } from './helpers/testPolicy'
70
+ import { pointSegments, pointParts, pointTerminalParam, pointPathKey }
71
+ from './helpers/pointPath'
72
+ import type { PathSegment } from './helpers/pointPath'
70
73
  import {
71
74
  featureOf,
72
75
  availableFeatures,
@@ -1014,6 +1017,7 @@ export type {
1014
1017
  SdkGenOptions,
1015
1018
  ExampleLang,
1016
1019
  DepEntry,
1020
+ PathSegment,
1017
1021
  FeatureSource,
1018
1022
  DoctorReport,
1019
1023
  RegisterOptions,
@@ -1119,6 +1123,13 @@ export {
1119
1123
  buildIdNames,
1120
1124
  getMatchEntries,
1121
1125
  collectDeps,
1126
+
1127
+ // apidef ADR-003: the model carries typed path segments, not braced
1128
+ // strings. Scaffold components read the path through these.
1129
+ pointSegments,
1130
+ pointParts,
1131
+ pointTerminalParam,
1132
+ pointPathKey,
1122
1133
  canonToType,
1123
1134
  canonToDtype,
1124
1135
  canonKey,
package/src/utility.ts CHANGED
@@ -8,6 +8,7 @@ import { KIT, getModelPath } from '@voxgig/apidef'
8
8
  import { targetFeatures } from './helpers/applicability'
9
9
 
10
10
  import { serverVariables } from './helpers/serverVars'
11
+ import { pointParts } from './helpers/pointPath'
11
12
  import { packageVersion } from './helpers/packageMeta'
12
13
 
13
14
 
@@ -350,6 +351,35 @@ const SPEC_FACTS: Record<string, (model: any) => any> = {
350
351
  //
351
352
  // Key order is `each`'s order, which is sorted, so the JSON is byte-stable
352
353
  // across runs exactly like the literal it replaces.
354
+ // The embedded config still speaks the braced-string path form
355
+ // (`['element', '{id}']`) that every generated runtime reads. apidef now
356
+ // emits the resolved vector instead (its ADR-003), so the old shape is
357
+ // reconstructed HERE — the single point at which a point reaches generated
358
+ // output — via the single reconstruction in helpers/pointPath.
359
+ //
360
+ // Both are written: `parts` for the runtimes as they stand, `segments`
361
+ // alongside it so a runtime can be moved over one language at a time. That
362
+ // duplication is deliberate and temporary, and it lives in GENERATED output,
363
+ // not in the model — which is what ADR-003 forbids. It ends when the last
364
+ // runtime reads segments and `parts` is dropped from this function.
365
+ function withPointParts(op: any): any {
366
+ if (null == op) {
367
+ return op
368
+ }
369
+
370
+ const out: any = {}
371
+ each(op, (o: any, opname: string) => {
372
+ out[opname] = null == o || null == o.points ? o : {
373
+ ...o,
374
+ points: each(o.points).map((pt: any) =>
375
+ null == pt ? pt : { ...pt, parts: pointParts(pt) }),
376
+ }
377
+ })
378
+
379
+ return out
380
+ }
381
+
382
+
353
383
  function configDefinition(model: any, targetname?: string): { def: any, json: string } {
354
384
  const entity = getModelPath(model, `main.${KIT}.entity`)
355
385
 
@@ -377,7 +407,7 @@ function configDefinition(model: any, targetname?: string): { def: any, json: st
377
407
  entityDefs[e.name] = clean({
378
408
  fields: e.fields,
379
409
  name: e.name,
380
- op: e.op,
410
+ op: withPointParts(e.op),
381
411
  relations: e.relations,
382
412
  }, true)
383
413
  entityStubs[e.name] = {}