@blamejs/blamejs-shop 0.5.12 → 0.5.13
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 +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -0,0 +1,1019 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* db-query — canonical test file for lib/db-query.js (the chainable Query
|
|
6
|
+
* builder behind b.db.from()).
|
|
7
|
+
*
|
|
8
|
+
* Two harnesses, matching the existing db-query sub-concern files:
|
|
9
|
+
* - a SQL-recording fake handle (mirrors db-query-cross-schema.test.js's
|
|
10
|
+
* _fakeDb) for the constructor / column-gate / dialect / WhereBuilder /
|
|
11
|
+
* JSONB-guard / whereRaw branches that throw or emit before a row is
|
|
12
|
+
* touched — no db bootstrap needed (sealRow/unsealRow no-op on an
|
|
13
|
+
* unregistered table, exactly as the cross-schema sibling relies on);
|
|
14
|
+
* - the in-process node:sqlite handle via setupTestDb for the real
|
|
15
|
+
* execution paths: sealed-field query rewrite, residency write-gate,
|
|
16
|
+
* per-row-key writes, streaming, and the raw-write residency helpers.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var helpers = require("../helpers");
|
|
20
|
+
var b = helpers.b;
|
|
21
|
+
var fs = helpers.fs;
|
|
22
|
+
var os = helpers.os;
|
|
23
|
+
var path = helpers.path;
|
|
24
|
+
var check = helpers.check;
|
|
25
|
+
var setupTestDb = helpers.setupTestDb;
|
|
26
|
+
var teardownTestDb = helpers.teardownTestDb;
|
|
27
|
+
var dbQuery = require("../../lib/db-query");
|
|
28
|
+
var Query = dbQuery.Query;
|
|
29
|
+
|
|
30
|
+
// SQL-recording fake handle. `prepare(sql)` records the statement and
|
|
31
|
+
// returns a stmt stub whose result rows are configurable per test (get /
|
|
32
|
+
// all / iterate / run.changes). Rolling this mirrors the sibling
|
|
33
|
+
// db-query-cross-schema.test.js `_fakeDb` — no shared helper provides a
|
|
34
|
+
// stmt-recording db handle.
|
|
35
|
+
function _recordingDb(opts) {
|
|
36
|
+
opts = opts || {};
|
|
37
|
+
var prepared = [];
|
|
38
|
+
return {
|
|
39
|
+
prepared: prepared,
|
|
40
|
+
dialect: opts.dialect,
|
|
41
|
+
prepare: function (sql) {
|
|
42
|
+
prepared.push(sql);
|
|
43
|
+
return {
|
|
44
|
+
get: function () { return opts.getResult !== undefined ? opts.getResult : null; },
|
|
45
|
+
all: function () { return opts.allResult !== undefined ? opts.allResult : []; },
|
|
46
|
+
run: function () { return { changes: opts.changes !== undefined ? opts.changes : 0 }; },
|
|
47
|
+
iterate: function () {
|
|
48
|
+
if (opts.iterateThrows) throw new Error("iterate boom");
|
|
49
|
+
if (opts.nextThrows) return { next: function () { throw new Error("next boom"); } };
|
|
50
|
+
return { next: function () { return { done: true }; } };
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _codeOf(fn) {
|
|
58
|
+
try { fn(); } catch (e) { return (e && e.code) || (e && e.message) || "threw"; }
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function _threw(fn) { return _codeOf(fn) !== null; }
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Fake-handle group — validation / SQL-shape branches (no db bootstrap).
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
async function testConstructorValidation() {
|
|
68
|
+
var db = _recordingDb();
|
|
69
|
+
check("non-string tableName → TypeError",
|
|
70
|
+
_threw(function () { new Query(db, 123); }));
|
|
71
|
+
check("three-part identifier rejected",
|
|
72
|
+
/exactly 'schema.table'/.test(_codeOf(function () { new Query(db, "a.b.c"); })));
|
|
73
|
+
check("empty schema part rejected",
|
|
74
|
+
_threw(function () { new Query(db, ".t"); }));
|
|
75
|
+
check("empty table part rejected",
|
|
76
|
+
_threw(function () { new Query(db, "s."); }));
|
|
77
|
+
check("invalid schema identifier rejected",
|
|
78
|
+
_threw(function () { new Query(db, "DROP TABLE.users"); }));
|
|
79
|
+
check("invalid table identifier rejected",
|
|
80
|
+
_threw(function () { new Query(db, "audit.DROP TABLE"); }));
|
|
81
|
+
|
|
82
|
+
// primaryKey construction opt: valid identifier accepted, invalid rejected.
|
|
83
|
+
var withPk = new Query(db, "t", { primaryKey: "pk" });
|
|
84
|
+
check("primaryKey opt sets the pk column", withPk._pkColumn() === "pk");
|
|
85
|
+
check("invalid primaryKey identifier rejected",
|
|
86
|
+
_threw(function () { new Query(db, "t", { primaryKey: "bad name" }); }));
|
|
87
|
+
var noPk = new Query(db, "t");
|
|
88
|
+
check("default pk column is _id when no primaryKey opt", noPk._pkColumn() === "_id");
|
|
89
|
+
|
|
90
|
+
// declaredColumns accepts both an array and a Set.
|
|
91
|
+
var fromArr = new Query(db, "t", { declaredColumns: ["a", "b"] });
|
|
92
|
+
check("declaredColumns array is normalized to a Set", fromArr._declaredColumns instanceof Set);
|
|
93
|
+
var fromSet = new Query(db, "t", { declaredColumns: new Set(["a"]) });
|
|
94
|
+
check("declaredColumns Set is used as-is", fromSet._declaredColumns.has("a"));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function testColumnGate() {
|
|
98
|
+
var db = _recordingDb();
|
|
99
|
+
var declared = { declaredColumns: ["a", "b"] };
|
|
100
|
+
|
|
101
|
+
// reject (default) — unknown column throws before any SQL is built.
|
|
102
|
+
check("reject mode: unknown column refused",
|
|
103
|
+
/not a declared column/.test(
|
|
104
|
+
_codeOf(function () { new Query(db, "t", declared).where({ c: 1 }); })));
|
|
105
|
+
// declared column passes.
|
|
106
|
+
check("reject mode: declared column passes",
|
|
107
|
+
!_threw(function () { new Query(db, "t", declared).where({ a: 1 }); }));
|
|
108
|
+
|
|
109
|
+
// warn mode — unknown column is allowed (audit drop-silent), no throw.
|
|
110
|
+
check("warn mode: unknown column allowed (audit-only)",
|
|
111
|
+
!_threw(function () {
|
|
112
|
+
new Query(db, "t", { declaredColumns: ["a"], columnGateMode: "warn" }).where({ z: 9 });
|
|
113
|
+
}));
|
|
114
|
+
// off mode — gate skipped entirely.
|
|
115
|
+
check("off mode: unknown column allowed",
|
|
116
|
+
!_threw(function () {
|
|
117
|
+
new Query(db, "t", { declaredColumns: ["a"], columnGateMode: "off" }).where({ z: 9 });
|
|
118
|
+
}));
|
|
119
|
+
// no declaredColumns — gate disabled.
|
|
120
|
+
check("no declaredColumns: gate disabled",
|
|
121
|
+
!_threw(function () { new Query(db, "t").where({ anything: 1 }); }));
|
|
122
|
+
|
|
123
|
+
// allowedColumns() — always enforced, tighter than the schema set.
|
|
124
|
+
check("allowedColumns() refuses a column outside the explicit set",
|
|
125
|
+
/allowedColumns\(\) set/.test(
|
|
126
|
+
_codeOf(function () {
|
|
127
|
+
new Query(db, "t", declared).allowedColumns(["a"]).where({ b: 1 });
|
|
128
|
+
})));
|
|
129
|
+
check("allowedColumns() allows a column inside the explicit set",
|
|
130
|
+
!_threw(function () {
|
|
131
|
+
new Query(db, "t", declared).allowedColumns(["a"]).where({ a: 1 });
|
|
132
|
+
}));
|
|
133
|
+
check("allowedColumns([]) → TypeError (non-empty array required)",
|
|
134
|
+
_threw(function () { new Query(db, "t").allowedColumns([]); }));
|
|
135
|
+
check("allowedColumns(non-array) → TypeError",
|
|
136
|
+
_threw(function () { new Query(db, "t").allowedColumns("a"); }));
|
|
137
|
+
check("allowedColumns([bad identifier]) rejected",
|
|
138
|
+
_threw(function () { new Query(db, "t").allowedColumns(["bad name"]); }));
|
|
139
|
+
|
|
140
|
+
// select() / orderBy() also run the gate.
|
|
141
|
+
check("select() refuses an undeclared column",
|
|
142
|
+
/not a declared column/.test(
|
|
143
|
+
_codeOf(function () { new Query(db, "t", declared).select(["nope"]); })));
|
|
144
|
+
check("orderBy() refuses an undeclared column",
|
|
145
|
+
/not a declared column/.test(
|
|
146
|
+
_codeOf(function () { new Query(db, "t", declared).orderBy("nope"); })));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function testDialectAndSingleRowWrite() {
|
|
150
|
+
// Dialect resolution: postgres / mysql / unknown-fallback / undefined.
|
|
151
|
+
var pg = _recordingDb({ dialect: "postgres", getResult: { _id: "x" } });
|
|
152
|
+
new Query(pg, "t").where({ a: 1 }).first();
|
|
153
|
+
check("postgres handle drives a SELECT", pg.prepared.some(function (s) { return /SELECT/.test(s); }));
|
|
154
|
+
|
|
155
|
+
var weird = _recordingDb({ dialect: "wat" });
|
|
156
|
+
new Query(weird, "t").where({ a: 1 }).first();
|
|
157
|
+
check("unknown dialect falls back to sqlite quoting",
|
|
158
|
+
weird.prepared.some(function (s) { return /FROM "t"/.test(s); }));
|
|
159
|
+
|
|
160
|
+
// MySQL single-row UPDATE: no self-referential subquery — resolve the PK
|
|
161
|
+
// in a prior SELECT then write WHERE pk = ?. getResult supplies the PK.
|
|
162
|
+
var my = _recordingDb({ dialect: "mysql", getResult: { pk: "row-7" }, changes: 1 });
|
|
163
|
+
var updated = new Query(my, "t", { primaryKey: "pk" }).where({ a: 1 }).updateOne({ name: "y" });
|
|
164
|
+
check("mysql single-row update returns true when a row matched", updated === true);
|
|
165
|
+
check("mysql update emits backtick-quoted UPDATE",
|
|
166
|
+
my.prepared.some(function (s) { return /UPDATE `t` SET/.test(s); }));
|
|
167
|
+
check("mysql update resolves the PK in a prior SELECT",
|
|
168
|
+
my.prepared.some(function (s) { return /SELECT `pk` FROM `t`/.test(s); }));
|
|
169
|
+
|
|
170
|
+
// MySQL single-row update with NO matching row → _resolveSinglePk null → 0.
|
|
171
|
+
var myNone = _recordingDb({ dialect: "mysql", getResult: null });
|
|
172
|
+
check("mysql single-row update returns false when no row matched",
|
|
173
|
+
new Query(myNone, "t").where({ a: 1 }).updateOne({ name: "y" }) === false);
|
|
174
|
+
|
|
175
|
+
// MySQL: a matched row whose PK value is NULL still resolves to "no row".
|
|
176
|
+
var myNullPk = _recordingDb({ dialect: "mysql", getResult: { pk: null } });
|
|
177
|
+
check("mysql single-row update treats a NULL pk value as no match",
|
|
178
|
+
new Query(myNullPk, "t", { primaryKey: "pk" }).where({ a: 1 }).updateOne({ name: "y" }) === false);
|
|
179
|
+
|
|
180
|
+
// Postgres single-row update uses the PK sub-select idiom (not the sqlite
|
|
181
|
+
// rowid), exercising the non-sqlite row-locator column.
|
|
182
|
+
var pgUpd = _recordingDb({ dialect: "postgres", getResult: { _id: "x" }, changes: 1 });
|
|
183
|
+
check("postgres single-row update returns true when a row matched",
|
|
184
|
+
new Query(pgUpd, "t").where({ a: 1 }).updateOne({ name: "y" }) === true);
|
|
185
|
+
check("postgres single-row update uses the _id sub-select",
|
|
186
|
+
pgUpd.prepared.some(function (s) { return /SELECT "_id" FROM "t"/.test(s); }));
|
|
187
|
+
|
|
188
|
+
// count() over a handle that returns no aggregate row falls back to 0.
|
|
189
|
+
var noRow = _recordingDb({ getResult: null });
|
|
190
|
+
check("count() returns 0 when the handle yields no row",
|
|
191
|
+
new Query(noRow, "t").where({ a: 1 }).count() === 0);
|
|
192
|
+
|
|
193
|
+
// MySQL single-row DELETE, matching + non-matching.
|
|
194
|
+
var myDel = _recordingDb({ dialect: "mysql", getResult: { _id: "d1" }, changes: 1 });
|
|
195
|
+
check("mysql single-row delete returns true when a row matched",
|
|
196
|
+
new Query(myDel, "t").where({ a: 1 }).deleteOne() === true);
|
|
197
|
+
var myDelNone = _recordingDb({ dialect: "mysql", getResult: null });
|
|
198
|
+
check("mysql single-row delete returns false when no row matched",
|
|
199
|
+
new Query(myDelNone, "t").where({ a: 1 }).deleteOne() === false);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function testWhereOperatorAndJsonbGuards() {
|
|
203
|
+
var db = _recordingDb();
|
|
204
|
+
|
|
205
|
+
// invalid operator.
|
|
206
|
+
check("invalid where operator rejected",
|
|
207
|
+
/invalid where operator/.test(
|
|
208
|
+
_codeOf(function () { new Query(db, "t").where("c", "NOPE", 1); })));
|
|
209
|
+
|
|
210
|
+
// IN shape validation (non-sealed path).
|
|
211
|
+
check("IN with a non-array value rejected",
|
|
212
|
+
_threw(function () { new Query(db, "t").where("c", "IN", "x"); }));
|
|
213
|
+
check("IN with an empty array rejected",
|
|
214
|
+
_threw(function () { new Query(db, "t").where("c", "IN", []); }));
|
|
215
|
+
|
|
216
|
+
// JSONB containment @> — object value (validate + canonical stringify).
|
|
217
|
+
check("@> accepts a valid object value",
|
|
218
|
+
!_threw(function () { new Query(db, "t").where("meta", "@>", { a: 1 }); }));
|
|
219
|
+
// @> — pre-stringified JSON string value (parse + validate).
|
|
220
|
+
check("@> accepts a valid JSON string value",
|
|
221
|
+
!_threw(function () { new Query(db, "t").where("meta", "@>", '{"a":1}'); }));
|
|
222
|
+
check("@> rejects an invalid JSON string value",
|
|
223
|
+
/invalid JSON string/.test(
|
|
224
|
+
_codeOf(function () { new Query(db, "t").where("meta", "@>", "{not json"); })));
|
|
225
|
+
check("@> rejects an object with a control-char string leaf",
|
|
226
|
+
_threw(function () { new Query(db, "t").where("meta", "@>", { a: "" }); }));
|
|
227
|
+
|
|
228
|
+
// JSONB key-existence operators.
|
|
229
|
+
check("? accepts a string key", !_threw(function () { new Query(db, "t").where("meta", "?", "k"); }));
|
|
230
|
+
check("? rejects a non-string key",
|
|
231
|
+
_threw(function () { new Query(db, "t").where("meta", "?", 123); }));
|
|
232
|
+
check("?| accepts a non-empty array of keys",
|
|
233
|
+
!_threw(function () { new Query(db, "t").where("meta", "?|", ["k1", "k2"]); }));
|
|
234
|
+
check("?| rejects a non-array value",
|
|
235
|
+
_threw(function () { new Query(db, "t").where("meta", "?|", "k"); }));
|
|
236
|
+
check("?& rejects an empty array",
|
|
237
|
+
_threw(function () { new Query(db, "t").where("meta", "?&", []); }));
|
|
238
|
+
check("?& accepts a non-empty array of keys",
|
|
239
|
+
!_threw(function () { new Query(db, "t").where("meta", "?&", ["k"]); }));
|
|
240
|
+
|
|
241
|
+
// where() arg-shape variants + whereNull / whereNotNull recording.
|
|
242
|
+
check("where(field, value) 2-arg shorthand records",
|
|
243
|
+
!_threw(function () { new Query(db, "t").where("c", "v"); }));
|
|
244
|
+
check("whereNull / whereNotNull record IS / IS NOT predicates",
|
|
245
|
+
!_threw(function () { new Query(db, "t").whereNull("c").whereNotNull("d"); }));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function testWhereRawValidation() {
|
|
249
|
+
var db = _recordingDb();
|
|
250
|
+
check("whereRaw with a non-string sql rejected",
|
|
251
|
+
_threw(function () { new Query(db, "t").whereRaw(123); }));
|
|
252
|
+
check("whereRaw with an empty sql rejected",
|
|
253
|
+
_threw(function () { new Query(db, "t").whereRaw(""); }));
|
|
254
|
+
check("whereRaw placeholder/param count mismatch rejected",
|
|
255
|
+
/placeholder/.test(_codeOf(function () { new Query(db, "t").whereRaw("a = ?", []); })));
|
|
256
|
+
check("whereRaw coerces a scalar param to a single-element array",
|
|
257
|
+
!_threw(function () { new Query(db, "t").whereRaw("a = ?", 5); }));
|
|
258
|
+
check("whereRaw treats null params as an empty list",
|
|
259
|
+
!_threw(function () { new Query(db, "t").whereRaw("a IS NOT NULL", null); }));
|
|
260
|
+
check("whereRaw refuses an embedded string literal by default",
|
|
261
|
+
_threw(function () { new Query(db, "t").whereRaw("name = 'x'"); }));
|
|
262
|
+
check("whereRaw allows an embedded literal under allowLiterals:true",
|
|
263
|
+
!_threw(function () { new Query(db, "t").whereRaw("name = 'x'", [], { allowLiterals: true }); }));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function testSelectOrderLimitOffsetValidation() {
|
|
267
|
+
var db = _recordingDb();
|
|
268
|
+
check("select(non-array) rejected",
|
|
269
|
+
_threw(function () { new Query(db, "t").select("c"); }));
|
|
270
|
+
check("orderBy invalid direction rejected",
|
|
271
|
+
/asc.*desc|desc/.test(_codeOf(function () { new Query(db, "t").orderBy("c", "sideways"); })));
|
|
272
|
+
|
|
273
|
+
// Multi-column orderBy: first call single-object shape, second promotes to
|
|
274
|
+
// array, third pushes.
|
|
275
|
+
var q = new Query(db, "t");
|
|
276
|
+
q.orderBy("a");
|
|
277
|
+
check("first orderBy keeps the single-object shape", q._orderBy && q._orderBy.field === "a");
|
|
278
|
+
q.orderBy("b", "desc");
|
|
279
|
+
check("second orderBy promotes to an array", Array.isArray(q._orderBy) && q._orderBy.length === 2);
|
|
280
|
+
q.orderBy("c");
|
|
281
|
+
check("third orderBy pushes onto the array", q._orderBy.length === 3);
|
|
282
|
+
|
|
283
|
+
check("limit(non-integer) rejected", _threw(function () { new Query(db, "t").limit(1.5); }));
|
|
284
|
+
check("limit(negative) rejected", _threw(function () { new Query(db, "t").limit(-1); }));
|
|
285
|
+
check("offset(non-integer) rejected", _threw(function () { new Query(db, "t").offset(1.5); }));
|
|
286
|
+
check("offset(negative) rejected", _threw(function () { new Query(db, "t").offset(-1); }));
|
|
287
|
+
|
|
288
|
+
// Full SELECT clause assembly emits through b.sql.
|
|
289
|
+
var db2 = _recordingDb();
|
|
290
|
+
new Query(db2, "t").where({ a: 1 }).select(["a", "b"]).orderBy("a", "desc").limit(5).offset(10).all();
|
|
291
|
+
check("select+orderBy+limit+offset assemble a SELECT",
|
|
292
|
+
db2.prepared.some(function (s) { return /ORDER BY "a" DESC/.test(s) && /LIMIT/.test(s); }));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function testWhereGroupOrWhereValidation() {
|
|
296
|
+
var db = _recordingDb();
|
|
297
|
+
check("whereGroup(non-function) rejected",
|
|
298
|
+
_threw(function () { new Query(db, "t").whereGroup("x"); }));
|
|
299
|
+
check("whereGroup(empty closure) is a no-op (chainable)",
|
|
300
|
+
new Query(db, "t").whereGroup(function () {}) instanceof Query);
|
|
301
|
+
check("orWhere without a prior where rejected",
|
|
302
|
+
/no prior where/.test(_codeOf(function () { new Query(db, "t").orWhere({ a: 1 }); })));
|
|
303
|
+
|
|
304
|
+
// WhereBuilder shape validation + build() back-compat shim, exercised
|
|
305
|
+
// inside a whereGroup closure (the only way to reach a WhereBuilder).
|
|
306
|
+
var built = null, empty = null, badField = false, badIn = false, badRawStr = false, badRawCount = false;
|
|
307
|
+
new Query(db, "t").whereGroup(function (qb) {
|
|
308
|
+
empty = qb.build(); // 0 parts → { sql:"", params:[] }
|
|
309
|
+
badField = _threw(function () { qb.eq(123, "v"); });
|
|
310
|
+
badIn = _threw(function () { qb.in("a", "notarray"); });
|
|
311
|
+
badRawStr = _threw(function () { qb.raw(123); });
|
|
312
|
+
badRawCount = _threw(function () { qb.raw("a = ?", []); });
|
|
313
|
+
// WhereBuilder.raw param coercion + allowLiterals opt-out.
|
|
314
|
+
qb.raw("m = ?", 5); // scalar coerced to [5]
|
|
315
|
+
qb.raw("n IS NULL", null); // null → []
|
|
316
|
+
qb.raw("p = 'lit'", [], { allowLiterals: true }); // static literal allowed
|
|
317
|
+
qb.eq("a", 1).neq("b", 2).gt("c", 3).gte("d", 4).lt("e", 5).lte("f", 6)
|
|
318
|
+
.in("g", [1, 2]).like("h", "x%")
|
|
319
|
+
.orEq("a", 9).orNeq("b", 8).orGt("c", 7).orGte("d", 6).orLt("e", 5).orLte("f", 4)
|
|
320
|
+
.orIn("g", [3]).orLike("h", "y%")
|
|
321
|
+
.raw("j = ?", [1]);
|
|
322
|
+
built = qb.build(); // non-empty → fragment + params
|
|
323
|
+
}).all();
|
|
324
|
+
check("WhereBuilder.build() returns an empty fragment for no parts",
|
|
325
|
+
empty && empty.sql === "" && empty.params.length === 0);
|
|
326
|
+
check("WhereBuilder.build() returns a fragment once parts exist",
|
|
327
|
+
built && built.sql.length > 0 && built.params.length > 0);
|
|
328
|
+
check("WhereBuilder rejects a non-string field", badField);
|
|
329
|
+
check("WhereBuilder rejects a non-array IN value", badIn);
|
|
330
|
+
check("WhereBuilder.raw rejects a non-string sql", badRawStr);
|
|
331
|
+
check("WhereBuilder.raw rejects a placeholder/param count mismatch", badRawCount);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function testCryptoFieldKeyFallback() {
|
|
335
|
+
// Schema-qualified table with no per-schema sealed registration falls back
|
|
336
|
+
// to the bare table name for the sealed-field registry lookup.
|
|
337
|
+
var db = _recordingDb();
|
|
338
|
+
var q = new Query(db, "audit.events");
|
|
339
|
+
check("schema-qualified key falls back to the bare table", q._cryptoFieldKey() === "events");
|
|
340
|
+
q.where({ a: 1 }).first();
|
|
341
|
+
check("schema-qualified SELECT emits the quoted two-part name",
|
|
342
|
+
db.prepared.some(function (s) { return /"audit"\."events"/.test(s); }));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function testIncrementBranches() {
|
|
346
|
+
// increment() runs entirely on the fake handle: column/delta validation
|
|
347
|
+
// (throws before any SQL), then the COALESCE emission.
|
|
348
|
+
var db = _recordingDb({ changes: 1 });
|
|
349
|
+
check("increment(non-string column) rejected",
|
|
350
|
+
_threw(function () { new Query(db, "t").where({ a: 1 }).increment(123, 1); }));
|
|
351
|
+
check("increment(non-integer delta) rejected",
|
|
352
|
+
/finite integer/.test(
|
|
353
|
+
_codeOf(function () { new Query(db, "t").where({ a: 1 }).increment("cnt", 1.5); })));
|
|
354
|
+
check("increment without a where(...) refused",
|
|
355
|
+
/unconditional/.test(_codeOf(function () { new Query(db, "t").increment("cnt", 1); })));
|
|
356
|
+
|
|
357
|
+
var n = new Query(db, "t").where({ a: 1 }).increment("cnt", 5);
|
|
358
|
+
check("increment returns rows-changed", n === 1);
|
|
359
|
+
check("increment emits a COALESCE(col,0)+? expression",
|
|
360
|
+
db.prepared.some(function (s) { return /COALESCE\("cnt", 0\) \+ \?/.test(s); }));
|
|
361
|
+
|
|
362
|
+
db = _recordingDb({ changes: 1 });
|
|
363
|
+
new Query(db, "t").where({ a: 1 }).increment("cnt");
|
|
364
|
+
check("increment default delta is 1 (emits COALESCE)",
|
|
365
|
+
db.prepared.some(function (s) { return /COALESCE/.test(s); }));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function testStreamValidationOnFakeHandle() {
|
|
369
|
+
// iterate() throwing during stream setup destroys the returned Readable
|
|
370
|
+
// with that error rather than throwing synchronously. getStreamLimit is
|
|
371
|
+
// read from the live db module, so run under an initialized db.
|
|
372
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-stream-fake-"));
|
|
373
|
+
try {
|
|
374
|
+
await setupTestDb(tmpDir);
|
|
375
|
+
var db = _recordingDb({ iterateThrows: true });
|
|
376
|
+
var err = await _streamError(new Query(db, "users").stream());
|
|
377
|
+
check("stream() surfaces an iterate() failure as a stream error",
|
|
378
|
+
err && /iterate boom/.test(String(err.message)));
|
|
379
|
+
|
|
380
|
+
// A throw DURING iteration (iter.next()) is caught inside the read pump
|
|
381
|
+
// and destroys the stream rather than crashing the process.
|
|
382
|
+
var db2 = _recordingDb({ nextThrows: true });
|
|
383
|
+
var err2 = await _streamError(new Query(db2, "users").stream());
|
|
384
|
+
check("stream() catches a mid-iteration throw and errors the stream",
|
|
385
|
+
err2 && /next boom/.test(String(err2.message)));
|
|
386
|
+
} finally {
|
|
387
|
+
await teardownTestDb(tmpDir);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---------------------------------------------------------------------------
|
|
392
|
+
// Real-execution group (setupTestDb — default users schema).
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
async function testInsertUpdateDeleteExecution() {
|
|
396
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-crud-"));
|
|
397
|
+
try {
|
|
398
|
+
await setupTestDb(tmpDir);
|
|
399
|
+
|
|
400
|
+
check("insertOne(non-object) rejected",
|
|
401
|
+
_threw(function () { b.db.from("users").insertOne("nope"); }));
|
|
402
|
+
check("insertMany(non-array) rejected",
|
|
403
|
+
_threw(function () { b.db.from("users").insertMany("nope"); }));
|
|
404
|
+
|
|
405
|
+
// _id auto-generated when omitted.
|
|
406
|
+
var made = b.db.from("users").insertOne({ email: "auto@example.com", name: "Auto" });
|
|
407
|
+
check("insertOne auto-generates an _id", typeof made._id === "string" && made._id.length > 0);
|
|
408
|
+
|
|
409
|
+
// insertMany over a loop.
|
|
410
|
+
var many = b.db.from("users").insertMany([
|
|
411
|
+
{ _id: "m1", email: "m1@example.com", name: "M1" },
|
|
412
|
+
{ _id: "m2", email: "m2@example.com", name: "M2" },
|
|
413
|
+
]);
|
|
414
|
+
check("insertMany returns one filled row per input", many.length === 2 && many[0]._id === "m1");
|
|
415
|
+
|
|
416
|
+
// count(): populated + filtered-to-zero.
|
|
417
|
+
check("count() reflects inserted rows", b.db.from("users").count() >= 3);
|
|
418
|
+
check("count() is 0 when nothing matches",
|
|
419
|
+
b.db.from("users").where({ _id: "does-not-exist" }).count() === 0);
|
|
420
|
+
check("first() returns null when nothing matches",
|
|
421
|
+
b.db.from("users").where({ _id: "does-not-exist" }).first() === null);
|
|
422
|
+
|
|
423
|
+
// Unconditional update / delete are refused.
|
|
424
|
+
check("unconditional update refused",
|
|
425
|
+
/unconditional update/.test(_codeOf(function () { b.db.from("users").updateMany({ name: "x" }); })));
|
|
426
|
+
check("unconditional delete refused",
|
|
427
|
+
/unconditional delete/.test(_codeOf(function () { b.db.from("users").deleteMany(); })));
|
|
428
|
+
|
|
429
|
+
// update requires a non-object → throw; empty change set → throw.
|
|
430
|
+
check("update(non-object) rejected",
|
|
431
|
+
_threw(function () { b.db.from("users").where({ _id: "m1" }).updateMany(5); }));
|
|
432
|
+
check("update with an empty change set rejected",
|
|
433
|
+
/empty/.test(_codeOf(function () { b.db.from("users").where({ _id: "m1" }).updateOne({}); })));
|
|
434
|
+
|
|
435
|
+
// updateOne single-row (sqlite rowid idiom): matched + unmatched.
|
|
436
|
+
check("updateOne returns true when a row matched",
|
|
437
|
+
b.db.from("users").where({ _id: "m1" }).updateOne({ name: "M1b" }) === true);
|
|
438
|
+
check("updateOne re-read shows the change",
|
|
439
|
+
b.db.from("users").where({ _id: "m1" }).first().name === "M1b");
|
|
440
|
+
check("updateOne returns false when no row matched",
|
|
441
|
+
b.db.from("users").where({ _id: "ghost" }).updateOne({ name: "z" }) === false);
|
|
442
|
+
|
|
443
|
+
// updateMany returns the changed count.
|
|
444
|
+
var changed = b.db.from("users").where({ status: "active" }).updateMany({ status: "active" });
|
|
445
|
+
check("updateMany returns a numeric changed count", typeof changed === "number");
|
|
446
|
+
|
|
447
|
+
// deleteOne single-row + deleteMany.
|
|
448
|
+
check("deleteOne returns true when a row matched",
|
|
449
|
+
b.db.from("users").where({ _id: "m2" }).deleteOne() === true);
|
|
450
|
+
check("deleteOne returns false when no row matched",
|
|
451
|
+
b.db.from("users").where({ _id: "m2" }).deleteOne() === false);
|
|
452
|
+
var delN = b.db.from("users").where({ _id: "m1" }).deleteMany();
|
|
453
|
+
check("deleteMany returns the deleted count", delN === 1);
|
|
454
|
+
|
|
455
|
+
// Multi-column ORDER BY reaches the array-of-entries clause path.
|
|
456
|
+
check("multi-column orderBy assembles and returns rows",
|
|
457
|
+
b.db.from("users").orderBy("status", "asc").orderBy("_id", "desc").all().length >= 1);
|
|
458
|
+
|
|
459
|
+
// whereNull / whereNotNull execute (createdAt is omitted → NULL).
|
|
460
|
+
check("whereNull finds rows with a NULL column",
|
|
461
|
+
b.db.from("users").whereNull("createdAt").all().length >= 1);
|
|
462
|
+
check("whereNotNull finds no rows when the column is always NULL",
|
|
463
|
+
b.db.from("users").whereNotNull("createdAt").all().length === 0);
|
|
464
|
+
} finally {
|
|
465
|
+
await teardownTestDb(tmpDir);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async function testSealedFieldQuery() {
|
|
470
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-sealed-"));
|
|
471
|
+
try {
|
|
472
|
+
await setupTestDb(tmpDir);
|
|
473
|
+
b.db.from("users").insertOne({ _id: "s1", email: "alice@example.com", name: "Alice" });
|
|
474
|
+
|
|
475
|
+
// Equality on a sealed field rewrites to the derived hash + dual-reads
|
|
476
|
+
// the legacy digest (op "=" → IN of [active, legacy]).
|
|
477
|
+
var eq = b.db.from("users").where("email", "=", "alice@example.com").all();
|
|
478
|
+
check("sealed-field equality finds the row via the derived hash",
|
|
479
|
+
eq.length === 1 && eq[0]._id === "s1");
|
|
480
|
+
|
|
481
|
+
// Non-"=" operator on a sealed field takes the scalar-value branch.
|
|
482
|
+
check("sealed-field != executes without error",
|
|
483
|
+
!_threw(function () { b.db.from("users").where("email", "!=", "nobody@example.com").all(); }));
|
|
484
|
+
|
|
485
|
+
// whereIn on a sealed field maps each element through the hash.
|
|
486
|
+
var inRows = b.db.from("users").whereIn("email", ["alice@example.com"]).all();
|
|
487
|
+
check("sealed-field whereIn finds matching rows",
|
|
488
|
+
inRows.length === 1 && inRows[0]._id === "s1");
|
|
489
|
+
|
|
490
|
+
// A sealed column with NO derived hash (name) cannot be queried.
|
|
491
|
+
check("sealed field without a derived hash refuses equality",
|
|
492
|
+
/derived hash/.test(_codeOf(function () { b.db.from("users").where("name", "=", "Alice").all(); })));
|
|
493
|
+
check("sealed field without a derived hash refuses whereIn",
|
|
494
|
+
/derived hash/.test(_codeOf(function () { b.db.from("users").whereIn("name", ["Alice"]).all(); })));
|
|
495
|
+
// Empty IN array on a sealed field is refused too.
|
|
496
|
+
check("sealed field whereIn with an empty array rejected",
|
|
497
|
+
_threw(function () { b.db.from("users").whereIn("email", []).all(); }));
|
|
498
|
+
} finally {
|
|
499
|
+
await teardownTestDb(tmpDir);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
async function testStreamExecution() {
|
|
504
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-stream-"));
|
|
505
|
+
try {
|
|
506
|
+
await setupTestDb(tmpDir);
|
|
507
|
+
b.db.from("users").insertOne({ _id: "r1", email: "r1@example.com", name: "R1" });
|
|
508
|
+
b.db.from("users").insertOne({ _id: "r2", email: "r2@example.com", name: "R2" });
|
|
509
|
+
b.db.from("users").insertOne({ _id: "r3", email: "r3@example.com", name: "R3" });
|
|
510
|
+
|
|
511
|
+
// Normal stream: every row auto-unsealed.
|
|
512
|
+
var rows = await _collect(b.db.from("users").stream());
|
|
513
|
+
check("stream() yields every row unsealed", rows.length === 3 && rows[0].email.indexOf("@") !== -1);
|
|
514
|
+
|
|
515
|
+
// Per-call streamLimit override under the ceiling still yields all rows.
|
|
516
|
+
var rowsCapped = await _collect(b.db.from("users").stream({ streamLimit: 100 }));
|
|
517
|
+
check("stream() honours a per-call streamLimit override", rowsCapped.length === 3);
|
|
518
|
+
|
|
519
|
+
// streamLimit exceeded → the Readable is destroyed with an error.
|
|
520
|
+
var streamErr = null;
|
|
521
|
+
try { await _collect(b.db.from("users").stream({ streamLimit: 1 })); }
|
|
522
|
+
catch (e) { streamErr = e; }
|
|
523
|
+
check("stream() destroys with an error past the streamLimit",
|
|
524
|
+
streamErr && /exceeding streamLimit/.test(String(streamErr.message)));
|
|
525
|
+
|
|
526
|
+
// Bad per-call streamLimit values are rejected at setup.
|
|
527
|
+
check("stream({streamLimit:0}) rejected", _threw(function () { b.db.from("users").stream({ streamLimit: 0 }); }));
|
|
528
|
+
check("stream({streamLimit:-5}) rejected", _threw(function () { b.db.from("users").stream({ streamLimit: -5 }); }));
|
|
529
|
+
check("stream({streamLimit:1.5}) rejected", _threw(function () { b.db.from("users").stream({ streamLimit: 1.5 }); }));
|
|
530
|
+
} finally {
|
|
531
|
+
await teardownTestDb(tmpDir);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
async function testWhereGroupOrWhereExecution() {
|
|
536
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-group-"));
|
|
537
|
+
try {
|
|
538
|
+
await setupTestDb(tmpDir);
|
|
539
|
+
b.db.from("users").insertOne({ _id: "a", email: "a@x.com", name: "A", status: "active" });
|
|
540
|
+
b.db.from("users").insertOne({ _id: "p", email: "p@x.com", name: "P", status: "pending" });
|
|
541
|
+
b.db.from("users").insertOne({ _id: "c", email: "c@x.com", name: "C", status: "closed" });
|
|
542
|
+
|
|
543
|
+
// whereGroup with an AND/OR sub-expression + LIKE + IN replay paths.
|
|
544
|
+
var grouped = b.db.from("users").whereGroup(function (qb) {
|
|
545
|
+
qb.eq("status", "active").orEq("status", "pending").orLike("status", "clos%").orIn("_id", ["c"]);
|
|
546
|
+
}).all();
|
|
547
|
+
check("whereGroup replays eq/orEq/orLike/orIn", grouped.length === 3);
|
|
548
|
+
|
|
549
|
+
// whereGroup empty closure is a no-op (returns all rows).
|
|
550
|
+
check("whereGroup(empty) matches everything",
|
|
551
|
+
b.db.from("users").whereGroup(function () {}).all().length === 3);
|
|
552
|
+
|
|
553
|
+
// whereGroup with an AND LIKE + AND raw path. LIKE / raw run against the
|
|
554
|
+
// stored bytes, so match on non-sealed columns (status / _id), not the
|
|
555
|
+
// sealed email / name (which hold ciphertext).
|
|
556
|
+
var likeAnd = b.db.from("users").where({ status: "active" }).whereGroup(function (qb) {
|
|
557
|
+
qb.like("status", "act%").raw('"_id" = ?', ["a"]);
|
|
558
|
+
}).all();
|
|
559
|
+
check("whereGroup AND-replays like + raw", likeAnd.length === 1 && likeAnd[0]._id === "a");
|
|
560
|
+
|
|
561
|
+
// orWhere — object map, 2-arg, 3-arg, closure, empty closure.
|
|
562
|
+
check("orWhere object-map form",
|
|
563
|
+
b.db.from("users").where({ status: "active" }).orWhere({ status: "pending" }).all().length === 2);
|
|
564
|
+
check("orWhere 2-arg shorthand",
|
|
565
|
+
b.db.from("users").where({ status: "active" }).orWhere("status", "closed").all().length === 2);
|
|
566
|
+
check("orWhere 3-arg operator form",
|
|
567
|
+
b.db.from("users").where({ status: "active" }).orWhere("_id", "=", "c").all().length === 2);
|
|
568
|
+
check("orWhere with an IN operator OR-joins a membership predicate",
|
|
569
|
+
b.db.from("users").where({ status: "active" }).orWhere("_id", "IN", ["c"]).all().length === 2);
|
|
570
|
+
check("orWhere closure form",
|
|
571
|
+
b.db.from("users").where({ status: "active" })
|
|
572
|
+
.orWhere(function (qb) { qb.eq("status", "pending"); }).all().length === 2);
|
|
573
|
+
check("orWhere empty closure restores the prior leaf",
|
|
574
|
+
b.db.from("users").where({ status: "active" }).orWhere(function () {}).all().length === 1);
|
|
575
|
+
|
|
576
|
+
// search() modes + validation. Search runs LIKE on the stored bytes, so
|
|
577
|
+
// target the non-sealed `status` column (name / email hold ciphertext).
|
|
578
|
+
check("search substring matches", b.db.from("users").search(["status"], "ctiv").all().length === 1);
|
|
579
|
+
check("search across multiple fields OR-joins the LIKE clauses",
|
|
580
|
+
b.db.from("users").search(["status", "_id"], "a").all().length >= 1);
|
|
581
|
+
check("search prefix mode", b.db.from("users").search(["status"], "act", { match: "prefix" }).all().length === 1);
|
|
582
|
+
check("search exact mode", b.db.from("users").search(["status"], "active", { match: "exact" }).all().length === 1);
|
|
583
|
+
check("search empty term is a no-op", b.db.from("users").search(["status"], "").all().length === 3);
|
|
584
|
+
check("search(non-array fields) rejected", _threw(function () { b.db.from("users").search("status", "x"); }));
|
|
585
|
+
check("search(non-string term) rejected", _threw(function () { b.db.from("users").search(["status"], 5); }));
|
|
586
|
+
check("search(bad match mode) rejected",
|
|
587
|
+
/substring.*prefix.*exact|match/.test(
|
|
588
|
+
_codeOf(function () { b.db.from("users").search(["status"], "x", { match: "fuzzy" }); })));
|
|
589
|
+
check("search(undefined term) is a no-op",
|
|
590
|
+
b.db.from("users").search(["status"], undefined).all().length === 3);
|
|
591
|
+
|
|
592
|
+
// paginate — defaults + orderDir / orderDirection + opts refusals.
|
|
593
|
+
var pg = b.db.from("users").where({ status: "active" }).paginate();
|
|
594
|
+
check("paginate default limit/offset returns an envelope", pg.limit === 25 && pg.offset === 0);
|
|
595
|
+
var pgDir = b.db.from("users").paginate({ orderBy: "status", orderDirection: "desc" });
|
|
596
|
+
check("paginate honours orderDirection", pgDir.items.length === 3);
|
|
597
|
+
var pgDir2 = b.db.from("users").paginate({ orderBy: "status", orderDir: "asc" });
|
|
598
|
+
check("paginate honours orderDir", pgDir2.items.length === 3);
|
|
599
|
+
var pgDir3 = b.db.from("users").paginate({ orderBy: "status" });
|
|
600
|
+
check("paginate defaults order direction to asc", pgDir3.items.length === 3);
|
|
601
|
+
|
|
602
|
+
// whereRaw executes end-to-end through the terminal (the recorded leaf is
|
|
603
|
+
// replayed onto the b.sql builder).
|
|
604
|
+
check("whereRaw executes and filters through the real terminal",
|
|
605
|
+
b.db.from("users").whereRaw('"status" = ?', ["active"]).all().length === 1);
|
|
606
|
+
check("paginate refuses limit=0", _threw(function () { b.db.from("users").paginate({ limit: 0 }); }));
|
|
607
|
+
check("paginate refuses limit>1000", _threw(function () { b.db.from("users").paginate({ limit: 1001 }); }));
|
|
608
|
+
check("paginate refuses negative offset",
|
|
609
|
+
_threw(function () { b.db.from("users").paginate({ limit: 5, offset: -1 }); }));
|
|
610
|
+
} finally {
|
|
611
|
+
await teardownTestDb(tmpDir);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// ---------------------------------------------------------------------------
|
|
616
|
+
// Per-row-key writes (declarePerRowKey — the insertOne / _updatePerRowKey path).
|
|
617
|
+
// ---------------------------------------------------------------------------
|
|
618
|
+
|
|
619
|
+
var KEYED_SCHEMA = [{
|
|
620
|
+
name: "vault_rows",
|
|
621
|
+
columns: { _id: "TEXT PRIMARY KEY", subjectId: "TEXT", secret: "TEXT" },
|
|
622
|
+
indexes: ["subjectId"],
|
|
623
|
+
sealedFields: ["secret"],
|
|
624
|
+
subjectField: "subjectId",
|
|
625
|
+
}];
|
|
626
|
+
|
|
627
|
+
async function testPerRowKeyWrites() {
|
|
628
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-prk-"));
|
|
629
|
+
try {
|
|
630
|
+
await setupTestDb(tmpDir, KEYED_SCHEMA);
|
|
631
|
+
b.cryptoField.clearResidencyForTest();
|
|
632
|
+
b.cryptoField.declarePerRowKey("vault_rows", { keySize: 32 });
|
|
633
|
+
|
|
634
|
+
// INSERT materializes a K_row and seals the sealed column under it.
|
|
635
|
+
b.db.from("vault_rows").insertOne({ _id: "k1", subjectId: "subj-A", secret: "top-secret" });
|
|
636
|
+
b.db.from("vault_rows").insertOne({ _id: "k2", subjectId: "subj-A", secret: "second-secret" });
|
|
637
|
+
check("per-row-key insert round-trips the sealed cell",
|
|
638
|
+
b.db.from("vault_rows").where({ _id: "k1" }).first().secret === "top-secret");
|
|
639
|
+
|
|
640
|
+
// updateOne on a per-row-key table re-seals under the row's own K_row.
|
|
641
|
+
var one = b.db.from("vault_rows").where({ _id: "k1" }).updateOne({ secret: "rotated" });
|
|
642
|
+
check("per-row-key updateOne returns true", one === true);
|
|
643
|
+
check("per-row-key updateOne re-seal round-trips",
|
|
644
|
+
b.db.from("vault_rows").where({ _id: "k1" }).first().secret === "rotated");
|
|
645
|
+
|
|
646
|
+
// updateMany on a per-row-key table walks every matched row.
|
|
647
|
+
var many = b.db.from("vault_rows").where({ subjectId: "subj-A" }).updateMany({ secret: "bulk" });
|
|
648
|
+
check("per-row-key updateMany re-seals every matched row", many === 2);
|
|
649
|
+
check("per-row-key updateMany changed both rows",
|
|
650
|
+
b.db.from("vault_rows").where({ _id: "k2" }).first().secret === "bulk");
|
|
651
|
+
|
|
652
|
+
// Empty change set on a per-row-key update is still refused.
|
|
653
|
+
check("per-row-key update with an empty change set rejected",
|
|
654
|
+
/empty/.test(_codeOf(function () {
|
|
655
|
+
b.db.from("vault_rows").where({ _id: "k1" }).updateMany({});
|
|
656
|
+
})));
|
|
657
|
+
} finally {
|
|
658
|
+
try { b.cryptoField.clearResidencyForTest(); } catch (_e) {}
|
|
659
|
+
await teardownTestDb(tmpDir);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ---------------------------------------------------------------------------
|
|
664
|
+
// Residency write-gate + raw-write residency helpers (dedicated db.init).
|
|
665
|
+
// ---------------------------------------------------------------------------
|
|
666
|
+
|
|
667
|
+
var RESIDENCY_SCHEMA = [
|
|
668
|
+
{ name: "residents", columns: { _id: "TEXT PRIMARY KEY", name: "TEXT", dataRegion: "TEXT" } },
|
|
669
|
+
{ name: "colnotes", columns: { _id: "TEXT PRIMARY KEY", note: "TEXT" } },
|
|
670
|
+
{ name: "notes", columns: { _id: "TEXT PRIMARY KEY", body: "TEXT" } },
|
|
671
|
+
];
|
|
672
|
+
|
|
673
|
+
async function _initResidencyDb(tmpDir) {
|
|
674
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
675
|
+
helpers.setTestPassphraseEnv();
|
|
676
|
+
b.cluster._resetForTest();
|
|
677
|
+
b.audit._resetForTest();
|
|
678
|
+
b.vault._resetForTest();
|
|
679
|
+
b.db._resetForTest();
|
|
680
|
+
await b.vault.init({ dataDir: tmpDir });
|
|
681
|
+
await b.db.init({
|
|
682
|
+
dataDir: tmpDir,
|
|
683
|
+
tmpDir: path.join(tmpDir, "tmpfs"),
|
|
684
|
+
allowNonTmpfsTmpDir: true,
|
|
685
|
+
schema: RESIDENCY_SCHEMA,
|
|
686
|
+
dataResidency: { region: "eu-west-1", allowedStorageRegions: ["eu-central-1"] },
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async function _teardownResidencyDb(tmpDir) {
|
|
691
|
+
try { b.compliance.clear(); } catch (_e) {}
|
|
692
|
+
try { b.cryptoField.clearResidencyForTest(); } catch (_e) {}
|
|
693
|
+
try { await b.audit.flush(); } catch (_e) {}
|
|
694
|
+
try { b.db.close(); } catch (_e) {}
|
|
695
|
+
b.audit._resetForTest();
|
|
696
|
+
b.db._resetForTest();
|
|
697
|
+
b.vault._resetForTest();
|
|
698
|
+
b.cluster._resetForTest();
|
|
699
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (_e) {}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
async function testResidencyGates() {
|
|
703
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-resid-"));
|
|
704
|
+
try {
|
|
705
|
+
await _initResidencyDb(tmpDir);
|
|
706
|
+
b.cryptoField.clearResidencyForTest();
|
|
707
|
+
b.cryptoField.declarePerRowResidency("residents", {
|
|
708
|
+
residencyColumn: "dataRegion",
|
|
709
|
+
allowedTags: ["eu-west-1", "us-east-1", "eu-central-1", "global"],
|
|
710
|
+
});
|
|
711
|
+
b.cryptoField.declareColumnResidency("colnotes", { columnResidency: { note: "us-east-1" } });
|
|
712
|
+
|
|
713
|
+
// ----- regulated posture (GDPR) -----
|
|
714
|
+
b.compliance.set("gdpr");
|
|
715
|
+
|
|
716
|
+
check("insert missing residency tag refused",
|
|
717
|
+
_codeOf(function () { b.db.from("residents").insertOne({ _id: "r0", name: "x" }); })
|
|
718
|
+
=== "db-query/row-residency-tag-missing");
|
|
719
|
+
check("insert with a tag outside allowedTags refused",
|
|
720
|
+
_codeOf(function () {
|
|
721
|
+
b.db.from("residents").insertOne({ _id: "r0", name: "x", dataRegion: "zz-nope" });
|
|
722
|
+
}) === "db-query/row-residency-tag-invalid");
|
|
723
|
+
check("insert with a cross-border tag refused under a regulated posture",
|
|
724
|
+
_codeOf(function () {
|
|
725
|
+
b.db.from("residents").insertOne({ _id: "r0", name: "x", dataRegion: "us-east-1" });
|
|
726
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
727
|
+
check("the refused cross-border row did not persist",
|
|
728
|
+
b.db.from("residents").where({ _id: "r0" }).first() === null);
|
|
729
|
+
|
|
730
|
+
// In-region + allowedStorageRegions + global all pass.
|
|
731
|
+
b.db.from("residents").insertOne({ _id: "eu1", name: "in-region", dataRegion: "eu-west-1" });
|
|
732
|
+
check("in-region insert persists", b.db.from("residents").where({ _id: "eu1" }).first().name === "in-region");
|
|
733
|
+
b.db.from("residents").insertOne({ _id: "ec1", name: "allowed-store", dataRegion: "eu-central-1" });
|
|
734
|
+
check("allowedStorageRegions tag persists",
|
|
735
|
+
b.db.from("residents").where({ _id: "ec1" }).first() !== null);
|
|
736
|
+
b.db.from("residents").insertOne({ _id: "gl1", name: "global-row", dataRegion: "global" });
|
|
737
|
+
check("global tag bypasses the region check",
|
|
738
|
+
b.db.from("residents").where({ _id: "gl1" }).first() !== null);
|
|
739
|
+
|
|
740
|
+
// UPDATE that clears the residency column is refused; one that leaves it
|
|
741
|
+
// untouched passes; one that moves it cross-border is refused.
|
|
742
|
+
check("update clearing the residency tag refused",
|
|
743
|
+
_codeOf(function () {
|
|
744
|
+
b.db.from("residents").where({ _id: "eu1" }).updateOne({ dataRegion: null });
|
|
745
|
+
}) === "db-query/row-residency-tag-missing");
|
|
746
|
+
check("update not touching the residency column passes",
|
|
747
|
+
b.db.from("residents").where({ _id: "eu1" }).updateOne({ name: "renamed" }) === true);
|
|
748
|
+
check("update moving the row cross-border refused",
|
|
749
|
+
_codeOf(function () {
|
|
750
|
+
b.db.from("residents").where({ _id: "eu1" }).updateOne({ dataRegion: "us-east-1" });
|
|
751
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
752
|
+
|
|
753
|
+
// Column-residency gate.
|
|
754
|
+
check("column-residency mismatch refused under a regulated posture",
|
|
755
|
+
_codeOf(function () { b.db.from("colnotes").insertOne({ _id: "cn1", note: "hello" }); })
|
|
756
|
+
=== "db-query/column-residency-mismatch");
|
|
757
|
+
check("column-residency passes when the bound column is null",
|
|
758
|
+
!_threw(function () { b.db.from("colnotes").insertOne({ _id: "cn0", note: null }); }));
|
|
759
|
+
|
|
760
|
+
// ----- unregulated posture — advisory, writes pass -----
|
|
761
|
+
// A runtime posture switch is forbidden; clear then re-pin.
|
|
762
|
+
b.compliance.clear();
|
|
763
|
+
b.compliance.set("soc2");
|
|
764
|
+
check("cross-border tag passes (advisory) under an unregulated posture",
|
|
765
|
+
!_threw(function () {
|
|
766
|
+
b.db.from("residents").insertOne({ _id: "adv1", name: "advisory", dataRegion: "us-east-1" });
|
|
767
|
+
}));
|
|
768
|
+
check("advisory cross-border row DID persist",
|
|
769
|
+
b.db.from("residents").where({ _id: "adv1" }).first() !== null);
|
|
770
|
+
check("column-residency passes (advisory) under an unregulated posture",
|
|
771
|
+
!_threw(function () { b.db.from("colnotes").insertOne({ _id: "cn2", note: "world" }); }));
|
|
772
|
+
|
|
773
|
+
// ----- no posture pinned at all — advisory with a null posture -----
|
|
774
|
+
b.compliance.clear();
|
|
775
|
+
check("per-row cross-border tag is advisory when no posture is pinned",
|
|
776
|
+
!_threw(function () {
|
|
777
|
+
b.db.from("residents").insertOne({ _id: "np1", name: "np", dataRegion: "us-east-1" });
|
|
778
|
+
}));
|
|
779
|
+
check("column-residency mismatch is advisory when no posture is pinned",
|
|
780
|
+
!_threw(function () { b.db.from("colnotes").insertOne({ _id: "npc", note: "x" }); }));
|
|
781
|
+
|
|
782
|
+
// ----- raw-write residency helpers (regulated again) -----
|
|
783
|
+
b.compliance.set("gdpr");
|
|
784
|
+
|
|
785
|
+
check("_isRawWriteToresidencyTable true for a residency-table INSERT",
|
|
786
|
+
dbQuery._isRawWriteToResidencyTable(
|
|
787
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES ('a','b','eu-west-1')") === true);
|
|
788
|
+
check("_isRawWriteToResidencyTable false for a SELECT",
|
|
789
|
+
dbQuery._isRawWriteToResidencyTable("SELECT * FROM residents") === false);
|
|
790
|
+
check("_isRawWriteToResidencyTable false for a non-residency table",
|
|
791
|
+
dbQuery._isRawWriteToResidencyTable("INSERT INTO notes (_id,body) VALUES ('n','x')") === false);
|
|
792
|
+
|
|
793
|
+
check("_assertRawWriteResidency returns for a non-write statement",
|
|
794
|
+
_codeOf(function () { dbQuery._assertRawWriteResidency("SELECT 1"); }) === null);
|
|
795
|
+
check("_assertRawWriteResidency returns for a non-residency write",
|
|
796
|
+
_codeOf(function () {
|
|
797
|
+
dbQuery._assertRawWriteResidency("INSERT INTO notes (_id,body) VALUES (?,?)", ["n", "x"]);
|
|
798
|
+
}) === null);
|
|
799
|
+
|
|
800
|
+
var overLong = "INSERT INTO residents (_id,name,dataRegion) VALUES ('a','b','" +
|
|
801
|
+
new Array(100002).join("x") + "')";
|
|
802
|
+
check("_assertRawWriteResidency fails closed on an over-length statement",
|
|
803
|
+
_codeOf(function () { dbQuery._assertRawWriteResidency(overLong); })
|
|
804
|
+
=== "db-query/row-residency-raw-unparseable");
|
|
805
|
+
check("_assertRawWriteResidency fails closed on an unparseable body",
|
|
806
|
+
_codeOf(function () {
|
|
807
|
+
dbQuery._assertRawWriteResidency("INSERT INTO residents SELECT * FROM elsewhere");
|
|
808
|
+
}) === "db-query/row-residency-raw-unparseable");
|
|
809
|
+
check("_assertRawWriteResidency fails closed on a column/value count mismatch",
|
|
810
|
+
_codeOf(function () {
|
|
811
|
+
dbQuery._assertRawWriteResidency("INSERT INTO residents (_id, name, dataRegion) VALUES ('only-one')");
|
|
812
|
+
}) === "db-query/row-residency-raw-unparseable");
|
|
813
|
+
check("_assertRawWriteResidency parses a cross-border INSERT and refuses it",
|
|
814
|
+
_codeOf(function () {
|
|
815
|
+
dbQuery._assertRawWriteResidency(
|
|
816
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES ('a','b','us-east-1')");
|
|
817
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
818
|
+
check("_assertRawWriteResidency binds ? params and refuses the cross-border value",
|
|
819
|
+
_codeOf(function () {
|
|
820
|
+
dbQuery._assertRawWriteResidency(
|
|
821
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES (?,?,?)", ["a", "b", "us-east-1"]);
|
|
822
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
823
|
+
check("_assertRawWriteResidency parses an UPDATE and refuses the cross-border value",
|
|
824
|
+
_codeOf(function () {
|
|
825
|
+
dbQuery._assertRawWriteResidency("UPDATE residents SET dataRegion='us-east-1' WHERE _id='eu1'");
|
|
826
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
827
|
+
check("_assertRawWriteResidency fails closed on a writable-CTE residency write",
|
|
828
|
+
_codeOf(function () {
|
|
829
|
+
dbQuery._assertRawWriteResidency(
|
|
830
|
+
"WITH s AS (SELECT 1) INSERT INTO residents (_id,name,dataRegion) SELECT 'x','y','us-east-1'");
|
|
831
|
+
}) === "db-query/row-residency-raw-unparseable");
|
|
832
|
+
|
|
833
|
+
check("_stripLeadingSqlComments strips leading block + line comments",
|
|
834
|
+
dbQuery._stripLeadingSqlComments("/* a */ -- b\n INSERT INTO x (a) VALUES (1)").indexOf("INSERT") === 0);
|
|
835
|
+
|
|
836
|
+
// Quoted identifiers in the raw INSERT are unquoted before the gate reads
|
|
837
|
+
// them; a comma inside a quoted string value is not a column separator.
|
|
838
|
+
check("quoted-identifier raw INSERT is parsed and gated",
|
|
839
|
+
_codeOf(function () {
|
|
840
|
+
dbQuery._assertRawWriteResidency(
|
|
841
|
+
'INSERT INTO residents ("_id","name","dataRegion") VALUES (\'a\',\'b\',\'us-east-1\')');
|
|
842
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
843
|
+
check("a comma inside a quoted string value is not a column separator",
|
|
844
|
+
_codeOf(function () {
|
|
845
|
+
dbQuery._assertRawWriteResidency(
|
|
846
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES ('a,b','n','us-east-1')");
|
|
847
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
848
|
+
|
|
849
|
+
// Non-string value tokens: numeric, NULL literal, and a bare expression
|
|
850
|
+
// all resolve through _rawValue before the residency tag is checked.
|
|
851
|
+
check("numeric + bare-expression value tokens parse; the cross-border tag still refuses",
|
|
852
|
+
_codeOf(function () {
|
|
853
|
+
dbQuery._assertRawWriteResidency(
|
|
854
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES (42, foo, 'us-east-1')");
|
|
855
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
856
|
+
check("a NULL value token parses; the cross-border tag still refuses",
|
|
857
|
+
_codeOf(function () {
|
|
858
|
+
dbQuery._assertRawWriteResidency(
|
|
859
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES ('a', NULL, 'us-east-1')");
|
|
860
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
861
|
+
|
|
862
|
+
// UPDATE with no WHERE clause — the whole SET body is the assignment list.
|
|
863
|
+
check("UPDATE with no WHERE clause is parsed and gated",
|
|
864
|
+
_codeOf(function () {
|
|
865
|
+
dbQuery._assertRawWriteResidency("UPDATE residents SET dataRegion='us-east-1'");
|
|
866
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
867
|
+
// A WHERE embedded inside a quoted SET value is skipped by the quote-aware
|
|
868
|
+
// scan, so the residency assignment after it is still parsed.
|
|
869
|
+
check("a quoted WHERE inside a SET value does not truncate the parse",
|
|
870
|
+
_codeOf(function () {
|
|
871
|
+
dbQuery._assertRawWriteResidency(
|
|
872
|
+
"UPDATE residents SET name='x WHERE y', dataRegion='us-east-1' WHERE _id='eu1'");
|
|
873
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
874
|
+
// SQL unquoted identifiers fold case: a differently-cased residency column
|
|
875
|
+
// still engages the gate.
|
|
876
|
+
check("a differently-cased residency column on UPDATE is still gated",
|
|
877
|
+
_codeOf(function () {
|
|
878
|
+
dbQuery._assertRawWriteResidency("UPDATE residents SET DATAREGION='us-east-1' WHERE _id='eu1'");
|
|
879
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
880
|
+
|
|
881
|
+
// A writable-CTE write to a NON-residency table finds no residency target.
|
|
882
|
+
check("_isRawWriteToResidencyTable false for a CTE write to a non-residency table",
|
|
883
|
+
dbQuery._isRawWriteToResidencyTable(
|
|
884
|
+
"WITH s AS (SELECT 1) INSERT INTO notes (_id,body) SELECT 'n','x'") === false);
|
|
885
|
+
// A writable-CTE UPDATE to a residency table is detected via the UPDATE
|
|
886
|
+
// target token (the second alternation) and then fails closed.
|
|
887
|
+
check("writable-CTE UPDATE to a residency table is detected + fails closed",
|
|
888
|
+
_codeOf(function () {
|
|
889
|
+
dbQuery._assertRawWriteResidency(
|
|
890
|
+
"WITH x AS (SELECT 1) UPDATE residents SET dataRegion='us-east-1' WHERE _id='eu1'");
|
|
891
|
+
}) === "db-query/row-residency-raw-unparseable");
|
|
892
|
+
|
|
893
|
+
// Doubled single-quote (SQL-escaped quote) inside a value + a
|
|
894
|
+
// parenthesised value token both parse through the value splitter.
|
|
895
|
+
check("a doubled '' inside a string value parses; cross-border tag refuses",
|
|
896
|
+
_codeOf(function () {
|
|
897
|
+
dbQuery._assertRawWriteResidency(
|
|
898
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES ('a''b','n','us-east-1')");
|
|
899
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
900
|
+
check("a parenthesised value token parses; cross-border tag refuses",
|
|
901
|
+
_codeOf(function () {
|
|
902
|
+
dbQuery._assertRawWriteResidency(
|
|
903
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES ('a', substr('x',1), 'us-east-1')");
|
|
904
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
905
|
+
// A parenthesised SET value exercises the depth tracking in the
|
|
906
|
+
// before-WHERE clause splitter.
|
|
907
|
+
check("a parenthesised SET value parses; cross-border tag refuses",
|
|
908
|
+
_codeOf(function () {
|
|
909
|
+
dbQuery._assertRawWriteResidency(
|
|
910
|
+
"UPDATE residents SET name=(1), dataRegion='us-east-1' WHERE _id='eu1'");
|
|
911
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
912
|
+
// run(...) may pass its bound params as a single array argument; the
|
|
913
|
+
// flattener unwraps it before value binding.
|
|
914
|
+
check("array-wrapped bound params are flattened before binding",
|
|
915
|
+
_codeOf(function () {
|
|
916
|
+
dbQuery._assertRawWriteResidency(
|
|
917
|
+
"INSERT INTO residents (_id,name,dataRegion) VALUES (?,?,?)", [["a", "b", "us-east-1"]]);
|
|
918
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
919
|
+
|
|
920
|
+
// Integrated raw path through b.db.runSql / prepare.
|
|
921
|
+
check("b.db.runSql cross-border write refused",
|
|
922
|
+
_codeOf(function () {
|
|
923
|
+
b.db.runSql("INSERT INTO residents (_id,name,dataRegion) VALUES ('rw1','x','us-east-1')");
|
|
924
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
925
|
+
check("b.db.runSql cross-border row did not persist",
|
|
926
|
+
b.db.from("residents").where({ _id: "rw1" }).first() === null);
|
|
927
|
+
b.db.runSql("INSERT INTO residents (_id,name,dataRegion) VALUES ('rw-eu','y','eu-west-1')");
|
|
928
|
+
check("in-region raw write persists (no over-rejection)",
|
|
929
|
+
(b.db.from("residents").where({ _id: "rw-eu" }).first() || {}).dataRegion === "eu-west-1");
|
|
930
|
+
check("b.db.prepare().run() cross-border write refused",
|
|
931
|
+
_codeOf(function () {
|
|
932
|
+
b.db.prepare("INSERT INTO residents (_id,name,dataRegion) VALUES (?,?,?)")
|
|
933
|
+
.run("rw2", "x", "us-east-1");
|
|
934
|
+
}) === "db-query/row-residency-local-mismatch");
|
|
935
|
+
} finally {
|
|
936
|
+
await _teardownResidencyDb(tmpDir);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// A residency-declared table in a deployment with NO configured region: the
|
|
941
|
+
// region-set cross-border check is skipped (no region to compare against) but
|
|
942
|
+
// the allowedTags membership is still enforced.
|
|
943
|
+
async function testResidencyWithoutDeploymentRegion() {
|
|
944
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbq-resid-noregion-"));
|
|
945
|
+
try {
|
|
946
|
+
await setupTestDb(tmpDir); // default users schema, no dataResidency
|
|
947
|
+
b.cryptoField.clearResidencyForTest();
|
|
948
|
+
b.cryptoField.declarePerRowResidency("users", {
|
|
949
|
+
residencyColumn: "status",
|
|
950
|
+
allowedTags: ["active", "pending", "global"],
|
|
951
|
+
});
|
|
952
|
+
b.compliance.set("gdpr");
|
|
953
|
+
check("residency tag membership is enforced with no deployment region",
|
|
954
|
+
_codeOf(function () {
|
|
955
|
+
b.db.from("users").insertOne({ _id: "nr1", email: "n@x.com", name: "N", status: "zz-nope" });
|
|
956
|
+
}) === "db-query/row-residency-tag-invalid");
|
|
957
|
+
check("an allowed tag passes when no deployment region is configured",
|
|
958
|
+
!_threw(function () {
|
|
959
|
+
b.db.from("users").insertOne({ _id: "nr2", email: "n2@x.com", name: "N2", status: "active" });
|
|
960
|
+
}));
|
|
961
|
+
} finally {
|
|
962
|
+
try { b.compliance.clear(); } catch (_e) {}
|
|
963
|
+
try { b.cryptoField.clearResidencyForTest(); } catch (_e) {}
|
|
964
|
+
await teardownTestDb(tmpDir);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Collect a Readable's object-mode rows into a promise; rejects on error.
|
|
969
|
+
function _collect(readable) {
|
|
970
|
+
return new Promise(function (resolve, reject) {
|
|
971
|
+
var rows = [];
|
|
972
|
+
readable.on("data", function (r) { rows.push(r); });
|
|
973
|
+
readable.on("end", function () { resolve(rows); });
|
|
974
|
+
readable.on("error", function (e) { reject(e); });
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// Resolve with the stream's error object (or null on clean end). The `data`
|
|
979
|
+
// listener puts the stream in flowing mode so the read pump actually runs —
|
|
980
|
+
// a throw surfaced only inside _read never fires on a paused stream.
|
|
981
|
+
function _streamError(readable) {
|
|
982
|
+
return new Promise(function (resolve) {
|
|
983
|
+
readable.on("data", function () {});
|
|
984
|
+
readable.on("error", function (e) { resolve(e); });
|
|
985
|
+
readable.on("end", function () { resolve(null); });
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
async function run() {
|
|
990
|
+
// Fake-handle validation / SQL-shape branches (no db bootstrap).
|
|
991
|
+
await testConstructorValidation();
|
|
992
|
+
await testColumnGate();
|
|
993
|
+
await testDialectAndSingleRowWrite();
|
|
994
|
+
await testWhereOperatorAndJsonbGuards();
|
|
995
|
+
await testWhereRawValidation();
|
|
996
|
+
await testSelectOrderLimitOffsetValidation();
|
|
997
|
+
await testWhereGroupOrWhereValidation();
|
|
998
|
+
await testCryptoFieldKeyFallback();
|
|
999
|
+
await testIncrementBranches();
|
|
1000
|
+
await testStreamValidationOnFakeHandle();
|
|
1001
|
+
|
|
1002
|
+
// Real-execution branches.
|
|
1003
|
+
await testInsertUpdateDeleteExecution();
|
|
1004
|
+
await testSealedFieldQuery();
|
|
1005
|
+
await testStreamExecution();
|
|
1006
|
+
await testWhereGroupOrWhereExecution();
|
|
1007
|
+
await testPerRowKeyWrites();
|
|
1008
|
+
await testResidencyGates();
|
|
1009
|
+
await testResidencyWithoutDeploymentRegion();
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
module.exports = { run: run };
|
|
1013
|
+
|
|
1014
|
+
if (require.main === module) {
|
|
1015
|
+
run().then(
|
|
1016
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
|
|
1017
|
+
function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
|
|
1018
|
+
);
|
|
1019
|
+
}
|