@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.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/helpers/opShape.js +8 -11
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/helpers/pointPath.d.ts +10 -0
- package/dist/helpers/pointPath.js +91 -0
- package/dist/helpers/pointPath.js.map +1 -0
- package/dist/sdkgen.d.ts +4 -2
- package/dist/sdkgen.js +8 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.js +26 -1
- package/dist/utility.js.map +1 -1
- package/package.json +2 -2
- package/project/.sdk/src/cmp/c/Config_c.ts +7 -8
- package/project/.sdk/src/cmp/c/TestDirect_c.ts +4 -3
- package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +4 -3
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +5 -4
- package/project/.sdk/src/cmp/dart/Config_dart.ts +7 -8
- package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +6 -5
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +9 -8
- package/project/.sdk/src/cmp/go/Config_go.ts +1 -7
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +5 -4
- package/project/.sdk/src/cmp/java/Config_java.ts +7 -7
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +5 -4
- package/project/.sdk/src/cmp/js/Config_js.ts +7 -8
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +5 -4
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +7 -7
- package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +5 -4
- package/project/.sdk/src/cmp/lean/Config_lean.ts +9 -8
- package/project/.sdk/src/cmp/lua/Config_lua.ts +1 -7
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +5 -4
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +5 -4
- package/project/.sdk/src/cmp/php/Config_php.ts +1 -7
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +5 -4
- package/project/.sdk/src/cmp/py/Config_py.ts +1 -7
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +5 -4
- package/project/.sdk/src/cmp/rb/Config_rb.ts +1 -7
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +5 -4
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +5 -4
- package/project/.sdk/src/cmp/scala/Config_scala.ts +9 -7
- package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +4 -3
- package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +3 -1
- package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +9 -5
- package/project/.sdk/src/cmp/ts/Config_ts.ts +7 -8
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +6 -5
- package/project/sdkgen-package.json +1 -1
- package/src/helpers/opShape.ts +9 -14
- package/src/helpers/pointPath.ts +118 -0
- package/src/sdkgen.ts +11 -0
- 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] = {}
|