@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,818 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
// Behavioral coverage for b.outbox.create — the transactional-outbox
|
|
5
|
+
// primitive. The stale-in-flight reaper has its own file
|
|
6
|
+
// (outbox-inflight-reaper.test.js #128); this file drives the create() /
|
|
7
|
+
// enqueue() validation surface, the publish / retry / dead-letter state
|
|
8
|
+
// machine, the sqlite + postgres claim paths, the debezium envelope, and
|
|
9
|
+
// the start/stop worker lifecycle through the REAL consumer path.
|
|
10
|
+
//
|
|
11
|
+
// Every backend here is a faithful in-process node:sqlite externalDb (or a
|
|
12
|
+
// postgres-dialect facade over the same, rewriting FOR UPDATE SKIP LOCKED /
|
|
13
|
+
// $N / = ANY(?) so the pg claim SQL actually executes). No network, no
|
|
14
|
+
// NODE_ENV bypass — the production code path runs unchanged.
|
|
15
|
+
|
|
16
|
+
var { DatabaseSync } = require("node:sqlite");
|
|
17
|
+
var helpers = require("../helpers");
|
|
18
|
+
var b = helpers.b;
|
|
19
|
+
var check = helpers.check;
|
|
20
|
+
var C = b.constants;
|
|
21
|
+
|
|
22
|
+
var OutboxError = b.outbox.OutboxError;
|
|
23
|
+
|
|
24
|
+
// Faithful externalDb over an in-memory node:sqlite db: query(sql, params)
|
|
25
|
+
// + transaction(fn) + dialect, converting JS Date params to ISO strings the
|
|
26
|
+
// way the framework's real sqlite provider does. Mirrors the sibling reaper
|
|
27
|
+
// test — no shared helper provides an externalDb.
|
|
28
|
+
function _sqliteExternalDb() {
|
|
29
|
+
var db = new DatabaseSync(":memory:");
|
|
30
|
+
function _bind(params) {
|
|
31
|
+
return (params || []).map(function (v) {
|
|
32
|
+
if (v instanceof Date) return v.toISOString();
|
|
33
|
+
return v;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function _query(sqlText, params) {
|
|
37
|
+
var stmt = db.prepare(sqlText);
|
|
38
|
+
var args = _bind(params);
|
|
39
|
+
if (/^\s*select/i.test(sqlText)) {
|
|
40
|
+
return { rows: stmt.all.apply(stmt, args) };
|
|
41
|
+
}
|
|
42
|
+
var info = stmt.run.apply(stmt, args);
|
|
43
|
+
return { rows: [], changes: info.changes };
|
|
44
|
+
}
|
|
45
|
+
var xdb = {
|
|
46
|
+
dialect: "sqlite",
|
|
47
|
+
query: async function (s, p) { return _query(s, p); },
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
dialect: "sqlite",
|
|
51
|
+
query: async function (s, p) { return _query(s, p); },
|
|
52
|
+
transaction: async function (fn) {
|
|
53
|
+
db.exec("BEGIN");
|
|
54
|
+
try { var r = await fn(xdb); db.exec("COMMIT"); return r; }
|
|
55
|
+
catch (e) { try { db.exec("ROLLBACK"); } catch (_e) {} throw e; }
|
|
56
|
+
},
|
|
57
|
+
_raw: db,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Postgres-dialect facade over node:sqlite. Rewrites the pg-only SQL the
|
|
62
|
+
// outbox emits on a postgres backend so the FOR UPDATE SKIP LOCKED claim
|
|
63
|
+
// path (the double-publish guard) actually executes against a real engine:
|
|
64
|
+
// - strip `FOR UPDATE SKIP LOCKED` (sqlite is a single writer)
|
|
65
|
+
// - `= ANY($N)` → `IN (?, ?, ...)`, flattening the bound array param
|
|
66
|
+
// - `$N` → `?`, params re-ordered by textual appearance
|
|
67
|
+
// The table is created with sqlite-compatible DDL up front (declareSchema
|
|
68
|
+
// on a pg backend would emit BIGSERIAL / TIMESTAMPTZ / partial index).
|
|
69
|
+
function _pgExternalDb(tableName) {
|
|
70
|
+
var db = new DatabaseSync(":memory:");
|
|
71
|
+
db.exec(
|
|
72
|
+
'CREATE TABLE "' + tableName + '" (' +
|
|
73
|
+
' "id" INTEGER PRIMARY KEY AUTOINCREMENT,' +
|
|
74
|
+
' "topic" TEXT NOT NULL,' +
|
|
75
|
+
' "payload" TEXT NOT NULL,' +
|
|
76
|
+
' "key" TEXT,' +
|
|
77
|
+
' "headers" TEXT,' +
|
|
78
|
+
' "enqueued_at" TEXT NOT NULL,' +
|
|
79
|
+
' "next_attempt_at" TEXT NOT NULL,' +
|
|
80
|
+
' "published_at" TEXT,' +
|
|
81
|
+
' "claimed_at" TEXT,' +
|
|
82
|
+
' "attempts" INTEGER NOT NULL DEFAULT 0,' +
|
|
83
|
+
' "last_error" TEXT,' +
|
|
84
|
+
' "status" TEXT NOT NULL DEFAULT \'pending\'' +
|
|
85
|
+
')'
|
|
86
|
+
);
|
|
87
|
+
function _rewrite(sqlText, params) {
|
|
88
|
+
params = params || [];
|
|
89
|
+
var out = [];
|
|
90
|
+
var text = sqlText.replace(/\s+FOR UPDATE SKIP LOCKED/gi, "");
|
|
91
|
+
text = text.replace(/=\s*ANY\(\$(\d+)\)|\$(\d+)/gi, function (_m, anyN, plainN) {
|
|
92
|
+
if (anyN !== undefined) {
|
|
93
|
+
var arr = params[Number(anyN) - 1];
|
|
94
|
+
if (!Array.isArray(arr)) arr = [arr];
|
|
95
|
+
for (var i = 0; i < arr.length; i++) out.push(arr[i]);
|
|
96
|
+
return " IN (" + arr.map(function () { return "?"; }).join(", ") + ")";
|
|
97
|
+
}
|
|
98
|
+
out.push(params[Number(plainN) - 1]);
|
|
99
|
+
return "?";
|
|
100
|
+
});
|
|
101
|
+
var bound = out.map(function (v) { return v instanceof Date ? v.toISOString() : v; });
|
|
102
|
+
return { text: text, bound: bound };
|
|
103
|
+
}
|
|
104
|
+
function _query(sqlText, params) {
|
|
105
|
+
var r = _rewrite(sqlText, params);
|
|
106
|
+
var stmt = db.prepare(r.text);
|
|
107
|
+
if (/^\s*select/i.test(r.text)) {
|
|
108
|
+
return { rows: stmt.all.apply(stmt, r.bound) };
|
|
109
|
+
}
|
|
110
|
+
var info = stmt.run.apply(stmt, r.bound);
|
|
111
|
+
return { rows: [], changes: info.changes };
|
|
112
|
+
}
|
|
113
|
+
var xdb = {
|
|
114
|
+
dialect: "postgres",
|
|
115
|
+
query: async function (s, p) { return _query(s, p); },
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
dialect: "postgres",
|
|
119
|
+
query: async function (s, p) { return _query(s, p); },
|
|
120
|
+
transaction: async function (fn) {
|
|
121
|
+
db.exec("BEGIN");
|
|
122
|
+
try { var r = await fn(xdb); db.exec("COMMIT"); return r; }
|
|
123
|
+
catch (e) { try { db.exec("ROLLBACK"); } catch (_e) {} throw e; }
|
|
124
|
+
},
|
|
125
|
+
_raw: db,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function _expectThrow(fn, code, label) {
|
|
130
|
+
var threw = null;
|
|
131
|
+
try { fn(); } catch (e) { threw = e; }
|
|
132
|
+
check(label + " throws", threw !== null);
|
|
133
|
+
if (threw && code) {
|
|
134
|
+
check(label + " → code " + code, threw.code === code);
|
|
135
|
+
}
|
|
136
|
+
return threw;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function _expectReject(promise, code, label) {
|
|
140
|
+
var threw = null;
|
|
141
|
+
try { await promise; } catch (e) { threw = e; }
|
|
142
|
+
check(label + " rejects", threw !== null);
|
|
143
|
+
if (threw && code) {
|
|
144
|
+
check(label + " → code " + code, threw.code === code);
|
|
145
|
+
}
|
|
146
|
+
return threw;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function _mkOutbox(xdb, extra) {
|
|
150
|
+
var opts = {
|
|
151
|
+
externalDb: xdb,
|
|
152
|
+
table: "test_outbox",
|
|
153
|
+
publisher: async function () {},
|
|
154
|
+
audit: false,
|
|
155
|
+
};
|
|
156
|
+
if (extra) for (var k in extra) opts[k] = extra[k];
|
|
157
|
+
return b.outbox.create(opts);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// create() — config-time validation (THROW tier)
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
async function testCreateValidation() {
|
|
164
|
+
var xdb = _sqliteExternalDb();
|
|
165
|
+
|
|
166
|
+
_expectThrow(function () { b.outbox.create(null); }, "BAD_OPT",
|
|
167
|
+
"create(null)");
|
|
168
|
+
_expectThrow(function () { b.outbox.create("nope"); }, "BAD_OPT",
|
|
169
|
+
"create(non-object)");
|
|
170
|
+
|
|
171
|
+
// Unknown opt key is refused by the allowed-keys gate.
|
|
172
|
+
_expectThrow(function () { _mkOutbox(xdb, { bogusKey: 1 }); }, null,
|
|
173
|
+
"create({ unknown key })");
|
|
174
|
+
|
|
175
|
+
// externalDb missing / not a transaction-capable object.
|
|
176
|
+
_expectThrow(function () {
|
|
177
|
+
b.outbox.create({ table: "t", publisher: async function () {} });
|
|
178
|
+
}, "outbox/bad-externaldb", "create() with no externalDb");
|
|
179
|
+
_expectThrow(function () {
|
|
180
|
+
b.outbox.create({ externalDb: { query: function () {} }, table: "t", publisher: async function () {} });
|
|
181
|
+
}, "outbox/bad-externaldb", "create() externalDb without transaction()");
|
|
182
|
+
|
|
183
|
+
// Non-atomic backend: supportsTransactions === false → refuse.
|
|
184
|
+
_expectThrow(function () {
|
|
185
|
+
b.outbox.create({
|
|
186
|
+
externalDb: { transaction: function () {}, query: function () {}, supportsTransactions: false },
|
|
187
|
+
table: "t", publisher: async function () {},
|
|
188
|
+
});
|
|
189
|
+
}, "outbox/non-atomic-backend", "create() supportsTransactions:false");
|
|
190
|
+
|
|
191
|
+
// Non-atomic backend: supportsTransactions is a probe fn returning false.
|
|
192
|
+
_expectThrow(function () {
|
|
193
|
+
b.outbox.create({
|
|
194
|
+
externalDb: { transaction: function () {}, query: function () {}, supportsTransactions: function () { return false; } },
|
|
195
|
+
table: "t", publisher: async function () {},
|
|
196
|
+
});
|
|
197
|
+
}, "outbox/non-atomic-backend", "create() supportsTransactions()→false");
|
|
198
|
+
|
|
199
|
+
// A probe fn that THROWS is treated as atomic (historical assumption held).
|
|
200
|
+
var okThrows = b.outbox.create({
|
|
201
|
+
externalDb: { transaction: function () {}, query: function () {}, supportsTransactions: function () { throw new Error("boom"); } },
|
|
202
|
+
table: "t", publisher: async function () {}, audit: false,
|
|
203
|
+
});
|
|
204
|
+
check("create() supportsTransactions() throw → still constructs (atomic assumed)",
|
|
205
|
+
okThrows && typeof okThrows.enqueue === "function");
|
|
206
|
+
|
|
207
|
+
// table: empty / missing / bad identifier.
|
|
208
|
+
_expectThrow(function () {
|
|
209
|
+
b.outbox.create({ externalDb: xdb, table: "", publisher: async function () {} });
|
|
210
|
+
}, "outbox/bad-table", "create() empty table");
|
|
211
|
+
_expectThrow(function () {
|
|
212
|
+
b.outbox.create({ externalDb: xdb, table: 42, publisher: async function () {} });
|
|
213
|
+
}, "outbox/bad-table", "create() non-string table");
|
|
214
|
+
// Embedded quote — quoteIdentifier refuses (surfaces some error).
|
|
215
|
+
_expectThrow(function () {
|
|
216
|
+
b.outbox.create({ externalDb: xdb, table: 'bad"name', publisher: async function () {} });
|
|
217
|
+
}, null, "create() table with embedded quote");
|
|
218
|
+
|
|
219
|
+
// publisher: not a function.
|
|
220
|
+
_expectThrow(function () {
|
|
221
|
+
b.outbox.create({ externalDb: xdb, table: "t", publisher: 123 });
|
|
222
|
+
}, "outbox/bad-publisher", "create() non-function publisher");
|
|
223
|
+
|
|
224
|
+
// envelope: unknown value.
|
|
225
|
+
_expectThrow(function () {
|
|
226
|
+
_mkOutbox(xdb, { envelope: "avro" });
|
|
227
|
+
}, "outbox/bad-envelope", "create() bad envelope");
|
|
228
|
+
|
|
229
|
+
// retryBackoff sub-shape: non-positive / non-finite fields refuse.
|
|
230
|
+
_expectThrow(function () {
|
|
231
|
+
_mkOutbox(xdb, { retryBackoff: { initialMs: -1 } });
|
|
232
|
+
}, "outbox/bad-opts", "create() retryBackoff.initialMs negative");
|
|
233
|
+
_expectThrow(function () {
|
|
234
|
+
_mkOutbox(xdb, { retryBackoff: { maxMs: 0 } });
|
|
235
|
+
}, "outbox/bad-opts", "create() retryBackoff.maxMs zero");
|
|
236
|
+
_expectThrow(function () {
|
|
237
|
+
_mkOutbox(xdb, { retryBackoff: { factor: Infinity } });
|
|
238
|
+
}, "outbox/bad-opts", "create() retryBackoff.factor Infinity");
|
|
239
|
+
|
|
240
|
+
// Numeric caps refuse non-positive / non-finite.
|
|
241
|
+
_expectThrow(function () {
|
|
242
|
+
_mkOutbox(xdb, { pollIntervalMs: 0 });
|
|
243
|
+
}, "outbox/bad-opts", "create() pollIntervalMs zero");
|
|
244
|
+
_expectThrow(function () {
|
|
245
|
+
_mkOutbox(xdb, { batchSize: -5 });
|
|
246
|
+
}, "outbox/bad-opts", "create() batchSize negative");
|
|
247
|
+
_expectThrow(function () {
|
|
248
|
+
_mkOutbox(xdb, { maxAttempts: NaN });
|
|
249
|
+
}, "outbox/bad-opts", "create() maxAttempts NaN");
|
|
250
|
+
_expectThrow(function () {
|
|
251
|
+
_mkOutbox(xdb, { claimReclaimMs: Infinity });
|
|
252
|
+
}, "outbox/bad-opts", "create() claimReclaimMs Infinity");
|
|
253
|
+
|
|
254
|
+
// A fully-defaulted valid create constructs a usable handle.
|
|
255
|
+
var ok = _mkOutbox(xdb);
|
|
256
|
+
check("create() with defaults returns the outbox surface",
|
|
257
|
+
ok && typeof ok.enqueue === "function" && typeof ok.start === "function" &&
|
|
258
|
+
typeof ok.stop === "function" && typeof ok.pendingCount === "function" &&
|
|
259
|
+
typeof ok.deadCount === "function" && typeof ok.declareSchema === "function");
|
|
260
|
+
|
|
261
|
+
check("OutboxError is exported and is a constructor", typeof OutboxError === "function");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// enqueue() — event-shape validation (THROW tier), driven in a real txn
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
async function testEnqueueValidation() {
|
|
268
|
+
var xdb = _sqliteExternalDb();
|
|
269
|
+
var outbox = _mkOutbox(xdb);
|
|
270
|
+
await outbox.declareSchema();
|
|
271
|
+
|
|
272
|
+
function _inTxn(fn) {
|
|
273
|
+
return xdb.transaction(async function (tx) { return fn(tx); });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// bad txn handle — not the txClient.
|
|
277
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: {} }, null),
|
|
278
|
+
"outbox/bad-txn", "enqueue() null txn");
|
|
279
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: {} }, { notQuery: true }),
|
|
280
|
+
"outbox/bad-txn", "enqueue() txn without query()");
|
|
281
|
+
|
|
282
|
+
// The rest run inside a real transaction so the txClient is valid.
|
|
283
|
+
await _inTxn(async function (tx) {
|
|
284
|
+
await _expectReject(outbox.enqueue(null, tx), "outbox/bad-event", "enqueue() null event");
|
|
285
|
+
await _expectReject(outbox.enqueue("str", tx), "outbox/bad-event", "enqueue() non-object event");
|
|
286
|
+
|
|
287
|
+
await _expectReject(outbox.enqueue({ payload: {} }, tx), "outbox/bad-event", "enqueue() missing topic");
|
|
288
|
+
await _expectReject(outbox.enqueue({ topic: "", payload: {} }, tx), "outbox/bad-event", "enqueue() empty topic");
|
|
289
|
+
var longTopic = new Array(300).join("x") + "yyy"; // > 255
|
|
290
|
+
await _expectReject(outbox.enqueue({ topic: longTopic, payload: {} }, tx),
|
|
291
|
+
"outbox/bad-event", "enqueue() over-long topic");
|
|
292
|
+
|
|
293
|
+
await _expectReject(outbox.enqueue({ topic: "t" }, tx), "outbox/bad-event",
|
|
294
|
+
"enqueue() missing payload (undefined)");
|
|
295
|
+
|
|
296
|
+
// key: wrong type / over-long.
|
|
297
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: {}, key: 5 }, tx),
|
|
298
|
+
"outbox/bad-event", "enqueue() non-string key");
|
|
299
|
+
var longKey = new Array(300).join("k");
|
|
300
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: {}, key: longKey }, tx),
|
|
301
|
+
"outbox/bad-event", "enqueue() over-long key");
|
|
302
|
+
|
|
303
|
+
// headers: array / non-object.
|
|
304
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: {}, headers: [1, 2] }, tx),
|
|
305
|
+
"outbox/bad-event", "enqueue() array headers");
|
|
306
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: {}, headers: "x" }, tx),
|
|
307
|
+
"outbox/bad-event", "enqueue() string headers");
|
|
308
|
+
|
|
309
|
+
// non-JSON-serializable payload (circular) → wrapped bad-event.
|
|
310
|
+
var circular = {}; circular.self = circular;
|
|
311
|
+
await _expectReject(outbox.enqueue({ topic: "t", payload: circular }, tx),
|
|
312
|
+
"outbox/bad-event", "enqueue() circular payload");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// A valid enqueue with key:null and headers:null lands a pending row.
|
|
316
|
+
await _inTxn(async function (tx) {
|
|
317
|
+
await outbox.enqueue({ topic: "ok", payload: { a: 1 }, key: null, headers: null }, tx);
|
|
318
|
+
});
|
|
319
|
+
check("enqueue() valid row is pending", (await outbox.pendingCount()) === 1);
|
|
320
|
+
|
|
321
|
+
// payload:null is a valid JSON value (only undefined is rejected).
|
|
322
|
+
await _inTxn(async function (tx) {
|
|
323
|
+
await outbox.enqueue({ topic: "nullpay", payload: null }, tx);
|
|
324
|
+
});
|
|
325
|
+
check("enqueue() payload:null is accepted", (await outbox.pendingCount()) === 2);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
// _processOnce — publish success / empty batch / null payload round-trip
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
async function testProcessPublish() {
|
|
332
|
+
var xdb = _sqliteExternalDb();
|
|
333
|
+
var published = [];
|
|
334
|
+
var outbox = _mkOutbox(xdb, { publisher: async function (e) { published.push(e); } });
|
|
335
|
+
await outbox.declareSchema();
|
|
336
|
+
|
|
337
|
+
// Empty poll returns 0.
|
|
338
|
+
check("_processOnce() empty batch returns 0", (await outbox._processOnce()) === 0);
|
|
339
|
+
|
|
340
|
+
await xdb.transaction(async function (tx) {
|
|
341
|
+
await outbox.enqueue({ topic: "orders", payload: { id: "o-1" }, key: "k1", headers: { h: "1" } }, tx);
|
|
342
|
+
await outbox.enqueue({ topic: "nullpay", payload: null }, tx);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
var n = await outbox._processOnce();
|
|
346
|
+
check("_processOnce() publishes the whole batch", n === 2);
|
|
347
|
+
check("_processOnce() delivered both events", published.length === 2);
|
|
348
|
+
var order = published.filter(function (e) { return e.topic === "orders"; })[0];
|
|
349
|
+
check("_processOnce() deserializes payload JSON", order && order.payload && order.payload.id === "o-1");
|
|
350
|
+
check("_processOnce() deserializes headers JSON", order && order.headers && order.headers.h === "1");
|
|
351
|
+
check("_processOnce() carries key through", order && order.key === "k1");
|
|
352
|
+
var np = published.filter(function (e) { return e.topic === "nullpay"; })[0];
|
|
353
|
+
check("_processOnce() null payload round-trips to null", np && np.payload === null && np.headers === null);
|
|
354
|
+
|
|
355
|
+
check("_processOnce() marks both published", (await outbox.pendingCount()) === 0);
|
|
356
|
+
check("_processOnce() dead count stays 0", (await outbox.deadCount()) === 0);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
// _processOnce — retry curve + dead-letter transition on publisher failure
|
|
361
|
+
// ---------------------------------------------------------------------------
|
|
362
|
+
async function testRetryAndDeadLetter() {
|
|
363
|
+
var xdb = _sqliteExternalDb();
|
|
364
|
+
var attempts = 0;
|
|
365
|
+
var outbox = _mkOutbox(xdb, {
|
|
366
|
+
maxAttempts: 3,
|
|
367
|
+
// maxMs (1500) sits between the attempt-1 backoff (1000) and the attempt-2
|
|
368
|
+
// backoff (2000), so the second retry's curve is clamped to the ceiling —
|
|
369
|
+
// exercising both sides of the _backoffMs cap.
|
|
370
|
+
retryBackoff: { initialMs: C.TIME.seconds(1), maxMs: 1500, factor: 2 },
|
|
371
|
+
publisher: async function () { attempts += 1; throw new Error("bus down"); },
|
|
372
|
+
});
|
|
373
|
+
await outbox.declareSchema();
|
|
374
|
+
|
|
375
|
+
await xdb.transaction(async function (tx) {
|
|
376
|
+
await outbox.enqueue({ topic: "flaky", payload: { id: 1 } }, tx);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Attempt 1 fails → stays pending, attempts=1, next_attempt_at pushed out.
|
|
380
|
+
await outbox._processOnce();
|
|
381
|
+
var row1 = xdb._raw.prepare('SELECT attempts, status, last_error, next_attempt_at FROM "test_outbox"').get();
|
|
382
|
+
check("retry: after 1 failure attempts=1", row1.attempts === 1);
|
|
383
|
+
check("retry: row stays pending", row1.status === "pending");
|
|
384
|
+
check("retry: last_error recorded", /bus down/.test(row1.last_error));
|
|
385
|
+
check("retry: next_attempt_at pushed into the future",
|
|
386
|
+
new Date(row1.next_attempt_at).getTime() > Date.now());
|
|
387
|
+
|
|
388
|
+
// Force the row eligible again (next_attempt_at in the past) and re-poll.
|
|
389
|
+
function _makeEligible() {
|
|
390
|
+
xdb._raw.prepare('UPDATE "test_outbox" SET next_attempt_at = ? WHERE status = \'pending\'')
|
|
391
|
+
.run(new Date(Date.now() - C.TIME.seconds(60)).toISOString());
|
|
392
|
+
}
|
|
393
|
+
_makeEligible();
|
|
394
|
+
var t2 = Date.now();
|
|
395
|
+
await outbox._processOnce(); // attempt 2 → attempts=2, still pending
|
|
396
|
+
var row2 = xdb._raw.prepare('SELECT attempts, status, next_attempt_at FROM "test_outbox"').get();
|
|
397
|
+
check("retry: after 2 failures attempts=2 still pending", row2.attempts === 2 && row2.status === "pending");
|
|
398
|
+
// Unclamped the 2nd retry curve would be 2000ms out; the cap pins it to 1500.
|
|
399
|
+
check("retry: 2nd-retry backoff is clamped to maxMs (< unclamped 2000ms)",
|
|
400
|
+
new Date(row2.next_attempt_at).getTime() - t2 < 1800);
|
|
401
|
+
|
|
402
|
+
_makeEligible();
|
|
403
|
+
await outbox._processOnce(); // attempt 3 → nextAttempts(3) >= maxAttempts(3) → dead
|
|
404
|
+
var row3 = xdb._raw.prepare('SELECT attempts, status FROM "test_outbox"').get();
|
|
405
|
+
check("dead-letter: attempts hit max → status dead", row3.status === "dead");
|
|
406
|
+
check("dead-letter: attempts stamped as max", row3.attempts === 3);
|
|
407
|
+
check("dead-letter: deadCount reflects the dead row", (await outbox.deadCount()) === 1);
|
|
408
|
+
check("dead-letter: pendingCount drops to 0", (await outbox.pendingCount()) === 0);
|
|
409
|
+
check("dead-letter: publisher was invoked 3 times", attempts === 3);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
// _processOnce — drop-silent when _markRetry / _markDead itself fails
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
async function testMarkFailureDropSilent() {
|
|
416
|
+
var xdb = _sqliteExternalDb();
|
|
417
|
+
var outbox = _mkOutbox(xdb, {
|
|
418
|
+
maxAttempts: 1, // first failure → dead path
|
|
419
|
+
publisher: async function () { throw new Error("publish boom"); },
|
|
420
|
+
});
|
|
421
|
+
await outbox.declareSchema();
|
|
422
|
+
await xdb.transaction(async function (tx) {
|
|
423
|
+
await outbox.enqueue({ topic: "t", payload: { id: 1 } }, tx);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Make every write after the claim throw, so _markDead throws and is
|
|
427
|
+
// swallowed (drop-silent — worker keeps moving). Wrap the outer query()
|
|
428
|
+
// to reject UPDATE ... SET status = 'dead'.
|
|
429
|
+
var realQuery = xdb.query;
|
|
430
|
+
xdb.query = async function (s, p) {
|
|
431
|
+
if (/status\s*=\s*'?dead'?/i.test(s) || /SET\s+"?status"?\s*=\s*\?/i.test(s)) {
|
|
432
|
+
throw new Error("db write blocked");
|
|
433
|
+
}
|
|
434
|
+
return realQuery(s, p);
|
|
435
|
+
};
|
|
436
|
+
var n = await outbox._processOnce();
|
|
437
|
+
xdb.query = realQuery;
|
|
438
|
+
check("drop-silent: _processOnce still returns the batch length despite mark failure", n === 1);
|
|
439
|
+
// The row could not be marked dead; it remains in-flight (claimed) — the
|
|
440
|
+
// reaper will reclaim it later. The point is _processOnce did NOT throw.
|
|
441
|
+
check("drop-silent: _processOnce did not throw when _markDead failed", true);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// ---------------------------------------------------------------------------
|
|
445
|
+
// _processOnce — drop-silent when _markRetry itself fails (retry path)
|
|
446
|
+
// ---------------------------------------------------------------------------
|
|
447
|
+
async function testMarkRetryFailureDropSilent() {
|
|
448
|
+
var xdb = _sqliteExternalDb();
|
|
449
|
+
var outbox = _mkOutbox(xdb, {
|
|
450
|
+
maxAttempts: 3, // first failure → retry path (not dead)
|
|
451
|
+
publisher: async function () { throw new Error("publish boom"); },
|
|
452
|
+
});
|
|
453
|
+
await outbox.declareSchema();
|
|
454
|
+
await xdb.transaction(async function (tx) {
|
|
455
|
+
await outbox.enqueue({ topic: "t", payload: { id: 1 } }, tx);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Block the retry UPDATE (it stamps last_error) so _markRetry throws; the
|
|
459
|
+
// publish loop must swallow it (drop-silent) and still return the batch size.
|
|
460
|
+
var realQuery = xdb.query;
|
|
461
|
+
xdb.query = async function (s, p) {
|
|
462
|
+
if (/last_error/i.test(s)) throw new Error("db write blocked");
|
|
463
|
+
return realQuery(s, p);
|
|
464
|
+
};
|
|
465
|
+
var threw = null;
|
|
466
|
+
var n;
|
|
467
|
+
try { n = await outbox._processOnce(); } catch (e) { threw = e; }
|
|
468
|
+
xdb.query = realQuery;
|
|
469
|
+
check("drop-silent: _processOnce did not throw when _markRetry failed", threw === null);
|
|
470
|
+
check("drop-silent: _processOnce still returns the batch length", n === 1);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// ---------------------------------------------------------------------------
|
|
474
|
+
// _processOnce — reaper failure is swallowed (drop-silent)
|
|
475
|
+
// ---------------------------------------------------------------------------
|
|
476
|
+
async function testReaperFailureDropSilent() {
|
|
477
|
+
var xdb = _sqliteExternalDb();
|
|
478
|
+
var published = [];
|
|
479
|
+
var outbox = _mkOutbox(xdb, { publisher: async function (e) { published.push(e); } });
|
|
480
|
+
await outbox.declareSchema();
|
|
481
|
+
await xdb.transaction(async function (tx) {
|
|
482
|
+
await outbox.enqueue({ topic: "t", payload: { id: 1 } }, tx);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Fail only the reaper UPDATE (status = 'in-flight' predicate → pending),
|
|
486
|
+
// leave the claim path intact. _processOnce must swallow the reaper error
|
|
487
|
+
// and still publish.
|
|
488
|
+
var realQuery = xdb.query;
|
|
489
|
+
var failedOnce = false;
|
|
490
|
+
xdb.query = async function (s, p) {
|
|
491
|
+
if (!failedOnce && /SET\s+"?status"?\s*=\s*\?,\s*"?claimed_at"?\s*=\s*\?\s+WHERE\s+status\s*=\s*'in-flight'/i.test(s)) {
|
|
492
|
+
failedOnce = true;
|
|
493
|
+
throw new Error("reaper blocked");
|
|
494
|
+
}
|
|
495
|
+
return realQuery(s, p);
|
|
496
|
+
};
|
|
497
|
+
var n = await outbox._processOnce();
|
|
498
|
+
xdb.query = realQuery;
|
|
499
|
+
check("drop-silent: reaper failure did not abort the poll", n === 1);
|
|
500
|
+
check("drop-silent: the row still published despite reaper error", published.length === 1);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// ---------------------------------------------------------------------------
|
|
504
|
+
// Postgres claim path — FOR UPDATE SKIP LOCKED + `= ANY(?)` claim update
|
|
505
|
+
// ---------------------------------------------------------------------------
|
|
506
|
+
async function testPostgresClaimPath() {
|
|
507
|
+
var xdb = _pgExternalDb("test_outbox");
|
|
508
|
+
var published = [];
|
|
509
|
+
var outbox = _mkOutbox(xdb, { publisher: async function (e) { published.push(e); } });
|
|
510
|
+
|
|
511
|
+
// enqueue two rows through the real transactional path (pg INSERT).
|
|
512
|
+
await xdb.transaction(async function (tx) {
|
|
513
|
+
await outbox.enqueue({ topic: "pg1", payload: { id: 1 }, key: "a" }, tx);
|
|
514
|
+
await outbox.enqueue({ topic: "pg2", payload: { id: 2 } }, tx);
|
|
515
|
+
});
|
|
516
|
+
check("pg: two rows enqueued pending", (await outbox.pendingCount()) === 2);
|
|
517
|
+
|
|
518
|
+
var n = await outbox._processOnce();
|
|
519
|
+
check("pg: claim+publish drains both rows", n === 2 && published.length === 2);
|
|
520
|
+
check("pg: both marked published", (await outbox.pendingCount()) === 0);
|
|
521
|
+
var topics = published.map(function (e) { return e.topic; }).sort();
|
|
522
|
+
check("pg: correct topics delivered", topics[0] === "pg1" && topics[1] === "pg2");
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ---------------------------------------------------------------------------
|
|
526
|
+
// Debezium envelope — payload wrapping, op header, connector metadata
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
async function testDebeziumEnvelope() {
|
|
529
|
+
var xdb = _sqliteExternalDb();
|
|
530
|
+
var published = [];
|
|
531
|
+
var outbox = _mkOutbox(xdb, {
|
|
532
|
+
envelope: "debezium",
|
|
533
|
+
connectorName: "myconn",
|
|
534
|
+
dbName: "shopdb",
|
|
535
|
+
publisher: async function (e) { published.push(e); },
|
|
536
|
+
});
|
|
537
|
+
await outbox.declareSchema();
|
|
538
|
+
|
|
539
|
+
await xdb.transaction(async function (tx) {
|
|
540
|
+
// object payload with explicit before/after + a debezium-op header.
|
|
541
|
+
await outbox.enqueue({
|
|
542
|
+
topic: "accounts",
|
|
543
|
+
payload: { before: { bal: 10 }, after: { bal: 5 } },
|
|
544
|
+
key: "acct-1",
|
|
545
|
+
headers: { "debezium-op": "u" },
|
|
546
|
+
}, tx);
|
|
547
|
+
// scalar payload → wrapped as { value: <scalar> } and used as `after`.
|
|
548
|
+
await outbox.enqueue({ topic: "scalars", payload: 42 }, tx);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
await outbox._processOnce();
|
|
552
|
+
check("debezium: both events published", published.length === 2);
|
|
553
|
+
|
|
554
|
+
var acct = published.filter(function (e) { return e.payload && e.payload.source && e.payload.source.table === "accounts"; })[0];
|
|
555
|
+
check("debezium: envelope carries a schema", acct && acct.schema && acct.schema.type === "struct");
|
|
556
|
+
check("debezium: op comes from the debezium-op header", acct && acct.payload.op === "u");
|
|
557
|
+
check("debezium: before/after preserved", acct && acct.payload.before.bal === 10 && acct.payload.after.bal === 5);
|
|
558
|
+
check("debezium: connector metadata applied", acct &&
|
|
559
|
+
acct.payload.source.connector === "myconn" && acct.payload.source.db === "shopdb" &&
|
|
560
|
+
acct.payload.source.version === "1.0.0");
|
|
561
|
+
check("debezium: key travels as an extension field", acct && acct.payload.key === "acct-1");
|
|
562
|
+
|
|
563
|
+
var scalar = published.filter(function (e) { return e.payload && e.payload.source && e.payload.source.table === "scalars"; })[0];
|
|
564
|
+
check("debezium: scalar payload wrapped as { value }", scalar && scalar.payload.after && scalar.payload.after.value === 42);
|
|
565
|
+
check("debezium: default op is 'c' when no header", scalar && scalar.payload.op === "c");
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// ---------------------------------------------------------------------------
|
|
569
|
+
// declareSchema — dialect-correct DDL for postgres / mysql (recording backend)
|
|
570
|
+
// ---------------------------------------------------------------------------
|
|
571
|
+
function _recordingDb(dialect) {
|
|
572
|
+
var queries = [];
|
|
573
|
+
var api = {
|
|
574
|
+
dialect: dialect,
|
|
575
|
+
query: async function (s, p) { queries.push({ sql: s, params: p }); return { rows: [] }; },
|
|
576
|
+
_queries: queries,
|
|
577
|
+
};
|
|
578
|
+
api.transaction = async function (fn) { return fn(api); };
|
|
579
|
+
return api;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async function testDeclareSchemaDialects() {
|
|
583
|
+
// Postgres: identity PK renders BIGSERIAL, timestamps TIMESTAMPTZ, and the
|
|
584
|
+
// claim index is a partial index (WHERE status='pending') on next_attempt_at.
|
|
585
|
+
var pg = _recordingDb("postgres");
|
|
586
|
+
var pgOutbox = _mkOutbox(pg);
|
|
587
|
+
await pgOutbox.declareSchema();
|
|
588
|
+
var pgSql = pg._queries.map(function (q) { return q.sql; }).join("\n");
|
|
589
|
+
check("declareSchema(postgres) emits >=3 statements (table + index + alter)",
|
|
590
|
+
pg._queries.length >= 3);
|
|
591
|
+
check("declareSchema(postgres) uses TIMESTAMPTZ timestamps", /TIMESTAMPTZ/i.test(pgSql));
|
|
592
|
+
check("declareSchema(postgres) emits a partial claim index", /WHERE status = 'pending'/i.test(pgSql));
|
|
593
|
+
|
|
594
|
+
// MySQL: no partial index (WHERE on CREATE INDEX is a syntax error there) —
|
|
595
|
+
// falls back to a composite (status, next_attempt_at) index; timestamps TIMESTAMP.
|
|
596
|
+
var my = _recordingDb("mysql");
|
|
597
|
+
var myOutbox = _mkOutbox(my);
|
|
598
|
+
await myOutbox.declareSchema();
|
|
599
|
+
var mySql = my._queries.map(function (q) { return q.sql; }).join("\n");
|
|
600
|
+
check("declareSchema(mysql) emits >=3 statements", my._queries.length >= 3);
|
|
601
|
+
check("declareSchema(mysql) does NOT emit a partial index WHERE",
|
|
602
|
+
!/WHERE status = 'pending'/i.test(mySql));
|
|
603
|
+
check("declareSchema(mysql) composite index carries status",
|
|
604
|
+
/status/i.test(mySql) && /_pending_idx/i.test(mySql));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ---------------------------------------------------------------------------
|
|
608
|
+
// declareSchema — back-compat: adds claimed_at to a pre-existing legacy table
|
|
609
|
+
// ---------------------------------------------------------------------------
|
|
610
|
+
async function testDeclareSchemaLegacyMigration() {
|
|
611
|
+
var xdb = _sqliteExternalDb();
|
|
612
|
+
// A table created before the stale-in-flight reaper existed — no claimed_at.
|
|
613
|
+
xdb._raw.exec(
|
|
614
|
+
'CREATE TABLE "test_outbox" (' +
|
|
615
|
+
' "id" INTEGER PRIMARY KEY AUTOINCREMENT,' +
|
|
616
|
+
' "topic" TEXT NOT NULL, "payload" TEXT NOT NULL, "key" TEXT, "headers" TEXT,' +
|
|
617
|
+
' "enqueued_at" TEXT NOT NULL, "next_attempt_at" TEXT NOT NULL,' +
|
|
618
|
+
' "published_at" TEXT, "attempts" INTEGER NOT NULL DEFAULT 0,' +
|
|
619
|
+
' "last_error" TEXT, "status" TEXT NOT NULL DEFAULT \'pending\')'
|
|
620
|
+
);
|
|
621
|
+
var cols0 = xdb._raw.prepare('PRAGMA table_info("test_outbox")').all()
|
|
622
|
+
.map(function (c) { return c.name; });
|
|
623
|
+
check("legacy table starts WITHOUT claimed_at", cols0.indexOf("claimed_at") === -1);
|
|
624
|
+
|
|
625
|
+
var outbox = _mkOutbox(xdb);
|
|
626
|
+
// CREATE TABLE IF NOT EXISTS no-ops (table exists); the idempotent ALTER
|
|
627
|
+
// succeeds because claimed_at is genuinely missing — the back-compat path.
|
|
628
|
+
await outbox.declareSchema();
|
|
629
|
+
var cols1 = xdb._raw.prepare('PRAGMA table_info("test_outbox")').all()
|
|
630
|
+
.map(function (c) { return c.name; });
|
|
631
|
+
check("declareSchema back-fills claimed_at on a legacy table",
|
|
632
|
+
cols1.indexOf("claimed_at") !== -1);
|
|
633
|
+
|
|
634
|
+
// Running it AGAIN is idempotent — the ALTER now throws (duplicate column)
|
|
635
|
+
// and is swallowed; no exception surfaces.
|
|
636
|
+
var reran = null;
|
|
637
|
+
try { await outbox.declareSchema(); } catch (e) { reran = e; }
|
|
638
|
+
check("declareSchema is idempotent on the second run (duplicate ALTER swallowed)",
|
|
639
|
+
reran === null);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// ---------------------------------------------------------------------------
|
|
643
|
+
// _processOnce — corrupt/legacy row with an empty payload publishes null
|
|
644
|
+
// ---------------------------------------------------------------------------
|
|
645
|
+
async function testCorruptEmptyPayload() {
|
|
646
|
+
var xdb = _sqliteExternalDb();
|
|
647
|
+
var published = [];
|
|
648
|
+
var outbox = _mkOutbox(xdb, { publisher: async function (e) { published.push(e); } });
|
|
649
|
+
await outbox.declareSchema();
|
|
650
|
+
// A row whose payload column is the empty string (a corrupt/legacy write) —
|
|
651
|
+
// enqueue never produces this, but the publisher deserializer must fail
|
|
652
|
+
// closed to null rather than throw on a falsy payload.
|
|
653
|
+
var past = new Date(Date.now() - C.TIME.seconds(60)).toISOString();
|
|
654
|
+
xdb._raw.prepare(
|
|
655
|
+
'INSERT INTO "test_outbox" ("topic","payload","enqueued_at","next_attempt_at","attempts","status")' +
|
|
656
|
+
" VALUES ('corrupt','',?,?,0,'pending')"
|
|
657
|
+
).run(past, past);
|
|
658
|
+
|
|
659
|
+
await outbox._processOnce();
|
|
660
|
+
check("corrupt-payload: the empty-payload row still published", published.length === 1);
|
|
661
|
+
check("corrupt-payload: empty payload deserializes to null (fail-closed)",
|
|
662
|
+
published[0].payload === null);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// ---------------------------------------------------------------------------
|
|
666
|
+
// _processOnce — publisher rejecting a falsy (non-Error) value on retry + dead
|
|
667
|
+
// ---------------------------------------------------------------------------
|
|
668
|
+
async function testPublisherRejectsFalsy() {
|
|
669
|
+
var xdb = _sqliteExternalDb();
|
|
670
|
+
var outbox = _mkOutbox(xdb, {
|
|
671
|
+
maxAttempts: 2,
|
|
672
|
+
// Reject with `undefined` (not an Error) so `(e && e.message) || String(e)`
|
|
673
|
+
// takes its String(e) fallback on both the retry and dead-letter paths.
|
|
674
|
+
publisher: async function () { return Promise.reject(undefined); },
|
|
675
|
+
});
|
|
676
|
+
await outbox.declareSchema();
|
|
677
|
+
await xdb.transaction(async function (tx) {
|
|
678
|
+
await outbox.enqueue({ topic: "t", payload: { id: 1 } }, tx);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
await outbox._processOnce(); // attempt 1 → retry (nextAttempts 1 < 2)
|
|
682
|
+
var r1 = xdb._raw.prepare('SELECT attempts, status, last_error FROM "test_outbox"').get();
|
|
683
|
+
check("falsy-reject: retry records String(undefined) as last_error",
|
|
684
|
+
r1.status === "pending" && r1.attempts === 1 && r1.last_error === "undefined");
|
|
685
|
+
|
|
686
|
+
xdb._raw.prepare('UPDATE "test_outbox" SET next_attempt_at = ?')
|
|
687
|
+
.run(new Date(Date.now() - C.TIME.seconds(60)).toISOString());
|
|
688
|
+
await outbox._processOnce(); // attempt 2 → dead (nextAttempts 2 >= 2)
|
|
689
|
+
var r2 = xdb._raw.prepare('SELECT attempts, status, last_error FROM "test_outbox"').get();
|
|
690
|
+
check("falsy-reject: dead-letter records String(undefined) as last_error",
|
|
691
|
+
r2.status === "dead" && r2.last_error === "undefined");
|
|
692
|
+
check("falsy-reject: deadCount reflects the dead row", (await outbox.deadCount()) === 1);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// ---------------------------------------------------------------------------
|
|
696
|
+
// Debezium — default connector metadata (no connectorName / dbName supplied)
|
|
697
|
+
// ---------------------------------------------------------------------------
|
|
698
|
+
async function testDebeziumDefaults() {
|
|
699
|
+
var xdb = _sqliteExternalDb();
|
|
700
|
+
var published = [];
|
|
701
|
+
var outbox = _mkOutbox(xdb, {
|
|
702
|
+
envelope: "debezium",
|
|
703
|
+
publisher: async function (e) { published.push(e); },
|
|
704
|
+
});
|
|
705
|
+
await outbox.declareSchema();
|
|
706
|
+
await xdb.transaction(async function (tx) {
|
|
707
|
+
await outbox.enqueue({ topic: "def", payload: { x: 1 } }, tx);
|
|
708
|
+
});
|
|
709
|
+
await outbox._processOnce();
|
|
710
|
+
check("debezium-defaults: one event published", published.length === 1);
|
|
711
|
+
check("debezium-defaults: connector defaults to 'blamejs'",
|
|
712
|
+
published[0].payload.source.connector === "blamejs");
|
|
713
|
+
check("debezium-defaults: db defaults to null when dbName omitted",
|
|
714
|
+
published[0].payload.source.db === null);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
// Worker lifecycle — start/stop, double-start no-op, poll drains the outbox
|
|
719
|
+
// ---------------------------------------------------------------------------
|
|
720
|
+
async function testWorkerLifecycle() {
|
|
721
|
+
var xdb = _sqliteExternalDb();
|
|
722
|
+
var published = [];
|
|
723
|
+
var outbox = _mkOutbox(xdb, {
|
|
724
|
+
pollIntervalMs: 20, // fast poll so waitUntil resolves quickly
|
|
725
|
+
publisher: async function (e) { published.push(e); },
|
|
726
|
+
});
|
|
727
|
+
await outbox.declareSchema();
|
|
728
|
+
await xdb.transaction(async function (tx) {
|
|
729
|
+
await outbox.enqueue({ topic: "live", payload: { id: 1 } }, tx);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
try {
|
|
733
|
+
outbox.start();
|
|
734
|
+
outbox.start(); // second call is a no-op (workerHandle already set)
|
|
735
|
+
await helpers.waitUntil(function () { return published.length >= 1; }, {
|
|
736
|
+
timeoutMs: 5000, label: "outbox worker: published the pending row",
|
|
737
|
+
});
|
|
738
|
+
check("worker: start() drains the pending row", published.length === 1 && published[0].topic === "live");
|
|
739
|
+
} finally {
|
|
740
|
+
await outbox.stop();
|
|
741
|
+
}
|
|
742
|
+
check("worker: stop() completes cleanly", true);
|
|
743
|
+
|
|
744
|
+
// stop() again is safe (workerHandle already null).
|
|
745
|
+
await outbox.stop();
|
|
746
|
+
check("worker: double stop() is safe", true);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// ---------------------------------------------------------------------------
|
|
750
|
+
// Worker — a slow publish holds inFlight across ticks (the overlap guard),
|
|
751
|
+
// and stop() awaits the in-flight poll rather than abandoning it.
|
|
752
|
+
// ---------------------------------------------------------------------------
|
|
753
|
+
async function testWorkerInFlightGuardAndStop() {
|
|
754
|
+
var xdb = _sqliteExternalDb();
|
|
755
|
+
var published = [];
|
|
756
|
+
var entered = 0;
|
|
757
|
+
var releaseGate;
|
|
758
|
+
var gate = new Promise(function (resolve) { releaseGate = resolve; });
|
|
759
|
+
var outbox = _mkOutbox(xdb, {
|
|
760
|
+
pollIntervalMs: 15, // fast poll so several ticks fire while a publish blocks
|
|
761
|
+
publisher: async function (e) { entered += 1; await gate; published.push(e); },
|
|
762
|
+
});
|
|
763
|
+
await outbox.declareSchema();
|
|
764
|
+
await xdb.transaction(async function (tx) {
|
|
765
|
+
await outbox.enqueue({ topic: "slow", payload: { id: 1 } }, tx);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
var stopP = null;
|
|
769
|
+
try {
|
|
770
|
+
outbox.start();
|
|
771
|
+
// First tick claims the row and enters the publisher, which then blocks on
|
|
772
|
+
// the gate — inFlight is now a pending poll.
|
|
773
|
+
await helpers.waitUntil(function () { return entered >= 1; }, {
|
|
774
|
+
timeoutMs: 5000, label: "outbox worker: publisher entered (poll in-flight)",
|
|
775
|
+
});
|
|
776
|
+
// Let several more ticks fire while the publish is blocked; each must hit
|
|
777
|
+
// the `if (stopping || inFlight) return` overlap guard and NOT re-enter the
|
|
778
|
+
// publisher (no double-claim).
|
|
779
|
+
await helpers.passiveObserve(120, "outbox worker: overlap guard holds while publish blocks");
|
|
780
|
+
check("worker: overlap guard prevents re-entry while a publish is in-flight",
|
|
781
|
+
entered === 1 && published.length === 0);
|
|
782
|
+
|
|
783
|
+
// stop() while the poll is still in-flight: it must await the pending poll.
|
|
784
|
+
stopP = outbox.stop();
|
|
785
|
+
releaseGate(); // unblock the publisher so the in-flight poll finishes
|
|
786
|
+
await stopP; // stop() resolves only after the awaited poll completes
|
|
787
|
+
check("worker: stop() awaited the in-flight poll to completion",
|
|
788
|
+
published.length === 1 && published[0].topic === "slow");
|
|
789
|
+
} finally {
|
|
790
|
+
if (!stopP) { releaseGate(); await outbox.stop(); }
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async function run() {
|
|
795
|
+
await testCreateValidation();
|
|
796
|
+
await testEnqueueValidation();
|
|
797
|
+
await testProcessPublish();
|
|
798
|
+
await testRetryAndDeadLetter();
|
|
799
|
+
await testMarkFailureDropSilent();
|
|
800
|
+
await testMarkRetryFailureDropSilent();
|
|
801
|
+
await testReaperFailureDropSilent();
|
|
802
|
+
await testPostgresClaimPath();
|
|
803
|
+
await testDeclareSchemaDialects();
|
|
804
|
+
await testDeclareSchemaLegacyMigration();
|
|
805
|
+
await testCorruptEmptyPayload();
|
|
806
|
+
await testPublisherRejectsFalsy();
|
|
807
|
+
await testDebeziumEnvelope();
|
|
808
|
+
await testDebeziumDefaults();
|
|
809
|
+
await testWorkerLifecycle();
|
|
810
|
+
await testWorkerInFlightGuardAndStop();
|
|
811
|
+
console.log("OK — outbox create/enqueue/publish/retry/dead-letter/debezium/lifecycle tests");
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
module.exports = { run: run };
|
|
815
|
+
if (require.main === module) {
|
|
816
|
+
run().then(function () { process.exit(0); })
|
|
817
|
+
.catch(function (err) { process.stderr.write(String(err && err.stack || err) + "\n"); process.exit(1); });
|
|
818
|
+
}
|