@voxgig/sdkgen 4.27.0 → 4.29.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.
Files changed (53) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/helpers/packageMeta.d.ts +2 -1
  3. package/dist/helpers/packageMeta.js +7 -3
  4. package/dist/helpers/packageMeta.js.map +1 -1
  5. package/dist/sdkgen.d.ts +2 -2
  6. package/dist/sdkgen.js +10 -1
  7. package/dist/sdkgen.js.map +1 -1
  8. package/dist/testkit.js +16 -6
  9. package/dist/testkit.js.map +1 -1
  10. package/dist/tsconfig.tsbuildinfo +1 -1
  11. package/dist/types.d.ts +1 -0
  12. package/model/sdkgen.aontu +9 -0
  13. package/package.json +1 -1
  14. package/project/.sdk/model/feature/audit.aontu +2 -1
  15. package/project/.sdk/model/feature/cache.aontu +2 -1
  16. package/project/.sdk/model/feature/clienttrack.aontu +2 -1
  17. package/project/.sdk/model/feature/cost.aontu +2 -1
  18. package/project/.sdk/model/feature/debug.aontu +2 -1
  19. package/project/.sdk/model/feature/idempotency.aontu +2 -1
  20. package/project/.sdk/model/feature/log.aontu +2 -1
  21. package/project/.sdk/model/feature/metrics.aontu +2 -1
  22. package/project/.sdk/model/feature/netsim.aontu +2 -1
  23. package/project/.sdk/model/feature/paging.aontu +2 -1
  24. package/project/.sdk/model/feature/proxy.aontu +2 -1
  25. package/project/.sdk/model/feature/ratelimit.aontu +2 -1
  26. package/project/.sdk/model/feature/rbac.aontu +2 -1
  27. package/project/.sdk/model/feature/retry.aontu +2 -1
  28. package/project/.sdk/model/feature/secrets.aontu +2 -1
  29. package/project/.sdk/model/feature/streaming.aontu +2 -1
  30. package/project/.sdk/model/feature/telemetry.aontu +2 -1
  31. package/project/.sdk/model/feature/test.aontu +2 -1
  32. package/project/.sdk/model/feature/timeout.aontu +2 -1
  33. package/project/.sdk/model/feature/validate.aontu +2 -1
  34. package/project/.sdk/src/cmp/c/utility_c.ts +3 -1
  35. package/project/.sdk/src/cmp/cpp/ReadmeInstall_cpp.ts +3 -3
  36. package/project/.sdk/src/cmp/elixir/ReadmeInstall_elixir.ts +2 -2
  37. package/project/.sdk/src/cmp/java/Package_java.ts +2 -1
  38. package/project/.sdk/src/cmp/java/ReadmeInstall_java.ts +2 -2
  39. package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +2 -1
  40. package/project/.sdk/src/cmp/kotlin/ReadmeInstall_kotlin.ts +2 -2
  41. package/project/.sdk/src/cmp/lua/Package_lua.ts +3 -5
  42. package/project/.sdk/src/cmp/ocaml/utility_ocaml.ts +3 -1
  43. package/project/.sdk/src/cmp/php/Package_php.ts +0 -2
  44. package/project/.sdk/src/cmp/py/Package_py.ts +0 -2
  45. package/project/.sdk/src/cmp/py-data/Main_py-data.ts +1 -3
  46. package/project/.sdk/src/cmp/rb/Package_rb.ts +0 -2
  47. package/project/.sdk/src/cmp/rust/utility_rust.ts +3 -1
  48. package/project/.sdk/src/cmp/scala/ReadmeInstall_scala.ts +2 -2
  49. package/project/sdkgen-package.json +1 -1
  50. package/src/helpers/packageMeta.ts +9 -3
  51. package/src/sdkgen.ts +12 -0
  52. package/src/testkit.ts +50 -35
  53. package/src/types.ts +1 -0
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: clienttrack: {
3
3
 
4
4
  name: key()
5
- title: "Client identity and per-request correlation headers"
5
+ title: "Client tracking"
6
+ description: "Client identity and per-request correlation headers"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: cost: {
3
3
 
4
4
  name: key()
5
- title: "Cost tracking and spend budget for API calls"
5
+ title: "Cost tracking"
6
+ description: "Cost tracking and spend budget for API calls"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: debug: {
3
3
 
4
4
  name: key()
5
- title: "Request/response capture ring buffer for debugging"
5
+ title: "Debug capture"
6
+ description: "Request/response capture ring buffer for debugging"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: idempotency: {
3
3
 
4
4
  name: key()
5
- title: "Idempotency keys for safe retries of mutating operations"
5
+ title: "Idempotency"
6
+ description: "Idempotency keys for safe retries of mutating operations"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: log: {
3
3
 
4
4
  name: key()
5
- title: "Structured request and response logging"
5
+ title: "Logging"
6
+ description: "Structured request and response logging"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: metrics: {
3
3
 
4
4
  name: key()
5
- title: "Statistics capture: per-operation counters and latency"
5
+ title: "Metrics"
6
+ description: "Statistics capture: per-operation counters and latency"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: netsim: {
3
3
 
4
4
  name: key()
5
- title: "Network behaviour simulation for offline testing (latency, failures, outages)"
5
+ title: "Network simulation"
6
+ description: "Network behaviour simulation for offline testing (latency, failures, outages)"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: paging: {
3
3
 
4
4
  name: key()
5
- title: "Pagination signals for list operations"
5
+ title: "Paging"
6
+ description: "Pagination signals for list operations"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: proxy: {
3
3
 
4
4
  name: key()
5
- title: "Outbound HTTP(S) proxy routing"
5
+ title: "Proxy"
6
+ description: "Outbound HTTP(S) proxy routing"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: ratelimit: {
3
3
 
4
4
  name: key()
5
- title: "Client-side rate limiting via a token bucket"
5
+ title: "Rate limiting"
6
+ description: "Client-side rate limiting via a token bucket"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: rbac: {
3
3
 
4
4
  name: key()
5
- title: "Client-side role/permission enforcement"
5
+ title: "Access control"
6
+ description: "Client-side role/permission enforcement"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: retry: {
3
3
 
4
4
  name: key()
5
- title: "Automatic retry of transient failures with exponential backoff"
5
+ title: "Retry"
6
+ description: "Automatic retry of transient failures with exponential backoff"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: secrets: {
3
3
 
4
4
  name: key()
5
- title: "Secret access: resolve the API credential through a provider chain, and exchange a refresh token for short-lived access tokens"
5
+ title: "Secrets"
6
+ description: "Secret access: resolve the API credential through a provider chain, and exchange a refresh token for short-lived access tokens"
6
7
  version: '0.1.0'
7
8
  base: 'BASE'
8
9
 
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: streaming: {
3
3
 
4
4
  name: key()
5
- title: "Incremental streaming of list results via async iteration"
5
+ title: "Streaming"
6
+ description: "Incremental streaming of list results via async iteration"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: telemetry: {
3
3
 
4
4
  name: key()
5
- title: "Distributed tracing spans with W3C trace-context propagation"
5
+ title: "Telemetry"
6
+ description: "Distributed tracing spans with W3C trace-context propagation"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: test: {
3
3
 
4
4
  name: key()
5
- title: "In-memory mock transport for testing without a live server"
5
+ title: "Test transport"
6
+ description: "In-memory mock transport for testing without a live server"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: timeout: {
3
3
 
4
4
  name: key()
5
- title: "Per-request timeout with transport abort"
5
+ title: "Timeout"
6
+ description: "Per-request timeout with transport abort"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -2,7 +2,8 @@
2
2
  main: kit: feature: validate: {
3
3
 
4
4
  name: key()
5
- title: "Payload validation against the model's own field types"
5
+ title: "Validation"
6
+ description: "Payload validation against the model's own field types"
6
7
  version: '0.0.1'
7
8
  active: true
8
9
  base: 'BASE'
@@ -4,6 +4,8 @@ import {
4
4
  walk,
5
5
  } from '@voxgig/struct'
6
6
 
7
+ import { sdkName } from '@voxgig/sdkgen'
8
+
7
9
 
8
10
  // C reserved keywords illegal as identifiers.
9
11
  const C_RESERVED = new Set<string>([
@@ -31,7 +33,7 @@ function cIdent(model: any): string {
31
33
  // Package-ish name (used in comments only), e.g. voxgig-solar-sdk.
32
34
  function cName(model: any): string {
33
35
  const org = (model.origin || 'voxgig-sdk').replace(/-sdk$/, '')
34
- return `${org}-${model.name}-sdk`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
36
+ return `${org}-${sdkName(model.name)}`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
35
37
  }
36
38
 
37
39
 
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, repoInfo } from '@voxgig/sdkgen'
2
+ import { cmp, Content, repoInfo, sdkName } from '@voxgig/sdkgen'
3
3
 
4
4
 
5
5
  const ReadmeInstall = cmp(function ReadmeInstall(props: any) {
@@ -20,7 +20,7 @@ Releases are cut as the git tag \`${target.name}/vX.Y.Z\` (see
20
20
 
21
21
  \`\`\`bash
22
22
  # Add the SDK as a submodule (or copy the cpp/ directory into your tree).
23
- git submodule add <repo-url> third_party/${model.const.Name.toLowerCase()}-sdk
23
+ git submodule add <repo-url> third_party/${sdkName(model.const.Name.toLowerCase())}
24
24
  \`\`\`
25
25
 
26
26
  Then include the umbrella header and compile with C++17:
@@ -30,7 +30,7 @@ Then include the umbrella header and compile with C++17:
30
30
  \`\`\`
31
31
 
32
32
  \`\`\`bash
33
- g++ -std=c++17 -Ithird_party/${model.const.Name.toLowerCase()}-sdk/cpp your_app.cpp -o your_app
33
+ g++ -std=c++17 -Ithird_party/${sdkName(model.const.Name.toLowerCase())}/cpp your_app.cpp -o your_app
34
34
  \`\`\`
35
35
 
36
36
  `)
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isPublished, repoInfo, packageVersion } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isPublished, repoInfo, packageVersion, sdkName } from '@voxgig/sdkgen'
3
3
 
4
4
 
5
5
  const ReadmeInstall = cmp(function ReadmeInstall(props: any) {
@@ -51,7 +51,7 @@ Or from a local source checkout:
51
51
  \`\`\`elixir
52
52
  def deps do
53
53
  [
54
- {:${app}, path: "../${model.const.name.toLowerCase()}-sdk/${target.name}"}
54
+ {:${app}, path: "../${sdkName(model.const.name.toLowerCase())}/${target.name}"}
55
55
  ]
56
56
  end
57
57
  \`\`\`
@@ -5,6 +5,7 @@ import {
5
5
  cmp,
6
6
  collectDeps,
7
7
  packageVersion,
8
+ sdkName,
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -28,7 +29,7 @@ const Package = cmp(async function Package(props: any) {
28
29
  const model: Model = ctx$.model
29
30
 
30
31
  const groupId = mavenGroupId(model)
31
- const artifactId = `${model.name}-sdk`
32
+ const artifactId = sdkName(model.name)
32
33
 
33
34
  // Dep name convention: "groupId:artifactId" with an explicit version.
34
35
  const deps: { groupId: string, artifactId: string, version: string, kind: string }[] = []
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isPublished, repoInfo, packageVersion } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isPublished, repoInfo, packageVersion, sdkName } from '@voxgig/sdkgen'
3
3
 
4
4
  import { mavenGroupId } from './utility_java'
5
5
 
@@ -9,7 +9,7 @@ const ReadmeInstall = cmp(function ReadmeInstall(props: any) {
9
9
  const { model } = ctx$
10
10
 
11
11
  const groupId = mavenGroupId(model)
12
- const artifactId = `${model.name}-sdk`
12
+ const artifactId = sdkName(model.name)
13
13
 
14
14
  if (isPublished(model, target.name)) {
15
15
  Content(`Add the dependency to your \`pom.xml\`:
@@ -5,6 +5,7 @@ import {
5
5
  cmp,
6
6
  collectDeps,
7
7
  packageVersion,
8
+ sdkName,
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -27,7 +28,7 @@ const Package = cmp(async function Package(props: any) {
27
28
  const model: Model = ctx$.model
28
29
 
29
30
  const group = gradleGroup(model)
30
- const artifactId = `${model.name}-sdk`
31
+ const artifactId = sdkName(model.name)
31
32
 
32
33
  // Dep name convention: "group:artifact" with an explicit version.
33
34
  const deps: { group: string, artifact: string, version: string, kind: string }[] = []
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isPublished, repoInfo, packageVersion } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isPublished, repoInfo, packageVersion, sdkName } from '@voxgig/sdkgen'
3
3
 
4
4
  import { gradleGroup } from './utility_kotlin'
5
5
 
@@ -9,7 +9,7 @@ const ReadmeInstall = cmp(function ReadmeInstall(props: any) {
9
9
  const { model } = ctx$
10
10
 
11
11
  const group = gradleGroup(model)
12
- const artifactId = `${model.name}-sdk`
12
+ const artifactId = sdkName(model.name)
13
13
 
14
14
  if (isPublished(model, target.name)) {
15
15
  Content(`Add the dependency to your \`build.gradle.kts\`:
@@ -66,10 +66,8 @@ const Package = cmp(async function Package(props: any) {
66
66
  // Rock name is namespaced to model.origin (e.g. "voxgig-sdk"). LuaRocks has
67
67
  // no real namespaces, so the parts are hyphen-joined. The Lua module name
68
68
  // (`${model.name}_sdk`) used by `require` is unchanged.
69
- const ns = model.origin || 'voxgig-sdk'
70
- const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
71
69
  const rockName = packageName(model, target.name)
72
- const { repoUrl, issuesUrl } = repoInfo(model)
70
+ const { repo, repoUrl, issuesUrl } = repoInfo(model)
73
71
  const labels = keywords(model).map((k) => `"${k}"`).join(', ')
74
72
 
75
73
  // Single source for the version so the rockspec version and the source.tag
@@ -82,9 +80,9 @@ version = "${rockVersion}-1"
82
80
  source = {
83
81
  -- git+https (GitHub dropped git:// in 2022); pin the install to the release
84
82
  -- tag pushed by \`make publish\`, and point at the lua/ subdir of the monorepo.
85
- url = "git+https://github.com/${ns}/${model.name}-sdk.git",
83
+ url = "git+${repoUrl}.git",
86
84
  tag = "lua/v${rockVersion}",
87
- dir = "${model.name}-sdk/lua"
85
+ dir = "${repo}/lua"
88
86
  }
89
87
  description = {
90
88
  summary = "${pkgDescription(model, target.name)}",
@@ -4,6 +4,8 @@ import {
4
4
  walk,
5
5
  } from '@voxgig/struct'
6
6
 
7
+ import { sdkName } from '@voxgig/sdkgen'
8
+
7
9
 
8
10
  // OCaml keywords that are illegal as a lowercase identifier. sdkgen's shared
9
11
  // safeVarName has no ocaml entry, so the guard lives here.
@@ -36,7 +38,7 @@ function entityModule(name: string): string {
36
38
  // module naming: org prefix from model.origin).
37
39
  function packageName(model: any): string {
38
40
  const org = (model.origin || 'voxgig-sdk').replace(/-sdk$/, '')
39
- return `${org}-${model.name}-sdk`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
41
+ return `${org}-${sdkName(model.name)}`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
40
42
  }
41
43
 
42
44
 
@@ -31,8 +31,6 @@ const Package = cmp(async function Package(props: any) {
31
31
  // Package namespace mirrors the npm scope (model.origin, e.g. "voxgig-sdk").
32
32
  // If origin already ends in "-sdk" the slug stands alone; otherwise append
33
33
  // "-sdk" (matches the TS Package generator).
34
- const ns = model.origin || 'voxgig-sdk'
35
- const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
36
34
  const { repoUrl, issuesUrl } = repoInfo(model)
37
35
  const kw = keywords(model).map((k) => `"${k}"`).join(', ')
38
36
 
@@ -22,8 +22,6 @@ const Package = cmp(async function Package(props: any) {
22
22
 
23
23
  const model: Model = ctx$.model
24
24
 
25
- const ns = model.origin || 'voxgig-sdk'
26
- const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
27
25
  const distName = packageName(model, target.name)
28
26
  const { repoUrl, issuesUrl } = repoInfo(model)
29
27
  const kw = keywords(model).map((k) => `"${k}"`).join(', ')
@@ -99,9 +99,7 @@ const Package = cmp(function Package(props: any) {
99
99
  const { target, model, lower, dataModule } = props
100
100
  const ctx$ = props.ctx$
101
101
 
102
- const ns = model.origin || 'voxgig-sdk'
103
- const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
104
- const distName = `${ns}-${pkgBase}-data`
102
+ const distName = packageName(model, target.name)
105
103
  // The `py` SDK this package WRAPS — a different target, so it keeps its
106
104
  // own name and does not follow this target's alias.
107
105
  const sdkDist = packageName(model, 'py')
@@ -28,8 +28,6 @@ const Package = cmp(async function Package(props: any) {
28
28
  // constant here did.
29
29
  const author = authorInfo(model, target.name)
30
30
 
31
- const ns = model.origin || 'voxgig-sdk'
32
- const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
33
31
  const gemName = packageName(model, target.name)
34
32
  const { repoUrl, issuesUrl, changelogUrl } = repoInfo(model)
35
33
 
@@ -4,6 +4,8 @@ import {
4
4
  walk,
5
5
  } from '@voxgig/struct'
6
6
 
7
+ import { sdkName } from '@voxgig/sdkgen'
8
+
7
9
 
8
10
  // Rust keywords (strict + reserved) that are illegal as an identifier.
9
11
  // sdkgen's shared safeVarName has no rust entry, so the guard lives here.
@@ -44,7 +46,7 @@ function rustVarName(name: string): string {
44
46
 
45
47
  function crateName(model: any): string {
46
48
  const org = (model.origin || 'voxgig-sdk').replace(/-sdk$/, '')
47
- return `${org}-${model.name}-sdk`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
49
+ return `${org}-${sdkName(model.name)}`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
48
50
  }
49
51
 
50
52
 
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isPublished, repoInfo, packageVersion } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isPublished, repoInfo, packageVersion, sdkName } from '@voxgig/sdkgen'
3
3
 
4
4
  import { mavenGroupId } from './utility_scala'
5
5
 
@@ -9,7 +9,7 @@ const ReadmeInstall = cmp(function ReadmeInstall(props: any) {
9
9
  const { model } = ctx$
10
10
 
11
11
  const groupId = mavenGroupId(model)
12
- const artifactId = `${model.name}-sdk`
12
+ const artifactId = sdkName(model.name)
13
13
 
14
14
  if (isPublished(model, target.name)) {
15
15
  Content(`Add the dependency with scala-cli:
@@ -3,7 +3,7 @@
3
3
  "package": 1
4
4
  },
5
5
  "name": "@voxgig/sdkgen",
6
- "version": "4.27.0",
6
+ "version": "4.29.0",
7
7
  "provides": {
8
8
  "target": [
9
9
  "c",
@@ -33,6 +33,11 @@ function originName(model: any, target: string): string {
33
33
  return (null != orig && '' !== orig) ? String(orig) : target
34
34
  }
35
35
 
36
+ function sdkName(slug: string): string {
37
+ return slug.endsWith('-sdk') ? slug : `${slug}-sdk`
38
+ }
39
+
40
+
36
41
  function repoInfo(model: any) {
37
42
  const slug = model.name
38
43
  const origin = model.origin || 'voxgig-sdk'
@@ -40,7 +45,7 @@ function repoInfo(model: any) {
40
45
  const declared = (model && model.main && model.main[KIT] && model.main[KIT].repo) || {}
41
46
 
42
47
  const host = '' === (declared.host || '') ? 'github.com' : (declared.host || 'github.com')
43
- const path = '' === (declared.path || '') ? `${origin}/${slug}-sdk` : String(declared.path)
48
+ const path = '' === (declared.path || '') ? `${origin}/${sdkName(slug)}` : String(declared.path)
44
49
 
45
50
  const seg = path.split('/')
46
51
  const repo = seg[seg.length - 1]
@@ -190,8 +195,8 @@ function apiName(model: any): string {
190
195
  function packageName(model: any, eco: string): string {
191
196
  const slug = model.name
192
197
  const origin = model.origin || 'voxgig-sdk'
193
- const base = origin.endsWith('-sdk') ? slug : `${slug}-sdk`
194
- const npmScoped = `@${origin}/${slug}${origin.endsWith('-sdk') ? '' : '-sdk'}`
198
+ const base = sdkName(slug)
199
+ const npmScoped = `@${origin}/${base}`
195
200
 
196
201
  const ECO_TARGET: Record<string, string> = {
197
202
  npm: 'ts', pypi: 'py', gem: 'rb', luarocks: 'lua', composer: 'php',
@@ -324,6 +329,7 @@ function envName(model: any): string {
324
329
  }
325
330
 
326
331
  export {
332
+ sdkName,
327
333
  PUBLISHER,
328
334
  PUBLISHER_URL,
329
335
  SECURITY_EMAIL,
package/src/sdkgen.ts CHANGED
@@ -103,6 +103,7 @@ import {
103
103
  import type { Manifest, ManifestRead } from './helpers/manifest'
104
104
  import {
105
105
  packageName,
106
+ sdkName,
106
107
  installCommand,
107
108
  registryState,
108
109
  isPublished,
@@ -656,6 +657,8 @@ function externalItems(
656
657
 
657
658
  if (null != ov.path && '' !== ov.path) {
658
659
  output.path = ov.path
660
+ // The override relocates this run's output, root placement included.
661
+ delete output.root
659
662
  }
660
663
  if (null != ov.sdkrel && '' !== ov.sdkrel) {
661
664
  output.sdkrel = ov.sdkrel
@@ -718,6 +721,14 @@ function checkExternalFolders(external: ExternalPlan[], root: string, fs: any) {
718
721
  ext.target.output.path + '", which resolves to: ' + ext.folder +
719
722
  '\n (SDK project: ' + root + ')'
720
723
 
724
+ if (true === ext.target.output.root) {
725
+ throw new SdkGenError(
726
+ label + ' "' + ext.name + '" declares both `output: path` and ' +
727
+ '`output: root: true`.\n ' + where +
728
+ '\n `path` generates it into another repository and `root` at the ' +
729
+ 'root of this one. Keep one.')
730
+ }
731
+
721
732
  if (ext.folder === root || folderContains(root, ext.folder)) {
722
733
  throw new SdkGenError(
723
734
  'External output path is inside the SDK project.\n ' + where +
@@ -1094,6 +1105,7 @@ export {
1094
1105
  prefixLeadingDigit,
1095
1106
 
1096
1107
  packageName,
1108
+ sdkName,
1097
1109
  installCommand,
1098
1110
  registryState,
1099
1111
  isPublished,
package/src/testkit.ts CHANGED
@@ -563,7 +563,9 @@ function layeredFs(mem: any): any {
563
563
 
564
564
  function defaultRoot(): any {
565
565
  const { cmp, each, names, Project, Folder } = require('jostraca')
566
- const { Main, Entity, Feature, Test, Readme, AgentGuide } = require('./sdkgen')
566
+ const {
567
+ Main, Entity, Feature, Test, Readme, AgentGuide, SdkGenError,
568
+ } = require('./sdkgen')
567
569
 
568
570
  return cmp(function Root(props: any) {
569
571
  const { model, ctx$ } = props
@@ -581,41 +583,54 @@ function defaultRoot(): any {
581
583
  const feature = model.main[KIT].feature || {}
582
584
  const entity = model.main[KIT].entity || {}
583
585
 
586
+ const active = each(target).filter((t: any) => t && false !== t.active)
587
+
588
+ const atRoot = active.filter((t: any) => true === t.output?.root)
589
+ if (1 < atRoot.length) {
590
+ throw new SdkGenError('Only one target can be generated at the ' +
591
+ 'project root: ' + atRoot.map((t: any) => t.name).join(', '))
592
+ }
593
+
584
594
  Project({}, () => {
585
- each(target)
586
- .filter((t: any) => t && false !== t.active)
587
- .map((t: any) => {
588
- names(t, t.name)
589
-
590
- const phase = t.phase || {}
591
- const on = (n: string) => false !== (phase[n] && phase[n].active)
592
-
593
- Folder({ name: t.name }, () => {
594
- if (on('entity')) {
595
- each(entity)
596
- .filter((e: any) => e && false !== e.active)
597
- .map((e: any) => {
598
- names(e, e.name)
599
- Entity({ target: t, entity: e })
600
- })
601
- }
602
-
603
- if (on('feature')) {
604
- each(feature)
605
- .filter((f: any) => f && f.active)
606
- .map((f: any) => {
607
- names(f, f.name)
608
- Feature({ target: t, feature: f })
609
- })
610
- }
611
-
612
- Main({ target: t })
613
-
614
- if (on('readme')) Readme({ target: t })
615
- if (on('agentguide')) AgentGuide({ target: t })
616
- if (on('test')) Test({ target: t })
617
- })
618
- })
595
+ active.map((t: any) => {
596
+ names(t, t.name)
597
+
598
+ const phase = t.phase || {}
599
+ const on = (n: string) => false !== (phase[n] && phase[n].active)
600
+
601
+ const phases = () => {
602
+ if (on('entity')) {
603
+ each(entity)
604
+ .filter((e: any) => e && false !== e.active)
605
+ .map((e: any) => {
606
+ names(e, e.name)
607
+ Entity({ target: t, entity: e })
608
+ })
609
+ }
610
+
611
+ if (on('feature')) {
612
+ each(feature)
613
+ .filter((f: any) => f && f.active)
614
+ .map((f: any) => {
615
+ names(f, f.name)
616
+ Feature({ target: t, feature: f })
617
+ })
618
+ }
619
+
620
+ Main({ target: t })
621
+
622
+ if (on('readme')) Readme({ target: t })
623
+ if (on('agentguide')) AgentGuide({ target: t })
624
+ if (on('test')) Test({ target: t })
625
+ }
626
+
627
+ if (true === t.output?.root) {
628
+ phases()
629
+ }
630
+ else {
631
+ Folder({ name: t.name }, phases)
632
+ }
633
+ })
619
634
  })
620
635
  })
621
636
  }
package/src/types.ts CHANGED
@@ -62,6 +62,7 @@ type ModelTarget = NameCases & {
62
62
  create?: boolean
63
63
  adopt?: boolean
64
64
  sdkrel?: string
65
+ root?: boolean
65
66
  }
66
67
 
67
68
  // Per-generation-phase activation. A consumer target (go-cli, go-mcp,