@octalmesh/seagull 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -25
- package/dist/cli.mjs +5 -48
- package/dist/cli.mjs.map +1 -1
- package/dist/dist-Ou2inxGj.mjs +16783 -0
- package/dist/dist-Ou2inxGj.mjs.map +1 -0
- package/dist/index.d.mts +1207 -42
- package/dist/index.mjs +2 -4
- package/package.json +13 -9
- package/dist/serve-docs-BZaITOD0.mjs +0 -1567
- package/dist/serve-docs-BZaITOD0.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seagull
|
|
2
2
|
|
|
3
3
|
Contract-first OpenAPI SDK, docs, and publishing pipeline - driven by a
|
|
4
4
|
single config file. Point it at your `openapi.yaml` files, tell it which SDK
|
|
@@ -9,6 +9,26 @@ Built to manage **several services' contracts from one place** - each
|
|
|
9
9
|
service just needs an entry in the config; each artifact is generated by a
|
|
10
10
|
reusable, shareable "recipe".
|
|
11
11
|
|
|
12
|
+
## Packages
|
|
13
|
+
|
|
14
|
+
This repo is a monorepo, but only **one** package is ever published:
|
|
15
|
+
`@octalmesh/seagull`. The three packages under [`packages/`](./packages)
|
|
16
|
+
are private and exist purely for internal organization - each is bundled
|
|
17
|
+
straight into `@octalmesh/seagull`'s own `dist/` at build time (see the root
|
|
18
|
+
[`tsdown.config.ts`](./tsdown.config.ts)), so installing the one package is
|
|
19
|
+
installing all of it.
|
|
20
|
+
|
|
21
|
+
| Package | Role |
|
|
22
|
+
|------------------------------------------------|-------------------------------------------------------------------------|
|
|
23
|
+
| **`@octalmesh/seagull`** (this repo's root) | Published. The `seagull` CLI + programmatic API. |
|
|
24
|
+
| [`@octalmesh/seagull-core`](./packages/core) | Private. Config loading, the `Generator` primitive, built-in generators. |
|
|
25
|
+
| [`@octalmesh/seagull-cli`](./packages/cli) | Private. Pipeline commands + the `commander` program. |
|
|
26
|
+
| [`@octalmesh/seagull-docs`](./packages/docs) | Private. Docs-site generation - a Scalar-based placeholder today, a fully custom UI later. |
|
|
27
|
+
|
|
28
|
+
If you're just using seagull, none of that matters - `npm install @octalmesh/seagull`
|
|
29
|
+
gets you the `seagull` command and the full programmatic API in one package.
|
|
30
|
+
The split only matters if you're contributing to seagull itself.
|
|
31
|
+
|
|
12
32
|
## Install
|
|
13
33
|
|
|
14
34
|
```bash
|
|
@@ -23,7 +43,9 @@ Under the hood, `generate` shells out to
|
|
|
23
43
|
[`openapi-typescript`](https://www.npmjs.com/package/openapi-typescript);
|
|
24
44
|
`lint`/`bundle` use [`@redocly/cli`](https://www.npmjs.com/package/@redocly/cli);
|
|
25
45
|
`docs` uses [`@scalar/api-reference`](https://www.npmjs.com/package/@scalar/api-reference).
|
|
26
|
-
All four are seagull's own dependencies
|
|
46
|
+
All four are `@octalmesh/seagull`'s own dependencies, resolved via Node's
|
|
47
|
+
module resolution (not `npx`/`pnpm exec`) - nothing extra to install, and it
|
|
48
|
+
works the same regardless of which package manager you use.
|
|
27
49
|
|
|
28
50
|
## Quick start
|
|
29
51
|
|
|
@@ -32,6 +54,8 @@ Create a config file at the root of your contracts repo - any of
|
|
|
32
54
|
|
|
33
55
|
```yaml
|
|
34
56
|
# seagull.yaml
|
|
57
|
+
configVersion: 1
|
|
58
|
+
|
|
35
59
|
github:
|
|
36
60
|
owner: your-org
|
|
37
61
|
repo: your-contracts-repo
|
|
@@ -49,6 +73,17 @@ docs:
|
|
|
49
73
|
favicon: https://your-domain.com/favicon.ico
|
|
50
74
|
baseServerUrl: https://api.your-domain.com
|
|
51
75
|
|
|
76
|
+
publishing:
|
|
77
|
+
branch: "sdk/svc-{service}/{id}"
|
|
78
|
+
tag: "svc-{service}-{id}-v{version}"
|
|
79
|
+
repositoryUrl: "https://github.com/{github.owner}/{github.repo}"
|
|
80
|
+
npm:
|
|
81
|
+
registry: "https://npm.pkg.github.com"
|
|
82
|
+
access: restricted
|
|
83
|
+
maven:
|
|
84
|
+
repositoryId: github
|
|
85
|
+
repositoryUrl: "https://maven.pkg.github.com/{github.owner}/{github.repo}"
|
|
86
|
+
|
|
52
87
|
generators:
|
|
53
88
|
ts-client:
|
|
54
89
|
tool: openapi-generator
|
|
@@ -77,8 +112,12 @@ npx seagull docs generate && npx seagull docs serve
|
|
|
77
112
|
|
|
78
113
|
## Configuration
|
|
79
114
|
|
|
80
|
-
|
|
81
|
-
|
|
115
|
+
- **`configVersion:`** (required) - which version of the config *schema*
|
|
116
|
+
this file targets, currently always `1`. Decoupled from `@octalmesh/seagull`'s
|
|
117
|
+
own npm version on purpose: this only changes if `seagull.yaml`'s shape
|
|
118
|
+
changes in a breaking way, so an old config fails with a clear "expected
|
|
119
|
+
configVersion 1" error instead of a confusing failure on some unrelated
|
|
120
|
+
field once the schema moves on.
|
|
82
121
|
- **`generators:`** - reusable recipes: a `tool` (`openapi-generator` or
|
|
83
122
|
`openapi-typescript`), which `-g` template to use, and naming templates
|
|
84
123
|
for the npm package / Go module / Maven coordinates. Any string field may
|
|
@@ -107,11 +146,60 @@ Everything lives in the one config file:
|
|
|
107
146
|
override that.
|
|
108
147
|
- **`docs:`** - `server: { host, port }` for `docs serve`, and
|
|
109
148
|
`metadata: { title, description, favicon, baseServerUrl }` for the
|
|
110
|
-
generated
|
|
149
|
+
generated docs site.
|
|
150
|
+
- **`publishing:`** (required) - see below.
|
|
111
151
|
|
|
112
152
|
A typo or missing field fails immediately with a readable, path-annotated
|
|
113
153
|
error - config is validated with [zod](https://zod.dev) on every run.
|
|
114
154
|
|
|
155
|
+
### Publishing
|
|
156
|
+
|
|
157
|
+
`publishing:` controls where things get published to - git branch/tag
|
|
158
|
+
naming, and npm/Maven registry URLs. It's required at the root level:
|
|
159
|
+
seagull has no built-in convention here, so a config that omits it fails
|
|
160
|
+
validation with a message pointing at exactly what's missing, rather than
|
|
161
|
+
silently applying an opinionated default nobody chose.
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
publishing:
|
|
165
|
+
branch: "sdk/svc-{service}/{id}" # git branch each artifact publishes to
|
|
166
|
+
tag: "svc-{service}-{id}-v{version}" # git tag - the only field where {version} is available
|
|
167
|
+
repositoryUrl: "https://github.com/{github.owner}/{github.repo}"
|
|
168
|
+
npm:
|
|
169
|
+
registry: "https://npm.pkg.github.com"
|
|
170
|
+
access: restricted # or "public"
|
|
171
|
+
maven:
|
|
172
|
+
repositoryId: github
|
|
173
|
+
repositoryUrl: "https://maven.pkg.github.com/{github.owner}/{github.repo}"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Every field is a template - the same `{...}` engine as naming templates,
|
|
177
|
+
plus `{id}` (the artifact's id) and, for `tag` only, `{version}` (resolved
|
|
178
|
+
once the contract's spec is bundled, since a branch is created before a
|
|
179
|
+
version is known but a tag isn't).
|
|
180
|
+
|
|
181
|
+
Like `additionalProperties` and `readme`, `publishing:` can be overridden
|
|
182
|
+
per-generator (`generators.<id>.publishing`) or per-contract-artifact
|
|
183
|
+
(`artifacts[].overrides.publishing`) - only the fields that differ need
|
|
184
|
+
repeating, the rest fall through to the root-level config:
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
generators:
|
|
188
|
+
ts-client:
|
|
189
|
+
# ...
|
|
190
|
+
publishing:
|
|
191
|
+
npm:
|
|
192
|
+
registry: "https://registry.internal.example.com" # every contract's ts-client uses this registry
|
|
193
|
+
|
|
194
|
+
contracts:
|
|
195
|
+
- name: payment
|
|
196
|
+
artifacts:
|
|
197
|
+
- generator: ts-client
|
|
198
|
+
overrides:
|
|
199
|
+
publishing:
|
|
200
|
+
branch: "custom/{service}-{id}-branch" # ...except payment's ts-client, which also uses a different branch
|
|
201
|
+
```
|
|
202
|
+
|
|
115
203
|
### Custom README templates
|
|
116
204
|
|
|
117
205
|
Every generated artifact gets a `README.md` - by default a sensible
|
|
@@ -128,21 +216,22 @@ generators:
|
|
|
128
216
|
Template files support the same `{...}` placeholders as naming templates,
|
|
129
217
|
plus a few more:
|
|
130
218
|
|
|
131
|
-
| Placeholder
|
|
132
|
-
|
|
133
|
-
| `{service}`
|
|
134
|
-
| `{title}`
|
|
135
|
-
| `{version}`
|
|
136
|
-
| `{vars.*}`
|
|
137
|
-
| `{github.owner}` / `{github.repo}`
|
|
138
|
-
| `{artifact.id}`
|
|
139
|
-
| `{artifact.package}`
|
|
140
|
-
| `{artifact.goModule}` / `{artifact.goPackageName}`
|
|
141
|
-
| `{artifact.maven.groupId}` / `{artifact.maven.artifactId}`
|
|
142
|
-
| `{artifact.branch}` / `{artifact.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
219
|
+
| Placeholder | Value |
|
|
220
|
+
|--------------------------------------------------------------------|-------------------------------------------------|
|
|
221
|
+
| `{service}` | The contract's `name` |
|
|
222
|
+
| `{title}` | The contract's `title` |
|
|
223
|
+
| `{version}` | The resolved SDK version |
|
|
224
|
+
| `{vars.*}` | Anything under `vars:` |
|
|
225
|
+
| `{github.owner}` / `{github.repo}` | From `github:` |
|
|
226
|
+
| `{artifact.id}` | The artifact's id (as listed under `artifacts:`) |
|
|
227
|
+
| `{artifact.package}` | Resolved npm package name (TypeScript) |
|
|
228
|
+
| `{artifact.goModule}` / `{artifact.goPackageName}` | Resolved Go naming |
|
|
229
|
+
| `{artifact.maven.groupId}` / `{artifact.maven.artifactId}` | Resolved Maven coordinates |
|
|
230
|
+
| `{artifact.branch}` / `{artifact.tag}` | Resolved publishing branch / tag |
|
|
231
|
+
| `{artifact.npmRegistry}` / `{artifact.mavenRepositoryUrl}` | Resolved registry URLs from `publishing:` |
|
|
232
|
+
|
|
233
|
+
An unresolvable placeholder fails the build loudly (a typo'd `{vesion}`
|
|
234
|
+
won't silently ship as literal text).
|
|
146
235
|
|
|
147
236
|
## Commands
|
|
148
237
|
|
|
@@ -151,7 +240,7 @@ seagull lint Lint every contract with Redocly
|
|
|
151
240
|
seagull bundle Bundle every contract's spec into dist/specs
|
|
152
241
|
seagull generate Generate every configured SDK artifact into dist/sdk
|
|
153
242
|
seagull clean Remove the dist directory
|
|
154
|
-
seagull docs generate Generate the
|
|
243
|
+
seagull docs generate Generate the documentation site into dist/docs
|
|
155
244
|
seagull docs serve Serve the generated documentation site locally
|
|
156
245
|
seagull publish sdk [--dry-run] Publish generated SDKs to their git branches/tags
|
|
157
246
|
seagull publish registries [--dry-run]
|
|
@@ -170,10 +259,24 @@ const config = loadConfig("/path/to/seagull.yaml");
|
|
|
170
259
|
await generateSdkCommand(config);
|
|
171
260
|
```
|
|
172
261
|
|
|
173
|
-
`loadConfig`, every `*Command` function, the `
|
|
174
|
-
|
|
175
|
-
generators are
|
|
176
|
-
|
|
262
|
+
`loadConfig`, every `*Command` function, `createProgram` (the `commander`
|
|
263
|
+
program factory), the `Generator`/`GeneratorRegistry` primitives, and the
|
|
264
|
+
built-in `OpenApiGeneratorCli`/`OpenApiTypescriptGenerator` generators are
|
|
265
|
+
all exported, for anyone who wants to script against seagull directly or
|
|
266
|
+
register a custom `Generator` for another tool.
|
|
267
|
+
|
|
268
|
+
## Development
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
pnpm install
|
|
272
|
+
pnpm run build # builds packages/* first (topological), then bundles the root package
|
|
273
|
+
pnpm run typecheck # run after build - resolves the private packages via their built dist/
|
|
274
|
+
pnpm run lint
|
|
275
|
+
pnpm run test:run
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to propose changes, and
|
|
279
|
+
[RELEASING.md](./RELEASING.md) for how versioning and publishing work.
|
|
177
280
|
|
|
178
281
|
## License
|
|
179
282
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,58 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { _ as resolveConfigPath, a as generateSdkCommand, d as cleanCommand, f as bundleCommand, i as lintCommand, n as publishSdkCommand, r as publishRegistriesCommand, t as serveDocsCommand, u as generateDocsCommand, v as loadConfig } from "./serve-docs-BZaITOD0.mjs";
|
|
2
|
+
import { r as createProgram } from "./dist-Ou2inxGj.mjs";
|
|
4
3
|
import { readFileSync } from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { Command } from "commander";
|
|
8
6
|
//#region src/cli.ts
|
|
9
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
8
|
const pkg = JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
program.command("generate").description("Generate every configured SDK artifact into dist/sdk.").action(withErrorHandling(async () => generateSdkCommand(resolveConfig())));
|
|
16
|
-
program.command("clean").description("Remove the dist output directory.").action(withErrorHandling(async () => cleanCommand(resolveConfig())));
|
|
17
|
-
const docs = program.command("docs").description("Documentation site commands.");
|
|
18
|
-
docs.command("generate").description("Generate the Scalar documentation site into dist/docs.").action(withErrorHandling(async () => generateDocsCommand(resolveConfig())));
|
|
19
|
-
docs.command("serve").description("Serve the generated documentation site locally.").action(withErrorHandling(async () => serveDocsCommand(resolveConfig())));
|
|
20
|
-
const publish = program.command("publish").description("Publishing commands.");
|
|
21
|
-
publish.command("sdk").description("Publish generated SDKs to their per-artifact git branches/tags.").option("--dry-run", "print what would be pushed without pushing").action(withErrorHandling(async (opts) => {
|
|
22
|
-
await publishSdkCommand(resolveConfig(), { dryRun: opts.dryRun });
|
|
23
|
-
}));
|
|
24
|
-
publish.command("registries").description("Publish registry-backed packages (npm publish / mvn deploy).").option("--dry-run", "print what would be published without publishing").action(withErrorHandling(async (opts) => {
|
|
25
|
-
await publishRegistriesCommand(resolveConfig(), { dryRun: opts.dryRun });
|
|
26
|
-
}));
|
|
27
|
-
await program.parseAsync();
|
|
28
|
-
/**
|
|
29
|
-
* Resolves and loads the config, using `--config` if given, else
|
|
30
|
-
* auto-discovering it in the current directory.
|
|
31
|
-
*
|
|
32
|
-
* @returns The resolved config.
|
|
33
|
-
*/
|
|
34
|
-
function resolveConfig() {
|
|
35
|
-
const { config: configOption } = program.opts();
|
|
36
|
-
const configPath = configOption ? path.resolve(process.cwd(), configOption) : resolveConfigPath(process.cwd());
|
|
37
|
-
return loadConfig(configPath);
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Wraps a commander action so a thrown Error prints as `seagull: <message>`
|
|
41
|
-
* and exits non-zero, instead of an unhandled-rejection stack trace.
|
|
42
|
-
*
|
|
43
|
-
* @param fn The action function to wrap.
|
|
44
|
-
* @returns A wrapped action function that handles errors.
|
|
45
|
-
*/
|
|
46
|
-
function withErrorHandling(fn) {
|
|
47
|
-
return async (...args) => {
|
|
48
|
-
try {
|
|
49
|
-
await fn(...args);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.error(`seagull: ${error instanceof Error ? error.message : error}`);
|
|
52
|
-
process.exitCode = 1;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
9
|
+
await createProgram({
|
|
10
|
+
...pkg,
|
|
11
|
+
name: "seagull"
|
|
12
|
+
}).parseAsync();
|
|
56
13
|
//#endregion
|
|
57
14
|
export {};
|
|
58
15
|
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { createProgram } from \"@octalmesh/seagull-cli\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"),\n) as { version: string; description: string };\n\nawait createProgram({ ...pkg, name: \"seagull\" }).parseAsync();\n"],"mappings":";;;;;;AAOA,MAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,MAAM,MAAM,KAAK,MACf,aAAa,KAAK,KAAK,WAAW,MAAM,cAAc,GAAG,MAAM,CACjE;AAEA,MAAM,cAAc;CAAE,GAAG;CAAK,MAAM;AAAU,CAAC,CAAC,CAAC,WAAW"}
|