@arc-e-tect/api-only-publisher 0.6.0 → 0.6.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.adoc +54 -12
- package/package.json +1 -1
- package/src/config.js +3 -2
package/README.adoc
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:source-highlighter: rouge
|
|
7
7
|
// The released version of this component: its snippets use it. The release
|
|
8
8
|
// workflow updates it; do not change it by hand.
|
|
9
|
-
:api-only-publisher-version: 0.6.
|
|
9
|
+
:api-only-publisher-version: 0.6.2
|
|
10
10
|
|
|
11
11
|
image:https://github.com/Arc-E-Tect/SoftwareEngineeringDoneRight-API/actions/workflows/nvd-cache-refresh.yml/badge.svg[Vulnerability Scan,link=https://github.com/Arc-E-Tect/SoftwareEngineeringDoneRight-API/actions/workflows/nvd-cache-refresh.yml]
|
|
12
12
|
image:https://img.shields.io/npm/v/@arc-e-tect/api-only-publisher[npm,link=https://www.npmjs.com/package/@arc-e-tect/api-only-publisher]
|
|
@@ -199,6 +199,7 @@ An OpenAPI document's components each carry the path of the fragment they came f
|
|
|
199
199
|
A `publish: false` target keeps the version its source declares.
|
|
200
200
|
|
|
201
201
|
. **Lint** the finished document, so a broken fragment surfaces against the target it actually affects.
|
|
202
|
+
`build` prints the linter's output only when the lint fails, and writes no report; to see warnings, and to write each document's report under `reports.lint`, run `lint`.
|
|
202
203
|
|
|
203
204
|
[#versions]
|
|
204
205
|
== Versions
|
|
@@ -295,7 +296,7 @@ Declare the members and the aggregate's own identity:
|
|
|
295
296
|
portfolio:
|
|
296
297
|
publish: false
|
|
297
298
|
asyncapi:
|
|
298
|
-
bundle: portfolio_asyncapi_structure.yaml
|
|
299
|
+
bundle: bundles/portfolio_asyncapi_structure.yaml
|
|
299
300
|
aggregate:
|
|
300
301
|
- orders
|
|
301
302
|
- payments
|
|
@@ -369,20 +370,25 @@ Adapting the tool to another project means editing configuration.
|
|
|
369
370
|
|
|
370
371
|
[source,yaml]
|
|
371
372
|
----
|
|
373
|
+
# The version of this file's own format -- which keys apionly.yaml has and what
|
|
374
|
+
# they mean -- not of any API. This Publisher understands 1, and refuses others.
|
|
372
375
|
schemaVersion: 1
|
|
373
376
|
|
|
374
377
|
sources:
|
|
375
378
|
root: specs # staged wholesale; the kinds share fragments
|
|
376
|
-
openapi: openapi
|
|
379
|
+
openapi: openapi # each kind's directory under root; bundle paths start here
|
|
377
380
|
asyncapi: asyncapi
|
|
378
381
|
|
|
379
382
|
defaults:
|
|
380
383
|
openapi:
|
|
381
384
|
lint: .redocly.yaml
|
|
385
|
+
# The file name of every built OpenAPI document: build/dist/<target>/openapi.yaml.
|
|
386
|
+
# The Subscriber looks for exactly this name.
|
|
382
387
|
outputName: openapi.yaml
|
|
383
388
|
fragmentPaths: true # x-fragment-path on every component; the default
|
|
384
389
|
asyncapi:
|
|
385
|
-
outputName: asyncapi.yaml
|
|
390
|
+
outputName: asyncapi.yaml # as for OpenAPI: build/dist/<target>/asyncapi.yaml
|
|
391
|
+
fragmentPaths: true # x-fragment-path on every fragment; the default
|
|
386
392
|
placeholders:
|
|
387
393
|
strict: true # an unresolved {{token}} fails the build
|
|
388
394
|
|
|
@@ -408,17 +414,36 @@ targets:
|
|
|
408
414
|
# versionFile: versions/orders.properties
|
|
409
415
|
# default: orders.bundle.properties beside the first bundle root
|
|
410
416
|
openapi:
|
|
411
|
-
bundle
|
|
417
|
+
# The bundle root the document is built from, relative to specs/openapi/.
|
|
418
|
+
# It is built into build/dist/orders/openapi.yaml, named by outputName.
|
|
419
|
+
bundle: bundles/orders_openapi_structure.yaml
|
|
412
420
|
asyncapi:
|
|
413
|
-
|
|
421
|
+
# Relative to specs/asyncapi/; built into build/dist/orders/asyncapi.yaml.
|
|
422
|
+
bundle: bundles/orders_asyncapi_structure.yaml
|
|
414
423
|
portfolio:
|
|
415
424
|
publish: false # built and linted, never published
|
|
416
425
|
openapi:
|
|
417
|
-
bundle: bundles/
|
|
426
|
+
bundle: bundles/portfolio_openapi_structure.yaml
|
|
418
427
|
----
|
|
419
428
|
|
|
420
429
|
`targets` is the single source of truth for what gets built.
|
|
421
430
|
|
|
431
|
+
A target's `bundle` and a kind's `outputName` are the two ends of one build: the bundle root is what a document is built *from*, in the library, and `outputName` is what it is built *to*, in `<build.dist>/<target>/`.
|
|
432
|
+
Every target's document of one kind has the same `outputName`; keep the defaults, `openapi.yaml` and `asyncapi.yaml`, because the Subscriber looks for exactly those names.
|
|
433
|
+
|
|
434
|
+
[#bundle-roots]
|
|
435
|
+
=== Where bundle roots go
|
|
436
|
+
|
|
437
|
+
Put every bundle root in a `bundles/` directory of its kind's source directory, named `<target>_<kind>_structure.yaml`: `specs/openapi/bundles/orders_openapi_structure.yaml` and `specs/asyncapi/bundles/orders_asyncapi_structure.yaml`.
|
|
438
|
+
The `init` scaffold lays out its OpenAPI bundle root this way.
|
|
439
|
+
|
|
440
|
+
* A bundle root is a table of contents rather than a fragment, and a `bundles/` directory keeps the two apart.
|
|
441
|
+
* The name says which target and which kind, so the two roots of one target are never confused, in the tree or in a review.
|
|
442
|
+
* The version file's default place is beside the target's first bundle root (<<versions,Versions §>>), so it lands in `bundles/` too.
|
|
443
|
+
|
|
444
|
+
The Publisher does not require it: a bundle path is any path relative to the kind's source directory.
|
|
445
|
+
A bundle root moved into or out of `bundles/` needs its relative `$ref`s adjusted by one `../`, since they resolve against the file's own directory; the built document is otherwise the same.
|
|
446
|
+
|
|
422
447
|
[#pinning-the-bundlers]
|
|
423
448
|
=== Pinning the bundlers
|
|
424
449
|
|
|
@@ -463,6 +488,11 @@ Any other YAML file under the source root counts.
|
|
|
463
488
|
[#version-stamping]
|
|
464
489
|
=== Version stamping edits one scalar
|
|
465
490
|
|
|
491
|
+
Stamping sets `info.version` in the *built* document, `<build.dist>/<target>/<outputName>`, after bundling.
|
|
492
|
+
The value it replaces comes from the bundle root's `info` block, written in the bundle root or `$ref`'d from a fragment, which the bundler puts into the document: every bundle root therefore needs an `info` block with a `version`, conventionally `0.0.0`.
|
|
493
|
+
The `init` scaffold's bundle root refers to `specs/openapi/shared/info.yaml`, which declares `version: 0.0.0`.
|
|
494
|
+
The version that replaces it is the target's version file's (<<versions,Versions §>>).
|
|
495
|
+
|
|
466
496
|
`info.version` is located structurally and that one scalar is spliced.
|
|
467
497
|
It is deliberately not a re-serialisation: re-emitting a document reformats everything around the edit, because the AsyncAPI CLI wraps long descriptions at a width no YAML emitter reproduces.
|
|
468
498
|
Splicing keeps every other byte exactly as the bundler wrote it.
|
|
@@ -492,12 +522,14 @@ It is how the API-Only TranscriberJ ties generated code to the fragment it came
|
|
|
492
522
|
It is on by default, for both specification kinds.
|
|
493
523
|
`defaults.openapi.fragmentPaths: false` and `defaults.asyncapi.fragmentPaths: false` turn it off, each for its own kind.
|
|
494
524
|
|
|
495
|
-
How it is done::
|
|
525
|
+
How it is done, for OpenAPI::
|
|
496
526
|
The bundler is not told anything; it is shown stamped fragments, in copies of the staged tree under `<build.staging>/fragment-paths/<target>/`.
|
|
497
527
|
The first bundle is built with every fragment stamped, to learn which fragments become components.
|
|
498
528
|
The second, the one that is published, is built with only those stamped.
|
|
499
529
|
So a document differs from one built with `fragmentPaths: false` by exactly one line per component, and in no other byte.
|
|
500
530
|
The cost is a second bundler run per OpenAPI target.
|
|
531
|
+
+
|
|
532
|
+
An AsyncAPI target is stamped differently, in one run and with every fragment stamped, because its bundler inlines rather than hoists: <<fragment-paths-asyncapi,AsyncAPI, where the bundler inlines rather than hoists §>> describes it.
|
|
501
533
|
|
|
502
534
|
What stops the build::
|
|
503
535
|
A fragment that writes `x-fragment-path` itself: the Publisher sets that key and nothing else may.
|
|
@@ -540,7 +572,9 @@ A stamp in the built document that names no file in the library, which would mea
|
|
|
540
572
|
[#fragment-path-versions]
|
|
541
573
|
==== What a change to `x-fragment-path` means for a contract's version
|
|
542
574
|
|
|
543
|
-
Semantic versioning describes the contract, so the version follows from what a change does to the contract's promises
|
|
575
|
+
Semantic versioning describes the contract, so the version follows from what a change does to the contract's promises.
|
|
576
|
+
The Publisher does not decide it: it stamps whatever the target's version file declares.
|
|
577
|
+
Choosing the next version is the API designer's responsibility, and this table is their guideline:
|
|
544
578
|
|
|
545
579
|
[cols="2,1,3", options="header"]
|
|
546
580
|
|===
|
|
@@ -594,11 +628,11 @@ In order of what usually needs changing:
|
|
|
594
628
|
|The lint rules
|
|
595
629
|
|`defaults.openapi.lint`
|
|
596
630
|
|
|
597
|
-
|Whether
|
|
598
|
-
|`defaults.openapi.fragmentPaths`, defaulting to `true`
|
|
631
|
+
|Whether built documents carry `x-fragment-path`
|
|
632
|
+
|`defaults.openapi.fragmentPaths` and `defaults.asyncapi.fragmentPaths`, each defaulting to `true`
|
|
599
633
|
|
|
600
634
|
|Where lint reports go
|
|
601
|
-
|`reports.lint`, defaulting to `build/reports/lint
|
|
635
|
+
|`reports.lint`, a directory, defaulting to `build/reports/lint`: each document's report is `<target>/<kind>.txt` in it, beside `unreferenced.txt`
|
|
602
636
|
|
|
603
637
|
|Where built documents go
|
|
604
638
|
|`build.dist`
|
|
@@ -623,6 +657,14 @@ A library with no `{{token}}` anywhere simply has nothing substituted.
|
|
|
623
657
|
The tool does not police them.
|
|
624
658
|
`changed` tells a release which targets a commit altered, and every manifest records the closure hash of what was published, so the hooks a diff gate would need already exist, but the gate itself is deliberately not built.
|
|
625
659
|
|
|
660
|
+
[IMPORTANT]
|
|
661
|
+
.The API designer defines the contract's version, not the Publisher
|
|
662
|
+
====
|
|
663
|
+
The Publisher reads a target's version from its version file and stamps it; it refuses a version that is not a semantic release version, and nothing else.
|
|
664
|
+
It never works out a version from what changed, and never checks that a version matches the change.
|
|
665
|
+
Whether a change is major, minor or patch is the API designer's decision, and so is its consequence: raising the version in the target's version file.
|
|
666
|
+
====
|
|
667
|
+
|
|
626
668
|
A breaking change is declared where it is made: the major version in the target's version file changes in the same commit as the fragments it breaks.
|
|
627
669
|
A specification library should write down its own policy for what counts as breaking.
|
|
628
670
|
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -161,8 +161,9 @@ function load(configPath) {
|
|
|
161
161
|
requireString(this.sources[kind], `sources.${kind}`),
|
|
162
162
|
this.targets[target][kind].bundle);
|
|
163
163
|
},
|
|
164
|
-
// Whether built documents of this kind carry x-fragment-path on each
|
|
165
|
-
// component
|
|
164
|
+
// Whether built documents of this kind carry x-fragment-path: on each
|
|
165
|
+
// component of an OpenAPI document, on each fragment of an AsyncAPI one.
|
|
166
|
+
// On unless turned off, for both kinds.
|
|
166
167
|
fragmentPaths(kind) {
|
|
167
168
|
if (kind !== "openapi" && kind !== "asyncapi") return false;
|
|
168
169
|
const configured = (this.defaults[kind] || {}).fragmentPaths;
|