@superyachttimes/gen3-brochure-core
v0.4.0
Published
Gen3 brochure domain: override resolution, field registry, ops. Pure — no framework imports.
Downloads
379
Readme
@superyachttimes/gen3-brochure-core
The Gen3 brochure domain, shared by syt-backend and iq-next.
- Concept:
SYT/SYT iQ/Gen3 Brochure Editor/Concept.mdin the vault (r21) - Plan:
SYT/SYT iQ/Gen3 Brochure Editor/Package Plan.md
The one rule
No framework imports, no I/O, no ambient clock. This package serves a NestJS
backend and a React app from one implementation; a single react or @nestjs import
ends that, and neither consumer's tests would catch it. tests/purity.spec.ts and a
DOM-free lib in tsconfig enforce it.
Things that will surprise you
resolve()tests presence, never truthiness. A??chain would let anulloverride fall through and resurrect the value the user deleted.- Override keys contain dots (
sale.price.EUR). The backend stores them as an array because Mongo treats a dot in a field name as a path separator; the wire carries a map. Each consumer adapts at its own edge. applyOpstakessetAtfrom the caller. Nonew Date()in here.project()is not here on purpose — the public whitelist is server-only.
Ops arriving from a client go through parseClientOps
applyOps trusts what it is given. Anything originating in a browser must be parsed
first — it throws ZodError, and issues[].path leads with the op's index in the batch:
const ops = parseClientOps(req.body.ops) // → Op[], ready for applyOpsIt refuses two things that are server-written only, and both are security-relevant:
restoreOverride, which writes anOverrideEntryverbatim. A client sending one could attribute an edit to another user, or fakebasedOnsoisStale()calls stale data current.addYacht.snapshot, which is the yacht's data. A client-supplied one bypasses the §4.3 confidentiality gate, because the gate checks what ingest returned.
Ops replayed from the op log must not go through it — they are already ours, and the replay path needs the fields it refuses.
CLIENT_SENDABLE classifies every op. It is exhaustive over Op['op'], so adding an op
breaks the build until you decide which side of the wire it lives on.
Releasing
Publishing happens from CI via npm Trusted Publishing (OIDC) — no npm token
anywhere. One-time setup: on npmjs.com → package → Settings → Trusted Publisher →
GitHub Actions, with repository SuperYachtTimes/gen3-brochure-core and workflow
publish.yml. (The very first version must be published manually by a maintainer:
npm publish — publishConfig already sets --access public.)
Then, for every release:
# on development, with a clean tree and green CI
npm version patch|minor|major # bumps package.json + creates the vX.Y.Z tag
git push && git push --tags # the v* tag triggers .github/workflows/publish.ymlprepublishOnly (typecheck + build + test) gates every publish, local or CI, and CI
asserts the tarball ships dist/ only — no src/, no tests, no sourcemaps.
