@voxgig/create-sdkgen 0.24.0 → 0.26.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.
@@ -45,8 +45,6 @@ const { names, Jostraca } = JostracaModule
45
45
  const SDK_FOLDER = '.sdk'
46
46
 
47
47
 
48
- // TODO: CreateSdkGen opts and generate opts should be mostly the same, and
49
- // generate should override
50
48
  function CreateSdkGen(opts: FullCreateSdkGenOptions) {
51
49
  const fs = opts.fs || Fs
52
50
 
@@ -107,12 +105,6 @@ function CreateSdkGen(opts: FullCreateSdkGenOptions) {
107
105
  folder,
108
106
  log: log.child({ cmp: 'jostraca' }),
109
107
  meta: { spec },
110
- // Overwrite the scaffolded project (.sdk components, templates, build
111
- // config) rather than 3-way merge. The scaffold is toolchain-derived and
112
- // not hand-edited; merging against a drifting .jostraca base keeps STALE
113
- // components/templates on a toolchain bump (so a fix never propagates) and
114
- // can inject <<<<<<< markers. See @voxgig/sdkgen
115
- // docs/explanation/regeneration-overwrite.md.
116
108
  existing: {
117
109
  txt: {
118
110
  write: true,
@@ -166,7 +158,6 @@ function CreateSdkGen(opts: FullCreateSdkGenOptions) {
166
158
 
167
159
  projectFolder = Path.resolve(Path.join(__dirname, 'project', spec.project))
168
160
 
169
- // TODO: support auto install project npm package (specific version) in a special cache folder
170
161
 
171
162
  if (!fs.existsSync(projectFolder)) {
172
163
  projectFolder = spec.project
@@ -275,17 +266,6 @@ async function installNpm(spec: GenerateSpec, opts: any, model: any) {
275
266
  await runNpm(['install'], spawn_opts)
276
267
  }
277
268
 
278
- // `-t` / `-f` are INDEPENDENT of `--no-install`: the documented contract is
279
- // that --no-install skips `npm install`, not that it drops the requested
280
- // targets and features. Adding them is still valid without an install when
281
- // the project already has its toolchain (re-scaffolding an existing folder
282
- // is a supported flow — "if run against an existing folder generated files
283
- // will be overwritten").
284
- //
285
- // They ARE npm run scripts that resolve @voxgig/sdkgen from node_modules, so
286
- // without one they cannot work. Fail with an actionable message rather than
287
- // the raw module-resolution error — and rather than silently skipping, which
288
- // reports success while leaving config.aon without the requested entries.
289
269
  const wanted = [
290
270
  (spec.target ?? []).length ? '--target' : '',
291
271
  (spec.feature ?? []).length ? '--feature' : '',
@@ -342,21 +322,6 @@ async function installFeatures(spec: GenerateSpec, opts: any, model: any, spawn_
342
322
  }
343
323
 
344
324
 
345
- /*
346
- function logfiles(info: any, log: ReturnType<typeof Pino>) {
347
- const cwd = process.cwd()
348
-
349
- ; Object.keys(info.files).map(action => {
350
- let entries = info.files[action]
351
- if (0 < entries.length) {
352
- log.debug({
353
- point: 'file-' + action, entries,
354
- note: '\n' + entries.map((n: any) => n.replace(cwd, '.')).join('\n')
355
- })
356
- }
357
- })
358
- }
359
- */
360
325
 
361
326
  export type {
362
327
  CreateSdkGenOptions,
@@ -1,7 +1,6 @@
1
1
 
2
2
 
3
3
  import Path from 'node:path'
4
- // import * as Fs from 'node:fs'
5
4
 
6
5
  import {
7
6
  names,
@@ -62,39 +61,6 @@ log/
62
61
  `
63
62
 
64
63
 
65
- // The guide overlay is the ONE model file the user owns.
66
- //
67
- // Everything else the scaffold writes is toolchain-derived and is deliberately
68
- // OVERWRITTEN (see the `existing.txt.write` rationale in create-sdkgen.ts) so a
69
- // toolchain fix propagates. `model/guide/guide.aon` is the exception: apidef
70
- // unifies it OVER the heuristic classification, and it is where every
71
- // documented customization lives — entity rename, hide, move, activate,
72
- // per-path deactivation, method override, param rename, response transform.
73
- // Copying the stub over it destroyed that work on every re-scaffold, and
74
- // cedar-regen.sh re-scaffolds on every regen, so the loss was silent and
75
- // repeated.
76
- //
77
- // It is excluded from the bulk Copy and merged instead (see mergeGuide).
78
- // The PROJECT overlay: the second model file the user owns.
79
- //
80
- // `guide.aon` owns what the API looks like. This owns what the PROJECT is —
81
- // publication values above all. The sdkgen schema tells you to declare those
82
- // in `model/sdk.aon` "where they survive a resync", and that was simply not
83
- // true: ModelSdk rewrites sdk.aon from its template on every scaffold, so a
84
- // version or package name declared there vanished before the model was built,
85
- // with no error. The cedar fleet ran for months with every manifest pinned at
86
- // the schema default 0.0.1 while its git tags climbed past 0.1.1, and this is
87
- // why.
88
- //
89
- // sdk.aon cannot simply be merged the way guide.aon is. Its template owns
90
- // `name`, `origin` and especially `def` — the OpenAPI filename, which really
91
- // does change (three cedar specs were renamed in one week). A "keep the user's
92
- // file" merge would freeze `def` at a spec that no longer exists and break
93
- // regeneration. So the template keeps sole ownership of sdk.aon, and the
94
- // user gets a file of their own that sdk.aon includes.
95
- //
96
- // Written ONCE. If it exists, it is re-emitted byte-for-byte, so a
97
- // re-scaffold is a no-op on it.
98
64
  const PROJECT_FILE = 'project.aon'
99
65
  const PROJECT_STUB = `# Project overlay — YOURS. The scaffold creates this file once and never
100
66
  # overwrites it, unlike every other file it writes.
@@ -127,17 +93,6 @@ const GUIDE_FILE = 'guide.aon'
127
93
  const GUIDE_REL = ['model', 'guide', GUIDE_FILE]
128
94
 
129
95
 
130
- // Rename a user-owned overlay from its pre-2026-08 `.aontu` name to `.aon`.
131
- //
132
- // Both overlays are read-if-present and written-if-absent, which is exactly
133
- // the shape that turns an extension rename into DATA LOSS rather than a
134
- // rename: look for `guide.aon`, find nothing, write a fresh template over the
135
- // top, and the user's entity renames, hides and overrides are still sitting
136
- // in `guide.aontu` being ignored. 660 generated repos carry one.
137
- //
138
- // So the legacy file is moved to the new name BEFORE anything reads or writes
139
- // it. Contents are untouched; only the name changes. Idempotent — once the
140
- // `.aon` exists this does nothing, so a re-scaffold stays a no-op.
141
96
  function migrateOverlay(fs: any, dir: string, name: string): void {
142
97
  const next = Path.join(dir, name + '.aon')
143
98
  const prev = Path.join(dir, name + '.aontu')
@@ -145,15 +100,6 @@ function migrateOverlay(fs: any, dir: string, name: string): void {
145
100
  return
146
101
  }
147
102
  try {
148
- // RENAME THE FILE *AND* ITS INCLUDES. An overlay written before the
149
- // rename includes the toolchain schemas by their old names —
150
- // `@"@voxgig/apidef/model/guide.aontu"` — and apidef 8 no longer ships
151
- // that file, so a migration that moved the file but kept its contents
152
- // verbatim produced a project that could not resolve its own guide.
153
- // mergeGuide cannot repair this either: it keeps the user's lines and
154
- // only ADDS missing template includes, so the dead one would survive
155
- // beside the new one. Only `@`-include extensions are touched; nothing
156
- // else in the user's file is rewritten.
157
103
  const src = String(fs.readFileSync(prev))
158
104
  .replace(/(@['"][^'"]+)\.aontu(['"])/g, '$1.aon$2')
159
105
  fs.writeFileSync(next, src)
@@ -166,17 +112,6 @@ function migrateOverlay(fs: any, dir: string, name: string): void {
166
112
  }
167
113
 
168
114
 
169
- // Merge the guide template into an existing guide overlay.
170
- //
171
- // A guide is the template's `@`-includes plus whatever the user added. The
172
- // includes are the only part the template OWNS, so the merge is: keep the
173
- // user's file verbatim, and restore any include it is missing. When nothing
174
- // is missing — the normal case — the file is returned byte-identical, so a
175
- // re-scaffold is a no-op on it.
176
- //
177
- // Missing includes are restored at the TOP, not appended: `base-guide.aon`
178
- // carries the heuristic classification that the user's overrides unify over,
179
- // and the overlay reads correctly only when the includes precede it.
180
115
  function mergeGuide(existing: string | null, template: string): string {
181
116
  if (null == existing) {
182
117
  return template
@@ -184,18 +119,6 @@ function mergeGuide(existing: string | null, template: string): string {
184
119
 
185
120
  const isInclude = (line: string) => line.trim().startsWith('@')
186
121
 
187
- // AN INCLUDE IS THE FILE IT NAMES, whichever way the `./` falls.
188
- //
189
- // The template gained a `./` when aontu 0.65 made a bare single-segment
190
- // include name a PACKAGE (ADR-039). Every guide written before that says
191
- // `@"base-guide.aon"`, and `guide.aon` is project-owned: the scaffold
192
- // writes it once and never again, so those files stay as they are until
193
- // something migrates them.
194
- //
195
- // Comparing the raw lines read the old spelling as a DIFFERENT include, so
196
- // a re-scaffold restored one the file already had — leaving the guide
197
- // including `base-guide.aon` twice, under two spellings, which is the one
198
- // thing this merge exists to avoid.
199
122
  const key = (line: string) => line.trim().replace(/^@"\.\//, '@"')
200
123
 
201
124
  const have = new Set(existing.split('\n').map(key))
@@ -212,26 +135,10 @@ function mergeGuide(existing: string | null, template: string): string {
212
135
  }
213
136
 
214
137
 
215
- // The OpenAPI filename is inherited from the upstream API slug, and upstream
216
- // slugs are not constrained: the freepublicapis corpus contains apostrophes
217
- // (catherine-shulman's-quotes), accents (dólar-y-monedas, kölner-adressen),
218
- // and '!' / '>' / '_'. That name is copied into .sdk/def/ AND written verbatim
219
- // into a single-quoted aontu string in sdk.aon:
220
- //
221
- // def: 'catherine-shulman's-quotes_0.1.0.json'
222
- // ^ terminates the string -> AontuError, no SDK
223
- //
224
- // Slugs are [a-z0-9-]; a filename additionally needs '.' and '_' for the
225
- // version segment and extension, so those are kept — which also means every
226
- // already-conforming name (including cedar's dotted gitlab-v4-swagger-2.0.yaml)
227
- // is untouched and no repo churns. Everything else is folded to '-'.
228
138
  function sanitizeDefName(filename: string): string {
229
139
  const clean = filename
230
- .normalize('NFKD').replace(/[\u0300-\u036f]/g, '') // dólar -> dolar
140
+ .normalize('NFKD').replace(/[\u0300-\u036f]/g, '')
231
141
  .toLowerCase()
232
- // Quotes are ELIDED, not folded to '-', so the filename agrees with the
233
- // repo name apidef's sanitizeSlug derives from the same slug:
234
- // catherine-shulman's-quotes -> catherine-shulmans-quotes(-sdk).
235
142
  .replace(/['’"]/g, '')
236
143
  .replace(/[^a-z0-9._-]+/g, '-')
237
144
  .replace(/-+/g, '-')
@@ -244,17 +151,13 @@ function sanitizeDefName(filename: string): string {
244
151
  }
245
152
 
246
153
 
247
- // TODO: rename to RootSdk
248
154
  const CreateRoot = cmp(function CreateRoot(props: any) {
249
155
  const { ctx$, ctx$: { folder }, spec, model } = props
250
156
  const fs = ctx$.fs()
251
157
 
252
- // TODO: move to @voxgig/util as duplicated
253
158
  model.const = { name: model.name }
254
159
  names(model.const, model.name)
255
160
  model.const.year = new Date().getFullYear()
256
- // Scaffold is MIT-licensed (matches the emitted LICENSE and tm/LICENSE);
257
- // set directly rather than via names() so the value stays 'MIT', not 'Mit'.
258
161
  model.const.License = 'MIT'
259
162
 
260
163
  ctx$.model = model
@@ -263,11 +166,7 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
263
166
  const from =
264
167
  Path.resolve(Path.join(__dirname, '..', '..', '..', 'project', 'standard'))
265
168
 
266
- // console.log('FROM', from)
267
169
 
268
- // Copy `exclude` matches the SOURCE-RELATIVE path exactly, so this names
269
- // the one guide under the scaffold and nothing else. It is re-emitted,
270
- // merged, in the model folder below.
271
170
  const guideExclude = [spec.sdk_folder, ...GUIDE_REL].join('/')
272
171
 
273
172
  Copy({
@@ -296,7 +195,6 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
296
195
  })
297
196
 
298
197
  Folder({ name: 'def' }, () => {
299
- // TODO: file existence check should be jostraca util
300
198
  if (fs.existsSync(origdef)) {
301
199
  Copy({ from: origdef, to: projdef })
302
200
  }