{"_id":"@adysre/rules-storage","name":"@adysre/rules-storage","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@adysre/rules-storage","version":"0.1.0","type":"module","description":"Where rules live: the storage contract, versioning, querying, and a reference adapter every other one has to match.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/abhisheksahu093/Adysre.git","directory":"packages/rules-storage"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"main":"./dist/index.js","types":"./dist/index.d.ts","devDependencies":{"@types/node":"^20.16.10","tsx":"^4.19.1","typescript":"^5.6.3","@adysre/config":"0.1.0"},"dependencies":{"@adysre/rules-core":"0.1.0","@adysre/rules-types":"0.1.0"},"keywords":["rules-engine","business-rules","rule-builder","json-logic","decision-engine","typescript"],"engines":{"node":">=20.0.0"},"sideEffects":false,"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.build.json && node ../config/scripts/fix-declarations.mjs","lint":"eslint src","typecheck":"tsc --noEmit","test":"tsx --test 'src/**/*.test.ts'","clean":"rm -rf dist .turbo tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo"},"_id":"@adysre/rules-storage@0.1.0","bugs":{"url":"https://github.com/abhisheksahu093/Adysre/issues"},"homepage":"https://github.com/abhisheksahu093/Adysre#readme","_integrity":"sha512-dRTAdZlF0F7Z5YWrUW1dxG32I+sOJXdbydDFfC1t+dKfAJIDQfxiRWWFNUgGR8cRe1nKwAY4x6hIP07Hn62Mxw==","_resolved":"/private/var/folders/xx/vrjl61fs7dlb3dhyq5_lgzkm0000gn/T/1e2d1362ee481799fcebea296801649e/adysre-rules-storage-0.1.0.tgz","_from":"file:adysre-rules-storage-0.1.0.tgz","_nodeVersion":"22.23.1","_npmVersion":"10.9.8","dist":{"integrity":"sha512-dRTAdZlF0F7Z5YWrUW1dxG32I+sOJXdbydDFfC1t+dKfAJIDQfxiRWWFNUgGR8cRe1nKwAY4x6hIP07Hn62Mxw==","shasum":"b8cefa75b4f1d88ac0822b78cae5ae2adfcf9feb","tarball":"https://registry.npmjs.org/@adysre/rules-storage/-/rules-storage-0.1.0.tgz","fileCount":30,"unpackedSize":73419,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDtWTAaJ5eEFPmCp3GxjxUiIlARORGZNYqBRIezdK++WQIgJ5veUycSFHgjAMUTc8Zlq+Ho/JzCR/UsOJ1eowM1oXg="}]},"_npmUser":{"name":"adysre","email":"abhisheksahu093@gmail.com"},"directories":{},"maintainers":[{"name":"adysre","email":"abhisheksahu093@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rules-storage_0.1.0_1785148314863_0.32593708211211725"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-27T10:31:54.735Z","0.1.0":"2026-07-27T10:31:55.020Z","modified":"2026-07-27T10:31:55.236Z"},"maintainers":[{"name":"adysre","email":"abhisheksahu093@gmail.com"}],"description":"Where rules live: the storage contract, versioning, querying, and a reference adapter every other one has to match.","homepage":"https://github.com/abhisheksahu093/Adysre#readme","keywords":["rules-engine","business-rules","rule-builder","json-logic","decision-engine","typescript"],"repository":{"type":"git","url":"git+https://github.com/abhisheksahu093/Adysre.git","directory":"packages/rules-storage"},"bugs":{"url":"https://github.com/abhisheksahu093/Adysre/issues"},"license":"MIT","readme":"# @adysre/rules-storage\n\nWhere rules live.\n\n```ts\nconst storage = createMemoryStorage();\n\nawait storage.save(rule);            // version 1\nawait storage.save(edited);          // version 2\nawait storage.restore(rule.id, 1);   // version 3, holding version 1's content\n```\n\n## The contract is executable\n\n\"Storage adapters\" is plural, and a plural that only means \"several things with\nthe same method names\" is worth nothing: a screen that lists rules correctly\nagainst the in-memory store and wrongly against the database is a bug nobody\nfinds until production, because both adapters type-check.\n\nSo an adapter is checked rather than trusted:\n\n```ts\nconst results = await runStorageConformance(() => createMyAdapter());\nif (conformanceReport(results) !== '') throw new Error(conformanceReport(results));\n```\n\nIt is framework-free — no vitest, no `node:test` — so the same suite runs in a\nunit test, in CI against a real database, or in a deployment's own health check.\nIt never stops at the first failure, because an adapter's author wants the whole\nlist rather than one problem per run.\n\n## Decisions a store has to make\n\n**A save that changes nothing creates no version.** A builder autosaves, a form\nposts twice, a retry lands after the first attempt succeeded. A history padded\nwith identical entries is a history nobody scrolls, which defeats the point of\nkeeping one. Compared through `stringifyRule`, so two documents that differ only\nin the order their fields were assigned compare equal.\n\n**A rename still makes a version.** The AST says the version increments on each\nsaved change, and a rename is a change somebody made and may want back. Whether\nthe *logic* changed is a separate question, and `compareVersions` answers it —\nso a history list can grey out the cosmetic revisions without the plugin\ncontract growing a field.\n\n**The stored version wins over the one a client sent.** A stale editor holding\nversion 1 must not write 2 over 9.\n\n**A first save is version 1**, whatever the document claimed. An imported\ndocument carries whatever version it was written with, and honouring that would\nstart a fresh history at 7.\n\n**Restore moves forward.** The old content becomes a new version on top of the\nhistory rather than rewinding it: a history that can be rewritten is one nobody\ncan be asked to trust, and \"who changed this, and when\" is the question a\nversion list exists to answer. The identity stays the current one, so restoring\nnever resurrects a key something else may since have taken.\n\n**A tag filter narrows.** All of the given tags, not any — a filter that\nreturned *more* as tags were added would read as broken long before it read as a\nunion.\n\n**Search looks at what identifies a rule**: name, business key, tags. Not the\ndescription. A search that matches prose returns most of the list for most\nwords, which teaches people the box does not work.\n\n**Ties are broken by id.** Two rules saved in the same millisecond is ordinary,\nand a list whose order depends on iteration order pages differently on two\nmachines — so an item appears twice across two pages, or never.\n\n**An invalid rule is refused, not stored.** Unlike parsing, where a bad document\nis a message to show, an invalid one reaching `save` is a bug in the caller: a\nbuilder validates before it offers a Save button.\n\n## Adapters\n\n| Adapter | For |\n| --- | --- |\n| `createMemoryStorage` | tests, previews, the reference semantics |\n| `createWebStorage` | a browser, so a sandbox survives a refresh |\n\nBoth are `createStorage` over a `HistoryPort`, so the versioning, validation and\nquery rules exist once. A new synchronous adapter is a port. An asynchronous one\n— a database — implements `StoragePlugin` directly and translates the querying\ninto SQL rather than loading every rule to filter it, then runs the conformance\nsuite to find out whether it arrived at the same answers.\n\nThe web adapter reads through `parseRule`, so a document written by an older\nengine is migrated on the way in and one written by a **newer** engine is refused\nrather than half-understood. Storage is where a stored rule meets a different\nbuild, which is what `schemaVersion` was always for.\n\nZero runtime dependencies, like the core.\n\nSee [`documents/RULES_ENGINE.md`](../../documents/RULES_ENGINE.md).\n","readmeFilename":"README.md","_rev":"1-394a67db614510bbf105d4f23ee1ace3"}