Releases: aggiovato/yRest
Release list
v0.13.0
Added
-
__uuid_gendirective — UUID generation indb.yml: setting any string field to__uuid_gen(or__uuid_gen:alias) causes yRest to replace it with a randomly generated UUID v4 at load time. yRest then rewritesdb.ymlwith the resolved values so IDs are stable across restarts — no manual UUID copy-paste required.users: - id: __uuid_gen:ana # generates a UUID, registers alias "ana" name: Ana - id: __uuid_gen:luis # generates a UUID, registers alias "luis" name: Luis
-
__fkdirective — cross-collection UUID references: setting a field to__fk.<collection>:<alias>resolves it to the UUID registered under that alias in the named collection. This wires FK relationships between collections whose IDs are generated at load time.posts: - id: __uuid_gen title: Hello World userId: __fk.users:ana # → same UUID as users[alias=ana].id
Resolution runs in two passes — all
__uuid_genvalues first, then all__fkreferences — so order in the file does not matter. Unresolvable aliases emit a console warning and leave the field value unchanged. -
--uuidCLI flag: shorthand for--id-strategy uuidonyrest serve. Applies to new items created viaPOST(not to the initial data indb.yml).npx @yrest/cli serve db.yml --uuid
Internal
src/storage/resolveDirectives.ts— new module; two-pass resolver operating on the parsedDataobject. Returnstruewhen any directive was resolved so callers know whether to persist.yrestStorage.ts— callsresolveDirectives(data)afterparseData()in bothcreateYrestStorageandreload(). Triggerspersist()immediately ifmodified = true.relationalinit template updated to demonstrate__uuid_gen:aliasand__fkacross all collections (users, profiles, posts, post_tags, comments).
Tests
tests/storage/resolveDirectives.test.ts— 19 unit tests: UUID generation, alias registration, FK resolution, cross-collection scope isolation, unresolvable alias warning, non-string field pass-through, nested object non-recursion.tests/routes/directives.test.ts— 6 integration tests: GET returns UUID IDs, single-item GET by generated UUID, write-back on disk after first load, FK consistency between collections, no re-write on second load of resolved file.
v0.12.0
Added
-
SSE stream routes (
_method: SSE): entries in_routeswith_method: SSEand an_sseblock are registered as Server-Sent Events streams. The server pushes frames over the raw socket using Fastify'sreply.hijack()— no new dependencies, no extra peer deps. Clients connect with a standardGETrequest and receive events in the SSE wire format (event: …\ndata: …\n\n).YAML syntax:
_routes: - _method: SSE _path: /events/orders _sse: _interval: 1500 # ms between frames (default: 1000) _loop: true # restart after last event (default: true) _repeat: 3 # stop after N full cycles (omit = infinite) _events: - _event: update _data: { orderId: 1, status: processing } - _event: done _data: { orderId: 1, status: delivered }
Options:
Key Default Description _interval1000Milliseconds between frames _looptrueRestart the event sequence after the last frame _repeat— Stop after N complete cycles. Omit for an infinite stream _events— Ordered list of frames. Each has _data(required) and_event(optional)Template variables (
{{now}},{{uuid}},{{params.X}},{{query.X}}) are resolved per frame at emit time, so each event carries a fresh timestamp or unique ID. A keep-alive comment (: ping) is sent every 15 s to prevent proxy timeouts. The stream is cleaned up automatically when the client disconnects. -
getSseRoutes()onYrestStorage: the storage interface exposes parsed SSE routes separately from regular HTTP custom routes (getRoutes()). Both the file-backed and in-memory storage implementations are updated. -
/_aboutSSE streams accordion: SSE endpoints appear in their own accordion section with a sky-blue SSE badge (distinct from the green GET badge), showing interval, event count andno·loop/repeat·N×modifiers when applicable. -
SSEandWScolours pre-registered inMETHOD_COLORinabout.helpers.ts.WSis registered ahead of the Phase 10B WebSocket implementation.
Internal
src/storage/parseSseRoutes.ts— new module; extracts_method: SSEentries from the raw_routesarray and normalises them intoSseRouteobjects.src/router/routes/sse.routes.ts— newSSERouteCommand: registers one GET endpoint per SSE route, drives the frame loop with recursivesetTimeout, and clears the keep-alive interval on disconnect.src/storage/parseRoutes.ts— skips entries where_methodisSSE(case-insensitive) so they are not registered as regular HTTP routes.
Tests
tests/routes/sse.test.ts— 7 E2E tests using a real bound port (listen({ port: 0 })): frames emitted in order,_loop: falsecloses the stream,_repeat: Nstops after N cycles,{{params.X}}and{{now}}resolved per frame,Content-Type: text/event-streamheader, coexistence with collection routes,_method: GETentries not mistaken for SSE routes.
v0.11.0
Added
- Compact relation DSL:
_relfields now accept a compact string format in addition to the existing shorthand and verbose object forms"m2o:target"— type + target in one string"m2o:target[1..1->0..n]"— with cardinality notation (carDirect->carInverse)"m2o:target@foreignKey[1..1->0..n]"— explicit FK when the field name differs from the FK column"m2m:target@through(foreignKey,otherKey)[0..n->0..n]"— many2many compact form+nestedsuffix on any DSL string — equivalent to_nested: true- Type aliases:
m2o/many2one,o2o/one2one,m2m/many2many
- Cardinality fields on
RelationDef:carDirectandcarInverseare now stored on every resolved relation and used by/_aboutE/R diagram to render accurate crow's-foot notation instead of inferring cardinality from relation type parseRelations()DSL parser (src/storage/parseRelations.ts): newparseDslString()internal function handles all three DSL levels; all forms expand to the same canonicalRelationDefat parse timeparseRoutes.ts(src/storage/parseRoutes.ts): extracted_routesparsing logic fromyrestStorage.tsinto its own module- E/R diagram canvas split (
src/router/templates/about.diagram.canvas.ts): the inline JavaScript canvas renderer is now in a dedicated file using the/*js*/comment tag for editor syntax highlighting;about.diagram.tsretains only TypeScript types andgenerateERData()
Changed
_prefix convention enforced on all yrest reserved keys: verbose relation objects now use_type,_target,_foreignKey,_otherKey,_through,_nested,_car-direct,_car-inverse(all prefixed with_). The previous unprefixed form (type,target,nested, etc.) is no longer supportedyrestStorage.tsnow serialises_foreignKey,_car-directand_car-inverseback to YAML on snapshot/persistinit --sample ecommerceandinit --sample relationaltemplates updated to use the_prefix convention throughout
Breaking
- Verbose
_relobject keys without_prefix (type,target,nested,through,foreignKey,otherKey) are no longer parsed. Migrate existingdb.ymlfiles by adding_to each key:type→_type,target→_target,nested→_nested, etc.
v0.10.0
Added
- OpenAPI 3.0.3 spec generation: always-on
GET /_openapi(YAML,text/yaml) andGET /_openapi.json(JSON) endpoints — regenerated on every request so they stay in sync in watch mode yrest openapi <file>CLI command: generate an OpenAPI spec from adb.ymlfile without starting a server--output <file>— write to a specific file path (default:openapi.yaml/openapi.json)--format json|yaml— output format (default:yaml)--stdout— print to stdout instead of writing a file--base,--port,--host,--title— tune theserversandinfoblocks
_schemablock: declare field-level annotations per collection for accurate OpenAPI output- String shorthand:
fieldName: required— marks the field as required - Object form:
fieldName: { required: true, type: integer, format: email, enum: [...], description: "...", default: ... } - Fields absent from
_schemaare inferred from data and treated as optional _schemais excluded from the CRUD collections — does not appear as a REST resource
- String shorthand:
buildCollectionSchema()(src/openapi/inferSchema.ts): infers OpenAPI property types from the first 10 items in a collection, then merges_schemaoverrides; declaredrequired: truefields populate therequired[]arraybuildCrudPaths()(src/openapi/buildPaths.ts): generates full CRUD path items (GET list + POST + GET item + PUT + PATCH + DELETE) with all supported query parameters documentedbuildRelationPaths()(src/openapi/buildPaths.ts): generates relation path items formany2one(collection + item routes),one2one, and bidirectionalmany2manybuildCustomRoutePaths()(src/openapi/buildPaths.ts): generates path items for all_routesentries; extracts path params, collects all possible status codes fromscenarios,otherwise,responseanderrorblocksgenerateOpenApi()(src/openapi/generateOpenApi.ts): assembles the complete OpenAPI 3.0.3 document from storage state + server options
Fixed
- YAML anchors in OpenAPI output:
yaml.stringifyproduced YAML anchors (&a1,*a1) for shared parameter objects (COLLECTION_QUERY_PARAMS,ID_PATH_PARAM), which are rejected by OpenAPI validators; fixed by passingaliasDuplicateObjects: falseto allstringify()calls inopenapi.routes.tsand theopenapiCLI command $refnames truncated to a single letter:schemaRef()inbuildPaths.tswas re-singularizing an already-singular schema name ("User"→"U","Post"→"P"); fixed by passing the name through directly
v0.9.0
Added
- Explicit relation types in
_rel: each field can now be declared as an object with an explicittypeinstead of the legacy string shorthandmany2one— foreign key on the child pointing to a parent (same semantics as the previous string shorthand;userId: usersstill works)one2one— one child per parent;GET /parent/:id/childreturns a single object instead of an arraymany2many— join through a pivot collection; requiresthrough,foreignKeyandotherKeyfields
nested: trueflag: addnested: trueto any relation to auto-embed the related data in every GET response without needing?_expandor?_embed.many2one/one2oneembed the parent object under the derived key (userId→user, FK preserved);many2manyembed the resolved target array under the alias key- Bidirectional many2many nested routes: declaring a
many2manyrelation now automatically registers both directions —GET /posts/:id/tagsandGET /tags/:id/posts— from a single_relentry parseRelations()utility (src/storage/parseRelations.ts): normalises raw_relYAML to canonicalRelationDefobjects on startup; string shorthand is transparently upgraded to{ type: "many2one", target }. Used by both the file storage and the in-memory programmatic APIecommerceinit sample:yrest init --sample ecommercescaffolds a full e-commerce domain (products, orders, users, reviews, categories, order-items) with all relation types, six_routesentries (scenarios, template vars, delay, error injection)- Enriched
basicandrelationalinit samples: more collections, more fields, query examples in comments, and all three relation types demonstrated inrelational src/router/templates/about.helpers.ts: extracted all helper functions fromabout.template.tsinto a dedicated module (escapeHtml,badge,endpointRow,resourceAccordion,nestedRoutesAccordion,snapshotAccordion,customRoutesAccordion,handlersAccordion,examplesBlock).about.template.tsis now ~200 lines vs ~555 before
Changed
/_aboutnested routes accordion now shows:- Both forward and inverse routes for each
many2manyrelation (POST /posts/:id/tags+GET /tags/:id/posts) - Yellow
nestedbadge on relations declared withnested: true one2onebadge in teal for one-to-one relationsmany2manybadge in indigo for pivot-based relations
- Both forward and inverse routes for each
Relationstype is nowRecord<string, Record<string, RelationDef>>whereRelationDefis a discriminated union (many2one | one2one | many2many), each with an optionalnested?: trueflag
Fixed
/_aboutcrash with object-format_rel: in v0.8.1, using the newone2oneormany2manyobject syntax in_relcausedTypeError: parent.endsWith is not a functioninside the nested-routes loop ofabout.template.ts, returning a 500 for every/_aboutrequest. Fixed byparseRelations()normalising all relation values before they reach the template layer
v0.8.0
Added
- Error injection (
error:):_routesentries can declare a fixed HTTP status code that always overrides handlers, scenarios and static responses — useful for simulating outages, payment failures or auth errors errorBody:: optional custom response body alongsideerror:. Defaults to{ "error": "Forced error NNN" }if omittedidStrategyoption: controls howidis generated on POST when the body does not include one —"increment"(default, next integer) or"uuid"(random UUID v4 viacrypto.randomUUID())--id-strategyCLI flag andidStrategy:config file key exposing the new option- Socket.dev badge in README — security audit badge for the published npm package
scripts/update-version-badge.mjs: auto-updates the Socket badge URL in README on everynpm versionbump via theversionlifecycle hook, so the badge always reflects the latest published version without manual editssocket.allow.filesysteminpackage.json— declares intentional filesystem access to suppress the Socket.dev security alert (yrest reads and writesdb.ymlby design)- Logo assets in
assets/— four variants:logo-white,logo-color,logo-text,logo-figure - Full-width banner image in README header
- Logo embedded as base64 data URI in the
/_aboutpage — no internet or external files required; falls back to text heading if the asset is missing - Logo in demo app header replacing the text-based logo
Changed
/_aboutcustom routes section: new rederror·NNNbadge for routes with error injection/_aboutactive modes: newid·uuidbadge whenidStrategyis not the defaultassets/added topackage.json"files"so logo assets ship with the npm package
v0.7.0
Added
- Conditional scenarios (
scenarios:): custom routes can now declare multiple response variants evaluated in declaration order — the first matchingwhen:block wins when:as object → AND semantics: all entries must match (dot-notation keys:body.X,params.X,query.X,headers.X)when:as array of objects → OR of ANDs: any group satisfying all its conditions triggers the scenariootherwise:fallback: explicit response whenscenarios:are defined but none matched — takes priority overresponse:- Field operator suffixes in conditions:
_ne,_like,_start,_regex,_gte,_ltework on scenario condition keys, reusing the same operators as the query layer - Template variables in scenarios/otherwise:
{{}}interpolation supported in scenario andotherwiseresponse bodies - Per-route
delay:: fixed delay (ms) applied to a specific_routesentry before any response is sent, regardless of which path resolved it (handler / scenario / otherwise / response) /_aboutroute badges: custom routes section now shows scenario count (purple),(OR)indicator,otherwiselabel,delay·Xmsbadge (orange), and{{…}}template indicatorOPERATORSandapplyOperatorexported fromquery.service.tsfor reuse by the condition engine- New
src/utils/conditions.tsmodule:findMatchingScenariowith dot-notation path resolver
v0.6.0
Added
- Phase 7 — Programmatic API:
createYrestServerfactory to embed the mock server directly in test suites (Vitest, Playwright, Cypress) without touching the CLI yresttagged template literal: define inline YAML data in TypeScript with automatic dedent and interpolation support —const data = yrest\users: [...]``- In-memory storage:
createYrestServer({ data })runs fully in-memory with nodb.ymlfile required; each test instance is isolated and stateless port: 0support: assigns a random available port at startup — no port conflicts between parallel test workerssrc/server/index.tsbarrel: unified import point forcreateServerandcreateYrestServerFromStorage- GitHub Release notes now populated from
CHANGELOG.mdinstead of auto-generated commit list
Changed
- Internal rename:
YamlStorage→YrestStorage,createYamlStorage→createYrestStorage - Internal rename:
ServerOptions→YrestOptions,serverOptionsSchema→yrestOptionsSchema createYrestServerFromStorageextracted tosrc/server/yrestServer.tsas shared lifecycle core used by both CLI and programmatic API — eliminates duplicated Fastifylisten/closelogic- Public exports in
src/index.tsreorganised into two sections: Programmatic API and low-level building blocks
Fixed
start()called twice no longer leaks an orphaned Fastify instance — idempotent by guardyresttemplate now throws a descriptive[yrest]error when interpolating an object or array instead of silently producing invalid YAML
v0.5.3
Changed
- Package renamed from
@aggiovato/yrestto@yrest/cli - Repository renamed to
yReston GitHub package.jsondescription and keywords optimized for discoverability
Added
CHANGELOG.mdwith full version history- GitHub issue templates and PR template
v0.5.2
Full Changelog: v0.5.1...v0.5.2
Fixed
_routespreserved correctly on everypersist()andreload()cycle