@voxgig/sdkgen 3.0.0 → 3.2.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 (45) hide show
  1. package/dist/action/doctor.js +65 -10
  2. package/dist/action/doctor.js.map +1 -1
  3. package/dist/cmp/ExternalTarget.d.ts +2 -0
  4. package/dist/cmp/ExternalTarget.js +104 -0
  5. package/dist/cmp/ExternalTarget.js.map +1 -0
  6. package/dist/helpers/packageMeta.d.ts +9 -1
  7. package/dist/helpers/packageMeta.js +54 -0
  8. package/dist/helpers/packageMeta.js.map +1 -1
  9. package/dist/sdkgen.d.ts +2 -2
  10. package/dist/sdkgen.js +229 -2
  11. package/dist/sdkgen.js.map +1 -1
  12. package/dist/tsconfig.tsbuildinfo +1 -1
  13. package/dist/types.d.ts +12 -0
  14. package/dist/utility.js +10 -1
  15. package/dist/utility.js.map +1 -1
  16. package/model/sdkgen.aontu +130 -0
  17. package/package.json +1 -1
  18. package/project/.sdk/model/target/go-cli.aontu +0 -1
  19. package/project/.sdk/model/target/go-mcp.aontu +0 -1
  20. package/project/.sdk/model/target/java.aontu +0 -1
  21. package/project/.sdk/model/target/kotlin.aontu +0 -1
  22. package/project/.sdk/model/target/lean.aontu +0 -1
  23. package/project/.sdk/model/target/py-data.aontu +0 -1
  24. package/project/.sdk/model/target/scala.aontu +0 -1
  25. package/project/.sdk/model/target/seneca-provider.aontu +118 -0
  26. package/project/.sdk/model/target/zig.aontu +0 -1
  27. package/project/.sdk/src/cmp/js/Package_js.ts +8 -1
  28. package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +9 -2
  29. package/project/.sdk/src/cmp/php/Package_php.ts +9 -2
  30. package/project/.sdk/src/cmp/rb/Package_rb.ts +9 -2
  31. package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +3915 -0
  32. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +960 -0
  33. package/project/.sdk/src/cmp/ts/Package_ts.ts +8 -1
  34. package/project/.sdk/tm/seneca-provider/CODE_OF_CONDUCT.md +132 -0
  35. package/project/.sdk/tm/seneca-provider/LICENSE +21 -0
  36. package/project/.sdk/tm/seneca-provider/Makefile +15 -0
  37. package/project/.sdk/tm/seneca-provider/src/tsconfig.json +18 -0
  38. package/project/.sdk/tm/seneca-provider/test/tsconfig.json +16 -0
  39. package/project/.sdk/tm/seneca-provider/tsfmt.json +3 -0
  40. package/src/action/doctor.ts +77 -12
  41. package/src/cmp/ExternalTarget.ts +125 -0
  42. package/src/helpers/packageMeta.ts +63 -0
  43. package/src/sdkgen.ts +280 -1
  44. package/src/types.ts +24 -1
  45. package/src/utility.ts +10 -1
@@ -32,6 +32,25 @@ main: kit: test: {
32
32
  # Overridable per target:
33
33
  # main: kit: target: go: test: live: strict: true
34
34
  strict: *false | boolean
35
+
36
+ # The base URL a live run points at. '' derives it from the spec's first
37
+ # declared server, but ONLY when that is a loopback address.
38
+ #
39
+ # For an OpenAPI-derived model `servers[0].url` is the PRODUCTION host of
40
+ # a third-party API, and a generated live suite runs under `npm test` on
41
+ # every CI push. Inferring the base from the spec therefore pointed CI at
42
+ # a stranger's API — unauthenticated, on three operating systems, on every
43
+ # push — and pointed the generated write scripts there too. A live target
44
+ # is now taken only when it is unambiguously ours: declared here, or
45
+ # loopback, which nobody else can be behind.
46
+ base: *'' | string
47
+
48
+ # Whether the SDK's own repo ships a runnable companion server under
49
+ # `app/`. NOTHING in an API definition says so — it is a property of the
50
+ # sibling project — so it cannot be inferred, and inferring it put an
51
+ # unconditional `git clone && cd app && npm install && npm run build` into
52
+ # a generated provider's CI, which fails the job for any repo without one.
53
+ app: *false | boolean
35
54
  }
36
55
  }
37
56
 
