{"_id":"@buttress/crag","_rev":"10-364402ecb0a50776aceb427accd23f1d","name":"@buttress/crag","dist-tags":{"latest":"0.1.1"},"versions":{"0.0.16":{"name":"@buttress/crag","version":"0.0.16","author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","_id":"@buttress/crag@0.0.16","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"}],"dist":{"shasum":"66c2ee842ceb7e882f2ce48d1f3b5a89efd4abcf","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.0.16.tgz","fileCount":83,"integrity":"sha512-UepI6vVSbnIka+BTJpFXbyfaLOmmtH6FsHcWiq+GaytIeWH2u/NeIZFg2kTfp0VVzh6IxfGoOYnfmCIuXwf4Cw==","signatures":[{"sig":"MEUCIDnVkRTyGmwjRnX/Ai8S8hzpPM6nTerm6tB+znMpKJpnAiEAwsiEmmhwyghOMNac1/x4VOMMDv55s0s5xqda/HQw+2Q=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":607524},"main":"./dist/src/index.js","husky":{"hooks":{"pre-commit":"lint-staged"}},"types":"./dist/src/index.d.ts","module":"./dist/src/index.js","exports":{".":"./dist/src/index.js","./buttress-db-service.js":"./dist/src/components/buttress-db-service.js"},"gitHead":"c1ee60cc3db88b15752b4fa20c9a0e0e52fa3370","scripts":{"lint":"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore","test":"tsc && wtr --coverage","build":"tsc && npm run analyze -- --exclude dist","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore","analyze":"cem analyze --litelement","prepare":"husky","storybook":"tsc && npm run analyze -- --exclude dist && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds -c .storybook/server.mjs\"","prepublish":"tsc && npm run analyze -- --exclude dist","test:watch":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\"","licence-check":"./.husky/licence-check","storybook:build":"tsc && npm run analyze -- --exclude dist && build-storybook"},"_npmUser":{"name":"tom.cahill","email":"me@tomcahill.co.uk"},"prettier":{"arrowParens":"avoid","singleQuote":true},"_npmVersion":"10.7.0","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"18.20.3","dependencies":{"lit":"^2.7.2","bson":"~4.7.2","uuid":"^9.0.0","sugar":"^2.0.6","socket.io-client":"^4.6.1","@lighten/ltn-element":"0.0.7"},"eslintConfig":{"rules":{"no-unused-vars":"off","import/extensions":["error","always",{"ignorePackages":true}],"import/no-unresolved":"off","@typescript-eslint/no-unused-vars":2},"parser":"@typescript-eslint/parser","extends":["@open-wc","prettier"],"plugins":["@typescript-eslint"]},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.4","tslib":"^2.5.0","eslint":"^8.38.0","prettier":"^2.8.7","typescript":"^5.0.4","@types/node":"^18.15.11","lint-staged":"^13.2.1","concurrently":"^8.0.1","@open-wc/testing":"^3.1.8","@web/test-runner":"^0.15.3","@open-wc/eslint-config":"^10.0.0","eslint-config-prettier":"^8.8.0","@typescript-eslint/parser":"^5.58.0","@typescript-eslint/eslint-plugin":"^5.58.0","@custom-elements-manifest/analyzer":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.0.16_1723715198736_0.8794052260700331","host":"s3://npm-registry-packages"}},"0.0.15":{"name":"@buttress/crag","version":"0.0.15","author":{"name":"buttress-db-service"},"license":"MIT","_id":"@buttress/crag@0.0.15","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"}],"dist":{"shasum":"5d01dc0c76d7ae6afeb5e39d19bde800539f50c4","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.0.15.tgz","fileCount":66,"integrity":"sha512-kwqPNn9U/uQP2ZP7lR2W8MJd59n7ERGjznHIyRAsTV9eXPffEIV2RUrTmHak0VIucjRNQBfE5BXIL09C5cV34A==","signatures":[{"sig":"MEUCIQC4cThUXah0cgjZJOdlAGduGQj/erxP00+JGM9JHWyhyQIgUHgRi6rju8r/sWSIsRh4mLGbLii2IUDfgscvtaCTVNs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":453233},"main":"./dist/src/index.js","husky":{"hooks":{"pre-commit":"lint-staged"}},"types":"./dist/src/index.d.ts","module":"./dist/src/index.js","exports":{".":"./dist/src/index.js","./buttress-db-service.js":"./dist/src/components/buttress-db-service.js"},"gitHead":"dfcb3ced7c95be30f84d28cc9342f6d5c2c5bfeb","scripts":{"lint":"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore","test":"tsc && wtr --coverage","build":"tsc && npm run analyze -- --exclude dist","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore","analyze":"cem analyze --litelement","storybook":"tsc && npm run analyze -- --exclude dist && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds -c .storybook/server.mjs\"","prepublish":"tsc && npm run analyze -- --exclude dist","test:watch":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\"","storybook:build":"tsc && npm run analyze -- --exclude dist && build-storybook"},"_npmUser":{"name":"tom.cahill","email":"me@tomcahill.co.uk"},"prettier":{"arrowParens":"avoid","singleQuote":true},"_npmVersion":"10.7.0","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"18.20.3","dependencies":{"lit":"^2.7.2","bson":"~4.7.2","uuid":"^9.0.0","sugar":"^2.0.6","socket.io-client":"^4.6.1","@lighten/ltn-element":"0.0.6"},"eslintConfig":{"rules":{"no-unused-vars":"off","import/extensions":["error","always",{"ignorePackages":true}],"import/no-unresolved":"off","@typescript-eslint/no-unused-vars":["error"]},"parser":"@typescript-eslint/parser","extends":["@open-wc","prettier"],"plugins":["@typescript-eslint"]},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^8.0.3","tslib":"^2.5.0","eslint":"^8.38.0","prettier":"^2.8.7","typescript":"^5.0.4","@types/node":"^18.15.11","lint-staged":"^13.2.1","concurrently":"^8.0.1","@open-wc/testing":"^3.1.8","@web/test-runner":"^0.15.3","@open-wc/eslint-config":"^10.0.0","eslint-config-prettier":"^8.8.0","@typescript-eslint/parser":"^5.58.0","@typescript-eslint/eslint-plugin":"^5.58.0","@custom-elements-manifest/analyzer":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.0.15_1730991853415_0.3697085407049876","host":"s3://npm-registry-packages"}},"0.0.17":{"name":"@buttress/crag","version":"0.0.17","author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","_id":"@buttress/crag@0.0.17","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"}],"dist":{"shasum":"26fa46d863595b8ced1aaba82bb0d2da74bd07bc","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.0.17.tgz","fileCount":26,"integrity":"sha512-8FU5qZCuDxSNcVdLIo8CguotvSDVcK+BjFu7fNC59FrNjxrOSh+rbmeCB1gOq8T22ZK/k3kosrGj/xLnGgNG/g==","signatures":[{"sig":"MEYCIQCEmodUtZHywtg4UTBNmYFsp+XPeLUepZhUg7C5Cteq4QIhAMu7IkLN7MsB7LTkTag8lRBPdXsZtASnSocFoTw7fmTU","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":182192},"main":"./development/index.js","type":"module","husky":{"hooks":{"pre-commit":"lint-staged"}},"types":"./development/index.d.ts","module":"./development/index.js","exports":{".":{"types":"./development/index.d.ts","default":"./index.js","development":"./development/index.js"},"./components/buttress-db-service.js":{"types":"./development/components/buttress-db-service.d.ts","default":"./components/buttress-db-service.js","development":"./development/components/buttress-db-service.js"}},"gitHead":"812ececd82dc75bcabd130168fec7a21563e7a18","scripts":{"lint":"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore","roll":"rimraf dist && rollup -c rollup.config.mjs","test":"npm run build && npm run roll && npm run test:e2e","build":"rimraf development tsconfig.tsbuildinfo && tsc --build --pretty && cp -R development/* .","clean":"rimraf development dist components types ./ButtressDataService.* ./ButtressDbService.* ./ButtressRealtime.* ./ButtressSchema.* ./ButtressSchemaFactory.* ./ButtressStore.* ./helpers.* ./index.* test-app-token.json tsconfig.tsbuildinfo","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore","analyze":"cem analyze --litelement","package":"cp -R development/* . && rm -rf test","prepare":"husky","test:e2e":"node ./scripts/before-e2e.js && wtr --node-resolve","storybook":"tsc && npm run analyze -- --exclude dist && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds -c .storybook/server.mjs\"","test:watch":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\"","licence-check":"./.husky/licence-check","prepublishOnly":"npm run build && npm run package","storybook:build":"tsc && npm run analyze -- --exclude dist && build-storybook"},"_npmUser":{"name":"tom.cahill","email":"me@tomcahill.co.uk"},"prettier":{"arrowParens":"avoid","singleQuote":true},"_npmVersion":"10.9.2","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"18.20.3","dependencies":{"lit":"^2.7.2","bson":"~6.8.0","uuid":"^9.0.0","socket.io-client":"^4.6.1","@lighten/ltn-element":"0.0.7"},"eslintConfig":{"rules":{"no-bitwise":"off","no-continue":"off","no-unused-vars":"off","import/extensions":["error","always",{"ignorePackages":true}],"import/no-unresolved":"off","no-promise-executor-return":"off","@typescript-eslint/no-unused-vars":2},"parser":"@typescript-eslint/parser","extends":["@open-wc","prettier"],"plugins":["@typescript-eslint"]},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.4","mocha":"^10.7.3","tslib":"^2.5.0","eslint":"^9.12.0","rimraf":"^5.0.10","rollup":"^2.79.2","prettier":"^2.8.7","typescript":"^5.0.4","@types/node":"^18.15.11","lint-staged":"^13.2.1","@types/mocha":"^10.0.9","concurrently":"^8.0.1","@open-wc/testing":"^4.0.0","@web/test-runner":"^0.19.0","@open-wc/eslint-config":"^12.0.3","@rollup/plugin-replace":"^6.0.1","@web/dev-server-rollup":"^0.6.4","eslint-config-prettier":"^9.1.0","@rollup/plugin-commonjs":"^28.0.0","@web/dev-server-esbuild":"^1.0.2","@rollup/plugin-typescript":"^12.1.0","@typescript-eslint/parser":"^8.9.0","@rollup/plugin-node-resolve":"^15.3.0","@web/dev-server-import-maps":"^0.2.1","@typescript-eslint/eslint-plugin":"^8.9.0","@custom-elements-manifest/analyzer":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.0.17_1733744316080_0.8884009791531549","host":"s3://npm-registry-packages-npm-production"}},"0.0.18":{"name":"@buttress/crag","version":"0.0.18","author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","_id":"@buttress/crag@0.0.18","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"},{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"}],"dist":{"shasum":"8b9cf9a84737c0ac16a1608bcf73c362a64464f5","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.0.18.tgz","fileCount":26,"integrity":"sha512-lZFZAaazw0cbFT+fukNGhRVhI6ljIB/Kl/O+Bk5q8mTqkffyaTBF/Fng87xRNZ6AYLRzaDmriAiissg5ShPqjA==","signatures":[{"sig":"MEUCIDMWJ1lNBb+ZuwAxqBRtxtBN3/J591fdfzSyebAkXc1/AiEA/7uTWpNCBE61nR/VBLvXW3C2aACkyomzNhZDiF5ZgIw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":183388},"main":"./development/index.js","type":"module","husky":{"hooks":{"pre-commit":"lint-staged"}},"types":"./development/index.d.ts","module":"./development/index.js","exports":{".":{"types":"./development/index.d.ts","default":"./index.js","development":"./development/index.js"},"./components/buttress-db-service.js":{"types":"./development/components/buttress-db-service.d.ts","default":"./components/buttress-db-service.js","development":"./development/components/buttress-db-service.js"}},"gitHead":"a101225fe882d9cd2021c9eacdb51ab260cd2c0b","scripts":{"lint":"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore","roll":"rimraf dist && rollup -c rollup.config.mjs","test":"npm run build && npm run roll && npm run test:e2e","build":"rimraf development tsconfig.tsbuildinfo && tsc --build --pretty && cp -R development/* .","clean":"rimraf development dist components types ./ButtressDataService.* ./ButtressDbService.* ./ButtressRealtime.* ./ButtressSchema.* ./ButtressSchemaFactory.* ./ButtressStore.* ./helpers.* ./index.* test-app-token.json tsconfig.tsbuildinfo","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore","analyze":"cem analyze --litelement","package":"cp -R development/* . && rm -rf test","prepare":"husky","test:e2e":"node ./scripts/before-e2e.js && wtr --node-resolve","storybook":"tsc && npm run analyze -- --exclude dist && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds -c .storybook/server.mjs\"","test:unit":"npm run build && npm run roll && wtr --config web-test-runner.unit.config.mjs --node-resolve","test:watch":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\"","licence-check":"./.husky/licence-check","prepublishOnly":"npm run package","storybook:build":"tsc && npm run analyze -- --exclude dist && build-storybook"},"_npmUser":{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"},"prettier":{"arrowParens":"avoid","singleQuote":true},"_npmVersion":"10.2.3","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"18.19.0","dependencies":{"lit":"^2.7.2","bson":"~6.8.0","uuid":"^9.0.0","socket.io-client":"^4.6.1","@lighten/ltn-element":"0.0.7"},"eslintConfig":{"rules":{"no-bitwise":"off","no-continue":"off","no-unused-vars":"off","import/extensions":["error","always",{"ignorePackages":true}],"import/no-unresolved":"off","no-promise-executor-return":"off","@typescript-eslint/no-unused-vars":2},"parser":"@typescript-eslint/parser","extends":["@open-wc","prettier"],"plugins":["@typescript-eslint"]},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.4","mocha":"^10.7.3","tslib":"^2.5.0","eslint":"^9.12.0","rimraf":"^5.0.10","rollup":"^2.79.2","prettier":"^2.8.7","typescript":"^5.0.4","@types/node":"^18.15.11","lint-staged":"^13.2.1","@types/mocha":"^10.0.9","concurrently":"^8.0.1","@open-wc/testing":"^4.0.0","@web/test-runner":"^0.19.0","@open-wc/eslint-config":"^12.0.3","@rollup/plugin-replace":"^6.0.1","@web/dev-server-rollup":"^0.6.4","eslint-config-prettier":"^9.1.0","@rollup/plugin-commonjs":"^28.0.0","@web/dev-server-esbuild":"^1.0.2","@rollup/plugin-typescript":"^12.1.0","@typescript-eslint/parser":"^8.9.0","@rollup/plugin-node-resolve":"^15.3.0","@web/dev-server-import-maps":"^0.2.1","@typescript-eslint/eslint-plugin":"^8.9.0","@custom-elements-manifest/analyzer":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.0.18_1754645600880_0.7241955202445134","host":"s3://npm-registry-packages-npm-production"}},"0.0.20":{"name":"@buttress/crag","version":"0.0.20","author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","_id":"@buttress/crag@0.0.20","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"},{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"}],"dist":{"shasum":"bdd02e1cfb604ecc95b1b2f83f41507ec29cae06","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.0.20.tgz","fileCount":26,"integrity":"sha512-9J/se1FJ5mcqchEPspbEbC5seTw8Lk1A/byNr4Sccirk5ICHt0qzlzTHoQyw56XLiKUrDAJsfJSqo1HSROTywA==","signatures":[{"sig":"MEQCIFThGELzkysTma3OVa0FLg3mQ9d5kZfa7efNFrb6wOrNAiAc2bXrMNMklMNjtTSFQMbfWE+r0H0fm7emERELG6Mr8w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":183796},"main":"./development/index.js","type":"module","husky":{"hooks":{"pre-commit":"lint-staged"}},"types":"./development/index.d.ts","module":"./development/index.js","exports":{".":{"types":"./development/index.d.ts","default":"./index.js","development":"./development/index.js"},"./components/buttress-db-service.js":{"types":"./development/components/buttress-db-service.d.ts","default":"./components/buttress-db-service.js","development":"./development/components/buttress-db-service.js"}},"gitHead":"9f2505d7873becaaf85877fd60285a07fd39fd7e","scripts":{"lint":"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore","roll":"rimraf dist && rollup -c rollup.config.mjs","test":"npm run build && npm run roll && npm run test:e2e","build":"rimraf development tsconfig.tsbuildinfo && tsc --build --pretty && cp -R development/* .","clean":"rimraf development dist components types ./ButtressDataService.* ./ButtressDbService.* ./ButtressRealtime.* ./ButtressSchema.* ./ButtressSchemaFactory.* ./ButtressStore.* ./helpers.* ./index.* test-app-token.json tsconfig.tsbuildinfo","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore","analyze":"cem analyze --litelement","package":"cp -R development/* . && rm -rf test","prepare":"husky","test:e2e":"node ./scripts/before-e2e.js && wtr --node-resolve","storybook":"tsc && npm run analyze -- --exclude dist && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds -c .storybook/server.mjs\"","test:unit":"npm run build && npm run roll && wtr --config web-test-runner.unit.config.mjs --node-resolve","test:watch":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\"","licence-check":"./.husky/licence-check","prepublishOnly":"npm run test && npm run package","storybook:build":"tsc && npm run analyze -- --exclude dist && build-storybook"},"_npmUser":{"name":"tom.cahill","email":"me@tomcahill.co.uk"},"prettier":{"arrowParens":"avoid","singleQuote":true},"_npmVersion":"10.9.2","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"18.20.3","dependencies":{"lit":"^2.7.2","bson":"~6.8.0","uuid":"^9.0.0","socket.io-client":"^4.6.1","@lighten/ltn-element":"0.0.7"},"eslintConfig":{"rules":{"no-bitwise":"off","no-continue":"off","no-unused-vars":"off","import/extensions":["error","always",{"ignorePackages":true}],"import/no-unresolved":"off","no-promise-executor-return":"off","@typescript-eslint/no-unused-vars":2},"parser":"@typescript-eslint/parser","extends":["@open-wc","prettier"],"plugins":["@typescript-eslint"]},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.4","mocha":"^10.7.3","tslib":"^2.5.0","eslint":"^9.12.0","rimraf":"^5.0.10","rollup":"^2.79.2","prettier":"^2.8.7","typescript":"^5.0.4","@types/node":"^18.15.11","lint-staged":"^13.2.1","@types/mocha":"^10.0.9","concurrently":"^8.0.1","@open-wc/testing":"^4.0.0","@web/test-runner":"^0.19.0","@open-wc/eslint-config":"^12.0.3","@rollup/plugin-replace":"^6.0.1","@web/dev-server-rollup":"^0.6.4","eslint-config-prettier":"^9.1.0","@rollup/plugin-commonjs":"^28.0.0","@web/dev-server-esbuild":"^1.0.2","@rollup/plugin-typescript":"^12.1.0","@typescript-eslint/parser":"^8.9.0","@rollup/plugin-node-resolve":"^15.3.0","@web/dev-server-import-maps":"^0.2.1","@typescript-eslint/eslint-plugin":"^8.9.0","@custom-elements-manifest/analyzer":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.0.20_1768574616136_0.1665725014166708","host":"s3://npm-registry-packages-npm-production"}},"0.0.21":{"name":"@buttress/crag","version":"0.0.21","author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","_id":"@buttress/crag@0.0.21","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"},{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"}],"dist":{"shasum":"e478d110a3b9dcf6d779fcccac09d3f1aa07265d","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.0.21.tgz","fileCount":26,"integrity":"sha512-bNiiZtXT/IUIs2MPjn4mJXWjelXfEzILC+GtdUddQxJ33ir+f7ODaWWC7OmbNHNo6moujgjl3ha1tluzyoC8dw==","signatures":[{"sig":"MEUCIQCTl1P3aPUk7AZVLYY/E0iCgX6hpSGCFJ8+Ln8MYWFiigIgYemOHu0dqDWMf0OeQrpenhpkzuDbs5TSFWMsiEJuVLM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":183854},"main":"./development/index.js","type":"module","husky":{"hooks":{"pre-commit":"lint-staged"}},"types":"./development/index.d.ts","module":"./development/index.js","exports":{".":{"types":"./development/index.d.ts","default":"./index.js","development":"./development/index.js"},"./components/buttress-db-service.js":{"types":"./development/components/buttress-db-service.d.ts","default":"./components/buttress-db-service.js","development":"./development/components/buttress-db-service.js"}},"gitHead":"ee9a8cea8cd5302bc0f037cc1b0d46fbecaad0ca","scripts":{"lint":"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore","roll":"rimraf dist && rollup -c rollup.config.mjs","test":"npm run build && npm run roll && npm run test:e2e","build":"rimraf development tsconfig.tsbuildinfo && tsc --build --pretty && cp -R development/* .","clean":"rimraf development dist components types ./ButtressDataService.* ./ButtressDbService.* ./ButtressRealtime.* ./ButtressSchema.* ./ButtressSchemaFactory.* ./ButtressStore.* ./helpers.* ./index.* test-app-token.json tsconfig.tsbuildinfo","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore","analyze":"cem analyze --litelement","package":"cp -R development/* . && rm -rf test","prepare":"husky","test:e2e":"node ./scripts/before-e2e.js && wtr --node-resolve","storybook":"tsc && npm run analyze -- --exclude dist && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds -c .storybook/server.mjs\"","test:unit":"npm run build && npm run roll && wtr --config web-test-runner.unit.config.mjs --node-resolve","test:watch":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\"","licence-check":"./.husky/licence-check","prepublishOnly":"npm run test && npm run package","storybook:build":"tsc && npm run analyze -- --exclude dist && build-storybook"},"_npmUser":{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"},"prettier":{"arrowParens":"avoid","singleQuote":true},"_npmVersion":"10.8.2","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"_nodeVersion":"20.19.1","dependencies":{"lit":"^2.7.2","bson":"~6.8.0","uuid":"^9.0.0","socket.io-client":"^4.6.1","@lighten/ltn-element":"0.0.7"},"eslintConfig":{"rules":{"no-bitwise":"off","no-continue":"off","no-unused-vars":"off","import/extensions":["error","always",{"ignorePackages":true}],"import/no-unresolved":"off","no-promise-executor-return":"off","@typescript-eslint/no-unused-vars":2},"parser":"@typescript-eslint/parser","extends":["@open-wc","prettier"],"plugins":["@typescript-eslint"]},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.4","mocha":"^10.7.3","tslib":"^2.5.0","eslint":"^9.12.0","rimraf":"^5.0.10","rollup":"^2.79.2","prettier":"^2.8.7","typescript":"^5.0.4","@types/node":"^18.15.11","lint-staged":"^13.2.1","@types/mocha":"^10.0.9","concurrently":"^8.0.1","@open-wc/testing":"^4.0.0","@web/test-runner":"^0.19.0","@open-wc/eslint-config":"^12.0.3","@rollup/plugin-replace":"^6.0.1","@web/dev-server-rollup":"^0.6.4","eslint-config-prettier":"^9.1.0","@rollup/plugin-commonjs":"^28.0.0","@web/dev-server-esbuild":"^1.0.2","@rollup/plugin-typescript":"^12.1.0","@typescript-eslint/parser":"^8.9.0","@rollup/plugin-node-resolve":"^15.3.0","@web/dev-server-import-maps":"^0.2.1","@typescript-eslint/eslint-plugin":"^8.9.0","@custom-elements-manifest/analyzer":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.0.21_1774541646237_0.43490734649750484","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@buttress/crag","version":"0.1.0","author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","_id":"@buttress/crag@0.1.0","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"},{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"}],"homepage":"https://github.com/datapeopleconnected/crag#readme","bugs":{"url":"https://github.com/datapeopleconnected/crag/issues"},"dist":{"shasum":"eb6534cdab29c7371d45af8880906d0dc59e7971","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.1.0.tgz","fileCount":80,"integrity":"sha512-BXxPTa7nTUHGM9j9PYfxEmnvWB+mKGLtsrHUTxoWiy/MDKDPVrj6GElhuDsgGVEpChG/1AbERu5F9UE0IiX2Pg==","signatures":[{"sig":"MEYCIQDPA86mtNAVyBbuzzo9G4xq/v1uPEUvdZ1S3v1vacjydQIhAKiydjO5kS44x+eon00KrexhAozolk6u9oCkvPK8DdHr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIBVuisUXLaMzt/bdFONVcP4neMeB9sXjHnaWzTiYCmlMAiEAsC9dNT+2FveF2MK+CjcUhspumr96Y3Muco9pHMgt6A0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@buttress%2fcrag@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":577352},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json","./custom-elements.json":"./custom-elements.json","./components/buttress-db-service.js":{"types":"./dist/components/buttress-db-service.d.ts","default":"./dist/components/buttress-db-service.js"}},"gitHead":"416c05e7ff4d9428ef2818eff67cdc4a29172a64","scripts":{"lint":"eslint .","roll":"rimraf .test-bundle && rollup -c rollup.config.mjs","test":"npm run build && npm run roll && npm run test:e2e","build":"rimraf dist tsconfig.tsbuildinfo && tsc --build --pretty && npm run analyze","check":"npm run lint && npm run format && npm run typecheck && npm run test:coverage","clean":"rimraf dist .test-bundle tsconfig.tsbuildinfo custom-elements.json","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"prettier \"**/*.ts\" --check --ignore-path .gitignore","analyze":"cem analyze --litelement","prepack":"npm run build","prepare":"husky","publint":"publint && attw --pack . --profile esm-only","lint:fix":"eslint . --fix","test:e2e":"node ./scripts/e2e.js wtr --node-resolve","test:unit":"wtr --config web-test-runner.unit.config.mjs","typecheck":"tsc --noEmit -p . && tsc -p test","format:fix":"prettier \"**/*.ts\" --write --ignore-path .gitignore","test:watch":"npm run test:unit -- --watch","licence-check":"./.husky/licence-check","test:coverage":"npm run test:unit -- --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8269131d-a3ee-4cbf-aa12-439c2f3da8a9"}},"devEngines":{"runtime":{"name":"node","onFail":"error","version":">=24"}},"repository":{"url":"git+https://github.com/datapeopleconnected/crag.git","type":"git"},"_npmVersion":"11.19.0","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"sideEffects":["./dist/components/*.js"],"_nodeVersion":"24.21.0","dependencies":{"lit":"^3.3.3","uuid":"^14.0.2","@lit/context":"^1.1.6","socket.io-client":"^4.8.3"},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.7","eslint":"^10.11.0","rimraf":"^6.1.3","rollup":"^4.63.5","esbuild":"^0.28.2","globals":"^17.12.0","publint":"^0.3.24","prettier":"^3.9.9","@eslint/js":"^10.0.1","typescript":"~6.0.3","lint-staged":"^17.5.1","@types/mocha":"^10.0.10","concurrently":"^10.0.5","@web/dev-server":"^1.0.0","@open-wc/testing":"^5.0.0","@web/test-runner":"^1.0.0","eslint-plugin-wc":"^3.1.0","eslint-plugin-lit":"^2.3.1","typescript-eslint":"^8.70.1","@arethetypeswrong/cli":"^0.18.5","@open-wc/eslint-config":"^13.1.0","@rollup/plugin-replace":"^6.0.3","@web/dev-server-rollup":"^1.0.0","eslint-config-prettier":"^10.1.8","@web/dev-server-esbuild":"^2.0.0","@rollup/plugin-node-resolve":"^16.0.3","@web/dev-server-import-maps":"^0.3.0","@custom-elements-manifest/analyzer":"^0.11.0"},"_npmOperationalInternal":{"tmp":"tmp/crag_0.1.0_1790267422069_0.22629211484719747","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"_id":"@buttress/crag@0.1.1","bugs":{"url":"https://github.com/datapeopleconnected/crag/issues"},"dist":{"shasum":"4a7018280e484ad85b59a7c377f3244303a4055b","tarball":"https://registry.npmjs.org/@buttress/crag/-/crag-0.1.1.tgz","fileCount":80,"integrity":"sha512-L7zWg9hHqWWFGlgDFnFcNNbE9+zAM0ZfEoQUirhW11F3fMK2Rd1Z9MjXFUhG/v1rLfTy+puS/CP0jAav0p26cQ==","signatures":[{"sig":"MEQCIH1gd9qsn93T5DpWtesE5JoZhSN3SzzmC7u4ultzy7qcAiBA+xz+X17YUaGxEeJ7N3NVsEaJm4tC9QLtADLL1yRLow==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCtYHyt1d0wgldJSxhyPrP7+9ZRf9lp5T/9UIIZJdBtGQIhAI2UEyIcbIw5VWdbTw7L64AHB7yNBaalUzyTw9+bT9S5"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@buttress%2fcrag@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":637792},"main":"./dist/index.js","name":"@buttress/crag","type":"module","types":"./dist/index.d.ts","author":{"name":"Data People Connected LTD"},"module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json","./custom-elements.json":"./custom-elements.json","./components/buttress-db-service.js":{"types":"./dist/components/buttress-db-service.d.ts","default":"./dist/components/buttress-db-service.js"}},"gitHead":"8343f80434ff84238453c976ab37c553296f1130","license":"AGPL-3.0-or-later","scripts":{"lint":"eslint .","roll":"rimraf .test-bundle && rollup -c rollup.config.mjs","test":"npm run build && npm run roll && npm run test:e2e","build":"rimraf dist tsconfig.tsbuildinfo && tsc --build --pretty && npm run analyze","check":"npm run lint && npm run format && npm run typecheck && npm run test:coverage && npm run test:scripts","clean":"rimraf dist .test-bundle tsconfig.tsbuildinfo custom-elements.json","start":"tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"","watch":"tsc --watch --preserveWatchOutput","format":"prettier \"**/*.ts\" --check --ignore-path .gitignore","analyze":"cem analyze --litelement","prepack":"npm run build","prepare":"husky","publint":"publint && attw --pack . --profile esm-only","lint:fix":"eslint . --fix","test:e2e":"node ./scripts/e2e.js wtr --node-resolve","test:unit":"wtr --config web-test-runner.unit.config.mjs","typecheck":"tsc --noEmit -p . && tsc -p test","format:fix":"prettier \"**/*.ts\" --write --ignore-path .gitignore","test:watch":"npm run test:unit -- --watch","test:scripts":"node --test \"test/scripts/*.test.js\"","licence-check":"./.husky/licence-check","test:coverage":"npm run test:unit -- --coverage"},"version":"0.1.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"8269131d-a3ee-4cbf-aa12-439c2f3da8a9"}},"homepage":"https://github.com/datapeopleconnected/crag#readme","devEngines":{"runtime":{"name":"node","onFail":"error","version":">=24"}},"repository":{"url":"git+https://github.com/datapeopleconnected/crag.git","type":"git"},"_npmVersion":"11.19.0","description":"Webcomponent buttress-db-service following open-wc recommendations","directories":{},"lint-staged":{"*.ts":["eslint --fix","prettier --write"]},"maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"},{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"}],"sideEffects":["./dist/components/*.js"],"_nodeVersion":"24.21.0","dependencies":{"lit":"^3.3.3","uuid":"^14.0.2","@lit/context":"^1.1.6","socket.io-client":"^4.8.3"},"_hasShrinkwrap":false,"customElements":"custom-elements.json","devDependencies":{"husky":"^9.1.7","eslint":"^10.11.0","rimraf":"^6.1.3","rollup":"^4.63.5","esbuild":"^0.28.2","globals":"^17.12.0","publint":"^0.3.24","prettier":"^3.9.9","@eslint/js":"^10.0.1","typescript":"~6.0.3","lint-staged":"^17.5.1","@types/mocha":"^10.0.10","concurrently":"^10.0.5","@web/dev-server":"^1.0.0","@open-wc/testing":"^5.0.0","@web/test-runner":"^1.0.0","eslint-plugin-wc":"^3.1.0","eslint-plugin-lit":"^2.3.1","typescript-eslint":"^8.70.1","@arethetypeswrong/cli":"^0.18.5","@open-wc/eslint-config":"^13.1.0","@rollup/plugin-replace":"^6.0.3","@web/dev-server-rollup":"^1.0.0","eslint-config-prettier":"^10.1.8","@web/dev-server-esbuild":"^2.0.0","@rollup/plugin-node-resolve":"^16.0.3","@web/dev-server-import-maps":"^0.3.0","@custom-elements-manifest/analyzer":"^0.11.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/crag_0.1.1_1790691828028_0.9202414834269135"}}},"time":{"created":"2024-08-15T09:46:38.639Z","modified":"2026-09-29T14:23:48.706Z","0.0.16":"2024-08-15T09:46:38.868Z","0.0.15":"2024-11-07T15:04:13.681Z","0.0.17":"2024-12-09T11:38:36.268Z","0.0.18":"2025-08-08T09:33:21.064Z","0.0.20":"2026-01-16T14:43:36.282Z","0.0.21":"2026-03-26T16:14:06.502Z","0.1.0":"2026-09-24T16:30:22.157Z","0.1.1":"2026-09-29T14:23:48.159Z"},"bugs":{"url":"https://github.com/datapeopleconnected/crag/issues"},"author":{"name":"Data People Connected LTD"},"license":"AGPL-3.0-or-later","homepage":"https://github.com/datapeopleconnected/crag#readme","repository":{"url":"git+https://github.com/datapeopleconnected/crag.git","type":"git"},"description":"Webcomponent buttress-db-service following open-wc recommendations","maintainers":[{"name":"tom.cahill","email":"me@tomcahill.co.uk"},{"name":"mahmoud.abousere3","email":"mahmoud@wearelighten.co.uk"}],"readme":"# Buttress Crag\n\n[![CI](https://img.shields.io/github/actions/workflow/status/datapeopleconnected/crag/ci.yml?branch=main&label=CI)](https://github.com/datapeopleconnected/crag/actions/workflows/ci.yml?query=branch%3Amain)\n[![npm](https://img.shields.io/npm/v/@buttress/crag)](https://www.npmjs.com/package/@buttress/crag)\n[![Licence](https://img.shields.io/npm/l/@buttress/crag)](LICENSE)\n\n`@buttress/crag` connects a browser app to a Buttress server. It provides one headless web component,\n`<buttress-db-service>`, built with [Lit](https://lit.dev). The component:\n\n- loads your app's schemas and keeps a local store of the entities you work with;\n- sends your changes to Buttress, batching them where it can;\n- applies changes made by other clients, which arrive over a realtime socket;\n- provides itself to the components inside it through [`@lit/context`](https://lit.dev/docs/data/context/).\n\nUpgrading from 0.0.x? Follow the [migration guide](docs/migrating-to-0.1.md).\n\n## Installation\n\n```bash\nnpm install @buttress/crag\n```\n\nThe package includes a [Custom Elements Manifest](https://custom-elements-manifest.open-wc.org/)\n(`custom-elements.json`), so editors and documentation tools can describe the element's attributes, events and slot.\n\ncrag depends on Lit 3 and `@lit/context`. If your app also uses Lit, use Lit 3 so the page loads a single copy of it.\n\ncrag needs a Buttress built from its `develop` branch at `3f044191` or later, published as\n`dpcltd/buttress:sha-3f04419`. It relies on Buttress answering a bulk update for each update in it, and on\n`auth.token` for the realtime socket. No tagged Buttress release has these yet.\n\ncrag runs in the browser, so installing it doesn't need a particular version of Node. Working on crag itself needs\nNode 24 or newer; see [Development](#development).\n\n## Quick start\n\n**1. Wrap the part of your app that uses the database in the element, and connect.**\n\n```html\n<buttress-db-service endpoint=\"https://buttress.example.com\" token=\"APP_TOKEN\" api-path=\"my-app\">\n  <organisation-list></organisation-list>\n</buttress-db-service>\n\n<script type=\"module\">\n  import '@buttress/crag/components/buttress-db-service.js';\n\n  await document.querySelector('buttress-db-service').connect();\n</script>\n```\n\nThe element shows its children through a slot and takes up no space in the layout (`display: contents`).\n\n**2. Use it from the components inside.**\n\n```ts\nimport { LitElement, html } from 'lit';\nimport { state } from 'lit/decorators.js';\nimport { consume } from '@lit/context';\nimport { buttressDbServiceContext, type ButtressDbService, type ButtressEntity } from '@buttress/crag';\n\nclass OrganisationList extends LitElement {\n  @consume({ context: buttressDbServiceContext })\n  db?: ButtressDbService;\n\n  @state()\n  private organisations: ButtressEntity[] = [];\n\n  async firstUpdated() {\n    if (!this.db) return;\n    await this.db.awaitConnection();\n    const { results } = await this.db.query('organisation', { status: { $eq: 'active' } });\n    this.organisations = results;\n  }\n\n  render() {\n    return html`<ul>${this.organisations.map((organisation) => html`<li>${organisation.name}</li>`)}</ul>`;\n  }\n}\ncustomElements.define('organisation-list', OrganisationList);\n```\n\n## How it works\n\n- `connect()` fetches your app's schemas and creates a data service for each one, then opens the realtime socket.\n  `awaitConnection()` resolves once the schemas have loaded. Calling `connect()` again replaces the socket.\n  Removing the element closes the socket, and adding it back (or moving it) opens it again.\n- Entities you query, fetch or create are kept in a local store, addressed by path: `organisation` (a `Map` of every\n  loaded organisation), `organisation.<id>`, `organisation.<id>.name`.\n- Writes change the store straight away, then queue a request to Buttress. Each schema sends its requests one at a\n  time, and additions and updates are combined into bulk requests of up to 100. Each write in one still succeeds or\n  fails on its own: Buttress answers a bulk update for each update in it, and a bulk add it refuses, because one of the\n  entities is invalid, is sent again one create at a time.\n- Changes made by other clients arrive over the realtime socket and are applied to the store. Each\n  `<buttress-db-service>` has its own session id, so crag ignores realtime messages about its own changes.\n- `subscribe()` calls you back when paths in the store change.\n\n## Getting the service in a component\n\n`<buttress-db-service>` answers context requests from any element inside it, including elements in shadow roots. If\nthere's more than one `<buttress-db-service>` above a component, the nearest one answers.\n\n- **Timing.** A component receives the service when it connects, provided `buttress-db-service` is already defined, so\n  import `@buttress/crag/components/buttress-db-service.js` before your own components. If you can't guarantee that,\n  attach a `ContextRoot` and consume with `subscribe: true`. The root holds on to early requests and answers them once\n  the element is defined, but only requests that subscribe:\n\n  ```ts\n  import { ContextRoot } from '@lit/context';\n\n  new ContextRoot().attach(document.body);\n  ```\n\n- **Moving components.** A component that can move from one `<buttress-db-service>` to another should use\n  `@consume({ context: buttressDbServiceContext, subscribe: true })`. Otherwise it keeps the first service it received.\n- **Keeping the element beside your content.** Provide it from a common ancestor instead; the\n  [migration guide](docs/migrating-to-0.1.md#option-b-provide-it-from-your-root-element) shows how.\n- **Without Lit.** Look the element up with `document.querySelector('buttress-db-service')`, or dispatch a\n  `ContextEvent` as the [migration guide](docs/migrating-to-0.1.md#3-replace-_getservice-with-consume) shows.\n- **TypeScript.** If you use `experimentalDecorators`, set `useDefineForClassFields: false`, as Lit requires for\n  decorated fields.\n\n## Attributes\n\n| Attribute     | Property     | Description                                                                                                                                                 |\n| ------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `endpoint`    | `endpoint`   | Base URL of the Buttress server. Required by `connect()`.                                                                                                   |\n| `token`       | `token`      | Token sent with every request and used to open the realtime socket. Required.                                                                               |\n| `api-path`    | `apiPath`    | Your app's API path on the server. Required.                                                                                                                |\n| `userid`      | `userId`     | Id of the signed-in user, returned by `getUserId()`. crag doesn't use it itself. |\n| `core-schema` | `coreSchema` | JSON array of Buttress core schemas to load as well as your app's own. Locally, core schema names are singular: `users` becomes `user`, and `activities` becomes `activity`. |\n| `request-timeout` | `requestTimeout` | How long a request to Buttress may take, in milliseconds, before it fails. Defaults to 60000 (a minute); `0` means no limit. A request that times out is rejected like one that fails on the network, and the requests queued behind it are then sent. |\n| `loglevel`    | `logLevel`   | `error`, `warn`, `info` (the default), `debug` or `sys`. Applies to the element, the store, the data services and the realtime connection.                  |\n| `log-label`   |              | Label for the element's own log lines. Defaults to the tag name.                                                                                           |\n| `log-disable` |              | Turns off the element's own log lines. Errors are still printed.                                                                                           |\n\nThe connection settings are copied when the element connects and whenever they change. When the element connects, an\nunset attribute leaves its setting alone, so a value set with `setEndpoint()` or the other setters survives the element\nbeing moved. A change made after `connect()` applies to later requests but doesn't reconnect the realtime socket. The\nlogging attributes are read when the element connects.\n\n## API\n\nThe data methods need the schemas, so call them after `awaitConnection()`. `subscribe()` and `unsubscribe()` work at\nany time.\n\n### Connection\n\n| Method                                | Description                                                                                                                |\n| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |\n| `connect(): Promise<void>`            | Loads the schemas, creates the data services, then opens the realtime socket. Rejects if `endpoint`, `token` or `api-path` is missing. |\n| `awaitConnection(): Promise<boolean>` | Resolves once `connect()` has loaded the schemas. Can be called before `connect()`. Rejects with the same error if that `connect()` fails; called after a failure, it waits for the next `connect()`. |\n| `isDbConnected(): boolean`            | Whether the schemas have loaded.                                                                                           |\n\n### Reading\n\n| Method                               | Description                                                                                             |\n| ------------------------------------ | ------------------------------------------------------------------------------------------------------- |\n| `get(path)`                          | Reads from the local store. Returns `undefined` for anything that isn't loaded.                        |\n| `query(schema, query, opts?)`        | Loads matching entities into the store and resolves to `{ total, results, skip, limit }`. See [Queries](#queries). |\n| `getById(schema, id)`                | Resolves to the entity, from the store if it's loaded and from Buttress if not, or to `undefined` if it was deleted while being fetched. |\n| `count(schema, query, actualCount?)` | Resolves to the number of matching entities, as counted by Buttress.                                    |\n| `getSchema(name)`                    | The schema definition, or `false` if there's no such schema.                                            |\n\n### Writing\n\n| Method                                        | Description                                                                                                                                         |\n| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `createObject(path)`                          | A new entity filled in with the schema's defaults and a new `id`. Pass a nested path such as `organisation.address` for a sub-object, without an `id`. Nothing is stored. A date whose default is `'now'` gets the current time; one crag can't read, such as `'today'`, is left out for Buttress to fill in. |\n| `create(schema, entity, opts?)`               | Adds the entity to the store and to Buttress, generating an `id` if it has none. Returns its path, e.g. `organisation.6709476b082b32233234259c`. |\n| `set(path, value, opts?)`                     | Sets a value in the store and on Buttress. Returns the path. Setting a whole entity, `set('organisation.<id>', entity)`, adds it if it isn't in the store. Otherwise crag sends each value that differs from what Buttress has, by its full path (`address.city`). Arrays are sent whole, and a property the new value leaves out, or sets to `undefined`, is set to `null`. The entity's `id` must match the path's, and is filled in if it's missing. |\n| `push(path, ...items)`                        | Appends to an array property, creating the array if the schema says the property is one. Returns the new length.                                   |\n| `splice(path, start, deleteCount?, ...items)` | Splices an array property. Returns the removed items.                                                                                               |\n| `pushWith(path, opts, ...items)`              | `push` with options. The options come before the items, since an item can be an object too.                                                        |\n| `spliceWith(path, start, deleteCount, opts, ...items)` | `splice` with options, before the items as for `pushWith`.                                                                                 |\n| `delete(path, opts?)`                         | Deletes an entity: `delete('organisation.<id>')`. Returns whether it was in the store.                                                             |\n| `nextIdle(schema)`                            | Resolves once that schema has no requests queued or waiting for a response from Buttress. It also waits for requests queued in the meantime. |\n\nBefore you write:\n\n- `get()`, `query()` results and `getById()` give you the store's own objects, not copies. You can change one in\n  place and then `set()` it, or a copy of it: crag compares it with what Buttress has, so it sends the changes and tells\n  subscribers. A change made in place and never `set()` isn't sent, and subscribers don't hear of it.\n- `set`, `push` and `splice` only work inside entities that are already in the store: queried, fetched or created.\n  Inside one, `set` creates any object missing on the way to its path, as Buttress does, and throws if one on the\n  way is `null` or isn't an object.\n- Buttress can append to an array and remove from it, but not insert into the middle. So `push`, and a `splice` that\n  only adds at the end, send each added item; a `splice` that only removes sends each removal; and any other `splice`\n  sends the whole new array, which overwrites any change someone else makes to that array at the same time.\n- Objects added to an array are given an `id` if they don't have one.\n- `create` and `delete` work on whole entities: `create` takes a schema name and `delete` takes `<schema>.<id>`.\n\n`create`, `set`, `delete`, `pushWith` and `spliceWith` take these options:\n\n| Option                             | Effect                                                                                                    |\n| ---------------------------------- | --------------------------------------------------------------------------------------------------------- |\n| `wait`                             | Returns a promise of the usual result, which resolves once Buttress has accepted the write and rejects with a [`ButtressError`](#errors) if it doesn't. The store still changes straight away. |\n| `localOnly`                        | Changes the store without sending anything to Buttress.                                                   |\n| `silent`                           | Doesn't notify subscribers, and sends nothing to Buttress.                                                |\n| `forceChanged`                     | Notifies subscribers even if the value hasn't changed, and sends nothing to Buttress.                     |\n| `dboComplete: { resolve, reject }` | Called when the request to Buttress finishes, or straight away if nothing is sent.                        |\n\nTo wait until a change has reached Buttress:\n\n```ts\nawait db.set(`${path}.name`, 'New name', { wait: true });\nconst length = await db.pushWith(`${path}.tags`, { wait: true }, 'new-tag');\n```\n\nA write that isn't sent resolves straight away. An invalid call, such as creating an entity whose `id` is already in\nthe store, still throws rather than returning a promise.\n\n### Subscribing\n\n| Method                        | Description                                                                          |\n| ----------------------------- | ------------------------------------------------------------------------------------ |\n| `subscribe(paths, callback)`  | Calls `callback` when any of the comma-separated `paths` change. Returns an id.      |\n| `unsubscribe(id)`             | Removes the subscription. Returns whether it existed.                                |\n\nA path can be:\n\n- `organisation.*`: anything in the `organisation` schema;\n- `organisation.<id>.name`: that property, including when the whole entity is replaced;\n- `organisation`: the collection itself, when it's replaced.\n\nThe callback receives one change record for each path you subscribed to, in the same order. For a path ending in `.*`\nthe record is `{ path, value, base, opts }`, where `path` is what changed and `base` is the schema's `Map`. For other\npaths it's `{ value, opts }`. An array change arrives as `<path>.splices`, with the changes in `value.indexSplices`.\nDeleting an entity arrives as `<schema>.<id>.splices`, with the entity in `value.indexSplices[0].removed`.\n\n```ts\nconst id = db.subscribe('organisation.*', ({ path, value }) => {\n  console.log(`${path} is now`, value);\n});\n\ndb.unsubscribe(id);\n```\n\nCallbacks run in a microtask, after the store has changed. Entities loaded by `query()` and `getById()` are added to\nthe store without notifying subscribers, so use the values those methods return. A write made while a query or\n`getById()` is waiting for Buttress keeps its value: the response, which is older, doesn't overwrite it.\n\n### Settings\n\n`getEndpoint()`, `setEndpoint()`, `getToken()`, `setToken()`, `setApiPath()`, `getUserId()`, `setUserId()`,\n`getCoreSchemas()` and `setCoreSchemas()` read and change the connection settings. As with the attributes, a change\napplies to later requests and doesn't reconnect.\n\n### App administration\n\nThese call Buttress's app-management endpoints for the app identified by `apiPath`, so the token needs permission to\nuse them. Each one rejects with a [`ButtressError`](#errors) if Buttress responds with an error.\n\n| Method                                                     | Resolves to              |\n| ---------------------------------------------------------- | ------------------------ |\n| `addSchema(apiPath, schema)`                               | `true`                   |\n| `updateAppPolicySelectors(apiPath, policySelectors)`       | `true`                   |\n| `addLambda(lambda, auth, apiPath)`                         | `true`                   |\n| `deployLambda(lambda, apiPath)`                            | `true`. Deploys `lambda.git.branch` at `lambda.git.hash`. |\n| `addDataSharing(appDataSharing, apiPath)`                  | The remote app's token   |\n| `activateDataSharing(dataSharingId, apiPath, remoteToken)` | `true`                   |\n\n### Errors\n\nWhen Buttress responds with an error status, crag rejects with a `ButtressError`. That covers `connect()`,\n`awaitConnection()`, queries, `getById()`, `count()` and the app administration methods. Writes report it through\n`dboComplete.reject`. A request that gets no response at all, for example because the network is down, rejects with\nthe browser's own error instead. So does one that takes longer than `request-timeout`: fetch rejects it with a\n`DOMException` named `TimeoutError`.\n\n```ts\nimport { ButtressError } from '@buttress/crag';\n\ntry {\n  await db.addSchema('my-app', schema);\n} catch (err) {\n  if (err instanceof ButtressError && err.status === 403) {\n    // The token can't change this app's schema.\n  }\n}\n```\n\n| Property        | Description                                                                  |\n| --------------- | ---------------------------------------------------------------------------- |\n| `status`        | The HTTP status Buttress responded with.                                     |\n| `method`, `url` | The request, without its query string.                                       |\n| `serverMessage` | The message Buttress sent, or the status text if it didn't send one.         |\n\n## Queries\n\nA query maps paths to operators. Paths use dots and reach into nested objects and arrays. Combine conditions with\n`$and` and `$or`:\n\n```ts\nconst active = await db.query('organisation', { status: { $eq: 'active' } }, { sort: { path: 'name', direction: 'ASC' } });\n\nconst activeOrLarge = await db.query(\n  'organisation',\n  { $or: [{ status: { $eq: 'active' } }, { number: { $gte: 50 } }] },\n  { limit: 20 },\n);\n```\n\n`query()` sends the query to Buttress and merges what comes back into the store, and asks Buttress for the total. How\nit then chooses `results` depends on whether you ask for a page, with `limit` or `skip`.\n\n**Without `limit` or `skip`,** crag runs the query against everything in the store. So:\n\n- `results` includes matching entities that are only in the store, such as ones you've just created, and leaves out\n  ones you've changed so they no longer match;\n- `$or` returns its matches grouped by the condition they met, so it doesn't keep the `sort` order. If you need both,\n  sort the results yourself.\n\n**With `limit` or `skip`,** `results` is the page Buttress returned, in Buttress's order, whatever else is in the store.\nWhen the page is served from the cache, crag leaves out entities that have since been deleted or changed so they no\nlonger match, so a page can come back shorter than `limit`. Once an entity has been created in that schema, by you or\nby another client, the next call for a page searches again, because only Buttress knows which page the new entity\nbelongs on. A search sent in the same tick as a `create()` can reach Buttress before the new entity does. Entities\nButtress has that crag hasn't heard about appear once you pass `bust: true`.\n\nEither way:\n\n- `total` is Buttress's count, so it can differ from `results.length`;\n- running the same query again, with the same `limit`, `skip`, `sort` and `project`, doesn't fetch the entities again\n  unless you pass `bust: true`. The total is always requested.\n\n| Option        | Description                                                                        |\n| ------------- | ---------------------------------------------------------------------------------- |\n| `limit`       | Maximum number of results, applied by Buttress.                                    |\n| `skip`        | Number of results to skip, applied by Buttress.                                    |\n| `sort`        | `{ path, direction: 'ASC' \\| 'DESC', type? }`, where `type` is `STRING` (the default), `NUMBER` or `DATE`. |\n| `project`     | Projection, applied by Buttress.                                                   |\n| `bust`        | Fetches even if this exact query has already run.                                 |\n| `actualCount` | Passed to Buttress with the count request.                                        |\n\nButtress evaluates the query when fetching; crag evaluates it again locally to choose `results`. Locally, a path that\npasses through arrays can give several values, and an entity matches if any of them passes:\n\n| Operator                                     | Matches when a value at the path…                                           |\n| -------------------------------------------- | --------------------------------------------------------------------------- |\n| `$eq`                                        | equals the operand                                                          |\n| `$not`                                       | differs from the operand                                                    |\n| `$gt`, `$gte`, `$lt`, `$lte`                 | is greater than, at least, less than, or at most the operand                |\n| `$in`                                        | is in the operand array                                                     |\n| `$nin`                                       | is not in the operand array. Every value must pass this one.               |\n| `$rex`, `$rexi`                              | matches the regular expression. `$rexi` ignores case.                      |\n| `$gtDate`, `$gteDate`, `$ltDate`, `$lteDate` | is after, on or after, before, or on or before the operand date. Dates are compared as times, whether they're `Date`s or the ISO strings they arrive from Buttress as. A missing or `null` date never matches, and nor does anything for a `null` operand. |\n| `$exists`                                    | is present, even if `null`, when the operand is `true`; is missing when it's `false`. |\n| `$elMatch`                                   | is an array with an element that matches the sub-query                     |\n| `$inProp`                                    | contains the operand. Top-level properties only.                           |\n\nAn unknown operator logs an error and matches nothing.\n\n## Events\n\nBoth events bubble and cross shadow roots.\n\n| Event                    | `detail`             | Fired                                                                                                                                  |\n| ------------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `bjs-connection-changed` | `boolean`            | With `true` when `connect()` opens the realtime socket, then whenever the socket connects (`true`) or disconnects (`false`), and with `false` when it can't connect. socket.io keeps trying after a network failure. It stops if Buttress refuses the connection, for a token it doesn't know or one for another app's api path, or closes it, as it does when the token is deleted. crag then logs it and closes the socket. |\n| `dataservice:loadById`   | `{ schemaName, id }` | When a realtime update arrives for an entity that isn't in the store. crag fetches the entity itself; the event is for information. |\n| `bjs-resync`             | none                 | When the realtime socket connects again after losing its connection, or after the element was moved in the DOM, and when it first connects after anything was queried, even a query still waiting for its answer. Updates sent in the meantime are lost, so crag has cleared its cached queries: query again to reload what you're showing. |\n\n```ts\ndb.addEventListener('bjs-connection-changed', (e) => {\n  const connected = (e as CustomEvent<boolean>).detail;\n});\n```\n\n## TypeScript\n\n`@buttress/crag` exports:\n\n| Export                     | What it is                                                                        |\n| -------------------------- | --------------------------------------------------------------------------------- |\n| `ButtressDbService`        | The element's class.                                                              |\n| `buttressDbServiceContext` | The context to consume the service with.                                          |\n| `LogLevel`                 | The log level enum: `ERROR`, `WARN`, `INFO`, `DEBUG`, `SYS`.                      |\n| `ButtressEntity`           | An entity: `{ id: string; [key: string]: any }`.                                  |\n| `QueryResult`              | What `query()` resolves to.                                                       |\n| `CR`, `CRCallback`         | A change record, and the type of a subscriber callback.                           |\n| `Settings`                 | The connection settings.                                                          |\n\nImporting `@buttress/crag` doesn't register the element; import `@buttress/crag/components/buttress-db-service.js` for\nthat.\n\n## Development\n\nYou'll need Node 24 or newer. If you use nvm, `nvm use` switches to the version in `.nvmrc`.\n\n```bash\nnpm install\nnpm run build\nnpm run test:unit\n```\n\n| Path                    | Contents                                                        |\n| ----------------------- | --------------------------------------------------------------- |\n| `src/`                  | Source. `src/components/` registers the element.                |\n| `test/unit/`            | Unit tests, run from source in Chrome.                          |\n| `test/e2e/`             | End-to-end tests, run in Chrome against Buttress in Docker.     |\n| `scripts/`              | Starts and seeds Buttress for the end-to-end tests.             |\n| `.docker/`              | The Buttress stack the end-to-end tests run against.            |\n| `demo/`                 | The page `npm start` serves: connects to a Buttress and lists one schema's entities. |\n| `docs/`                 | Guides, such as the [0.1 migration guide](docs/migrating-to-0.1.md). |\n| `dist/`                 | Build output. It's published along with `src/` and the licence. |\n| `custom-elements.json`  | The Custom Elements Manifest, generated by `npm run build` and published. |\n\n| Script                        | What it does                                                                   |\n| ----------------------------- | ------------------------------------------------------------------------------ |\n| `npm start`                   | Builds, watches, and serves `demo/`.                                           |\n| `npm run build`               | Compiles `src/` to `dist/`, then runs `analyze`.                               |\n| `npm run analyze`             | Writes `custom-elements.json` from `src/`. See `custom-elements-manifest.config.mjs`. |\n| `npm run test:unit`           | Runs the unit tests in Chrome. No build or server needed.                      |\n| `npm run test:watch`          | Runs the unit tests again whenever a file changes.                             |\n| `npm run test:scripts`        | Tests the scripts in `scripts/` in Node, with a fake `docker`. No Docker needed. |\n| `npm test`                    | Builds, bundles and runs the end-to-end tests. Needs Docker.                   |\n| `npm run lint`                | Runs ESLint, then Stylelint on the CSS in `src/`. `lint:fix` fixes what it can. |\n| `npm run format`              | Checks formatting with Prettier. `format:fix` applies it.                      |\n| `npm run typecheck`           | Type-checks `src/` and `test/`.                                                |\n| `npm run check`               | Runs `lint`, `format`, `typecheck`, `test:coverage` and `test:scripts`.        |\n| `npm run publint`             | Checks the packed package with publint and Are the Types Wrong.                |\n\n### End-to-end tests\n\nThe end-to-end tests need [Docker](https://docs.docker.com/get-docker/) with Compose v2, and nothing else:\n\n```bash\nnpm test\n```\n\nAfter building, `scripts/e2e.js` starts Buttress, MongoDB and Redis in containers, and seeds Buttress with a test app,\npolicies, users and organisations (`scripts/e2e-seed.js`). It then runs the tests in Chrome and removes the containers.\nEvery run starts from an empty database. If a run fails, the end of the Buttress log is printed first. Each run has\ncontainers of its own, so runs side by side, from two worktrees say, don't get in each other's way, and a run removes\nthe containers of any earlier run that was killed before it could.\n\nThe first run downloads the images. The Buttress image is pinned in `.docker/docker-compose.e2e.yml` to a `develop`\nbuild, so a run doesn't change as Buttress moves on; move it on deliberately. To test against a different image, such\nas one built from a Buttress checkout, set `BUTTRESS_IMAGE`:\n\n```bash\ndocker build -t buttress:local path/to/buttress-js\nBUTTRESS_IMAGE=buttress:local npm test\n```\n\n`scripts/e2e.js` runs whatever command it's given, with the endpoint and tokens in `BUTTRESS_E2E_*` environment\nvariables. To look around a seeded Buttress, open a shell with `node scripts/e2e.js bash`. The containers are removed\nwhen you exit it.\n\n### Commits and publishing\n\nThe pre-commit hook runs lint-staged, which fixes the staged `.ts` files with ESLint and Prettier, then\n`npm run build` and `npm run licence-check`. Every file in `src/`, `test/` and `scripts/`,\napart from HTML and JSON, must start with the header in `.husky/licencing_header.txt`.\n\n`npm pack` and `npm publish` build the package first. Run `npm run publint` beforehand to check its exports and types.\n\n## Security\n\nPlease report vulnerabilities privately, as [SECURITY.md](SECURITY.md) explains, not in a public issue.\n\n## Licence\n\nButtress Crag is free software, released under the [GNU Affero General Public Licence v3.0 or later](LICENSE).\nCopyright © 2016–2024 Data People Connected LTD.\n","readmeFilename":"README.md"}