gina 0.6.7 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +27 -26
- package/ROADMAP.md +3 -2
- package/framework/v0.6.8/VERSION +1 -0
- package/framework/{v0.6.7 → v0.6.8}/core/config.js +10 -0
- package/framework/v0.6.8/core/connectors/couchbase/lib/storage-store.js +1074 -0
- package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.js +532 -22
- package/framework/v0.6.8/core/controller/controller.render-stream.js +270 -0
- package/framework/{v0.6.7 → v0.6.8}/core/gna.js +10 -2
- package/framework/{v0.6.7 → v0.6.8}/core/router.js +30 -10
- package/framework/{v0.6.7 → v0.6.8}/core/server.isaac.js +42 -7
- package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/gc.js +6 -0
- package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/stats.js +7 -0
- package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/verify.js +6 -0
- package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/local-cas.js +107 -9
- package/framework/v0.6.8/lib/storage/src/local-stream.js +1418 -0
- package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/local.js +177 -16
- package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/main.js +406 -25
- package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/meta-store.js +13 -2
- package/framework/v0.6.8/lib/storage/src/s3.js +759 -0
- package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/util.js +26 -1
- package/framework/{v0.6.7 → v0.6.8}/lib/storage-store.js +33 -23
- package/framework/{v0.6.7 → v0.6.8}/package.json +1 -1
- package/gna.js +4 -4
- package/llms.txt +22 -47
- package/package.json +3 -3
- package/schema/routing.json +4 -0
- package/schema/settings.json +84 -10
- package/script/check_no_local_leak.js +2 -1
- package/script/retry_lockfile_sync.js +2 -1
- package/script/soak/storage-couchbase/README.md +61 -0
- package/script/soak/storage-couchbase/probe-cas.js +77 -0
- package/script/soak/storage-couchbase/probe-store.js +101 -0
- package/types/index.d.ts +18 -0
- package/framework/v0.6.7/VERSION +0 -1
- package/framework/v0.6.7/core/controller/controller.render-stream.js +0 -169
- /package/framework/{v0.6.7 → v0.6.8}/AUTHORS +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/LICENSE +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/html/nolayout.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/html/static.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/android-chrome-192x192.png +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/android-chrome-512x512.png +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/apple-touch-icon.png +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/favicon-16x16.png +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/favicon-32x32.png +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/favicon.ico +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.css +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/beemaster/index.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/css/gina.min.css +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/html/statusbar.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/have_heart_one-webfont.woff2 +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/index.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/inspector.css +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/inspector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/logo.svg +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.min.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.br +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.gz +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/ai/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/ai/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/connector.v3.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/connector.v4.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/n1ql.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/session-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/session-store.v3.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/session-store.v4.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/duckdb/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/duckdb/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/job-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/pipeline-loader.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/session-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mysql/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mysql/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/param-redact.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/postgresql/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/postgresql/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/job-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/render-cache-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/session-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/scylladb/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/scylladb/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/scylladb/lib/session-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sql-parser.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/lib/connector.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/lib/job-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/lib/session-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/content.encoding +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.framework.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-json.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-nunjucks-async.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-nunjucks.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-swig-async.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-swig.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-v1.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/inspector-window-emit.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/controller/release-banner.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/dev/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/dev/lib/class.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/dev/lib/factory.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/dev/lib/tools.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/currency.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/language/en.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/language/fr.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/region/en.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/region/fr.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/locales/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/mime.types +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/model/entity.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/model/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/model/template/entityFactory.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/model/template/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/csrf/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/csrf/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/csrf/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coep/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coep/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coep/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coop/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coop/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coop/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/corp/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/corp/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/corp/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/csp/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/csp/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/csp/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hide-powered-by/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hide-powered-by/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hide-powered-by/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hsts/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hsts/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hsts/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/origin-agent-cluster/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/origin-agent-cluster/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/origin-agent-cluster/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/referrer-policy/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/referrer-policy/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/referrer-policy/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-content-type-options/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-content-type-options/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-content-type-options/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-dns-prefetch-control/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-dns-prefetch-control/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-dns-prefetch-control/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-download-options/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-download-options/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-download-options/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-frame-options/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-frame-options/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-frame-options/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-xss-protection/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-xss-protection/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-xss-protection/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/session/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/session/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/session/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/build.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/build.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/src/form-validator.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/server.express.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/server.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/status.codes +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/_gitignore +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/app.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/connectors.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/routing.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/settings.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/settings.server.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/templates.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/watchers.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/controllers/controller.content.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/controllers/controller.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/controllers/setup.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/locales/en.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_namespace/controllers/controller.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/css/default.css +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/css/home.css +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/css/vendor/readme.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/favicon.ico +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/js/components/x-checklist.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/js/vendor/readme.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/manifest.webmanifest +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/readme.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/sw.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/handlers/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/content/homepage.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/includes/error-msg-noscript.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/includes/error-msg-outdated-browser.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/includes/x-checklist.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/layouts/main.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/command/gina.bat.tpl +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/command/gina.tpl +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/env.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/manifest.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/settings.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/statics.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/templates.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/error/client/json/401.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/error/client/json/403.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/error/client/json/404.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/error/server/html/50x.html +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/error/server/json/500.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/error/server/json/503.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/core/template/extensions/logger/config.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/console.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/context.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/data/LICENSE +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/data/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/data/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/data/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/dateFormat.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/json/LICENSE +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/json/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/json/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/json/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/path.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/src/api-error.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/prototypes.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/task.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/helpers/text.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/admin/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/admin/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/build.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/src/dep/jszip.min.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/async/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/async/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/audit/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/audit/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/audit-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/authn/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/authn/src/lockout.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/authn/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/authn/src/totp.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/authz-gate/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/authz-gate/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cache/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cache/build.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cache/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cache/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/aliases.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/audit/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/audit/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/audit/verify.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/build.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/copy.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/cp.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/inc/name-rewrite.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/man.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/mcp-start.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/mcp.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/oas.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/openapi.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/rename.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/restart.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/start.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/status.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/stop.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/types.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/clear.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/stats.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/infer.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/migrate.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/models.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/test.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/man.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/ps.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/stop.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/args.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/namespace.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/reference-rewrite.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/reference-scan.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/scaffold.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/rename.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/get.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/link-dev.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/set.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/unset.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/use.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/build.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/dot.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/get.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/init.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/link-node-modules.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/link.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/man.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/msg.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/open.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/reset.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/restart.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/set.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/start.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/status.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/stop.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/tail.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/update.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/version.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/gina-dev.1.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/gina-framework.1.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/gina.1.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/helper.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/export.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/import.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/scan.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/_host.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/build.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/man.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/run.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/inspector/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/inspector/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/inspector/open.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/man-render.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/kill.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/msg.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/inc/scan.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/reset.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/set.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/backup.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/build.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/import.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/man.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/move.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/rename.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/restart.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/restore.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/start.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/status.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/stop.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/set.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/link-local.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/link-production.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/remove.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/rm.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/use.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/check.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/scan.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/help.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/list.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/man.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/start.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/arguments.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/help.txt +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/view/add.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd-status-format/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cmd-status-format/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/collection/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/collection/build.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/collection/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/collection/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/config.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/connector-config/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/connector-config/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/connector-error/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/connector-error/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/connector-registry/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/connector-registry/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cron/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cron/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/cron/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/domain/LICENSE +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/domain/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/domain/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/domain/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/dto/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/dto/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/dto-pipe/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/dto-pipe/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/dto-types/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/dto-types/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/generator/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/i18n/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/i18n/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/image-build/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/image-build/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/LICENSE +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-events/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-events/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-redact/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-redact/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/instrument/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/instrument/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/job/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/job/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/job-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/json-config-header/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/json-config-header/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/loading-state/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/loading-state/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/default/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/file/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/file/lib/logrotator/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/file/lib/logrotator/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/mq/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/mq/listener.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/mq/speaker.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/helper.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/math/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-dispatch/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-dispatch/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-http/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-http/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-server/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-server/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/merge/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/merge/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/merge/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/metrics/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/metrics/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/model.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/net-locality/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/net-locality/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-filters/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-filters/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-filters/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-resolver/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-resolver/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/proc.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/release-watch/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/release-watch/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/render-cache/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/render-cache/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/render-cache-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing/build.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing/src/radix.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing-introspect/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/routing-introspect/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/backends/env.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/backends/file.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/env-file.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/session-store.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/shell.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/sqlite-driver.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/state.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/storage/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/swig-filters/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/swig-filters/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/swig-filters/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/swig-resolver/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/swig-resolver/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/src/loaders/http.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/src/loaders/memory.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/url/README.md +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/url/index.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/url/routing.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/uuid/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/uuid/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/validator.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/watcher/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/watcher/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/ws-framing/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/ws-framing/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/ws-query/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/ws-query/src/main.js +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/ws-session/package.json +0 -0
- /package/framework/{v0.6.7 → v0.6.8}/lib/ws-session/src/main.js +0 -0
|
@@ -0,0 +1,1074 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the gina package.
|
|
3
|
+
* Copyright (c) 2009-2026 Rhinostone <contact@gina.io>
|
|
4
|
+
*
|
|
5
|
+
* For the full copyright and license information, please view the LICENSE
|
|
6
|
+
* file that was distributed with this source code.
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @module gina/core/connectors/couchbase/storage-store
|
|
12
|
+
*
|
|
13
|
+
* Couchbase-backed `StorageMetaStore` for the object-storage layer
|
|
14
|
+
* (`lib/storage`) — the FIRST connector store of the #STO1 family, and the
|
|
15
|
+
* supported answer to the embedded backend's documented
|
|
16
|
+
* single-process-per-driver-root boundary. Backed by the consumer project's
|
|
17
|
+
* own `couchbase` driver (SDK major 3 or 4), resolved at construction — the
|
|
18
|
+
* framework keeps zero hard dependency on it.
|
|
19
|
+
*
|
|
20
|
+
* What it buys over the embedded SQLite store: the metadata row lives on a
|
|
21
|
+
* cluster every pod reaches, so several bundles (or replicas) can share ONE
|
|
22
|
+
* driver root. That is not a performance nicety — SQLite's locking is a
|
|
23
|
+
* known-broken story on a shared network filesystem, so a shared root is
|
|
24
|
+
* exactly what the embedded store refuses to support.
|
|
25
|
+
*
|
|
26
|
+
* **Per-key atomicity comes from Couchbase's own CAS**, which the refcount
|
|
27
|
+
* verbs need (`acquireRef` is what makes two concurrent identical `put()`s
|
|
28
|
+
* yield one blob and two references instead of a lost update). Every
|
|
29
|
+
* read-modify-write verb is a `get` → `mutateIn(..., {cas})` loop: the
|
|
30
|
+
* sub-document mutation applies all-or-none and only if the document has not
|
|
31
|
+
* changed since the read, so a lost update is impossible and a concurrent
|
|
32
|
+
* writer costs a bounded retry. CAS-guarded writes execute against the ACTIVE
|
|
33
|
+
* vBucket only (replicas serve reads, never writes), so the loop stays correct
|
|
34
|
+
* across a failover — each retry re-reads whatever node is active now.
|
|
35
|
+
*
|
|
36
|
+
* **Multi-process sweep coordination is the store's atomic claim, by design.**
|
|
37
|
+
* `removeIfZero` is a CAS-guarded `remove`: when several processes sweep the
|
|
38
|
+
* same root concurrently, exactly one claim per blob succeeds and every other
|
|
39
|
+
* one reads `CasMismatchError` / `DocumentNotFoundError` and skips — so the
|
|
40
|
+
* `local-cas` sweep protocol (claim the row FIRST, unlink second) is safe
|
|
41
|
+
* without any election layer, and adding one would close no gap while making
|
|
42
|
+
* `storage:gc` report `collected: 0` on whichever replica did not hold the
|
|
43
|
+
* lease. The residual the seam documents is unchanged and is NOT a
|
|
44
|
+
* sweeper-vs-sweeper race: it is put-vs-sweep (a fresh publish racing the
|
|
45
|
+
* unlink), mitigated by the grace window, the put-side heal and
|
|
46
|
+
* `storage:verify`.
|
|
47
|
+
*
|
|
48
|
+
* **Durability, stated rather than implied.** By default mutations use the
|
|
49
|
+
* SDK's own default level (in-memory on the active node), which is the same
|
|
50
|
+
* honesty class as the embedded store's SQLite WAL at `synchronous=NORMAL` —
|
|
51
|
+
* a crash can lose the most recent acknowledged write. Couchbase failover can
|
|
52
|
+
* likewise lose durably-written data. Consequences here are asymmetric and
|
|
53
|
+
* worth knowing: a lost `acquireRef` undercounts (a blob may reach zero early
|
|
54
|
+
* — gated by the grace window, adjudicated by the claim, visible to
|
|
55
|
+
* `storage:verify`), while a lost `releaseRef` overcounts and that blob is
|
|
56
|
+
* never collected. Set `durability` on the connectors.json entry when the
|
|
57
|
+
* content justifies the latency.
|
|
58
|
+
*
|
|
59
|
+
* Data layout — ONE JSON document per metadata row:
|
|
60
|
+
*
|
|
61
|
+
* <prefix><driver>:<key> the row. `d` (driver name) and `k` (logical key)
|
|
62
|
+
* are the sargable discriminators every query
|
|
63
|
+
* filters on; the contract fields ride beside them
|
|
64
|
+
* (`originalName`, `contentType`, `size`,
|
|
65
|
+
* `createdAt`), with `data` present only for
|
|
66
|
+
* size-tiered inline objects and `refs`/`zeroAt`
|
|
67
|
+
* present only on refcounted (cas) rows.
|
|
68
|
+
*
|
|
69
|
+
* The payload rides as a **base64 STRING inside the JSON document**, not as a
|
|
70
|
+
* binary document body: Couchbase cannot parse, index or query a binary value
|
|
71
|
+
* (it is retrievable by key alone), which would make `listZeroRefs` / `stats`
|
|
72
|
+
* structurally impossible — and the refcount and the payload must sit under
|
|
73
|
+
* ONE CAS for the verbs above to be atomic. The cost is base64's +33%: with
|
|
74
|
+
* the shipped 64KB tiering threshold a row is ~85KB, far below Couchbase's
|
|
75
|
+
* 20MB document ceiling, but an `inlineThreshold` above ~14MB would overflow
|
|
76
|
+
* it and the SDK will refuse the write.
|
|
77
|
+
*
|
|
78
|
+
* Config keys follow the couchbase connector's OWN conventions (the §11 rule —
|
|
79
|
+
* mirrors `connector.v4.js` / the session store): `protocol` + `host` +
|
|
80
|
+
* `username` + `password` + `database` (the BUCKET name — the same field the
|
|
81
|
+
* model layer's entity scan reads), plus `scope` / `collection` (both
|
|
82
|
+
* `_default`), `prefix` and the optional `durability`.
|
|
83
|
+
*
|
|
84
|
+
* Constructed by the `lib/storage-store` dispatcher at boot; not meant to be
|
|
85
|
+
* instantiated from application code.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* // connectors.json
|
|
89
|
+
* // { "assetsMeta": { "connector": "couchbase", "protocol": "couchbase://",
|
|
90
|
+
* // "host": "db1.internal", "username": "gina",
|
|
91
|
+
* // "password": "${secret:CB_PASSWORD}",
|
|
92
|
+
* // "database": "gina_storage" } }
|
|
93
|
+
* // settings.json
|
|
94
|
+
* // { "storage": { "drivers": { "assets": {
|
|
95
|
+
* // "adapter": "local", "strategy": "cas",
|
|
96
|
+
* // "root": "/var/data/assets", "store": "assetsMeta" } } } }
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* No-op callback used when a caller omits one.
|
|
101
|
+
* @inner
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function noop() {}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Wrap a callback so it can fire at most once.
|
|
108
|
+
*
|
|
109
|
+
* Every verb here runs a promise chain nested inside another (the connection
|
|
110
|
+
* gate, then a CAS retry loop), so a callback that THROWS would reject the
|
|
111
|
+
* enclosing chain and reach a second, outer handler — delivering the caller's
|
|
112
|
+
* own exception back to them as a store error. The guard makes the first
|
|
113
|
+
* delivery the only one; the exception then surfaces as an unhandled
|
|
114
|
+
* rejection, where it belongs, instead of being laundered into `err`.
|
|
115
|
+
*
|
|
116
|
+
* @inner
|
|
117
|
+
* @param {function} fn - The callback to guard.
|
|
118
|
+
* @returns {function} The guarded callback.
|
|
119
|
+
*/
|
|
120
|
+
function once(fn) {
|
|
121
|
+
if (typeof fn !== 'function') { return noop; }
|
|
122
|
+
var called = false;
|
|
123
|
+
return function() {
|
|
124
|
+
if (called) { return; }
|
|
125
|
+
called = true;
|
|
126
|
+
return fn.apply(null, arguments);
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Secondary indexes this store needs, and the fields they cover. Created at
|
|
132
|
+
* construction when missing (see the bootstrap below); an operator whose
|
|
133
|
+
* cluster credentials cannot create indexes gets the exact DDL in a log line
|
|
134
|
+
* instead.
|
|
135
|
+
*
|
|
136
|
+
* `gina_storage_refs` serves `listZeroRefs` (the GC sweep's listing) and
|
|
137
|
+
* `gina_storage_keys` serves `listKeys` / `stats` (the `storage:verify` walk
|
|
138
|
+
* and the operator surface). Both lead with `d`, which every query filters on,
|
|
139
|
+
* so one collection can host several drivers without cross-scanning.
|
|
140
|
+
*
|
|
141
|
+
* @inner
|
|
142
|
+
* @constant
|
|
143
|
+
* @type {Array<{name: string, fields: string}>}
|
|
144
|
+
*/
|
|
145
|
+
var INDEXES = [
|
|
146
|
+
{ name: 'gina_storage_refs', fields: '(d, refs, zeroAt)' },
|
|
147
|
+
{ name: 'gina_storage_keys', fields: '(d, k)' }
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Ceiling on how many times a CAS read-modify-write verb re-reads and retries
|
|
152
|
+
* before giving up. A BOUND, not a config knob (the `SWEEP_BATCH` precedent):
|
|
153
|
+
* it caps the work one contended key can cost, and exhaustion is reported
|
|
154
|
+
* through the callback as an ordinary error, never silently swallowed.
|
|
155
|
+
*
|
|
156
|
+
* @inner
|
|
157
|
+
* @constant
|
|
158
|
+
* @type {number}
|
|
159
|
+
*/
|
|
160
|
+
var MAX_CAS_ATTEMPTS = 10;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Backoff schedule between CAS attempts, in ms — jittered at use so several
|
|
164
|
+
* contending processes do not re-collide in lockstep. The last value repeats
|
|
165
|
+
* once the schedule is exhausted.
|
|
166
|
+
*
|
|
167
|
+
* @inner
|
|
168
|
+
* @constant
|
|
169
|
+
* @type {number[]}
|
|
170
|
+
*/
|
|
171
|
+
var CAS_BACKOFF_MS = [0, 5, 10, 20, 40];
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Couchbase's hard ceiling on a document KEY, in bytes. The composed docId
|
|
175
|
+
* (`<prefix><driver>:<key>`) is checked against it per call: today's key
|
|
176
|
+
* grammars peak around half of it, so tripping this means a caller minted an
|
|
177
|
+
* unusually long key — and a truncating store would silently alias two
|
|
178
|
+
* objects onto one row.
|
|
179
|
+
*
|
|
180
|
+
* @inner
|
|
181
|
+
* @constant
|
|
182
|
+
* @type {number}
|
|
183
|
+
*/
|
|
184
|
+
var MAX_DOC_ID_BYTES = 250;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Driver names that may safely compose a document-id namespace. A `:` would
|
|
188
|
+
* let one driver's name alias another's namespace boundary, so the character
|
|
189
|
+
* class is deliberately narrower than the file-system key guard's.
|
|
190
|
+
*
|
|
191
|
+
* @inner
|
|
192
|
+
* @constant
|
|
193
|
+
* @type {RegExp}
|
|
194
|
+
*/
|
|
195
|
+
var DRIVER_NAME_RE = /^[A-Za-z0-9_.-]+$/;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Couchbase identifier grammar, for the three names that MUST be interpolated
|
|
199
|
+
* into statements rather than bound as parameters (a keyspace is an
|
|
200
|
+
* identifier, and N1QL has no placeholder for one). Anything outside this
|
|
201
|
+
* class is refused at construction — that refusal is what keeps the
|
|
202
|
+
* interpolation safe.
|
|
203
|
+
*
|
|
204
|
+
* @inner
|
|
205
|
+
* @constant
|
|
206
|
+
* @type {RegExp}
|
|
207
|
+
*/
|
|
208
|
+
var IDENTIFIER_RE = /^[A-Za-z0-9_%\-]+$/;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Accepted `durability` values → the SDK's `DurabilityLevel` member name.
|
|
212
|
+
* BOTH spellings are accepted for the middle level because the server
|
|
213
|
+
* documentation and the Node SDK disagree: the docs call it
|
|
214
|
+
* `majorityAndPersistToActive`, the SDK enum member is
|
|
215
|
+
* `MajorityAndPersistOnMaster`. Accepting only one of them would silently
|
|
216
|
+
* drop a correctly-configured durability level.
|
|
217
|
+
*
|
|
218
|
+
* @inner
|
|
219
|
+
* @constant
|
|
220
|
+
* @type {Object.<string, string>}
|
|
221
|
+
*/
|
|
222
|
+
var DURABILITY_LEVELS = {
|
|
223
|
+
none : 'None',
|
|
224
|
+
majority : 'Majority',
|
|
225
|
+
majorityandpersisttoactive : 'MajorityAndPersistOnMaster',
|
|
226
|
+
majorityandpersistonmaster : 'MajorityAndPersistOnMaster',
|
|
227
|
+
persisttomajority : 'PersistToMajority'
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Build a Couchbase-backed metadata store implementing the
|
|
232
|
+
* {@link StorageMetaStore} seam — the four base verbs (`set` / `get` /
|
|
233
|
+
* `remove` / `close`), the four refcount verbs a `cas` driver requires
|
|
234
|
+
* (`acquireRef` / `releaseRef` / `listZeroRefs` / `removeIfZero`) and both
|
|
235
|
+
* optional operator verbs (`stats` / `listKeys`).
|
|
236
|
+
*
|
|
237
|
+
* The SDK is promise-based, so callbacks fire asynchronously — which the seam
|
|
238
|
+
* explicitly allows (the embedded store's synchronous timing is an
|
|
239
|
+
* implementation detail of `node:sqlite`, never part of the contract).
|
|
240
|
+
*
|
|
241
|
+
* @param {object} connConf - Resolved `connectors.json` entry for this store.
|
|
242
|
+
* @param {string} [connConf.protocol] - Connection scheme (default `'couchbase://'`).
|
|
243
|
+
* @param {string} [connConf.host] - Host or comma-separated hosts (default 127.0.0.1).
|
|
244
|
+
* @param {string} [connConf.username] - Cluster username.
|
|
245
|
+
* @param {string} [connConf.password] - Cluster password.
|
|
246
|
+
* @param {string} connConf.database - Bucket NAME (required — the couchbase connector's
|
|
247
|
+
* own convention; the model layer reads the same field).
|
|
248
|
+
* @param {string} [connConf.scope] - Scope name (default `'_default'`).
|
|
249
|
+
* @param {string} [connConf.collection] - Collection name (default `'_default'`).
|
|
250
|
+
* @param {string} [connConf.prefix] - Document-id prefix (default `'stor:'`).
|
|
251
|
+
* @param {string} [connConf.durability] - Durability level applied to every mutation:
|
|
252
|
+
* `majority` | `majorityAndPersistToActive` |
|
|
253
|
+
* `persistToMajority` (or `none`). Omitted, the SDK
|
|
254
|
+
* default applies — see the module header.
|
|
255
|
+
* @param {string} bundle - Bundle name — used in log lines.
|
|
256
|
+
* @param {string} driverName - Storage driver this store backs. It NAMESPACES every
|
|
257
|
+
* document id and scopes every query, so several
|
|
258
|
+
* drivers may share one connectors.json entry without
|
|
259
|
+
* colliding keys, stamps or refcounts.
|
|
260
|
+
* @param {object} [injected] - Test-only dependency injection (the entity-layer
|
|
261
|
+
* `injected` precedent): `{ driver }` replaces the
|
|
262
|
+
* project-resolved couchbase module.
|
|
263
|
+
* @returns {StorageMetaStore} - A ready store (plus `close()`).
|
|
264
|
+
* @throws {Error} - When the couchbase driver is not installed, the SDK
|
|
265
|
+
* major is unsupported, `database` is missing, or a
|
|
266
|
+
* driver/keyspace name is unusable. `gna.js` treats a
|
|
267
|
+
* throw as a boot fatal — an explicitly configured
|
|
268
|
+
* store must build or refuse, never degrade to the
|
|
269
|
+
* embedded backend.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* // wired automatically at boot by gna.js via lib/storage-store
|
|
273
|
+
* var store = require('.../couchbase/lib/storage-store')(connConf, 'app', 'assets');
|
|
274
|
+
*/
|
|
275
|
+
module.exports = function CouchbaseStorageStore(connConf, bundle, driverName, injected) {
|
|
276
|
+
connConf = connConf || {};
|
|
277
|
+
|
|
278
|
+
if ( typeof driverName !== 'string' || !DRIVER_NAME_RE.test(driverName) ) {
|
|
279
|
+
throw new Error('[CouchbaseStorageStore] a storage driver name is required and must match '
|
|
280
|
+
+ DRIVER_NAME_RE + ' (got: ' + JSON.stringify(driverName) + ') — it namespaces every '
|
|
281
|
+
+ 'document id, so a `:` in it would alias another driver\'s namespace');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Resolve the couchbase driver from the project's node_modules (the
|
|
285
|
+
// connector's own precedent — core/connectors/couchbase/index.js:8-9 —
|
|
286
|
+
// and the mongodb job-store's). The injected branch is test-only; the
|
|
287
|
+
// default branch is the only place the injected `_` / `getPath` globals
|
|
288
|
+
// are read, so tests stay standalone.
|
|
289
|
+
var couchbase;
|
|
290
|
+
if (injected && injected.driver) {
|
|
291
|
+
couchbase = injected.driver;
|
|
292
|
+
} else {
|
|
293
|
+
try {
|
|
294
|
+
var driverPath = _(getPath('project') + '/node_modules/couchbase', true);
|
|
295
|
+
couchbase = require(driverPath);
|
|
296
|
+
} catch (e) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
'[CouchbaseStorageStore] couchbase is not installed. '
|
|
299
|
+
+ 'Run `npm install couchbase` in your project (SDK major 3 or 4).\n'
|
|
300
|
+
+ e.message
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if ( typeof couchbase.connect !== 'function' ) {
|
|
306
|
+
// #CN8 parity: SDK v2 exposed cluster.openBucket() and no connect().
|
|
307
|
+
throw new Error('[CouchbaseStorageStore] the installed couchbase driver exposes no `connect()` — '
|
|
308
|
+
+ 'supported SDK majors are 3 and 4. Upgrade the couchbase dependency in your project '
|
|
309
|
+
+ 'package.json: npm install couchbase@^4 (or ^3).');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
var bucketName = connConf.database;
|
|
313
|
+
if (!bucketName) {
|
|
314
|
+
throw new Error('[CouchbaseStorageStore] missing required `database` field (the bucket name) in the connectors.json entry');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
var scopeName = connConf.scope || '_default';
|
|
318
|
+
var collectionName = connConf.collection || '_default';
|
|
319
|
+
var prefix = ( typeof connConf.prefix === 'string' && connConf.prefix.length > 0 ) ? connConf.prefix : 'stor:';
|
|
320
|
+
|
|
321
|
+
// The keyspace is an IDENTIFIER, and N1QL has no placeholder for one, so
|
|
322
|
+
// these three names are interpolated into every statement. Validating them
|
|
323
|
+
// here — once, at construction — is what makes that interpolation safe;
|
|
324
|
+
// every VALUE below is a positional parameter.
|
|
325
|
+
[['database', bucketName], ['scope', scopeName], ['collection', collectionName]].forEach(function(pair) {
|
|
326
|
+
if ( typeof pair[1] !== 'string' || !IDENTIFIER_RE.test(pair[1]) ) {
|
|
327
|
+
throw new Error('[CouchbaseStorageStore] `' + pair[0] + '` must match ' + IDENTIFIER_RE
|
|
328
|
+
+ ' (got: ' + JSON.stringify(pair[1]) + ') — it is interpolated into N1QL statements as an identifier');
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Backtick-quoted keyspace, e.g. `` `gina_storage`.`_default`.`_default` ``.
|
|
334
|
+
* @inner
|
|
335
|
+
* @type {string}
|
|
336
|
+
*/
|
|
337
|
+
var keyspace = '`' + bucketName + '`.`' + scopeName + '`.`' + collectionName + '`';
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Resolved SDK durability option, merged into every mutation. `null` when
|
|
341
|
+
* unconfigured — the SDK's own default then applies.
|
|
342
|
+
*
|
|
343
|
+
* @inner
|
|
344
|
+
* @type {?object}
|
|
345
|
+
*/
|
|
346
|
+
var durabilityOpt = null;
|
|
347
|
+
if ( typeof connConf.durability === 'string' && connConf.durability.length > 0 ) {
|
|
348
|
+
var member = DURABILITY_LEVELS[connConf.durability.toLowerCase()];
|
|
349
|
+
if (!member) {
|
|
350
|
+
throw new Error('[CouchbaseStorageStore] unknown `durability` ' + JSON.stringify(connConf.durability)
|
|
351
|
+
+ ' — expected one of: majority, majorityAndPersistToActive, persistToMajority, none');
|
|
352
|
+
}
|
|
353
|
+
if ( !couchbase.DurabilityLevel || typeof couchbase.DurabilityLevel[member] === 'undefined' ) {
|
|
354
|
+
throw new Error('[CouchbaseStorageStore] `durability` ' + JSON.stringify(connConf.durability)
|
|
355
|
+
+ ' is not available on the installed couchbase SDK (no DurabilityLevel.' + member + ')');
|
|
356
|
+
}
|
|
357
|
+
durabilityOpt = { durabilityLevel: couchbase.DurabilityLevel[member] };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Merge the configured durability into a per-operation options bag.
|
|
362
|
+
*
|
|
363
|
+
* @inner
|
|
364
|
+
* @param {object} [opt] - Base options (e.g. `{cas}`).
|
|
365
|
+
* @returns {object} The options to hand the SDK.
|
|
366
|
+
*/
|
|
367
|
+
var withDurability = function(opt) {
|
|
368
|
+
opt = opt || {};
|
|
369
|
+
if (durabilityOpt) { opt.durabilityLevel = durabilityOpt.durabilityLevel; }
|
|
370
|
+
return opt;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Whether `err` is the named Couchbase error class. Checked by
|
|
375
|
+
* constructor identity first, then by name — a driver that surfaces the
|
|
376
|
+
* class only by name (or a test double) still classifies correctly, and
|
|
377
|
+
* misclassifying here would turn a resurrection signal into a hard
|
|
378
|
+
* failure.
|
|
379
|
+
*
|
|
380
|
+
* @inner
|
|
381
|
+
* @param {*} err - Any thrown/rejected value.
|
|
382
|
+
* @param {string} className - e.g. `'CasMismatchError'`.
|
|
383
|
+
* @returns {boolean}
|
|
384
|
+
*/
|
|
385
|
+
var isCbError = function(err, className) {
|
|
386
|
+
if (!err) { return false; }
|
|
387
|
+
var Ctor = couchbase[className];
|
|
388
|
+
if ( typeof Ctor === 'function' && err instanceof Ctor ) { return true; }
|
|
389
|
+
if ( err.name === className ) { return true; }
|
|
390
|
+
if ( err.constructor && err.constructor.name === className ) { return true; }
|
|
391
|
+
return false;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Compose the document id for a logical key, refusing one that would
|
|
396
|
+
* exceed Couchbase's key ceiling.
|
|
397
|
+
*
|
|
398
|
+
* @inner
|
|
399
|
+
* @param {string} key - The logical storage key.
|
|
400
|
+
* @returns {{id: ?string, error: ?Error}}
|
|
401
|
+
*/
|
|
402
|
+
var docIdFor = function(key) {
|
|
403
|
+
var id = prefix + driverName + ':' + key;
|
|
404
|
+
if ( Buffer.byteLength(id, 'utf8') > MAX_DOC_ID_BYTES ) {
|
|
405
|
+
return { id: null, error: new Error('[CouchbaseStorageStore] the composed document id for key `' + key
|
|
406
|
+
+ '` is ' + Buffer.byteLength(id, 'utf8') + ' bytes, over Couchbase\'s ' + MAX_DOC_ID_BYTES
|
|
407
|
+
+ '-byte key limit (prefix `' + prefix + '` + driver `' + driverName + '`)') };
|
|
408
|
+
}
|
|
409
|
+
return { id: id, error: null };
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Build the stored document for `key` + `meta`.
|
|
414
|
+
*
|
|
415
|
+
* Absent optional fields are OMITTED rather than written as `null`, which
|
|
416
|
+
* is what lets `get()` rebuild a row's exact contract shape: a
|
|
417
|
+
* non-refcounted row must come back without `refs`/`zeroAt`, and a
|
|
418
|
+
* file-backed row without `data`. A refcounted row always carries BOTH
|
|
419
|
+
* `refs` and `zeroAt` (the latter valued `null` while `refs > 0`) —
|
|
420
|
+
* mirroring the embedded store, whose NULL column reads back as `null`.
|
|
421
|
+
*
|
|
422
|
+
* @inner
|
|
423
|
+
* @param {string} key - Logical storage key.
|
|
424
|
+
* @param {StorageMeta} meta - Caller metadata (may carry `data`).
|
|
425
|
+
* @param {boolean} refcounted - Stamp `refs: 1` / `zeroAt: null` (the acquireRef create).
|
|
426
|
+
* @returns {object} The document to store.
|
|
427
|
+
*/
|
|
428
|
+
var buildDoc = function(key, meta, refcounted) {
|
|
429
|
+
meta = meta || {};
|
|
430
|
+
var doc = {
|
|
431
|
+
d : driverName,
|
|
432
|
+
k : key,
|
|
433
|
+
originalName : ( typeof meta.originalName === 'string' ) ? meta.originalName : null,
|
|
434
|
+
contentType : ( typeof meta.contentType === 'string' ) ? meta.contentType : null,
|
|
435
|
+
size : ( typeof meta.size === 'number' ) ? meta.size : null,
|
|
436
|
+
createdAt : ( typeof meta.createdAt === 'number' ) ? meta.createdAt : Date.now()
|
|
437
|
+
};
|
|
438
|
+
// Binary safety: a Buffer IS a Uint8Array and base64-encodes exactly;
|
|
439
|
+
// anything else (a string above all — utf8 coercion would corrupt a
|
|
440
|
+
// binary payload) is refused rather than encoded, matching the
|
|
441
|
+
// embedded store's refusal-to-NULL.
|
|
442
|
+
if ( meta.data instanceof Uint8Array ) {
|
|
443
|
+
doc.data = Buffer.isBuffer(meta.data)
|
|
444
|
+
? meta.data.toString('base64')
|
|
445
|
+
: Buffer.from(meta.data).toString('base64');
|
|
446
|
+
}
|
|
447
|
+
if (refcounted) {
|
|
448
|
+
doc.refs = 1;
|
|
449
|
+
doc.zeroAt = null;
|
|
450
|
+
}
|
|
451
|
+
return doc;
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Rebuild the contract-shaped metadata object from a stored document.
|
|
456
|
+
*
|
|
457
|
+
* @inner
|
|
458
|
+
* @param {object} doc - The stored document.
|
|
459
|
+
* @param {boolean} includePayload - Attach `data` when the row carries one.
|
|
460
|
+
* @returns {StorageMeta}
|
|
461
|
+
*/
|
|
462
|
+
var metaFromDoc = function(doc, includePayload) {
|
|
463
|
+
var meta = {
|
|
464
|
+
originalName : ( typeof doc.originalName === 'string' ) ? doc.originalName : null,
|
|
465
|
+
contentType : ( typeof doc.contentType === 'string' ) ? doc.contentType : null,
|
|
466
|
+
size : ( typeof doc.size === 'number' ) ? doc.size : null,
|
|
467
|
+
createdAt : ( typeof doc.createdAt === 'number' ) ? doc.createdAt : null
|
|
468
|
+
};
|
|
469
|
+
// Present only for inline objects, so a file-backed row keeps its
|
|
470
|
+
// exact pre-tiering shape. A zero-length payload is a real empty
|
|
471
|
+
// object, distinct from an absent one — base64 '' decodes to it.
|
|
472
|
+
if ( includePayload && typeof doc.data === 'string' ) {
|
|
473
|
+
meta.data = Buffer.from(doc.data, 'base64');
|
|
474
|
+
}
|
|
475
|
+
// Present only for refcounted rows — the same pattern as `data`.
|
|
476
|
+
if ( typeof doc.refs === 'number' ) {
|
|
477
|
+
meta.refs = doc.refs;
|
|
478
|
+
meta.zeroAt = ( typeof doc.zeroAt === 'number' ) ? doc.zeroAt : null;
|
|
479
|
+
}
|
|
480
|
+
return meta;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Pause between CAS attempts, jittered so contending processes do not
|
|
485
|
+
* re-collide in lockstep.
|
|
486
|
+
*
|
|
487
|
+
* @inner
|
|
488
|
+
* @param {number} attempt - Zero-based attempt index.
|
|
489
|
+
* @param {function} next - Called after the pause.
|
|
490
|
+
* @returns {void}
|
|
491
|
+
*/
|
|
492
|
+
var backoff = function(attempt, next) {
|
|
493
|
+
var base = CAS_BACKOFF_MS[Math.min(attempt, CAS_BACKOFF_MS.length - 1)];
|
|
494
|
+
if (!base) { return setImmediate(next); }
|
|
495
|
+
setTimeout(next, base + Math.floor(Math.random() * base));
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* The error a CAS loop reports once {@link MAX_CAS_ATTEMPTS} is spent.
|
|
500
|
+
* Carries a code so a caller can distinguish sustained contention from a
|
|
501
|
+
* cluster fault.
|
|
502
|
+
*
|
|
503
|
+
* @inner
|
|
504
|
+
* @param {string} verb - Verb name, for the message.
|
|
505
|
+
* @param {string} key - Logical key.
|
|
506
|
+
* @returns {Error}
|
|
507
|
+
*/
|
|
508
|
+
var contentionError = function(verb, key) {
|
|
509
|
+
var err = new Error('[CouchbaseStorageStore] ' + verb + '(`' + key + '`) gave up after '
|
|
510
|
+
+ MAX_CAS_ATTEMPTS + ' CAS attempts — the row is under sustained concurrent modification');
|
|
511
|
+
err.code = 'GINA_STORAGE_CAS_CONTENTION';
|
|
512
|
+
return err;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// ─── Connection ──────────────────────────────────────────────────────────
|
|
516
|
+
|
|
517
|
+
var connStr = ( connConf.protocol || 'couchbase://' ) + ( connConf.host || '127.0.0.1' );
|
|
518
|
+
var cluster = null;
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Resolves to the open collection once the cluster is up. Every verb
|
|
522
|
+
* chains on it, so operations issued before the connection completes are
|
|
523
|
+
* queued rather than lost — and a connection failure surfaces through
|
|
524
|
+
* each verb's own callback instead of as an unhandled rejection.
|
|
525
|
+
*
|
|
526
|
+
* @inner
|
|
527
|
+
* @type {Promise<object>}
|
|
528
|
+
*/
|
|
529
|
+
var ready = Promise.resolve()
|
|
530
|
+
.then(function() {
|
|
531
|
+
return couchbase.connect(connStr, {
|
|
532
|
+
username : connConf.username,
|
|
533
|
+
password : connConf.password
|
|
534
|
+
});
|
|
535
|
+
})
|
|
536
|
+
.then(function(c) {
|
|
537
|
+
cluster = c;
|
|
538
|
+
return c.bucket(bucketName).scope(scopeName).collection(collectionName);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
ready.catch(function(err) {
|
|
542
|
+
console.error('[CouchbaseStorageStore] connection to `' + connStr + '` failed (bundle: '
|
|
543
|
+
+ bundle + ', driver: ' + driverName + '): ' + ((err && err.message) || err));
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Run `fn` against the open collection, funnelling any connection failure
|
|
548
|
+
* into the caller's callback.
|
|
549
|
+
*
|
|
550
|
+
* @inner
|
|
551
|
+
* @param {function} fn - `fn(collection)`.
|
|
552
|
+
* @param {function} cb - The verb's callback; receives a connection error.
|
|
553
|
+
* @returns {void}
|
|
554
|
+
*/
|
|
555
|
+
var withCollection = function(fn, cb) {
|
|
556
|
+
ready.then(fn).catch(function(err) { cb(err); });
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Run a N1QL statement. `NotBounded` scan consistency is deliberate and
|
|
561
|
+
* safe here: index staleness can only make a listing MISS a row or offer
|
|
562
|
+
* a stale candidate, and every destructive step that follows re-adjudicates
|
|
563
|
+
* against the document itself through CAS — the listing is a trigger, the
|
|
564
|
+
* KV operation is the verdict.
|
|
565
|
+
*
|
|
566
|
+
* @inner
|
|
567
|
+
* @param {string} statement - The N1QL statement.
|
|
568
|
+
* @param {Array} params - Positional parameters (`$1..$N`).
|
|
569
|
+
* @param {function} cb - `cb(err, rows)`.
|
|
570
|
+
* @returns {void}
|
|
571
|
+
*/
|
|
572
|
+
var runQuery = function(statement, params, cb) {
|
|
573
|
+
ready.then(function() {
|
|
574
|
+
var opt = {
|
|
575
|
+
parameters : params,
|
|
576
|
+
// never re-plan a statement we compose ourselves
|
|
577
|
+
adhoc : false
|
|
578
|
+
};
|
|
579
|
+
if ( couchbase.QueryScanConsistency && typeof couchbase.QueryScanConsistency.NotBounded !== 'undefined' ) {
|
|
580
|
+
opt.scanConsistency = couchbase.QueryScanConsistency.NotBounded;
|
|
581
|
+
}
|
|
582
|
+
return cluster.query(statement, opt);
|
|
583
|
+
}).then(function(res) {
|
|
584
|
+
cb(null, ( res && res.rows ) || []);
|
|
585
|
+
}).catch(function(err) {
|
|
586
|
+
cb(err);
|
|
587
|
+
});
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Validate a caller-supplied row limit. It is INTERPOLATED into the
|
|
592
|
+
* statement rather than bound, which keeps each statement a stable string
|
|
593
|
+
* for the query plan cache — so it must be a plain positive integer, and
|
|
594
|
+
* anything else falls back to the caller's documented default rather than
|
|
595
|
+
* reaching the server.
|
|
596
|
+
*
|
|
597
|
+
* @inner
|
|
598
|
+
* @param {*} limit - Caller value.
|
|
599
|
+
* @param {number} dflt - Fallback.
|
|
600
|
+
* @returns {number}
|
|
601
|
+
*/
|
|
602
|
+
var safeLimit = function(limit, dflt) {
|
|
603
|
+
return ( Number.isSafeInteger(limit) && limit > 0 ) ? limit : dflt;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// ─── Index bootstrap ─────────────────────────────────────────────────────
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Ensure the two secondary indexes exist — eager and fire-and-forget (the
|
|
610
|
+
* mongodb job-store precedent): no operation waits on it.
|
|
611
|
+
*
|
|
612
|
+
* Existence is probed through `system:indexes` rather than relying on
|
|
613
|
+
* `CREATE INDEX IF NOT EXISTS`, whose availability depends on the server
|
|
614
|
+
* version this store has no way to require. The probe covers both
|
|
615
|
+
* keyspace shapes: on the default collection `keyspace_id` IS the bucket
|
|
616
|
+
* and `bucket_id` is missing; on a named collection the three ids are
|
|
617
|
+
* bucket / scope / collection.
|
|
618
|
+
*
|
|
619
|
+
* A failure here is NOT fatal — an operator whose credentials cannot
|
|
620
|
+
* create indexes is a normal deployment — but it is logged with the exact
|
|
621
|
+
* DDL to run by hand, because the consequence is not "slower queries": an
|
|
622
|
+
* unindexed query ERRORS, and the periodic sweep's bare call would
|
|
623
|
+
* swallow that error forever. Every query verb therefore logs its own
|
|
624
|
+
* failures too.
|
|
625
|
+
*
|
|
626
|
+
* @inner
|
|
627
|
+
* @returns {void}
|
|
628
|
+
*/
|
|
629
|
+
var ensureIndexes = function() {
|
|
630
|
+
var names = INDEXES.map(function(i) { return '"' + i.name + '"'; }).join(', ');
|
|
631
|
+
var probe = 'SELECT name FROM system:indexes'
|
|
632
|
+
+ ' WHERE name IN [' + names + ']'
|
|
633
|
+
+ ' AND ( (bucket_id IS MISSING AND keyspace_id = $1)'
|
|
634
|
+
+ ' OR (bucket_id = $1 AND scope_id = $2 AND keyspace_id = $3) )';
|
|
635
|
+
|
|
636
|
+
runQuery(probe, [bucketName, scopeName, collectionName], function(err, rows) {
|
|
637
|
+
if (err) {
|
|
638
|
+
return console.warn('[CouchbaseStorageStore] could not inspect system:indexes (bundle: ' + bundle
|
|
639
|
+
+ ', driver: ' + driverName + '): ' + ((err && err.message) || err)
|
|
640
|
+
+ ' — create these by hand if queries fail: ' + ddlLines().join(' ; '));
|
|
641
|
+
}
|
|
642
|
+
var have = {};
|
|
643
|
+
(rows || []).forEach(function(r) { if (r && r.name) { have[r.name] = true; } });
|
|
644
|
+
INDEXES.forEach(function(idx) {
|
|
645
|
+
if (have[idx.name]) { return; }
|
|
646
|
+
var ddl = 'CREATE INDEX `' + idx.name + '` ON ' + keyspace + idx.fields;
|
|
647
|
+
runQuery(ddl, [], function(createErr) {
|
|
648
|
+
if (createErr) {
|
|
649
|
+
// IndexExistsError is the benign race: a sibling
|
|
650
|
+
// process created it between the probe and here.
|
|
651
|
+
if ( isCbError(createErr, 'IndexExistsError') ) { return; }
|
|
652
|
+
console.warn('[CouchbaseStorageStore] could not create index `' + idx.name + '` (bundle: '
|
|
653
|
+
+ bundle + ', driver: ' + driverName + '): ' + ((createErr && createErr.message) || createErr)
|
|
654
|
+
+ ' — run it by hand: ' + ddl);
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* The DDL for every index this store needs — surfaced in log lines so an
|
|
663
|
+
* operator can create them by hand.
|
|
664
|
+
*
|
|
665
|
+
* @inner
|
|
666
|
+
* @returns {string[]}
|
|
667
|
+
*/
|
|
668
|
+
var ddlLines = function() {
|
|
669
|
+
return INDEXES.map(function(idx) {
|
|
670
|
+
return 'CREATE INDEX `' + idx.name + '` ON ' + keyspace + idx.fields;
|
|
671
|
+
});
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
ensureIndexes();
|
|
675
|
+
|
|
676
|
+
// ─── Statements ──────────────────────────────────────────────────────────
|
|
677
|
+
//
|
|
678
|
+
// Composed once. Every VALUE is a positional parameter; only the keyspace
|
|
679
|
+
// (an identifier, which N1QL cannot bind) and the row limit are
|
|
680
|
+
// interpolated, both from validated input.
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* `refs IS VALUED` is the LOAD-BEARING guard, not decoration: a
|
|
684
|
+
* non-refcounted row carries no `refs` field at all, and this is what
|
|
685
|
+
* keeps such rows structurally invisible to the GC sweep — the same role
|
|
686
|
+
* the embedded store gets free from SQL's `NULL != 0`.
|
|
687
|
+
*
|
|
688
|
+
* @inner
|
|
689
|
+
* @param {number} limit
|
|
690
|
+
* @returns {string}
|
|
691
|
+
*/
|
|
692
|
+
var zeroRefsStatement = function(limit) {
|
|
693
|
+
return 'SELECT k FROM ' + keyspace
|
|
694
|
+
+ ' WHERE d = $1 AND refs IS VALUED AND refs = 0'
|
|
695
|
+
+ ' AND zeroAt IS VALUED AND zeroAt <= $2'
|
|
696
|
+
+ ' ORDER BY zeroAt LIMIT ' + limit;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Key-ordered pagination, reserved dot-keys excluded — ordering by the
|
|
701
|
+
* key is what keeps the cursor stable while the bundle keeps writing.
|
|
702
|
+
*
|
|
703
|
+
* @inner
|
|
704
|
+
* @param {number} limit
|
|
705
|
+
* @returns {string}
|
|
706
|
+
*/
|
|
707
|
+
var keysStatement = function(limit) {
|
|
708
|
+
return 'SELECT k FROM ' + keyspace
|
|
709
|
+
+ ' WHERE d = $1 AND k > $2 AND k NOT LIKE ".%"'
|
|
710
|
+
+ ' ORDER BY k LIMIT ' + limit;
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* One aggregate pass for the operator surface. `IFMISSINGORNULL` is what
|
|
715
|
+
* makes an EMPTY driver report zeroes rather than nulls (SUM over no rows
|
|
716
|
+
* is NULL) — the embedded store's `COALESCE`.
|
|
717
|
+
*
|
|
718
|
+
* `inline` is BACKTICKED because it is a N1QL reserved word (#B356) — the
|
|
719
|
+
* server refuses the whole statement with `at: inline (reserved word)`, so
|
|
720
|
+
* `stats` was the one maintenance verb that could not parse against a real
|
|
721
|
+
* cluster. Escaping it changes nothing else: a quoted alias still yields
|
|
722
|
+
* the bare key `inline` in the result row, which `stats()` below reads.
|
|
723
|
+
* The other four aliases are NOT reserved (checked against Couchbase's
|
|
724
|
+
* reserved-word list, which is why they are left bare) and `§07` pins that
|
|
725
|
+
* none of them silently becomes one.
|
|
726
|
+
*
|
|
727
|
+
* @inner
|
|
728
|
+
* @type {string}
|
|
729
|
+
*/
|
|
730
|
+
var statsStatement = 'SELECT COUNT(1) AS objects,'
|
|
731
|
+
+ ' IFMISSINGORNULL(SUM(CASE WHEN refs IS VALUED THEN 1 ELSE 0 END), 0) AS refcounted,'
|
|
732
|
+
+ ' IFMISSINGORNULL(SUM(CASE WHEN refs = 0 THEN 1 ELSE 0 END), 0) AS zeroRefPending,'
|
|
733
|
+
+ ' IFMISSINGORNULL(SUM(CASE WHEN data IS VALUED THEN 1 ELSE 0 END), 0) AS `inline`,'
|
|
734
|
+
+ ' IFMISSINGORNULL(SUM(size), 0) AS bytes'
|
|
735
|
+
+ ' FROM ' + keyspace
|
|
736
|
+
+ ' WHERE d = $1 AND k NOT LIKE ".%"';
|
|
737
|
+
|
|
738
|
+
return {
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Upsert `meta` under `key` — REPLACE semantics, exactly like the
|
|
742
|
+
* embedded store's `INSERT OR REPLACE`.
|
|
743
|
+
*
|
|
744
|
+
* Never call this on a refcounted row: it would drop `refs`/`zeroAt`
|
|
745
|
+
* and reset the count. `acquireRef` owns those rows.
|
|
746
|
+
*
|
|
747
|
+
* @param {string} key
|
|
748
|
+
* @param {StorageMeta} meta
|
|
749
|
+
* @param {function} [fn] - `fn(err, meta)`.
|
|
750
|
+
* @returns {void}
|
|
751
|
+
*/
|
|
752
|
+
set: function(key, meta, fn) {
|
|
753
|
+
fn = once(fn);
|
|
754
|
+
meta = meta || {};
|
|
755
|
+
var d = docIdFor(key);
|
|
756
|
+
if (d.error) { return fn(d.error); }
|
|
757
|
+
withCollection(function(coll) {
|
|
758
|
+
return coll.upsert(d.id, buildDoc(key, meta, false), withDurability())
|
|
759
|
+
.then(function() { fn(null, meta); });
|
|
760
|
+
}, fn);
|
|
761
|
+
},
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Fetch metadata by `key`.
|
|
765
|
+
*
|
|
766
|
+
* @param {string} key
|
|
767
|
+
* @param {function} fn - `fn(err, meta|null)`; `null` when the key is unknown.
|
|
768
|
+
* @returns {void}
|
|
769
|
+
*/
|
|
770
|
+
get: function(key, fn) {
|
|
771
|
+
fn = once(fn);
|
|
772
|
+
var d = docIdFor(key);
|
|
773
|
+
if (d.error) { return fn(d.error); }
|
|
774
|
+
withCollection(function(coll) {
|
|
775
|
+
return coll.get(d.id)
|
|
776
|
+
.then(function(res) { fn(null, metaFromDoc(res.content, true)); })
|
|
777
|
+
.catch(function(err) {
|
|
778
|
+
if ( isCbError(err, 'DocumentNotFoundError') ) { return fn(null, null); }
|
|
779
|
+
fn(err);
|
|
780
|
+
});
|
|
781
|
+
}, fn);
|
|
782
|
+
},
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Delete metadata by `key`.
|
|
786
|
+
*
|
|
787
|
+
* @param {string} key
|
|
788
|
+
* @param {function} [fn] - `fn(err, existed)`.
|
|
789
|
+
* @returns {void}
|
|
790
|
+
*/
|
|
791
|
+
remove: function(key, fn) {
|
|
792
|
+
fn = once(fn);
|
|
793
|
+
var d = docIdFor(key);
|
|
794
|
+
if (d.error) { return fn(d.error); }
|
|
795
|
+
withCollection(function(coll) {
|
|
796
|
+
return coll.remove(d.id, withDurability())
|
|
797
|
+
.then(function() { fn(null, true); })
|
|
798
|
+
.catch(function(err) {
|
|
799
|
+
if ( isCbError(err, 'DocumentNotFoundError') ) { return fn(null, false); }
|
|
800
|
+
fn(err);
|
|
801
|
+
});
|
|
802
|
+
}, fn);
|
|
803
|
+
},
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Atomically take one reference on `key` — insert-or-increment.
|
|
807
|
+
*
|
|
808
|
+
* Missing row: inserted with `meta` and `refs` = 1 (`created: true`);
|
|
809
|
+
* a concurrent insert loses on `DocumentExistsError` and retries into
|
|
810
|
+
* the increment branch. Existing refcounted row: `refs` incremented
|
|
811
|
+
* and `zeroAt` cleared (a blob inside the GC grace window is
|
|
812
|
+
* resurrected), with the INCOMING meta ignored — first-write-wins.
|
|
813
|
+
*
|
|
814
|
+
* Atomicity is Couchbase's CAS: the read's CAS token guards the
|
|
815
|
+
* sub-document mutation, so an interleaved writer invalidates this
|
|
816
|
+
* attempt rather than silently overwriting it.
|
|
817
|
+
*
|
|
818
|
+
* A row that exists but carries NO `refs` is REFUSED loudly rather
|
|
819
|
+
* than adopted: it belongs to something else (a `sharded` row, or the
|
|
820
|
+
* `.driver` stamp), and quietly stamping a refcount onto it would
|
|
821
|
+
* corrupt that owner's data. The embedded store gets the same
|
|
822
|
+
* loudness from its PRIMARY KEY conflict.
|
|
823
|
+
*
|
|
824
|
+
* @param {string} key - The blob key.
|
|
825
|
+
* @param {StorageMeta} meta - Metadata for the CREATE case (may carry `data`).
|
|
826
|
+
* @param {function} [fn] - `fn(err, {created: boolean, refs: number, meta: StorageMeta})`.
|
|
827
|
+
* @returns {void}
|
|
828
|
+
*/
|
|
829
|
+
acquireRef: function(key, meta, fn) {
|
|
830
|
+
fn = once(fn);
|
|
831
|
+
meta = meta || {};
|
|
832
|
+
var d = docIdFor(key);
|
|
833
|
+
if (d.error) { return fn(d.error); }
|
|
834
|
+
|
|
835
|
+
withCollection(function(coll) {
|
|
836
|
+
var attempt = function(n) {
|
|
837
|
+
if ( n >= MAX_CAS_ATTEMPTS ) { return fn(contentionError('acquireRef', key)); }
|
|
838
|
+
|
|
839
|
+
coll.get(d.id).then(function(res) {
|
|
840
|
+
var doc = res.content;
|
|
841
|
+
if ( typeof doc.refs !== 'number' ) {
|
|
842
|
+
return fn(new Error('[CouchbaseStorageStore] acquireRef(`' + key + '`) refuses to adopt an '
|
|
843
|
+
+ 'existing non-refcounted row — it belongs to another strategy (or is the `.driver` '
|
|
844
|
+
+ 'stamp), and taking a reference on it would corrupt its owner'));
|
|
845
|
+
}
|
|
846
|
+
var next = doc.refs + 1;
|
|
847
|
+
return coll.mutateIn(d.id, [
|
|
848
|
+
couchbase.MutateInSpec.upsert('refs', next),
|
|
849
|
+
couchbase.MutateInSpec.upsert('zeroAt', null)
|
|
850
|
+
], withDurability({ cas: res.cas })).then(function() {
|
|
851
|
+
fn(null, {
|
|
852
|
+
created : false,
|
|
853
|
+
refs : next,
|
|
854
|
+
meta : {
|
|
855
|
+
originalName : ( typeof doc.originalName === 'string' ) ? doc.originalName : null,
|
|
856
|
+
contentType : ( typeof doc.contentType === 'string' ) ? doc.contentType : null,
|
|
857
|
+
size : ( typeof doc.size === 'number' ) ? doc.size : null,
|
|
858
|
+
createdAt : ( typeof doc.createdAt === 'number' ) ? doc.createdAt : null,
|
|
859
|
+
refs : next
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
}).catch(function(err) {
|
|
863
|
+
if ( isCbError(err, 'CasMismatchError') || isCbError(err, 'DocumentNotFoundError') ) {
|
|
864
|
+
return backoff(n, function() { attempt(n + 1); });
|
|
865
|
+
}
|
|
866
|
+
fn(err);
|
|
867
|
+
});
|
|
868
|
+
}).catch(function(getErr) {
|
|
869
|
+
if ( !isCbError(getErr, 'DocumentNotFoundError') ) { return fn(getErr); }
|
|
870
|
+
coll.insert(d.id, buildDoc(key, meta, true), withDurability())
|
|
871
|
+
.then(function() { fn(null, { created: true, refs: 1, meta: meta }); })
|
|
872
|
+
.catch(function(insErr) {
|
|
873
|
+
if ( isCbError(insErr, 'DocumentExistsError') ) {
|
|
874
|
+
return backoff(n, function() { attempt(n + 1); });
|
|
875
|
+
}
|
|
876
|
+
fn(insErr);
|
|
877
|
+
});
|
|
878
|
+
});
|
|
879
|
+
};
|
|
880
|
+
attempt(0);
|
|
881
|
+
}, fn);
|
|
882
|
+
},
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Atomically drop one reference on `key`.
|
|
886
|
+
*
|
|
887
|
+
* Floors at 0 and stamps `zeroAt` at the 1 → 0 transition — the
|
|
888
|
+
* timestamp the GC grace window reads. Both fields move in ONE
|
|
889
|
+
* `mutateIn`, which applies all-or-none, so the invariant (`zeroAt`
|
|
890
|
+
* non-null exactly when `refs === 0`) can never be observed broken.
|
|
891
|
+
*
|
|
892
|
+
* A missing, non-refcounted, or already-zero row answers
|
|
893
|
+
* `{existed: false}`: the reference the caller wanted to drop was
|
|
894
|
+
* already gone, which is the outcome they asked for.
|
|
895
|
+
*
|
|
896
|
+
* @param {string} key - The blob key.
|
|
897
|
+
* @param {function} [fn] - `fn(err, {existed: boolean, refs: number})` — `refs` is the
|
|
898
|
+
* count AFTER the decrement.
|
|
899
|
+
* @returns {void}
|
|
900
|
+
*/
|
|
901
|
+
releaseRef: function(key, fn) {
|
|
902
|
+
fn = once(fn);
|
|
903
|
+
var d = docIdFor(key);
|
|
904
|
+
if (d.error) { return fn(d.error); }
|
|
905
|
+
|
|
906
|
+
withCollection(function(coll) {
|
|
907
|
+
var attempt = function(n) {
|
|
908
|
+
if ( n >= MAX_CAS_ATTEMPTS ) { return fn(contentionError('releaseRef', key)); }
|
|
909
|
+
|
|
910
|
+
coll.get(d.id).then(function(res) {
|
|
911
|
+
var doc = res.content;
|
|
912
|
+
if ( typeof doc.refs !== 'number' || doc.refs < 1 ) {
|
|
913
|
+
return fn(null, { existed: false, refs: ( typeof doc.refs === 'number' ) ? doc.refs : 0 });
|
|
914
|
+
}
|
|
915
|
+
var next = doc.refs - 1;
|
|
916
|
+
return coll.mutateIn(d.id, [
|
|
917
|
+
couchbase.MutateInSpec.upsert('refs', next),
|
|
918
|
+
couchbase.MutateInSpec.upsert('zeroAt', ( next === 0 ) ? Date.now() : null)
|
|
919
|
+
], withDurability({ cas: res.cas })).then(function() {
|
|
920
|
+
fn(null, { existed: true, refs: next });
|
|
921
|
+
}).catch(function(err) {
|
|
922
|
+
if ( isCbError(err, 'CasMismatchError') || isCbError(err, 'DocumentNotFoundError') ) {
|
|
923
|
+
return backoff(n, function() { attempt(n + 1); });
|
|
924
|
+
}
|
|
925
|
+
fn(err);
|
|
926
|
+
});
|
|
927
|
+
}).catch(function(getErr) {
|
|
928
|
+
if ( isCbError(getErr, 'DocumentNotFoundError') ) {
|
|
929
|
+
return fn(null, { existed: false, refs: 0 });
|
|
930
|
+
}
|
|
931
|
+
fn(getErr);
|
|
932
|
+
});
|
|
933
|
+
};
|
|
934
|
+
attempt(0);
|
|
935
|
+
}, fn);
|
|
936
|
+
},
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Keys whose refcount has sat at 0 since before `olderThanMs` —
|
|
940
|
+
* oldest first, at most `limit`.
|
|
941
|
+
*
|
|
942
|
+
* Non-refcounted rows carry no `refs` field, which `refs IS VALUED`
|
|
943
|
+
* excludes, so they are structurally invisible here.
|
|
944
|
+
*
|
|
945
|
+
* @param {number} olderThanMs - Epoch ms cutoff (typically now − grace).
|
|
946
|
+
* @param {number} limit - Batch cap.
|
|
947
|
+
* @param {function} fn - `fn(err, string[])`.
|
|
948
|
+
* @returns {void}
|
|
949
|
+
*/
|
|
950
|
+
listZeroRefs: function(olderThanMs, limit, fn) {
|
|
951
|
+
fn = once(fn);
|
|
952
|
+
runQuery(zeroRefsStatement(safeLimit(limit, 100)), [driverName, olderThanMs], function(err, rows) {
|
|
953
|
+
if (err) {
|
|
954
|
+
// The sweep's own call passes no callback, so an error
|
|
955
|
+
// that only travelled through `fn` would vanish and GC
|
|
956
|
+
// would appear to work forever while collecting nothing.
|
|
957
|
+
console.error('[CouchbaseStorageStore] listZeroRefs failed (bundle: ' + bundle + ', driver: '
|
|
958
|
+
+ driverName + '): ' + ((err && err.message) || err)
|
|
959
|
+
+ ' — if this names a missing index, run: ' + ddlLines()[0]);
|
|
960
|
+
return fn(err);
|
|
961
|
+
}
|
|
962
|
+
fn(null, rows.map(function(r) { return r.k; }));
|
|
963
|
+
});
|
|
964
|
+
},
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Delete the row ONLY IF its refcount is still 0 — the sweep's claim
|
|
968
|
+
* step, and the reason concurrent sweeps across processes are safe
|
|
969
|
+
* without an election: the CAS guard makes exactly one claim win.
|
|
970
|
+
*
|
|
971
|
+
* A concurrent `acquireRef` resurrection wins the same way: it moves
|
|
972
|
+
* the document's CAS, this guarded remove reports `CasMismatchError`,
|
|
973
|
+
* and the sweep skips the blob.
|
|
974
|
+
*
|
|
975
|
+
* @param {string} key - The blob key.
|
|
976
|
+
* @param {function} [fn] - `fn(err, removed: boolean)`.
|
|
977
|
+
* @returns {void}
|
|
978
|
+
*/
|
|
979
|
+
removeIfZero: function(key, fn) {
|
|
980
|
+
fn = once(fn);
|
|
981
|
+
var d = docIdFor(key);
|
|
982
|
+
if (d.error) { return fn(d.error); }
|
|
983
|
+
|
|
984
|
+
withCollection(function(coll) {
|
|
985
|
+
return coll.get(d.id).then(function(res) {
|
|
986
|
+
if ( res.content.refs !== 0 ) { return fn(null, false); }
|
|
987
|
+
return coll.remove(d.id, withDurability({ cas: res.cas }))
|
|
988
|
+
.then(function() { fn(null, true); })
|
|
989
|
+
.catch(function(err) {
|
|
990
|
+
// Both mean somebody else got there first — a
|
|
991
|
+
// resurrection (CAS moved) or another sweeper
|
|
992
|
+
// (document gone). Neither is an error.
|
|
993
|
+
if ( isCbError(err, 'CasMismatchError') || isCbError(err, 'DocumentNotFoundError') ) {
|
|
994
|
+
return fn(null, false);
|
|
995
|
+
}
|
|
996
|
+
fn(err);
|
|
997
|
+
});
|
|
998
|
+
}).catch(function(getErr) {
|
|
999
|
+
if ( isCbError(getErr, 'DocumentNotFoundError') ) { return fn(null, false); }
|
|
1000
|
+
fn(getErr);
|
|
1001
|
+
});
|
|
1002
|
+
}, fn);
|
|
1003
|
+
},
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Aggregate counts over this driver's rows — the `storage:stats`
|
|
1007
|
+
* operator surface. Reserved dot-key rows (the `.driver` stamp) are
|
|
1008
|
+
* excluded, so an empty root reports zero objects.
|
|
1009
|
+
*
|
|
1010
|
+
* @param {function} fn - `fn(err, {objects, refcounted, zeroRefPending, inline, bytes})`.
|
|
1011
|
+
* @returns {void}
|
|
1012
|
+
*/
|
|
1013
|
+
stats: function(fn) {
|
|
1014
|
+
fn = once(fn);
|
|
1015
|
+
runQuery(statsStatement, [driverName], function(err, rows) {
|
|
1016
|
+
if (err) {
|
|
1017
|
+
console.error('[CouchbaseStorageStore] stats failed (bundle: ' + bundle + ', driver: '
|
|
1018
|
+
+ driverName + '): ' + ((err && err.message) || err));
|
|
1019
|
+
return fn(err);
|
|
1020
|
+
}
|
|
1021
|
+
var row = rows[0] || {};
|
|
1022
|
+
fn(null, {
|
|
1023
|
+
objects : row.objects || 0,
|
|
1024
|
+
refcounted : row.refcounted || 0,
|
|
1025
|
+
zeroRefPending : row.zeroRefPending || 0,
|
|
1026
|
+
inline : row.inline || 0,
|
|
1027
|
+
bytes : row.bytes || 0
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1030
|
+
},
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Page over this driver's object keys, ordered by key — the
|
|
1034
|
+
* enumeration `storage:verify` walks for its rows-without-files
|
|
1035
|
+
* direction. Reserved dot-key rows are excluded.
|
|
1036
|
+
*
|
|
1037
|
+
* @param {string} afterKey - Exclusive lower bound (`''` for the first page).
|
|
1038
|
+
* @param {number} limit - Page size cap.
|
|
1039
|
+
* @param {function} fn - `fn(err, string[])`.
|
|
1040
|
+
* @returns {void}
|
|
1041
|
+
*/
|
|
1042
|
+
listKeys: function(afterKey, limit, fn) {
|
|
1043
|
+
fn = once(fn);
|
|
1044
|
+
runQuery(
|
|
1045
|
+
keysStatement(safeLimit(limit, 500)),
|
|
1046
|
+
[driverName, ( typeof afterKey === 'string' ) ? afterKey : ''],
|
|
1047
|
+
function(err, rows) {
|
|
1048
|
+
if (err) {
|
|
1049
|
+
console.error('[CouchbaseStorageStore] listKeys failed (bundle: ' + bundle + ', driver: '
|
|
1050
|
+
+ driverName + '): ' + ((err && err.message) || err)
|
|
1051
|
+
+ ' — if this names a missing index, run: ' + ddlLines()[1]);
|
|
1052
|
+
return fn(err);
|
|
1053
|
+
}
|
|
1054
|
+
fn(null, rows.map(function(r) { return r.k; }));
|
|
1055
|
+
}
|
|
1056
|
+
);
|
|
1057
|
+
},
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Release the cluster connection. NOT called on the request path —
|
|
1061
|
+
* provided for teardown and tests.
|
|
1062
|
+
*
|
|
1063
|
+
* @returns {void}
|
|
1064
|
+
*/
|
|
1065
|
+
close: function() {
|
|
1066
|
+
try {
|
|
1067
|
+
if ( cluster && typeof cluster.close === 'function' ) {
|
|
1068
|
+
var p = cluster.close();
|
|
1069
|
+
if ( p && typeof p.catch === 'function' ) { p.catch(noop); }
|
|
1070
|
+
}
|
|
1071
|
+
} catch (e) { /* already closed */ }
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
};
|