@@ -81,6 +100,37 @@ main: kit: repo: {
81
100
  }
82
101
 
83
102
 
103
+ # WHO WROTE THIS PACKAGE — the manifest's `author` and `contributors`.
104
+ #
105
+ # Generated output is overwritten, so attribution hand-edited into a
106
+ # package.json is silently DELETED on the next regeneration. That is exactly
107
+ # what happened the first time the seneca-provider target regenerated a
108
+ # hand-written provider repo: the manifest's author and its two named
109
+ # contributors were dropped, and nothing failed to warn about it — credit is
110
+ # not something a build can miss.
111
+ #
112
+ # So it is declared here, like every other value a project owns about itself.
113
+ # Unset means the PUBLISHER (Voxgig), which is what a generated SDK carries
114
+ # and what a project that has never thought about it should get.
115
+ #
116
+ # main: kit: author: { name: 'Ada Lovelace', url: 'https://example.com' }
117
+ # main: kit: contributor: 'ada': { name: 'Ada Lovelace', url: '...' }
118
+ #
119
+ # `contributor` is a MAP, not a list, because aontu unifies maps by key: a
120
+ # project can add one contributor without restating the others, and a
121
+ # duplicate key is a conflict rather than a silent second entry. Emitters
122
+ # render it in sorted-key order, so the manifest is byte-stable.
123
+ main: kit: author: {
124
+ name: *'' | string
125
+ url: *'' | string
126
+ }
127
+
128
+ main: kit: contributor: &: {
129
+ name: string
130
+ url: *'' | string
131
+ }
132
+
133
+
84
134
  main: kit: target: &: {
85
135
  name: key()
86
136
  active: *true | boolean
@@ -109,6 +159,16 @@ main: kit: target: &: {
109
159
  # floors imposed by their dependencies.
110
160
  goversion: *'' | string
111
161
  }
162
+ # WHO WROTE THIS PACKAGE, when it differs from the model-wide
163
+ # `main: kit: author`. One model can produce packages with different
164
+ # authors: an SDK is a generated artefact of the publisher, while a Seneca
165
+ # provider is independently released and has its own maintainers. Unset
166
+ # inherits the model-wide value, which itself defaults to the publisher.
167
+ author: {
168
+ name: *'' | string
169
+ url: *'' | string
170
+ }
171
+
112
172
  deps: &: {
113
173
  active: *false | boolean
114
174
  version: *'*' | string
@@ -147,6 +207,76 @@ main: kit: target: &: {
147
207
  # Tag-only ports (the go family: the Go proxy resolves from GitHub
148
208
  # tags) simply leave `registry` unset — they are implicitly pending
149
209
  # until their `<target>/vX.Y.Z` tag exists.
210
+ # WHERE this target's files are written.
211
+ #
212
+ # Empty (the default) means the ordinary place: `<sdk-repo>/<target>/`,
213
+ # a folder inside the SDK repo, which is what every language target wants.
214
+ #
215
+ # A target that generates a SEPARATE, independently released package needs
216
+ # somewhere else. The Seneca provider is the case: it is its own npm
217
+ # package in its own repo (`@seneca/<name>-provider`), depends on the ts
218
+ # SDK as a normal published dependency, and carries a repo's worth of
219
+ # furniture — LICENSE, CI workflow, doc/ — that must not land inside the
220
+ # SDK repo.
221
+ #
222
+ # Relative paths resolve against the SDK repo root, so a sibling checkout
223
+ # is `'../<repo>'`. Absolute paths are taken as given.
224
+ #
225
+ # Files are written at the ROOT of that path, NOT under a `<target>/`
226
+ # subfolder: the destination IS the package.
227
+ #
228
+ # Generation into it is a SEPARATE pass, so the repo receives only what
229
+ # this target emits — none of the SDK repo's own root files (README,
230
+ # AGENTS.md, the build scaffold) follow it out.
231
+ output: {
232
+ path: *'' | string
233
+
234
+ # Write into a destination that already holds content this generator did
235
+ # not write.
236
+ #
237
+ # Generation OVERWRITES, jostraca creates missing parent directories, and
238
+ # the path is taken verbatim from the model — so a typo ('.', '..', a
239
+ # target name) fabricates a package tree somewhere arbitrary, or replaces
240
+ # an unrelated repo's package.json, README, LICENSE and CI workflow in
241
+ # place. Generation therefore refuses a destination that is neither
242
+ # absent, nor empty, nor carrying the `.jostraca` bookkeeping a previous
243
+ # generation left there.
244
+ #
245
+ # A repo seeded with its own README/LICENCE before the first generation
246
+ # is the legitimate case that trips the guard. Say so here, once: the
247
+ # point is that the destination is a DECISION, not a typo.
248
+ adopt: *false | boolean
249
+
250
+ # The path from the destination BACK to the SDK project — what the
251
+ # target's own docs, scripts and live tests name when they reach for
252
+ # something the SDK repo distributes only in source (the companion test
253
+ # server in `app/`).
254
+ #
255
+ # '' derives it by inverting the resolved `path`, which is exact only
256
+ # while the walk back crosses nothing this model names — true for
257
+ # '../<repo>', false for anything ascending further. A project declaring
258
+ # '../../seneca/solardemo-provider' derived
259
+ # '../../voxgig-sdk/voxgig-solardemo-sdk', where `voxgig-sdk` is the name
260
+ # of the WORKSPACE DIRECTORY holding the checkout on ONE machine — and
261
+ # that string is committed into the destination's README and test files,
262
+ # so the next developer's layout regenerates a spurious diff and an
263
+ # instruction path that is wrong for everyone else. Declare it here when
264
+ # the destination is more than one level away; generation warns until you
265
+ # do.
266
+ sdkrel: *'' | string
267
+
268
+ # '<org>/<repo>' for the repo the files land in, under `main: kit: repo:
269
+ # host`. A target generating out of tree is usually released from that
270
+ # repo too, so its manifest's homepage/repository/bugs must point there
271
+ # and NOT at the SDK's own repo, which is what they would otherwise
272
+ # derive from.
273
+ #
274
+ # '' lets the target decide: a target that knows its convention (the
275
+ # Seneca provider's is `senecajs/seneca-<name>-provider`) supplies its
276
+ # own default, and falls back to the SDK's repo when it has none.
277
+ repo: *'' | string
278
+ }
279
+
150
280
  publish: {
151
281
  # The SDK's OWN release version — what the generated manifest declares
152
282
  # and what the port's Makefile tags (`<target>/v<version>`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/sdkgen",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "main": "dist/sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "engines": {
@@ -43,7 +43,6 @@ main: kit: feature: &: target: 'go-cli': deps: &: {
43
43
 
44
44
  # Publication: tag-only — the Go module proxy resolves versions from the
45
45
  # go-cli/vX.Y.Z GitHub tag; there is no package registry to upload to.
46
- # Publication.
47
46
  #
48
47
  # `target add <t>` OVERWRITES this file, so a project must never set its
49
48
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -37,7 +37,6 @@ main: kit: feature: &: target: 'go-mcp': deps: &: {
37
37
 
38
38
  # Publication: tag-only — the Go module proxy resolves versions from the
39
39
  # go-mcp/vX.Y.Z GitHub tag; there is no package registry to upload to.
40
- # Publication.
41
40
  #
42
41
  # `target add <t>` OVERWRITES this file, so a project must never set its
43
42
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -25,7 +25,6 @@ main: kit: feature: &: target: java: deps: &: {
25
25
 
26
26
  # Publication: tag-only for now (tag java/vX.Y.Z) — Maven Central
27
27
  # publication via the Sonatype Central Portal is declared but pending.
28
- # Publication.
29
28
  #
30
29
  # `target add <t>` OVERWRITES this file, so a project must never set its
31
30
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -25,7 +25,6 @@ main: kit: feature: &: target: kotlin: deps: &: {
25
25
 
26
26
  # Publication: tag-only for now (tag kotlin/vX.Y.Z) — Maven Central
27
27
  # publication via the Sonatype Central Portal is declared but pending.
28
- # Publication.
29
28
  #
30
29
  # `target add <t>` OVERWRITES this file, so a project must never set its
31
30
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -39,7 +39,6 @@ main: kit: feature: &: target: lean: deps: &: {
39
39
 
40
40
  # Publication: Reservoir (Lean package index) — declared, publish pending
41
41
  # (git-tag only for now).
42
- # Publication.
43
42
  #
44
43
  # `target add <t>` OVERWRITES this file, so a project must never set its
45
44
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -52,7 +52,6 @@ main: kit: feature: &: target: 'py-data': deps: &: {
52
52
 
53
53
  # Publication: pypi registry (declared, publish pending — git-tag only),
54
54
  # same posture as the `py` target.
55
- # Publication.
56
55
  #
57
56
  # `target add <t>` OVERWRITES this file, so a project must never set its
58
57
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -25,7 +25,6 @@ main: kit: feature: &: target: scala: deps: &: {
25
25
 
26
26
  # Publication: tag-only for now (tag scala/vX.Y.Z) — Maven Central
27
27
  # publication via the Sonatype Central Portal is declared but pending.
28
- # Publication.
29
28
  #
30
29
  # `target add <t>` OVERWRITES this file, so a project must never set its
31
30
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -0,0 +1,118 @@
1
+ # Seneca provider target — a Seneca plugin that exposes this API's entities
2
+ # as Seneca entities (`provider/<name>/<entity>`), layered on the sibling
3
+ # `ts` SDK. It is not a language SDK in its own right, so every standard
4
+ # generation phase is switched off below and Main_seneca-provider emits the
5
+ # whole package, exactly as go-cli / go-mcp / py-data do.
6
+ #
7
+ # Requires the `ts` target in the same SDK project (Main throws when it is
8
+ # absent): the plugin imports the published TypeScript SDK.
9
+ #
10
+ # UNLIKE the other consumer targets, this one generates into ITS OWN REPO.
11
+ # A Seneca provider is an independently released npm package under the
12
+ # @seneca scope, with its own LICENSE, CI workflow and release cadence, and
13
+ # it depends on the SDK as an ordinary published dependency rather than by
14
+ # path. Point it at that repo from the PROJECT model:
15
+ #
16
+ # main: kit: target: 'seneca-provider': output: path: '../../seneca/seneca-acme-provider'
17
+ #
18
+ # Relative to the SDK repo root. Left unset here on purpose — every project's
19
+ # provider repo is somewhere different, and a concrete value here would
20
+ # CONFLICT with the project's rather than yield to it (see the publication
21
+ # note below). Unset means the ordinary in-tree `<sdk-repo>/seneca-provider/`,
22
+ # which is a usable default for a first look at the output.
23
+
24
+ main: kit: target: 'seneca-provider': {
25
+
26
+ title: 'Seneca Provider'
27
+ ext: ts
28
+ comment: line: "//"
29
+ module: name: '$$name$$'
30
+ srcfeature: false
31
+
32
+ # Per-generation-phase activation. Mirrors py-data: every standard phase is
33
+ # off and Main emits the package. README, docs and tests ARE generated — by
34
+ # Main, not by the standard cmps, which assume an SDK-shaped package and
35
+ # would emit wrong content here.
36
+ phase: {
37
+ entity: { active: false }
38
+ feature: { active: false }
39
+ readme: { active: false }
40
+ agentguide: { active: false }
41
+ test: { active: false }
42
+ }
43
+
44
+ # `kind` is a COMMA-SEPARATED LIST of manifest sections, not a single one.
45
+ # collectDeps deduplicates by package name and this map is keyed by name, so
46
+ # a package belonging in two sections cannot be declared twice — it says so
47
+ # here instead. See `seneca` below for the case that forced it.
48
+ deps: &: {
49
+ kind: *'prod' | string
50
+ }
51
+
52
+ # The Seneca plugin contract. `@seneca/provider` supplies entityBuilder and
53
+ # the keymap message this plugin's `prepare` posts; the rest are the host
54
+ # framework a plugin runs inside and so are PEER deps — a plugin that
55
+ # bundled its own seneca would run against a different instance than the
56
+ # application it is loaded into.
57
+ #
58
+ # The ts SDK dependency is emitted by Main from the model's published
59
+ # package name, so it is not declared here.
60
+ deps: {
61
+ '@seneca/maintain': { active: true, version: '^0.1.0', kind: prod }
62
+
63
+ '@seneca/env': { active: true, version: '>=0.4', kind: peer }
64
+ '@seneca/provider': { active: true, version: '>=4', kind: peer }
65
+
66
+ # PEER *and* DEV. Peer for the reason above — the plugin must attach to
67
+ # the host application's seneca instance. Dev because the generated test
68
+ # suite does `require('seneca')` itself, so a clean `npm install && npm
69
+ # test` in a fresh checkout has to produce one. Relying on npm's automatic
70
+ # peer installation instead is what dropping this from the manifest
71
+ # amounted to, and that is npm-version- and client-specific: pnpm and
72
+ # `--legacy-peer-deps` both leave the suite with no seneca to require.
73
+ 'seneca': { active: true, version: '>=3||>=4.0.0-rc2', kind: 'peer,dev' }
74
+
75
+ 'seneca-entity': { active: true, version: '>=26', kind: peer }
76
+ 'seneca-promisify': { active: true, version: '>=3', kind: peer }
77
+
78
+ '@seneca/doc': { active: true, version: '^8.0.0', kind: dev }
79
+ '@types/node': { active: true, version: '^24.0.0', kind: dev }
80
+ 'seneca-msg-test': { active: true, version: '^4.1.0', kind: dev }
81
+ 'typescript': { active: true, version: '^5.4.5', kind: dev }
82
+ }
83
+ }
84
+
85
+
86
+ # Feature-deps map slot — every target carries this so the same feature can
87
+ # specify per-target dep variants.
88
+ main: kit: feature: &: target: 'seneca-provider': deps: &: {
89
+ kind: *'prod' | string
90
+ }
91
+
92
+
93
+ # Publication: npm registry, under the @seneca scope rather than the SDK's
94
+ # own. The package name is NOT derived from the SDK slug the way a language
95
+ # target's is — a provider is `@seneca/<name>-provider` — so Main sets it
96
+ # from the model and a project can pin it like any other.
97
+ #
98
+ # `target add <t>` OVERWRITES this file, so a project must never set its
99
+ # publication values here — voxgig-solardemo-sdk lost its pinned npm package
100
+ # name exactly that way. Set them in the project's own model
101
+ # (`model/sdk.aontu`), where they survive a resync.
102
+ #
103
+ # For that to work this file must LEAVE THOSE KEYS UNSET: in aontu a concrete
104
+ # value does not yield to another concrete value, it CONFLICTS, and two
105
+ # defaults conflict too. The schema (@voxgig/sdkgen/model/sdkgen.aontu)
106
+ # already defaults `tag.active`, `registry.state`, `registry.active` and
107
+ # `registry.package`, so a project's concrete value wins over the default and
108
+ # repeating them here would only take that away.
109
+ #
110
+ # What remains below is per-target registry IDENTITY, which is not a project
111
+ # choice and stays concrete.
112
+ main: kit: target: 'seneca-provider': publish: {
113
+ registry: {
114
+ name: 'npm'
115
+ url: 'https://registry.npmjs.org'
116
+ vault: { recipe: 'npm', alias: 'npm' }
117
+ }
118
+ }
@@ -25,7 +25,6 @@ main: kit: feature: &: target: zig: deps: &: {
25
25
  # routes deploy-zig to the root tag-push recipe. WITHOUT it, Deploy.ts falls
26
26
  # through to `make -C zig publish` — but zig's Makefile has no `publish` target,
27
27
  # so the deploy fails. Mirror c.aontu exactly.
28
- # Publication.
29
28
  #
30
29
  # `target add <t>` OVERWRITES this file, so a project must never set its
31
30
  # publication values here — voxgig-solardemo-sdk lost its pinned npm package
@@ -12,6 +12,7 @@ import {
12
12
  PUBLISHER,
13
13
  PUBLISHER_URL,
14
14
  packageVersion,
15
+ authorInfo,
15
16
  } from '@voxgig/sdkgen'
16
17
 
17
18
 
@@ -29,6 +30,12 @@ const Package = cmp(async function Package(props: any) {
29
30
 
30
31
  const model: Model = ctx$.model
31
32
 
33
+ // WHO WROTE THIS PACKAGE. Per target, falling back to the model-wide value
34
+ // and then to the publisher — so a manifest cannot go on naming Voxgig
35
+ // while the model names someone else, which is exactly what the hardcoded
36
+ // constant here did.
37
+ const author = authorInfo(model, target.name)
38
+
32
39
  const feature = getModelPath(model, `main.${KIT}.feature`)
33
40
 
34
41
  const only = (kind: string, deps: any) =>
@@ -79,7 +86,7 @@ const Package = cmp(async function Package(props: any) {
79
86
  "clean": "rm -rf node_modules yarn.lock package-lock.json",
80
87
  "reset": "npm run clean && npm i && npm test",
81
88
  },
82
- author: { name: PUBLISHER, url: PUBLISHER_URL },
89
+ author,
83
90
 
84
91
  // TODO: needs to be config
85
92
  license: 'MIT',
@@ -4,6 +4,7 @@ import {
4
4
  File,
5
5
  cmp,
6
6
  packageVersion,
7
+ authorInfo,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -19,6 +20,12 @@ const Package = cmp(async function Package(props: any) {
19
20
  const target = props.target
20
21
  const model = ctx$.model
21
22
 
23
+ // WHO WROTE THIS PACKAGE. Per target, falling back to the model-wide value
24
+ // and then to the publisher — so a manifest cannot go on naming Voxgig
25
+ // while the model names someone else, which is exactly what the hardcoded
26
+ // constant here did.
27
+ const author = authorInfo(model, target.name)
28
+
22
29
  const pkg = packageName(model)
23
30
 
24
31
  File({ name: pkg + '.opam' }, () => {
@@ -26,8 +33,8 @@ const Package = cmp(async function Package(props: any) {
26
33
  name: "${pkg}"
27
34
  version: "${packageVersion(model, target.name)}"
28
35
  synopsis: "${model.const.Name} SDK for OCaml (generated by @voxgig/sdkgen)"
29
- maintainer: "voxgig"
30
- authors: "voxgig"
36
+ maintainer: "${author.name}"
37
+ authors: "${author.name}"
31
38
  license: "MIT"
32
39
  homepage: "https://voxgig.com"
33
40
  bug-reports: "https://voxgig.com"
@@ -6,7 +6,8 @@ import {
6
6
  collectDeps,
7
7
  pkgDescription,
8
8
  keywords,
9
- repoInfo, packageName
9
+ repoInfo, packageName,
10
+ authorInfo,
10
11
  } from '@voxgig/sdkgen'
11
12
 
12
13
 
@@ -21,6 +22,12 @@ const Package = cmp(async function Package(props: any) {
21
22
 
22
23
  const model: Model = ctx$.model
23
24
 
25
+ // WHO WROTE THIS PACKAGE. Per target, falling back to the model-wide value
26
+ // and then to the publisher — so a manifest cannot go on naming Voxgig
27
+ // while the model names someone else, which is exactly what the hardcoded
28
+ // constant here did.
29
+ const author = authorInfo(model, target.name)
30
+
24
31
  // Package namespace mirrors the npm scope (model.origin, e.g. "voxgig-sdk").
25
32
  // If origin already ends in "-sdk" the slug stands alone; otherwise append
26
33
  // "-sdk" (matches the TS Package generator).
@@ -39,7 +46,7 @@ const Package = cmp(async function Package(props: any) {
39
46
  "homepage": "${repoUrl}",
40
47
  "license": "MIT",
41
48
  "authors": [
42
- { "name": "Voxgig", "homepage": "https://voxgig.com" }
49
+ { "name": "${author.name}"${'' === author.url ? '' : `, "homepage": "${author.url}"`} }
43
50
  ],
44
51
  "support": {
45
52
  "issues": "${issuesUrl}",
@@ -6,7 +6,8 @@ import {
6
6
  collectDeps,
7
7
  pkgDescription,
8
8
  repoInfo, packageName,
9
- packageVersion
9
+ packageVersion,
10
+ authorInfo,
10
11
  } from '@voxgig/sdkgen'
11
12
 
12
13
 
@@ -21,6 +22,12 @@ const Package = cmp(async function Package(props: any) {
21
22
 
22
23
  const model: Model = ctx$.model
23
24
 
25
+ // WHO WROTE THIS PACKAGE. Per target, falling back to the model-wide value
26
+ // and then to the publisher — so a manifest cannot go on naming Voxgig
27
+ // while the model names someone else, which is exactly what the hardcoded
28
+ // constant here did.
29
+ const author = authorInfo(model, target.name)
30
+
24
31
  // Gem name is namespaced to model.origin (e.g. "voxgig-sdk"). RubyGems
25
32
  // names can't contain "/", so the parts are hyphen-joined. The require
26
33
  // path (`${model.name}_sdk`) is unchanged.
@@ -58,7 +65,7 @@ gemspec
58
65
  Content(`Gem::Specification.new do |spec|
59
66
  spec.name = "${gemName}"
60
67
  spec.version = "${packageVersion(model, target.name)}"
61
- spec.authors = ["Voxgig"]
68
+ spec.authors = ["${author.name}"]
62
69
  spec.summary = "${pkgDescription(model, 'rb')}"
63
70
  spec.description = "${pkgDescription(model, 'rb')}"
64
71
  spec.license = "MIT"