@octalmesh/seagull-core 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +173 -39
  3. package/dist/index.mjs +218 -152
  4. package/package.json +6 -6
  5. package/src/config/loader.test.ts +505 -0
  6. package/src/config/loader.ts +31 -12
  7. package/src/config/publishing.test.ts +92 -0
  8. package/src/config/publishing.ts +9 -5
  9. package/src/config/resolve-config-file.test.ts +60 -0
  10. package/src/config/schema.test.ts +466 -0
  11. package/src/config/schema.ts +16 -10
  12. package/src/config/spec-format.test.ts +54 -0
  13. package/src/config/spec-format.ts +48 -0
  14. package/src/config/template.test.ts +168 -0
  15. package/src/config/types.ts +3 -3
  16. package/src/generator/generator.test.ts +59 -0
  17. package/src/generator/registry.test.ts +84 -0
  18. package/src/generator/types.ts +9 -14
  19. package/src/generators/openapi-generator-cli/openapi-generator-cli.generator.test.ts +259 -0
  20. package/src/generators/openapi-generator-cli/patchers/go-module.patcher.test.ts +119 -0
  21. package/src/generators/openapi-generator-cli/patchers/maven.patcher.test.ts +141 -0
  22. package/src/generators/openapi-generator-cli/patchers/npm.patcher.test.ts +132 -0
  23. package/src/generators/openapi-typescript/openapi-typescript.generator.test.ts +190 -0
  24. package/src/git/git.test.ts +234 -0
  25. package/src/git/git.ts +50 -4
  26. package/src/index.ts +8 -0
  27. package/src/process/exec.test.ts +103 -0
  28. package/src/process/exec.ts +1 -2
  29. package/src/process/resolve-bin.test.ts +43 -0
  30. package/src/readme/default-templates.test.ts +179 -0
  31. package/src/readme/default-templates.ts +5 -10
  32. package/src/readme/readme-renderer.test.ts +121 -0
  33. package/src/readme/readme-renderer.ts +3 -7
  34. package/src/redocly/redocly-sync.test.ts +190 -0
  35. package/src/redocly/redocly-sync.ts +3 -1
  36. package/src/test-support/fixtures.ts +65 -0
  37. package/src/version/version.test.ts +93 -0
  38. package/src/version/version.ts +4 -2
package/dist/index.mjs CHANGED
@@ -21,15 +21,14 @@ const varsTreeSchema = z.lazy(() => z.record(z.string(), z.union([
21
21
  z.boolean(),
22
22
  varsTreeSchema
23
23
  ])));
24
- const githubSchema = z.object({
25
- owner: z.string().min(1),
26
- repo: z.string().min(1)
27
- });
24
+ const specFormatSchema = z.enum(["json", "yaml"]);
25
+ const specFormatListSchema = z.union([specFormatSchema, z.array(specFormatSchema).min(1)]).default("json").transform((value) => Array.isArray(value) ? value : [value]).refine((formats) => new Set(formats).size === formats.length, { message: "paths.specFormat entries must be unique" });
28
26
  const pathsSchema = z.object({
29
27
  dist: z.string().min(1).default("dist"),
30
28
  specs: z.string().min(1).optional(),
31
29
  docs: z.string().min(1).optional(),
32
- sdk: z.string().min(1).optional()
30
+ sdk: z.string().min(1).optional(),
31
+ specFormat: specFormatListSchema
33
32
  });
