@voxgig/sdkgen 3.5.1 → 3.6.0
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/cmp/ExternalTarget.js +1 -1
- package/dist/cmp/ExternalTarget.js.map +1 -1
- package/dist/helpers/naming.d.ts +4 -1
- package/dist/helpers/naming.js +119 -5
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/opShape.js +72 -0
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/helpers/packageMeta.d.ts +2 -1
- package/dist/helpers/packageMeta.js +57 -20
- package/dist/helpers/packageMeta.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -3
- package/dist/sdkgen.js +61 -18
- package/dist/sdkgen.js.map +1 -1
- package/dist/testkit.d.ts +39 -0
- package/dist/testkit.js +663 -0
- package/dist/testkit.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -8
- package/project/.sdk/src/cmp/c/Test_c.ts +5 -7
- package/project/.sdk/src/cmp/clojure/Test_clojure.ts +3 -7
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -7
- package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -7
- package/project/.sdk/src/cmp/dart/Test_dart.ts +3 -4
- package/project/.sdk/src/cmp/elixir/Package_elixir.ts +1 -1
- package/project/.sdk/src/cmp/elixir/Test_elixir.ts +5 -6
- package/project/.sdk/src/cmp/go/EntityTypes_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Entity_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Gitignore_go.ts +6 -0
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -2
- package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeExamplesTest_go.ts +59 -6
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Test_go.ts +6 -8
- package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +6 -2
- package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +6 -2
- package/project/.sdk/src/cmp/java/Test_java.ts +4 -7
- package/project/.sdk/src/cmp/js/Package_js.ts +1 -1
- package/project/.sdk/src/cmp/js/Test_js.ts +4 -7
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +4 -7
- package/project/.sdk/src/cmp/lean/Test_lean.ts +3 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -7
- package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +3 -7
- package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -7
- package/project/.sdk/src/cmp/php/EntityTypes_php.ts +13 -2
- package/project/.sdk/src/cmp/php/Package_php.ts +2 -2
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -7
- package/project/.sdk/src/cmp/py/Package_py.ts +2 -2
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -7
- package/project/.sdk/src/cmp/py-data/Main_py-data.ts +14 -19
- package/project/.sdk/src/cmp/rb/Package_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +1 -1
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -7
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -7
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -2
- package/project/.sdk/src/cmp/seneca-provider/Gitignore_seneca-provider.ts +161 -0
- package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +20 -11
- package/project/.sdk/src/cmp/swift/Test_swift.ts +5 -7
- package/project/.sdk/src/cmp/ts/Package_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/Test_ts.ts +8 -7
- package/project/.sdk/src/cmp/zig/Test_zig.ts +3 -7
- package/project/.sdk/tm/c/utility/make_point.c +64 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +49 -14
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +55 -3
- package/project/.sdk/tm/csharp/feature/TestFeature.cs +36 -2
- package/project/.sdk/tm/csharp/utility/MakePoint.cs +65 -2
- package/project/.sdk/tm/dart/lib/feature/test/TestFeature.dart +34 -4
- package/project/.sdk/tm/dart/lib/utility/MakePointUtility.dart +48 -2
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +70 -10
- package/project/.sdk/tm/go/feature/test_feature.go +34 -2
- package/project/.sdk/tm/go/utility/make_point.go +66 -2
- package/project/.sdk/tm/java/feature/TestFeature.java +42 -2
- package/project/.sdk/tm/java/utility/MakePoint.java +61 -2
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +22 -3
- package/project/.sdk/tm/js/src/utility/MakePointUtility.js +72 -2
- package/project/.sdk/tm/kotlin/feature/TestFeature.kt +31 -2
- package/project/.sdk/tm/kotlin/utility/MakePoint.kt +58 -2
- package/project/.sdk/tm/lean/src/SdkUtility.lean +28 -0
- package/project/.sdk/tm/lua/utility/make_point.lua +65 -2
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +40 -2
- package/project/.sdk/tm/perl/utility/make_point.pm +55 -2
- package/project/.sdk/tm/php/feature/TestFeature.php +30 -1
- package/project/.sdk/tm/php/utility/MakePoint.php +49 -1
- package/project/.sdk/tm/py/pkg/utility/make_point.py +56 -2
- package/project/.sdk/tm/rb/utility/make_point.rb +42 -2
- package/project/.sdk/tm/rust/utility/make_point.rs +69 -2
- package/project/.sdk/tm/scala/feature/TestFeature.scala +36 -1
- package/project/.sdk/tm/scala/utility/Make.scala +57 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +29 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +51 -2
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +22 -3
- package/project/.sdk/tm/ts/src/utility/MakePointUtility.ts +72 -2
- package/project/.sdk/tm/zig/core/utility.zig +66 -3
- package/project/sdkgen-package.json +1 -2
- package/src/cmp/ExternalTarget.ts +1 -1
- package/src/helpers/naming.ts +129 -5
- package/src/helpers/opShape.ts +85 -0
- package/src/helpers/packageMeta.ts +59 -20
- package/src/sdkgen.ts +68 -20
- package/src/testkit.ts +863 -0
- package/testkit.d.ts +3 -0
- package/testkit.js +12 -0
- package/project/.sdk/model/target/haskell.aontu +0 -72
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +0 -98
- package/project/.sdk/src/cmp/haskell/Entity_haskell.ts +0 -13
- package/project/.sdk/src/cmp/haskell/Gitignore_haskell.ts +0 -27
- package/project/.sdk/src/cmp/haskell/Main_haskell.ts +0 -123
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +0 -60
- package/project/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +0 -186
- package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +0 -135
- package/project/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +0 -48
- package/project/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +0 -168
- package/project/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +0 -51
- package/project/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +0 -67
- package/project/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +0 -162
- package/project/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +0 -74
- package/project/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +0 -241
- package/project/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +0 -408
- package/project/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +0 -31
- package/project/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +0 -104
- package/project/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +0 -80
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +0 -300
- package/project/.sdk/src/cmp/haskell/tsconfig.json +0 -15
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +0 -166
- package/project/.sdk/tm/haskell/LICENSE +0 -21
- package/project/.sdk/tm/haskell/Makefile +0 -22
- package/project/.sdk/tm/haskell/VERSION +0 -1
- package/project/.sdk/tm/haskell/src/SdkFeatures.hs +0 -1440
- package/project/.sdk/tm/haskell/src/SdkHelpers.hs +0 -337
- package/project/.sdk/tm/haskell/src/SdkJson.hs +0 -111
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +0 -1219
- package/project/.sdk/tm/haskell/src/SdkTypes.hs +0 -195
- package/project/.sdk/tm/haskell/src/VoxgigStruct.hs +0 -2299
- package/project/.sdk/tm/haskell/src/Vregex.hs +0 -237
- package/project/.sdk/tm/haskell/src/feature/README.md +0 -5
- package/project/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/test/Harness.hs +0 -281
- package/project/.sdk/tm/haskell/test/Runner.hs +0 -47
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +0 -449
- package/project/.sdk/tm/haskell/test/TCustomUtility.hs +0 -38
- package/project/.sdk/tm/haskell/test/TFeature.hs +0 -794
- package/project/.sdk/tm/haskell/test/TNetsim.hs +0 -50
- package/project/.sdk/tm/haskell/test/TPipeline.hs +0 -334
- package/project/.sdk/tm/haskell/test/TPrimaryUtility.hs +0 -386
- package/project/.sdk/tm/haskell/test/Testutil.hs +0 -59
package/src/helpers/naming.ts
CHANGED
|
@@ -112,17 +112,67 @@ function isRbCoreConstant(Name: string): boolean {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
// RUBY SDK-OWNED CONSTANTS — the OTHER half of "already taken".
|
|
116
|
+
//
|
|
117
|
+
// `RB_CORE_CONSTANTS` guards names the LANGUAGE owns. This guards names the
|
|
118
|
+
// GENERATED SCAFFOLDING claims for itself, which is a distinct hazard and the
|
|
119
|
+
// one that stayed open: an entity named `Runner` produced `class Runner` in
|
|
120
|
+
// `<Sdk>_types.rb`, and the generated harness then does
|
|
121
|
+
//
|
|
122
|
+
// Runner = ProjectNameTestRunner # tm/rb/test/runner.rb
|
|
123
|
+
//
|
|
124
|
+
// so inside the test process the entity TYPE silently *is* the test runner.
|
|
125
|
+
// Ruby warns (`already initialized constant Runner`) and carries on, which is
|
|
126
|
+
// why it survived: nothing in the suite exercises the type, so `rb` passed.
|
|
127
|
+
// Reported on gitlab-sdk, which has a `Runner` entity (issue #64).
|
|
128
|
+
//
|
|
129
|
+
// EVERYTHING SHARES ONE NAMESPACE HERE, unlike swift. Swift's Tests module is
|
|
130
|
+
// separate from the SDK module, so `SWIFT_SDK_TYPES` can exclude test-only
|
|
131
|
+
// declarations. Ruby has no such split — the types file and the test files are
|
|
132
|
+
// all required into one process when the suite runs, which is exactly the
|
|
133
|
+
// reported symptom — so test-declared names belong in this set.
|
|
134
|
+
//
|
|
135
|
+
// PREFIXED DECLARATIONS ARE DELIBERATELY ABSENT. `ProjectNameUtility` and
|
|
136
|
+
// friends substitute to `<Sdk>Utility`, and a generated entity type is the
|
|
137
|
+
// bare entity name, so those cannot collide. Only the UNPREFIXED declarations
|
|
138
|
+
// are reachable.
|
|
139
|
+
//
|
|
140
|
+
// `rb-sdk-constants.test.ts` re-derives this list from the templates AND the
|
|
141
|
+
// components and fails on drift — the same discipline as
|
|
142
|
+
// `swift-sdk-types.test.ts`, and for the same reason: the first cut of that
|
|
143
|
+
// list was collected by hand and missed three names, one of them declared by
|
|
144
|
+
// a component rather than a template.
|
|
145
|
+
const RB_SDK_CONSTANTS = new Set<string>([
|
|
146
|
+
// module-level aliases the test harness defines for convenience
|
|
147
|
+
'Helpers', 'Runner', 'Vs',
|
|
148
|
+
// vendored struct library and its test scaffolding
|
|
149
|
+
'StructRunner', 'StructTestClient', 'StructUtilityTest', 'VoxgigStruct',
|
|
150
|
+
'STRUCT_TEST_JSON_FILE',
|
|
151
|
+
// the generated/templated test classes
|
|
152
|
+
'ExistsTest', 'FeatureTest', 'NetsimTest', 'PipelineTest',
|
|
153
|
+
'PrimaryUtilityTest', 'ReadmeExamplesTest', 'TestHookFeature',
|
|
154
|
+
'TestInitFeature',
|
|
155
|
+
])
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// Does `Name` collide with a constant the generated Ruby SDK already declares?
|
|
159
|
+
function isRbSdkConstant(Name: string): boolean {
|
|
160
|
+
return RB_SDK_CONSTANTS.has(Name)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
115
164
|
// A top-level-safe Ruby constant name for a generated type: the name
|
|
116
|
-
// unchanged, unless
|
|
117
|
-
// appended (`File` -> `FileType
|
|
118
|
-
//
|
|
119
|
-
//
|
|
165
|
+
// unchanged, unless it is ALREADY TAKEN — by Ruby core, or by the SDK's own
|
|
166
|
+
// scaffolding — in which case `Type` is appended (`File` -> `FileType`,
|
|
167
|
+
// `Runner` -> `RunnerType`). Applied ONLY to the bare entity data type: the
|
|
168
|
+
// per-op type names always carry a suffix (`FileCreateData`,
|
|
169
|
+
// `FileLoadMatch`), which nothing in either set matches.
|
|
120
170
|
//
|
|
121
171
|
// The entity ACCESSOR keeps its original name (`client.File(...)`): methods
|
|
122
172
|
// and constants live in separate namespaces in Ruby, so the accessor never
|
|
123
173
|
// collided and the public surface is unchanged.
|
|
124
174
|
function rbSafeTypeName(Name: string): string {
|
|
125
|
-
return isRbCoreConstant(Name) ? Name + 'Type' : Name
|
|
175
|
+
return isRbCoreConstant(Name) || isRbSdkConstant(Name) ? Name + 'Type' : Name
|
|
126
176
|
}
|
|
127
177
|
|
|
128
178
|
|
|
@@ -195,6 +245,77 @@ function swiftSafeTypeName(Name: string): string {
|
|
|
195
245
|
}
|
|
196
246
|
|
|
197
247
|
|
|
248
|
+
// PHP RESERVED WORDS THAT CANNOT BE A CLASS NAME — a fourth hazard, and the
|
|
249
|
+
// only one of the four where the check must be CASE-INSENSITIVE.
|
|
250
|
+
//
|
|
251
|
+
// PHP's grammar refuses a reserved word where a class name is expected, so a
|
|
252
|
+
// spec with a `Namespace` entity emitted a file that does not parse at all:
|
|
253
|
+
//
|
|
254
|
+
// /** Namespace entity data model. */
|
|
255
|
+
// class Namespace
|
|
256
|
+
// {
|
|
257
|
+
// }
|
|
258
|
+
// -> PHP Parse error: syntax error, unexpected token "namespace",
|
|
259
|
+
// expecting identifier
|
|
260
|
+
//
|
|
261
|
+
// Found on gitlab-sdk (issue #64), and LATENT in the worst way: `types/` is on
|
|
262
|
+
// the composer classmap but nothing references the file, so PHP never loads it
|
|
263
|
+
// and the php lane passed for as long as the target has existed. It would
|
|
264
|
+
// fatal the moment anything required it, and the documented type is unusable
|
|
265
|
+
// either way.
|
|
266
|
+
//
|
|
267
|
+
// CASE-INSENSITIVITY IS THE WHOLE TRICK. Class and function names in PHP are
|
|
268
|
+
// case-insensitive, so `Namespace`, `NAMESPACE` and `namespace` are one
|
|
269
|
+
// identifier — and the generated name is PascalCase while the reserved word is
|
|
270
|
+
// lowercase. A case-sensitive `Set.has('Namespace')` against a lowercase list
|
|
271
|
+
// therefore matches NOTHING and reopens the bug silently. Entries are stored
|
|
272
|
+
// folded, and the lookup folds too.
|
|
273
|
+
//
|
|
274
|
+
// Taken from the PHP manual's reserved-words tables wholesale rather than
|
|
275
|
+
// grown one collision at a time: the list is fixed by the language, and
|
|
276
|
+
// picking from it by hand is how the second `Namespace` gets shipped.
|
|
277
|
+
const PHP_RESERVED_TYPES = new Set<string>([
|
|
278
|
+
// keywords
|
|
279
|
+
'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch',
|
|
280
|
+
'class', 'clone', 'const', 'continue', 'declare', 'default', 'do', 'echo',
|
|
281
|
+
'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif',
|
|
282
|
+
'endswitch', 'endwhile', 'enum', 'eval', 'exit', 'extends', 'final',
|
|
283
|
+
'finally', 'fn', 'for', 'foreach', 'function', 'global', 'goto', 'if',
|
|
284
|
+
'implements', 'include', 'include_once', 'instanceof', 'insteadof',
|
|
285
|
+
'interface', 'isset', 'list', 'match', 'namespace', 'new', 'or', 'print',
|
|
286
|
+
'private', 'protected', 'public', 'readonly', 'require', 'require_once',
|
|
287
|
+
'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use',
|
|
288
|
+
'var', 'while', 'xor', 'yield',
|
|
289
|
+
// compile-time constants
|
|
290
|
+
'__class__', '__dir__', '__file__', '__function__', '__line__',
|
|
291
|
+
'__method__', '__namespace__', '__trait__',
|
|
292
|
+
// reserved as type names / cannot be declared as a class
|
|
293
|
+
'bool', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null',
|
|
294
|
+
'numeric', 'object', 'parent', 'resource', 'self', 'string', 'true', 'void',
|
|
295
|
+
])
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
// Does `Name` collide with a word PHP reserves, ignoring case as PHP does?
|
|
299
|
+
function isPhpReservedType(Name: string): boolean {
|
|
300
|
+
return PHP_RESERVED_TYPES.has(String(Name).toLowerCase())
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
// A declarable PHP class name for a generated type: unchanged, unless PHP
|
|
305
|
+
// reserves the word, in which case `Type` is appended (`Namespace` ->
|
|
306
|
+
// `NamespaceType`). Mirrors rbSafeTypeName and swiftSafeTypeName deliberately
|
|
307
|
+
// — same suffix, same "only rename on an actual collision" rule, so every SDK
|
|
308
|
+
// that does not collide is byte-identical to before.
|
|
309
|
+
//
|
|
310
|
+
// Applied ONLY to the bare entity data class. Per-op type names already carry
|
|
311
|
+
// their own suffix (`NamespaceLoadData`, `NamespaceCreateData`), which no
|
|
312
|
+
// reserved word matches, and the entity ACCESSOR is a method rather than a
|
|
313
|
+
// class — PHP resolves those separately — so the public surface is unchanged.
|
|
314
|
+
function phpSafeTypeName(Name: string): string {
|
|
315
|
+
return isPhpReservedType(Name) ? Name + 'Type' : Name
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
198
319
|
// Is `name` a reserved word (an illegal identifier) in the target language?
|
|
199
320
|
function isReservedName(name: string, lang: string): boolean {
|
|
200
321
|
const set = RESERVED[lang]
|
|
@@ -328,9 +449,12 @@ export {
|
|
|
328
449
|
phpEntityAccessor,
|
|
329
450
|
entityCacheField,
|
|
330
451
|
isRbCoreConstant,
|
|
452
|
+
isRbSdkConstant,
|
|
331
453
|
rbSafeTypeName,
|
|
332
454
|
isSwiftSdkType,
|
|
333
455
|
swiftSafeTypeName,
|
|
456
|
+
isPhpReservedType,
|
|
457
|
+
phpSafeTypeName,
|
|
334
458
|
jsProp,
|
|
335
459
|
jsOptProp,
|
|
336
460
|
jsKey,
|
package/src/helpers/opShape.ts
CHANGED
|
@@ -189,6 +189,72 @@ function entityPath(entity: any): string {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
// The entity's OWN endpoint among an op's points.
|
|
193
|
+
//
|
|
194
|
+
// The other notion of canonical above — "not a `$action` route" — separates a
|
|
195
|
+
// folded-in custom action from the real op. This one separates the entity's
|
|
196
|
+
// own route from a CROSS-REFERENCE: another resource's route that happens to
|
|
197
|
+
// return this entity (`/notifications/{id}/board` for a board). Both are
|
|
198
|
+
// plain GETs with no `$action`, so the path itself is all there is to go on.
|
|
199
|
+
//
|
|
200
|
+
// Two signals, in order:
|
|
201
|
+
//
|
|
202
|
+
// 1. A record route ends in the record's identifier (`/boards/{id}`,
|
|
203
|
+
// `/accounts/{account_id}/users/{id}`); a cross-reference ends in the
|
|
204
|
+
// relationship's name (`/posts/{id}/author`). A terminal parameter is
|
|
205
|
+
// the stronger signal, and unlike depth alone it survives an entity
|
|
206
|
+
// nested more deeply than the route that points at it — depth by itself
|
|
207
|
+
// picked `/posts/{id}/author` over `/accounts/{account_id}/users/{id}`
|
|
208
|
+
// and then generated that op's required params from the wrong route.
|
|
209
|
+
// 2. Failing that, fewest path segments — the shallower route is the one
|
|
210
|
+
// the entity is named for, which is what settles a `list` op where
|
|
211
|
+
// neither route ends in an id (`/boards` over `/members/{id}/boards`).
|
|
212
|
+
//
|
|
213
|
+
// Ties keep the earlier point, so the sorted-key order of the model decides
|
|
214
|
+
// and the output stays byte-stable.
|
|
215
|
+
//
|
|
216
|
+
// The same rule runs at RUNTIME, in each language's makePoint template, when
|
|
217
|
+
// no point's `select.exist` matches. It cannot be shared with them — a
|
|
218
|
+
// template ships standalone, outside this package — so it is written twice on
|
|
219
|
+
// purpose, and both sides must move together.
|
|
220
|
+
function terminalParam(point: any): boolean {
|
|
221
|
+
const parts: any[] = (point && point.parts) || []
|
|
222
|
+
const last = 0 < parts.length ? parts[parts.length - 1] : ''
|
|
223
|
+
return 'string' === typeof last && last.startsWith('{')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
function ownPoint(points: any[]): any {
|
|
228
|
+
let best = points[0]
|
|
229
|
+
|
|
230
|
+
for (const pt of points) {
|
|
231
|
+
if (null == pt || null == pt.parts || null == best || null == best.parts) {
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const ptterm = terminalParam(pt)
|
|
236
|
+
const bestterm = terminalParam(best)
|
|
237
|
+
|
|
238
|
+
if (ptterm !== bestterm ? ptterm : pt.parts.length < best.parts.length) {
|
|
239
|
+
best = pt
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return best
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
// Do all these points describe the same route? Then they are alternative
|
|
248
|
+
// selectors on one endpoint (the same path, chosen by different query
|
|
249
|
+
// params), not cross-references to different resources.
|
|
250
|
+
function samePath(points: any[]): boolean {
|
|
251
|
+
const first = ((points[0] && points[0].parts) || []).join('/')
|
|
252
|
+
|
|
253
|
+
return points.every((pt: any) =>
|
|
254
|
+
first === ((pt && pt.parts) || []).join('/'))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
192
258
|
function opParams(op: any): any[] {
|
|
193
259
|
let points: any[] = op && op.points ? each(op.points) : []
|
|
194
260
|
|
|
@@ -227,6 +293,25 @@ function opParams(op: any): any[] {
|
|
|
227
293
|
p.reqd = true === requiredOnAll[p.name]
|
|
228
294
|
})
|
|
229
295
|
|
|
296
|
+
// Unrelated cross-references share no param, so the intersection came out
|
|
297
|
+
// empty and NOTHING is required — which generates a `load({})` that drops
|
|
298
|
+
// the id. Fall back to the entity's own point and take its params whole.
|
|
299
|
+
//
|
|
300
|
+
// Two guards, and both are load-bearing:
|
|
301
|
+
//
|
|
302
|
+
// - only when the intersection produced nothing, so a genuine set of
|
|
303
|
+
// alternative routes with a shared param still merges (a `page_id` on
|
|
304
|
+
// every route stays required, per-route siblings stay optional);
|
|
305
|
+
// - only when the points describe DIFFERENT routes. Points on one path
|
|
306
|
+
// that differ solely by optional query params (`/users?email=` and
|
|
307
|
+
// `/users?name=`) legitimately require nothing, and are not
|
|
308
|
+
// cross-references — collapsing them to one point would drop the
|
|
309
|
+
// other's fields from the generated request type, so a caller could
|
|
310
|
+
// not express them at all.
|
|
311
|
+
if (1 < points.length && !out.some((p: any) => p.reqd) && !samePath(points)) {
|
|
312
|
+
return opParams({ points: [ownPoint(points)] })
|
|
313
|
+
}
|
|
314
|
+
|
|
230
315
|
return out
|
|
231
316
|
}
|
|
232
317
|
|
|
@@ -40,6 +40,27 @@ function langLabel(target: string): string {
|
|
|
40
40
|
return LANG_LABEL[target] || target
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
|
|
44
|
+
// THE NAME WHOSE LANGUAGE RULES APPLY.
|
|
45
|
+
//
|
|
46
|
+
// A target name does two unrelated jobs in this file, and conflating them is
|
|
47
|
+
// the defect this exists to end. `go~go2` installs a SECOND Go SDK: its
|
|
48
|
+
// CONFIG lives under `main.kit.target.go2` (its own module path, its own
|
|
49
|
+
// registry state), but it is still Go — the same go.mod shape, the same
|
|
50
|
+
// `go get` install line, the same "Go" label.
|
|
51
|
+
//
|
|
52
|
+
// So: look config up by the target's OWN name, and select behaviour by this.
|
|
53
|
+
// `origname` is stamped at add time precisely for aliased installs and is
|
|
54
|
+
// empty when a target was installed under its own name, which makes the
|
|
55
|
+
// unaliased case identical to what it was.
|
|
56
|
+
//
|
|
57
|
+
// Ecosystem keys ('npm', 'gem', 'composer') pass through untouched: there is
|
|
58
|
+
// no target node under those names, so they answer for themselves.
|
|
59
|
+
function originName(model: any, target: string): string {
|
|
60
|
+
const orig = model?.main?.[KIT]?.target?.[target]?.origname
|
|
61
|
+
return (null != orig && '' !== orig) ? String(orig) : target
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
// Git host, org/repo path, and the canonical repo URLs.
|
|
44
65
|
//
|
|
45
66
|
// `<origin>/<slug>-sdk` is a DERIVATION, not a law. A project whose repo is
|
|
@@ -185,7 +206,10 @@ function goPackageIdent(model: any, target: string): string {
|
|
|
185
206
|
// The legacy boolean `registry.active: true` is honoured as a back-compat
|
|
186
207
|
// alias for state === 'active'.
|
|
187
208
|
function registryState(model: any, target: string): 'tag' | 'pending' | 'active' | 'inactive' {
|
|
188
|
-
|
|
209
|
+
// Tag-only is a property of the Go toolchain, not of one target name, so
|
|
210
|
+
// an aliased `go~go2` must be tag-only too.
|
|
211
|
+
const eco = originName(model, target)
|
|
212
|
+
if ('go' === eco || 'go-cli' === eco || 'go-mcp' === eco) return 'tag'
|
|
189
213
|
const reg = model?.main?.[KIT]?.target?.[target]?.publish?.registry
|
|
190
214
|
if (null == reg || '' === (reg.name || '')) return 'tag'
|
|
191
215
|
if (true === reg.active) return 'active' // legacy alias
|
|
@@ -215,13 +239,16 @@ function registryName(model: any, target: string): string {
|
|
|
215
239
|
// pointer carrying the releases URL.
|
|
216
240
|
function vendorCommand(model: any, target: string): string {
|
|
217
241
|
const { releasesUrl } = repoInfo(model)
|
|
218
|
-
switch (target) {
|
|
242
|
+
switch (originName(model, target)) {
|
|
243
|
+
// `target`, not the literal — the case says WHICH command, the argument
|
|
244
|
+
// says whose module. Writing the literal here reintroduces the same bug
|
|
245
|
+
// the switch above exists to fix, one level down, which is exactly what
|
|
246
|
+
// the first cut of this change did.
|
|
219
247
|
case 'go':
|
|
220
|
-
return `go get ${packageName(model, 'go')}@latest`
|
|
221
248
|
case 'go-mcp':
|
|
222
|
-
return `go get ${packageName(model,
|
|
249
|
+
return `go get ${packageName(model, target)}@latest`
|
|
223
250
|
case 'go-cli':
|
|
224
|
-
return `go install ${packageName(model,
|
|
251
|
+
return `go install ${packageName(model, target)}/cmd/${model.name}@latest`
|
|
225
252
|
default: {
|
|
226
253
|
const reg = registryName(model, target)
|
|
227
254
|
return `not yet on ${reg || 'the registry'} — install from the git tag: ${releasesUrl}`
|
|
@@ -266,7 +293,12 @@ function packageName(model: any, eco: string): string {
|
|
|
266
293
|
return String(declared)
|
|
267
294
|
}
|
|
268
295
|
|
|
269
|
-
|
|
296
|
+
// The lookup above is keyed by the target's OWN name, so an alias reads its
|
|
297
|
+
// own declared package. The switch below is about FORM — npm scoping,
|
|
298
|
+
// slash-separated composer names — which belongs to the language, so it
|
|
299
|
+
// follows the origin. Passing `ts2` here without this would miss every case
|
|
300
|
+
// and fall to `default`, silently publishing under a non-npm name.
|
|
301
|
+
switch (originName(model, eco)) {
|
|
270
302
|
case 'npm':
|
|
271
303
|
case 'ts':
|
|
272
304
|
return npmScoped
|
|
@@ -282,12 +314,13 @@ function packageName(model: any, eco: string): string {
|
|
|
282
314
|
case 'composer':
|
|
283
315
|
case 'php':
|
|
284
316
|
return `${origin}/${base}`
|
|
317
|
+
// `eco`, not the literal: the case is chosen by the LANGUAGE but the
|
|
318
|
+
// module belongs to the target that asked. `packageName(model, 'go2')`
|
|
319
|
+
// returning go's module was the same conflation one level down.
|
|
285
320
|
case 'go':
|
|
286
|
-
return goModule(model, 'go')
|
|
287
321
|
case 'go-cli':
|
|
288
|
-
return goModule(model, 'go-cli')
|
|
289
322
|
case 'go-mcp':
|
|
290
|
-
return goModule(model,
|
|
323
|
+
return goModule(model, eco)
|
|
291
324
|
// The notebook/analyst package layered on `py`. Distinct PyPI name so it
|
|
292
325
|
// can version and publish independently of the SDK it wraps.
|
|
293
326
|
case 'py-data':
|
|
@@ -306,25 +339,29 @@ function installCommand(model: any, target: string): string {
|
|
|
306
339
|
if (!isPublished(model, target)) {
|
|
307
340
|
return vendorCommand(model, target)
|
|
308
341
|
}
|
|
309
|
-
switch
|
|
342
|
+
// WHICH package manager is the language's business, so the switch follows
|
|
343
|
+
// the origin; WHICH package name is this target's own, so every arm passes
|
|
344
|
+
// `target` rather than a hardcoded ecosystem key. Before, an aliased
|
|
345
|
+
// `ts~ts2` matched no case and returned '' — a README with an empty install
|
|
346
|
+
// line — and even had it matched, `packageName(model, 'npm')` would have
|
|
347
|
+
// printed the ORIGIN's package.
|
|
348
|
+
switch (originName(model, target)) {
|
|
310
349
|
case 'ts':
|
|
311
|
-
return `npm install ${packageName(model, 'npm')}`
|
|
312
350
|
case 'js':
|
|
313
|
-
return `npm install ${packageName(model,
|
|
351
|
+
return `npm install ${packageName(model, target)}`
|
|
314
352
|
case 'py':
|
|
315
|
-
return `pip install ${packageName(model, 'pypi')}`
|
|
316
353
|
case 'py-data':
|
|
317
|
-
return `pip install ${packageName(model,
|
|
354
|
+
return `pip install ${packageName(model, target)}`
|
|
318
355
|
case 'php':
|
|
319
|
-
return `composer require ${packageName(model,
|
|
356
|
+
return `composer require ${packageName(model, target)}`
|
|
320
357
|
case 'rb':
|
|
321
|
-
return `gem install ${packageName(model,
|
|
358
|
+
return `gem install ${packageName(model, target)}`
|
|
322
359
|
case 'lua':
|
|
323
|
-
return `luarocks install ${packageName(model,
|
|
360
|
+
return `luarocks install ${packageName(model, target)}`
|
|
324
361
|
case 'go':
|
|
325
|
-
return `go get ${packageName(model,
|
|
362
|
+
return `go get ${packageName(model, target)}`
|
|
326
363
|
case 'go-cli':
|
|
327
|
-
return `go install ${packageName(model,
|
|
364
|
+
return `go install ${packageName(model, target)}/cmd/${model.name}@latest`
|
|
328
365
|
default:
|
|
329
366
|
return ''
|
|
330
367
|
}
|
|
@@ -333,7 +370,8 @@ function installCommand(model: any, target: string): string {
|
|
|
333
370
|
// The standard one-line package description (with the generic non-affiliation
|
|
334
371
|
// statement inline) used in every manifest.
|
|
335
372
|
function pkgDescription(model: any, target: string): string {
|
|
336
|
-
return `Unofficial generated ${langLabel(target)} SDK
|
|
373
|
+
return `Unofficial generated ${langLabel(originName(model, target))} SDK` +
|
|
374
|
+
` for the ${apiName(model)} public API.` +
|
|
337
375
|
` Not affiliated with or endorsed by the upstream API provider.`
|
|
338
376
|
}
|
|
339
377
|
|
|
@@ -437,6 +475,7 @@ export {
|
|
|
437
475
|
GENERATOR_URL,
|
|
438
476
|
LANG_LABEL,
|
|
439
477
|
langLabel,
|
|
478
|
+
originName,
|
|
440
479
|
repoInfo,
|
|
441
480
|
apiName,
|
|
442
481
|
packageName,
|
package/src/sdkgen.ts
CHANGED
|
@@ -10,7 +10,6 @@ import { Aontu } from 'aontu'
|
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
showChanges,
|
|
13
|
-
getdlog,
|
|
14
13
|
} from '@voxgig/util'
|
|
15
14
|
|
|
16
15
|
import type {
|
|
@@ -60,7 +59,7 @@ import { collectDeps } from './helpers/collectDeps'
|
|
|
60
59
|
import type { DepEntry } from './helpers/collectDeps'
|
|
61
60
|
import { canonToType, canonToDtype, canonKey } from './helpers/canonType'
|
|
62
61
|
import { OP_SUFFIX, opTypeName, opParams, opActions, entityActions, entityPath, opRequestShape, entityIdField, entityDataIdField, entityOps, entityPrimaryOp, pickExampleEntity, entityClassName, entityTypeCollisions, warnEntityTypeCollisions, deriveEntityNames, entityCollection } from './helpers/opShape'
|
|
63
|
-
import { isReservedName, safeVarName, exampleVarName, phpEntityAccessor, entityCacheField, isRbCoreConstant, rbSafeTypeName, isSwiftSdkType, swiftSafeTypeName, jsProp, jsOptProp, jsKey } from './helpers/naming'
|
|
62
|
+
import { isReservedName, safeVarName, exampleVarName, phpEntityAccessor, entityCacheField, isRbCoreConstant, isRbSdkConstant, rbSafeTypeName, isSwiftSdkType, swiftSafeTypeName, isPhpReservedType, phpSafeTypeName, jsProp, jsOptProp, jsKey } from './helpers/naming'
|
|
64
63
|
import { serverVariables, hasServerVariables } from './helpers/serverVars'
|
|
65
64
|
import { primaryOpCall, idLiteral, matchArg, dataArg, litFor } from './helpers/opExample'
|
|
66
65
|
import type { ExampleLang } from './helpers/opExample'
|
|
@@ -108,6 +107,7 @@ import {
|
|
|
108
107
|
repoInfo,
|
|
109
108
|
apiName,
|
|
110
109
|
langLabel,
|
|
110
|
+
originName,
|
|
111
111
|
PUBLISHER,
|
|
112
112
|
PUBLISHER_URL,
|
|
113
113
|
SECURITY_EMAIL,
|
|
@@ -176,7 +176,38 @@ type SdkGenOptions = {
|
|
|
176
176
|
const { Jostraca } = JostracaModule
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
// A BROKEN PROJECT MODEL IS THE USER'S FILE, NOT A CRASH.
|
|
180
|
+
//
|
|
181
|
+
// One constructor for both routes out of `resolveModel` — the throw aontu
|
|
182
|
+
// actually takes, and the `errs` array it documents but never fills — because
|
|
183
|
+
// two spellings of the same error is how this repo keeps reintroducing the
|
|
184
|
+
// same defect.
|
|
185
|
+
//
|
|
186
|
+
// aontu's own diagnostic is the valuable part and is passed through
|
|
187
|
+
// UNTOUCHED: it carries the source excerpt, a caret under the offending
|
|
188
|
+
// token, and an explanation. What it lacks is any statement of WHOSE file
|
|
189
|
+
// this is, so the path leads. Wrapping it in an SdkGenError is what makes the
|
|
190
|
+
// CLI print it as a message instead of dumping the error object and a stack
|
|
191
|
+
// trace through sdkgen's internals — `handleError` reserves clean output for
|
|
192
|
+
// that name.
|
|
193
|
+
function modelError(path: string, cause: any, rooterrs: any[]): any {
|
|
194
|
+
const detail = String(cause?.msg ?? cause?.message ?? cause ?? '').trim()
|
|
195
|
+
|
|
196
|
+
const err: any = new SdkGenError(
|
|
197
|
+
'Model Error: ' + path + '\n' + detail)
|
|
198
|
+
|
|
199
|
+
err.cause$ = [cause]
|
|
200
|
+
err.rooterrs$ = rooterrs
|
|
201
|
+
|
|
202
|
+
// A syntax error is the user's typo, and there is nothing for them to do
|
|
203
|
+
// with a stack. `why` lives on the structured entries rather than the
|
|
204
|
+
// thrown wrapper, so it is read from the first of them.
|
|
205
|
+
if ('syntax' === (cause?.why ?? rooterrs?.[0]?.why)) {
|
|
206
|
+
err.uxmsg$ = true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return err
|
|
210
|
+
}
|
|
180
211
|
|
|
181
212
|
|
|
182
213
|
function SdkGen(opts: SdkGenOptions) {
|
|
@@ -393,13 +424,6 @@ function SdkGen(opts: SdkGenOptions) {
|
|
|
393
424
|
showChanges(jopts.log, 'generate-result', eres, Path.dirname(process.cwd()))
|
|
394
425
|
}
|
|
395
426
|
|
|
396
|
-
const dlogs = dlog.log()
|
|
397
|
-
if (0 < dlogs.length) {
|
|
398
|
-
for (let dlogentry of dlogs) {
|
|
399
|
-
log.debug({ point: 'generate-warning', dlogentry, note: String(dlogentry) })
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
427
|
log.info({ point: 'generate-end' })
|
|
404
428
|
|
|
405
429
|
return { ok: true, name: 'sdkgen' }
|
|
@@ -482,19 +506,39 @@ function SdkGen(opts: SdkGenOptions) {
|
|
|
482
506
|
const aopts = { path, errs }
|
|
483
507
|
const src = fs.readFileSync(path, 'utf8')
|
|
484
508
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
509
|
+
// AONTU THROWS; IT DOES NOT FILL `errs`.
|
|
510
|
+
//
|
|
511
|
+
// The `errs` array is handed to `generate` and stays empty on every
|
|
512
|
+
// failure — syntax, unresolved path and unify conflict alike all raise an
|
|
513
|
+
// AontuError instead (verified against 0.52 by running each). So the
|
|
514
|
+
// `0 < errs.length` branch this used to have could never fire, and every
|
|
515
|
+
// broken project model reached the CLI as a bare AontuError: `handleError`
|
|
516
|
+
// prints the raw object for anything that is not an SdkGenError, so the
|
|
517
|
+
// user got aontu's diagnostic followed by a stack trace into sdkgen's
|
|
518
|
+
// `dist/` and a dump of the error's own fields. It read as an sdkgen
|
|
519
|
+
// crash rather than a problem in their own file.
|
|
520
|
+
//
|
|
521
|
+
// The array is still passed, and still checked below, because that is
|
|
522
|
+
// aontu's documented option and a later version may start using it. Both
|
|
523
|
+
// routes build the error the same way, so they cannot drift.
|
|
524
|
+
let model: any
|
|
491
525
|
|
|
492
|
-
|
|
493
|
-
|
|
526
|
+
try {
|
|
527
|
+
model = aontu.generate(src, aopts)
|
|
528
|
+
}
|
|
529
|
+
catch (aerr: any) {
|
|
530
|
+
if (true !== aerr?.aontu) {
|
|
531
|
+
throw aerr
|
|
494
532
|
}
|
|
495
533
|
|
|
496
|
-
|
|
497
|
-
|
|
534
|
+
// The structured errors are reachable after all — the thrown error
|
|
535
|
+
// carries the accessor the empty array was standing in for.
|
|
536
|
+
throw modelError(path, aerr,
|
|
537
|
+
'function' === typeof aerr.errs ? aerr.errs() : [])
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (0 < errs.length) {
|
|
541
|
+
throw modelError(path, errs[0], errs)
|
|
498
542
|
}
|
|
499
543
|
|
|
500
544
|
model.const = { name: model.name }
|
|
@@ -1039,9 +1083,12 @@ export {
|
|
|
1039
1083
|
phpEntityAccessor,
|
|
1040
1084
|
entityCacheField,
|
|
1041
1085
|
isRbCoreConstant,
|
|
1086
|
+
isRbSdkConstant,
|
|
1042
1087
|
rbSafeTypeName,
|
|
1043
1088
|
isSwiftSdkType,
|
|
1044
1089
|
swiftSafeTypeName,
|
|
1090
|
+
isPhpReservedType,
|
|
1091
|
+
phpSafeTypeName,
|
|
1045
1092
|
serverVariables,
|
|
1046
1093
|
hasServerVariables,
|
|
1047
1094
|
liveStrict,
|
|
@@ -1090,6 +1137,7 @@ export {
|
|
|
1090
1137
|
repoInfo,
|
|
1091
1138
|
apiName,
|
|
1092
1139
|
langLabel,
|
|
1140
|
+
originName,
|
|
1093
1141
|
PUBLISHER,
|
|
1094
1142
|
PUBLISHER_URL,
|
|
1095
1143
|
SECURITY_EMAIL,
|