|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +--- |
| 4 | + |
| 5 | +feat(spec): `manifestId` is optional on `TemplateManifestSchema`, and every shipped template manifest is now parsed against it (#7319) |
| 6 | + |
| 7 | +`TemplateManifestSchema` describes the on-disk `objectstack.manifest.json`, and |
| 8 | +the bundled blank template declares it as its `$schema` — but the file did not |
| 9 | +satisfy it. The schema inherited `manifestId` from `CreatePackageRequestSchema` |
| 10 | +as **required**, and no template has ever declared one. Measured on `main`, that |
| 11 | +was the *only* complaint the shipped file produced: |
| 12 | + |
| 13 | +``` |
| 14 | +manifestId: Invalid input: expected string, received undefined (invalid_type) |
| 15 | +``` |
| 16 | + |
| 17 | +Nothing broke, because nothing parsed the file. `create-objectstack` reads and |
| 18 | +rewrites it as raw JSON and does not depend on `@objectstack/spec` at all; |
| 19 | +`objectstack package publish` also reads it raw, resolving the id as |
| 20 | +`--manifest-id ?? manifest.manifestId ?? deriveManifestId(artifact, path)`. That |
| 21 | +fallback is the measurement: on this file the id is a declarative **default**, |
| 22 | +not a requirement — a template tree that declares none publishes fine, deriving |
| 23 | +`local.<slug>` from the compiled artifact. |
| 24 | + |
| 25 | +**The key is now optional on the on-disk descriptor**, declared locally rather |
| 26 | +than by loosening the shared base: |
| 27 | + |
| 28 | +- `TemplateManifestSchema` omits the inherited field and re-declares it |
| 29 | + `.optional()`, reusing the publish field's value constraints — the same |
| 30 | + omit-then-extend split `namespace` uses (#6861), in the other direction. A |
| 31 | + malformed id is still rejected: optional is not unvalidated. |
| 32 | +- **`CreatePackageRequestSchema` is untouched.** The publish request that reaches |
| 33 | + the control plane still requires `manifestId` — the package row is addressed |
| 34 | + by it and it is immutable once set. Widening the base would have made a publish |
| 35 | + request with no package identity parse, which is the collapse the local |
| 36 | + override exists to avoid. |
| 37 | + |
| 38 | +Authoring is unchanged in the accepting direction: a manifest that declares a |
| 39 | +`manifestId` still parses exactly as before, and every other required key |
| 40 | +(`displayName`, `name`, `specVersion`) is still required. |
| 41 | + |
| 42 | +**New gate — `check:template-manifests`.** Every `objectstack.manifest.json` |
| 43 | +under `packages/create-objectstack/src/templates/` is parsed against |
| 44 | +`TemplateManifestSchema` on every PR (unfiltered, in the required |
| 45 | +`TypeScript Type Check` job). This is the check that would have caught both |
| 46 | +drifts this file has now accumulated — #6861's silently stripped `namespace` and |
| 47 | +this one — so the `$schema` line those files carry stops being an unverified |
| 48 | +claim. It walks the template tree rather than a hand-kept file list, so a |
| 49 | +template added later is covered on the day it lands, and it fails rather than |
| 50 | +reporting success if it finds nothing to parse. |
0 commit comments