34
33
  const docsSchema = z.object({
35
34
  server: z.object({
@@ -66,8 +65,8 @@ const mavenCoordsSchema = z.object({
66
65
  /**
67
66
  * Publishing conventions - git branch/tag naming, and where registry-backed
68
67
  * artifacts (npm, Maven) get pushed. Every field is a template supporting the
69
- * usual `{...}` placeholders (`{service}`, `{id}`, `{github.*}`, `{vars.*}`,
70
- * and for `tag` only, also `{version}`).
68
+ * usual `{...}` placeholders (`{service}`, `{id}`, `{vars.*}`, and for `tag`
69
+ * only, also `{version}`).
71
70
  *
72
71
  * Required at the root level - seagull has no built-in opinion on branch/tag
73
72
  * naming or which registry to use, so this has to come from the config, not
@@ -91,7 +90,7 @@ const publishingSchema = z.object({
91
90
  /**
92
91
  * Template for the `repository.url` field written into generated
93
92
  * `package.json` (and shown in default README templates), e.g.
94
- * `"https://github.com/{github.owner}/{github.repo}"`.
93
+ * `"https://github.com/{vars.repository.owner}/{vars.repository.repo}"`.
95
94
  */
96
95
  repositoryUrl: z.string().min(1),
97
96
  npm: z.object({
@@ -207,13 +206,15 @@ const contractSchema = z.object({
207
206
  });
208
207
  const rootConfigSchema = z.object({
209
208
  /**
210
- * The config schema version this file targets. Currently must be `1`
209
+ * The config schema version this file targets. Currently, must be `1`
211
210
  * (the only version that exists) - see {@link CONFIG_SCHEMA_VERSION}.
212
211
  */
213
212
  configVersion: z.literal(1),
214
- github: githubSchema,
215
213
  vars: varsTreeSchema.default({}),
216
- paths: pathsSchema.default({ dist: "dist" }),
214
+ paths: pathsSchema.default({
215
+ dist: "dist",
216
+ specFormat: ["json"]
217
+ }),
217
218
  docs: docsSchema,
218
219
  /**
219
220
  * Publishing conventions (branch/tag naming, registry URLs), applied to
@@ -226,6 +227,131 @@ const rootConfigSchema = z.object({
226
227
  contracts: z.array(contractSchema).min(1)
227
228
  });
228
229
  //#endregion
230
+ //#region src/git/git.ts
231
+ /**
232
+ * A git ref name (branch or tag) that's safe to pass as a CLI argument to
233
+ * `git`.
234
+ *
235
+ * @see {@link assertSafeRefName}
236
+ */
237
+ const gitRefNameSchema = z.string().min(1).refine((value) => !value.startsWith("-"), { message: "must not start with \"-\" - git would parse it as a command-line option instead of a ref name" });
238
+ /**
239
+ * Throws if `name` isn't a safe git ref name - see {@link gitRefNameSchema}.
240
+ *
241
+ * @param name - The candidate branch/tag name.
242
+ * @param label - What to call it in the error message (e.g. `"branch"`).
243
+ */
244
+ function assertSafeRefName(name, label) {
245
+ const result = gitRefNameSchema.safeParse(name);
246
+ if (!result.success) throw new Error(`Invalid git ${label} "${name}": ${result.error.issues[0]?.message}`);
247
+ }
248
+ /**
249
+ * Execute a git command in a given working directory and return the result.
250
+ *
251
+ * @param args - The command-line arguments to pass to the git command.
252
+ * @param cwd - The working directory in which to execute the git command.
253
+ * @returns The result of the git command.
254
+ *
255
+ * @see {@link GitResult} - The result of executing the git command.
256
+ */
257
+ function git(args, cwd) {
258
+ const result = spawnSync("git", args, {
259
+ cwd,
260
+ encoding: "utf8"
261
+ });
262
+ return {
263
+ status: result.status ?? 1,
264
+ stdout: (result.stdout ?? "").trim(),
265
+ stderr: (result.stderr ?? "").trim()
266
+ };
267
+ }
268
+ /**
269
+ * Check if a remote branch exists in the given repository.
270
+ *
271
+ * @param repoRoot - The root directory of the repository.
272
+ * @param branch - The name of the branch to check.
273
+ * @returns Whether the remote branch exists (true) or not (false).
274
+ */
275
+ function remoteBranchExists(repoRoot, branch) {
276
+ assertSafeRefName(branch, "branch");
277
+ return git([
278
+ "ls-remote",
279
+ "--exit-code",
280
+ "--heads",
281
+ "origin",
282
+ "--",
283
+ branch
284
+ ], repoRoot).status === 0;
285
+ }
286
+ /**
287
+ * Check if a remote tag exists in the given repository.
288
+ *
289
+ * @param repoRoot - The root directory of the repository.
290
+ * @param tag - The name of the tag to check.
291
+ * @returns Whether the remote tag exists (true) or not (false).
292
+ */
293
+ function tagExists(repoRoot, tag) {
294
+ assertSafeRefName(tag, "tag");
295
+ return git([
296
+ "ls-remote",
297
+ "--exit-code",
298
+ "--tags",
299
+ "origin",
300
+ "--",
301
+ tag
302
+ ], repoRoot).status === 0;
303
+ }
304
+ /**
305
+ * Fetch a single tag's object from origin into the local repo, without
306
+ * fetching the rest of history/tags.
307
+ *
308
+ * @param repoRoot - The root directory of the repository.
309
+ * @param tag - The name of the tag to fetch.
310
+ * @returns The result of the underlying `git fetch` command.
311
+ */
312
+ function fetchTag(repoRoot, tag) {
313
+ assertSafeRefName(tag, "tag");
314
+ return git([
315
+ "fetch",
316
+ "origin",
317
+ "--force",
318
+ "--",
319
+ `refs/tags/${tag}:refs/tags/${tag}`
320
+ ], repoRoot);
321
+ }
322
+ /**
323
+ * Read a single file's content as it existed at a given git tag, without
324
+ * checking out a worktree.
325
+ *
326
+ * Returns `null` (rather than throwing) both when the tag can't be fetched and
327
+ * when the tag exists but doesn't contain the requested file - the latter is
328
+ * expected for tags published before that file was introduced, and callers
329
+ * should treat "unknown" the same as "no mismatch to report".
330
+ *
331
+ * @param repoRoot - The root directory of the repository.
332
+ * @param tag - The tag to read the file from.
333
+ * @param filePath - The path of the file within that tag's tree.
334
+ * @returns The file's content, or `null` if it couldn't be read.
335
+ */
336
+ function readFileAtTag(repoRoot, tag, filePath) {
337
+ assertSafeRefName(tag, "tag");
338
+ if (filePath.length === 0) throw new Error("Invalid git file path: must not be empty");
339
+ if (fetchTag(repoRoot, tag).status !== 0) return null;
340
+ const show = git(["show", `${tag}:${filePath}`], repoRoot);
341
+ return show.status === 0 ? show.stdout.trim() : null;
342
+ }
343
+ /**
344
+ * Require that a git command succeeded, throwing an error with the given
345
+ * message if it did not.
346
+ *
347
+ * @param result - The result of the git command to check.
348
+ * @param message - The error message to throw if the command failed.
349
+ * @throws Error if the git command failed (non-zero exit code).
350
+ */
351
+ function requireOk(result, message) {
352
+ if (result.status !== 0) throw new Error(`${message}: ${result.stderr || result.stdout}`);
353
+ }
354
+ //#endregion
229
355
  //#region src/config/template.ts
230
356
  const PLACEHOLDER = /\{([a-zA-Z0-9_.]+)}/g;
231
357
  /**
@@ -295,7 +421,7 @@ function flatten(value, prefix, out) {
295
421
  * @returns The validated (but not yet resolved) raw config.
296
422
  */
297
423
  function readRawConfig(configPath) {
298
- const raw = parse(readFileSync(configPath, "utf8"));
424
+ const raw = parse(readFileSync(configPath, "utf8"), { merge: true });
299
425
  const result = rootConfigSchema.safeParse(raw);
300
426
  if (!result.success) {
301
427
  const issues = z.prettifyError(result.error);
@@ -413,13 +539,19 @@ function resolveArtifactRef(ref, generators, contractName) {
413
539
  * @param rootPublishing - The required root-level `publishing:` config
414
540
  * from the config file.
415
541
  * @param context - The flattened template context for this artifact
416
- * (`service`, `id`, `github.*`, `vars.*`).
542
+ * (`service`, `id`, `vars.*`).
543
+ * @param artifactLabel - `"<contract>/<artifact id>"`, used only to
544
+ * identify the offending artifact in the error
545
+ * thrown when `branch` resolves unsafely - see
546
+ * {@link assertSafeRefName}.
417
547
  * @returns The fully resolved publishing conventions for this artifact.
418
548
  */
419
- function resolvePublishing(generatorPublishing, rootPublishing, context) {
549
+ function resolvePublishing(generatorPublishing, rootPublishing, context, artifactLabel) {
420
550
  const merged = applyPublishingOverride(rootPublishing, generatorPublishing);
551
+ const branch = interpolate(merged.branch, context);
552
+ assertSafeRefName(branch, `publishing.branch for artifact "${artifactLabel}"`);
421
553
  return {
422
- branch: interpolate(merged.branch, context),
554
+ branch,
423
555
  tagTemplate: merged.tag,
424
556
  repositoryUrl: interpolate(merged.repositoryUrl, context),
425
557
  npmRegistry: interpolate(merged.npm.registry, context),
@@ -440,7 +572,7 @@ function resolvePublishing(generatorPublishing, rootPublishing, context) {
440
572
  * @param sdkDir - Absolute path to the SDK output root (`<dist>/sdk`).
441
573
  * @param contractName - The owning contract's name.
442
574
  * @param contractContext - The flattened template context for this contract
443
- * (`service`, `github.*`, `vars.*` - not yet `id`).
575
+ * (`service`, `vars.*` - not yet `id`).
444
576
  * @param rootPublishing - The required root-level `publishing:` config from
445
577
  * the config file.
446
578
  * @returns The fully resolved artifact.
@@ -451,7 +583,7 @@ function resolveArtifact(id, def, rootDir, sdkDir, contractName, contractContext
451
583
  id
452
584
  };
453
585
  const resolved = interpolateDeep(def, context);
454
- const publishing = resolvePublishing(def.publishing, rootPublishing, context);
586
+ const publishing = resolvePublishing(def.publishing, rootPublishing, context, `${contractName}/${id}`);
455
587
  return {
456
588
  id,
457
589
  tool: resolved.tool,
@@ -478,18 +610,15 @@ function resolveArtifact(id, def, rootDir, sdkDir, contractName, contractContext
478
610
  * resolved relative to this.
479
611
  * @param sdkDir - Absolute path to the SDK output root (`<dist>/sdk`).
480
612
  * @param generators - The full `generators:` map from the raw config.
481
- * @param githubCtx - `{ owner, repo }`, exposed to templates as
482
- * `{github.owner}`/`{github.repo}`.
483
613
  * @param vars - The `vars:` tree from the raw config, exposed as
484
614
  * `{vars.*}`.
485
615
  * @param rootPublishing - The required root-level `publishing:` config from the
486
616
  * config file.
487
617
  * @returns The fully resolved contract.
488
618
  */
489
- function resolveContract(input, rootDir, sdkDir, generators, githubCtx, vars, rootPublishing) {
619
+ function resolveContract(input, rootDir, sdkDir, generators, vars, rootPublishing) {
490
620
  const context = buildTemplateContext({
491
621
  service: input.name,
492
- github: githubCtx,
493
622
  vars
494
623
  });
495
624
  const artifacts = input.artifacts.map((ref) => {
@@ -525,7 +654,7 @@ function loadConfig(configPath) {
525
654
  const specsDir = raw.paths.specs ? path.resolve(rootDir, raw.paths.specs) : path.join(distDir, "specs");
526
655
  const docsDir = raw.paths.docs ? path.resolve(rootDir, raw.paths.docs) : path.join(distDir, "docs");
527
656
  const sdkDir = raw.paths.sdk ? path.resolve(rootDir, raw.paths.sdk) : path.join(distDir, "sdk");
528
- const contracts = raw.contracts.map((contract) => resolveContract(contract, rootDir, sdkDir, raw.generators, raw.github, raw.vars, raw.publishing));
657
+ const contracts = raw.contracts.map((contract) => resolveContract(contract, rootDir, sdkDir, raw.generators, raw.vars, raw.publishing));
529
658
  return {
530
659
  configVersion: raw.configVersion,
531
660
  rootDir,
@@ -533,9 +662,9 @@ function loadConfig(configPath) {
533
662
  dist: distDir,
534
663
  specs: specsDir,
535
664
  docs: docsDir,
536
- sdk: sdkDir
665
+ sdk: sdkDir,
666
+ specFormat: raw.paths.specFormat
537
667
  },
538
- github: raw.github,
539
668
  vars: raw.vars,
540
669
  docs: raw.docs,
541
670
  contracts,
@@ -559,20 +688,19 @@ function loadConfig(configPath) {
559
688
  * `{service}`.
560
689
  * @param version - The resolved SDK version, exposed to the template as
561
690
  * `{version}`.
562
- * @param github - `{ owner, repo }`, exposed as `{github.owner}`/
563
- * `{github.repo}`.
564
691
  * @param vars - The config's `vars:` tree, exposed as `{vars.*}`.
565
692
  * @returns The rendered tag name.
566
693
  */
567
- function renderArtifactTag(artifact, contractName, version, github, vars) {
694
+ function renderArtifactTag(artifact, contractName, version, vars) {
568
695
  const context = buildTemplateContext({
569
696
  service: contractName,
570
697
  id: artifact.id,
571
698
  version,
572
- github,
573
699
  vars
574
700
  });
575
- return interpolate(artifact.publishing.tagTemplate, context);
701
+ const tag = interpolate(artifact.publishing.tagTemplate, context);
702
+ assertSafeRefName(tag, `publishing.tag for artifact "${contractName}/${artifact.id}"`);
703
+ return tag;
576
704
  }
577
705
  //#endregion
578
706
  //#region src/config/resolve-config-file.ts
@@ -604,6 +732,46 @@ function resolveConfigPath(cwd) {
604
732
  throw new Error(`No CLI config found in ${cwd} - looked for: ${CONFIG_FILENAMES.join(", ")}. Create one of these, or pass --config <path>.`);
605
733
  }
606
734
  //#endregion
735
+ //#region src/config/spec-format.ts
736
+ /**
737
+ * The "primary" format among a `paths.specFormat` list - the one SDK generation,
738
+ * version/hash resolution, and the docs site actually read from when more than
739
+ * one format is configured. By convention, that's whichever format was listed
740
+ * first.
741
+ *
742
+ * @param formats - `config.paths.specFormat` (always non-empty).
743
+ * @returns The primary format.
744
+ */
745
+ function primarySpecFormat(formats) {
746
+ return formats[0];
747
+ }
748
+ /**
749
+ * The filename a contract's bundled spec is written to/read from, e.g.
750
+ * `auth.json` or `auth.yaml` - one place computing this so `bundle`, `generate`,
751
+ * and `docs generate` can't disagree about the extension.
752
+ *
753
+ * @param contractName - The contract's `name`.
754
+ * @param format - `config.paths.specFormat`.
755
+ * @returns The filename (no directory), e.g. `"auth.yaml"`.
756
+ */
757
+ function specFilename(contractName, format) {
758
+ return `${contractName}.${format}`;
759
+ }
760
+ /**
761
+ * Parses a bundled spec's raw file contents according to its configured format.
762
+ * JSON and YAML are both valid inputs to Redocly/openapi-generator/
763
+ * openapi-typescript, so seagull's own parsing has to match: `JSON.parse`
764
+ * rejects trailing commas and comments that valid YAML permits, and would
765
+ * silently misparse a YAML document that happens to look JSON-ish.
766
+ *
767
+ * @param raw - The raw bundled spec file contents.
768
+ * @param format - `config.paths.specFormat`.
769
+ * @returns The parsed document.
770
+ */
771
+ function parseBundledSpec(raw, format) {
772
+ return format === "json" ? JSON.parse(raw) : parse(raw, { merge: true });
773
+ }
774
+ //#endregion
607
775
  //#region src/generator/generator.ts
608
776
  /**
609
777
  * The root primitive every concrete SDK generator implements.
@@ -677,8 +845,7 @@ function run(command, args, cwd) {
677
845
  return new Promise((resolvePromise, reject) => {
678
846
  spawn(command, args, {
679
847
  cwd,
680
- stdio: "inherit",
681
- shell: true
848
+ stdio: "inherit"
682
849
  }).on("close", (code) => {
683
850
  if (code === 0) {
684
851
  resolvePromise();
@@ -700,8 +867,7 @@ function run(command, args, cwd) {
700
867
  function runSync(command, args, cwd) {
701
868
  return spawnSync(command, args, {
702
869
  cwd,
703
- stdio: "inherit",
704
- shell: true
870
+ stdio: "inherit"
705
871
  }).status ?? 1;
706
872
  }
707
873
  //#endregion
@@ -732,106 +898,6 @@ function resolveBinPath(pkgName, binName) {
732
898
  return path.join(pkgDir, bin);
733
899
  }
734
900
  //#endregion
735
- //#region src/git/git.ts
736
- /**
737
- * Execute a git command in a given working directory and return the result.
738
- *
739
- * @param args - The command-line arguments to pass to the git command.
740
- * @param cwd - The working directory in which to execute the git command.
741
- * @returns The result of the git command.
742
- *
743
- * @see {@link GitResult} - The result of executing the git command.
744
- */
745
- function git(args, cwd) {
746
- const result = spawnSync("git", args, {
747
- cwd,
748
- encoding: "utf8"
749
- });
750
- return {
751
- status: result.status ?? 1,
752
- stdout: (result.stdout ?? "").trim(),
753
- stderr: (result.stderr ?? "").trim()
754
- };
755
- }
756
- /**
757
- * Check if a remote branch exists in the given repository.
758
- *
759
- * @param repoRoot - The root directory of the repository.
760
- * @param branch - The name of the branch to check.
761
- * @returns Whether the remote branch exists (true) or not (false).
762
- */
763
- function remoteBranchExists(repoRoot, branch) {
764
- return git([
765
- "ls-remote",
766
- "--exit-code",
767
- "--heads",
768
- "origin",
769
- branch
770
- ], repoRoot).status === 0;
771
- }
772
- /**
773
- * Check if a remote tag exists in the given repository.
774
- *
775
- * @param repoRoot - The root directory of the repository.
776
- * @param tag - The name of the tag to check.
777
- * @returns Whether the remote tag exists (true) or not (false).
778
- */
779
- function tagExists(repoRoot, tag) {
780
- return git([
781
- "ls-remote",
782
- "--exit-code",
783
- "--tags",
784
- "origin",
785
- tag
786
- ], repoRoot).status === 0;
787
- }
788
- /**
789
- * Fetch a single tag's object from origin into the local repo, without
790
- * fetching the rest of history/tags.
791
- *
792
- * @param repoRoot - The root directory of the repository.
793
- * @param tag - The name of the tag to fetch.
794
- * @returns The result of the underlying `git fetch` command.
795
- */
796
- function fetchTag(repoRoot, tag) {
797
- return git([
798
- "fetch",
799
- "origin",
800
- `refs/tags/${tag}:refs/tags/${tag}`,
801
- "--force"
802
- ], repoRoot);
803
- }
804
- /**
805
- * Read a single file's content as it existed at a given git tag, without
806
- * checking out a worktree.
807
- *
808
- * Returns `null` (rather than throwing) both when the tag can't be fetched and
809
- * when the tag exists but doesn't contain the requested file - the latter is
810
- * expected for tags published before that file was introduced, and callers
811
- * should treat "unknown" the same as "no mismatch to report".
812
- *
813
- * @param repoRoot - The root directory of the repository.
814
- * @param tag - The tag to read the file from.
815
- * @param filePath - The path of the file within that tag's tree.
816
- * @returns The file's content, or `null` if it couldn't be read.
817
- */
818
- function readFileAtTag(repoRoot, tag, filePath) {
819
- if (fetchTag(repoRoot, tag).status !== 0) return null;
820
- const show = git(["show", `${tag}:${filePath}`], repoRoot);
821
- return show.status === 0 ? show.stdout.trim() : null;
822
- }
823
- /**
824
- * Require that a git command succeeded, throwing an error with the given
825
- * message if it did not.
826
- *
827
- * @param result - The result of the git command to check.
828
- * @param message - The error message to throw if the command failed.
829
- * @throws Error if the git command failed (non-zero exit code).
830
- */
831
- function requireOk(result, message) {
832
- if (result.status !== 0) throw new Error(`${message}: ${result.stderr || result.stdout}`);
833
- }
834
- //#endregion
835
901
  //#region src/version/version.ts
836
902
  /**
837
903
  * Resolves the version to stamp onto a single contract's generated SDK
@@ -861,12 +927,14 @@ function resolveVersion(spec, contractName) {
861
927
  return specVersion.replace(/^v/, "");
862
928
  }
863
929
  /**
864
- * Computes a stable content hash of a bundled OpenAPI document's raw JSON text.
930
+ * Computes a stable content hash of a bundled OpenAPI document's raw file
931
+ * contents (JSON or YAML, whichever `paths.specFormat` produced).
932
+ *
865
933
  * Stamped alongside `VERSION` into every generated SDK package so
866
934
  * `publish-sdk.ts` can tell a genuine no-op republish (same spec, same version)
867
935
  * apart from a spec that changed without its `info.version` being bumped.
868
936
  *
869
- * @param raw - The raw bundled spec file contents (JSON text).
937
+ * @param raw - The raw bundled spec file contents.
870
938
  * @returns A `sha256` hex digest of the raw contents.
871
939
  */
872
940
  function hashSpec(raw) {
@@ -883,19 +951,19 @@ function hashSpec(raw) {
883
951
  * come from the artifact's resolved `publishing:` config, so a repo that
884
952
  * overrides its registry sees that reflected here automatically.
885
953
  *
886
- * @param args - The contract, artifact, version, and github/vars coordinates
887
- * to render for.
954
+ * @param args - The contract, artifact, version, and vars coordinates to render
955
+ * for.
888
956
  * @returns The rendered README content.
889
957
  */
890
- function renderDefaultReadme({ contract, artifact, version, github, vars }) {
958
+ function renderDefaultReadme({ contract, artifact, version, vars }) {
891
959
  return `${`# ${contract.title} - ${label(artifact)}
892
960
 
893
- > Generated from \`${contract.entrypointRelative}\` in [${github.owner}/${github.repo}](https://github.com/${github.owner}/${github.repo}).
961
+ > Generated from \`${contract.entrypointRelative}\` in ${artifact.publishing.repositoryUrl}.
894
962
  > Do not edit by hand - this package is regenerated and republished on every release.
895
963
 
896
964
  Version: \`${version}\`
897
965
  Source branch: \`${artifact.branch}\`
898
- `}\n${body(contract, artifact, version, github, vars)}\n`;
966
+ `}\n${body(contract, artifact, version, vars)}\n`;
899
967
  }
900
968
  function label(artifact) {
901
969
  return `${{
@@ -904,11 +972,11 @@ function label(artifact) {
904
972
  java: "Java"
905
973
  }[artifact.lang]} ${artifact.kind === "client" ? "Client SDK" : artifact.lang === "typescript" ? "Server Types" : "Server Stubs"}`;
906
974
  }
907
- function body(contract, artifact, version, github, vars) {
975
+ function body(contract, artifact, version, vars) {
908
976
  switch (`${artifact.lang}-${artifact.kind}`) {
909
977
  case "typescript-client": return tsClient(artifact, version);
910
978
  case "typescript-server": return tsServer(artifact, version);
911
- case "go-client": return goClient(contract, artifact, github, vars);
979
+ case "go-client": return goClient(contract, artifact, vars);
912
980
  case "go-server": return goServer(artifact);
913
981
  case "java-client": return javaClient(artifact, version);
914
982
  case "java-server": return javaServer(artifact, version);
@@ -966,8 +1034,8 @@ app.post("/login", (req, res) => {
966
1034
  \`\`\`
967
1035
  `;
968
1036
  }
969
- function goClient(contract, artifact, github, vars) {
970
- const exampleTag = renderArtifactTag(artifact, contract.name, "<version>", github, vars);
1037
+ function goClient(contract, artifact, vars) {
1038
+ const exampleTag = renderArtifactTag(artifact, contract.name, "<version>", vars);
971
1039
  return `## Install
972
1040
 
973
1041
  Go has no package registry, so this module is pulled directly from its
@@ -1109,13 +1177,12 @@ public class SomeController implements SomeApi {
1109
1177
  * If the artifact has a `readme:` path configured (resolved at config-load time
1110
1178
  * to `artifact.readmeTemplate`), that file is read and interpolated with the
1111
1179
  * same `{...}` placeholder engine naming templates use - `{service}`,
1112
- * `{title}`, `{version}`, `{vars.*}`, `{github.owner}`, `{github.repo}`, plus
1113
- * `{artifact.*}` (id/lang/kind/package/goModule/goPackageName/maven.groupId/
1180
+ * `{title}`, `{version}`, `{vars.*}`, plus `{artifact.*}`
1181
+ * (id/lang/kind/package/goModule/goPackageName/maven.groupId/
1114
1182
  * maven.artifactId/branch/tag/npmRegistry/mavenRepositoryUrl). Otherwise,
1115
1183
  * falls back to a built-in default template for the artifact's language/kind.
1116
1184
  *
1117
- * @param args - The contract, artifact, version, and github/vars context to
1118
- * render for.
1185
+ * @param args - The contract, artifact, version, and vars context to render for.
1119
1186
  * @returns The rendered README content.
1120
1187
  */
1121
1188
  async function renderReadme(args) {
@@ -1124,7 +1191,6 @@ async function renderReadme(args) {
1124
1191
  service: args.contract.name,
1125
1192
  title: args.contract.title,
1126
1193
  version: args.version,
1127
- github: args.github,
1128
1194
  vars: args.vars,
1129
1195
  artifact: {
1130
1196
  id: args.artifact.id,
@@ -1135,7 +1201,7 @@ async function renderReadme(args) {
1135
1201
  goPackageName: args.artifact.goPackageName,
1136
1202
  maven: args.artifact.maven,
1137
1203
  branch: args.artifact.branch,
1138
- tag: renderArtifactTag(args.artifact, args.contract.name, args.version, args.github, args.vars),
1204
+ tag: renderArtifactTag(args.artifact, args.contract.name, args.version, args.vars),
1139
1205
  npmRegistry: args.artifact.publishing.npmRegistry,
1140
1206
  mavenRepositoryUrl: args.artifact.publishing.mavenRepositoryUrl
1141
1207
  }
@@ -1160,7 +1226,7 @@ const GENERATED_HEADER = "# AUTO-GENERATED by Seagull from its config and 'redoc
1160
1226
  */
1161
1227
  async function syncRedoclyConfig(config) {
1162
1228
  const basePath = path.join(config.rootDir, "redocly.base.yaml");
1163
- const base = parse(await readFile(basePath, "utf8"));
1229
+ const base = parse(await readFile(basePath, "utf8"), { merge: true });
1164
1230
  const apis = Object.fromEntries(config.contracts.map((contract) => {
1165
1231
  const typesArtifact = contract.artifacts.find((artifact) => artifact.tool === "openapi-typescript");
1166
1232
  return [`${contract.name}@v1`, {
@@ -1359,6 +1425,6 @@ var OpenApiTypescriptGenerator = class extends Generator {
1359
1425
  }
1360
1426
  };
1361
1427
  //#endregion
1362
- export { CONFIG_FILENAMES, CONFIG_SCHEMA_VERSION, Generator, GeneratorRegistry, OpenApiGeneratorCli, OpenApiTypescriptGenerator, git, hashSpec, loadConfig, readFileAtTag, remoteBranchExists, renderArtifactTag, renderReadme, requireOk, resolveBinPath, resolveConfigPath, resolveVersion, run, runSync, syncRedoclyConfig, tagExists };
1428
+ export { CONFIG_FILENAMES, CONFIG_SCHEMA_VERSION, Generator, GeneratorRegistry, OpenApiGeneratorCli, OpenApiTypescriptGenerator, assertSafeRefName, git, gitRefNameSchema, hashSpec, loadConfig, parseBundledSpec, primarySpecFormat, readFileAtTag, remoteBranchExists, renderArtifactTag, renderReadme, requireOk, resolveBinPath, resolveConfigPath, resolveVersion, run, runSync, specFilename, syncRedoclyConfig, tagExists };
1363
1429
 
1364
1430
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@octalmesh/seagull-core",
3
- "version": "0.0.2",
4
- "description": "The Seagull engine - config loading and SDK generators - as an internal library.",
3
+ "version": "0.1.1",
4
+ "description": "The Seagull engine.",
5
5
  "author": "OctalMesh <contact@octalmesh.com> (https://octalmesh.com)",
6
6
  "license": "MIT",
7
- "homepage": "https://github.com/OctalMesh/Seagull/tree/main/packages/core",
7
+ "homepage": "https://developers.octalmesh.com/seagull",
8
8
  "type": "module",
9
9
  "repository": {
10
10
  "type": "git",
@@ -32,10 +32,10 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@openapitools/openapi-generator-cli": "^2.41.0",
35
- "@redocly/cli": "^2.51.1",
35
+ "@redocly/cli": "^2.52.1",
36
36
  "openapi-typescript": "^7.13.0",
37
- "yaml": "^2.9.0",
38
- "zod": "^4.5.4"
37
+ "yaml": "^2.9.1",
38
+ "zod": "^4.6.4"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsdown",