@blamejs/blamejs-shop 0.4.31 → 0.4.32
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 +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live test of the framework's SYNCHRONOUS data layer
|
|
4
|
+
* (lib/db-query.js / lib/db-schema.js / lib/migrations.js /
|
|
5
|
+
* lib/seeders.js) against the docker Postgres container.
|
|
6
|
+
*
|
|
7
|
+
* Those four modules were built for node:sqlite (a SYNCHRONOUS engine):
|
|
8
|
+
* db.from(table) and the schema reconciler / migration runner / seed
|
|
9
|
+
* runner all call database.prepare(sql) then stmt.get/all/run(...params)
|
|
10
|
+
* synchronously, plus database.exec(sql) for DDL. Every statement is
|
|
11
|
+
* composed through b.sql with { dialect: "sqlite" } - `?` placeholders +
|
|
12
|
+
* double-quoted "camelCase" identifiers - and the modules NEVER translate
|
|
13
|
+
* that for an external dialect (the b.sql header advertises clusterStorage
|
|
14
|
+
* as the rewrite layer, but these single-node modules bypass clusterStorage
|
|
15
|
+
* entirely). Host smoke only ever runs them on sqlite.
|
|
16
|
+
*
|
|
17
|
+
* To prove the migrated b.sql actually executes on real Postgres, this
|
|
18
|
+
* test wraps a docker-exec psql shim in a node:sqlite-Statement-SHAPED
|
|
19
|
+
* adapter:
|
|
20
|
+
*
|
|
21
|
+
* adapter.prepare(sql) -> { get(...p), all(...p), run(...p) }
|
|
22
|
+
* adapter.exec(sql) -> run DDL / BEGIN / COMMIT / ROLLBACK
|
|
23
|
+
*
|
|
24
|
+
* The adapter is SYNCHRONOUS (execFileSync) so the sync modules drive it
|
|
25
|
+
* unmodified, and it is FAITHFUL to a real node-postgres driver:
|
|
26
|
+
* - `?` placeholders fold into the SQL as quoted literals at the adapter
|
|
27
|
+
* boundary (psql has no bind protocol over argv; every value here is
|
|
28
|
+
* operator-controlled).
|
|
29
|
+
* - the double-quoted "camelCase" identifiers b.sql emits pass through
|
|
30
|
+
* verbatim: Postgres HONORS double-quoted identifiers case-sensitively,
|
|
31
|
+
* so "monotonicCounter" stays camelCase (the casing-bug class lives here).
|
|
32
|
+
* - run() returns { changes } parsed from the psql command tag
|
|
33
|
+
* ("UPDATE 2" -> 2), matching node:sqlite's info.changes.
|
|
34
|
+
* - BIGINT comes back as a JS string (node-postgres int8 default) and
|
|
35
|
+
* BYTEA as a Buffer - the coercion the framework's readers must handle.
|
|
36
|
+
*
|
|
37
|
+
* Because the custom `check` helper is fail-fast (throws on the first
|
|
38
|
+
* false), this file records every assertion through a local soft-check so
|
|
39
|
+
* EVERY divergence surfaces in one run; a single summarizing check() at the
|
|
40
|
+
* end fails the file when any soft-check failed.
|
|
41
|
+
*
|
|
42
|
+
* Tables/migration-rows are namespaced (bjpg_*) + dropped in setup +
|
|
43
|
+
* teardown so a concurrent test can't collide.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
var execFileSync = require("node:child_process").execFileSync;
|
|
47
|
+
var helpers = require("../helpers");
|
|
48
|
+
var check = helpers.check;
|
|
49
|
+
var services = require("../helpers/services");
|
|
50
|
+
|
|
51
|
+
var safeSql = require("../../lib/safe-sql");
|
|
52
|
+
var frameworkSchema = require("../../lib/framework-schema");
|
|
53
|
+
var dbSchema = require("../../lib/db-schema");
|
|
54
|
+
var migrations = require("../../lib/migrations");
|
|
55
|
+
var seeders = require("../../lib/seeders");
|
|
56
|
+
var { Query } = require("../../lib/db-query");
|
|
57
|
+
|
|
58
|
+
var CONTAINER = "blamejs-test-postgres";
|
|
59
|
+
var NULL_SENTINEL = "__BJNULL__";
|
|
60
|
+
|
|
61
|
+
// ---- soft-check recorder (so all findings surface in one run) ----
|
|
62
|
+
var _results = [];
|
|
63
|
+
function soft(label, cond) {
|
|
64
|
+
_results.push({ label: label, ok: !!cond });
|
|
65
|
+
console.log((cond ? " ok " : " FAIL ") + label);
|
|
66
|
+
return !!cond;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---- shared synchronous docker-exec psql ----
|
|
70
|
+
// Field separator + footer are set via -P argv flags (NOT a \pset prelude)
|
|
71
|
+
// so psql prints no "Field separator is ..." confirmation line that a
|
|
72
|
+
// header parser would mistake for a data header. bytea_output is a SET
|
|
73
|
+
// (its only output is the harmless "SET" command tag, already filtered).
|
|
74
|
+
// fieldsep_zero would be cleaner but TAB suffices for our text columns.
|
|
75
|
+
// Field separator is a literal TAB embedded directly in the -P argument
|
|
76
|
+
// (built here in Node where the byte is exact — a $(printf) substitution
|
|
77
|
+
// inside sh -c gets eaten by word-splitting and yields an empty sep, which
|
|
78
|
+
// collapses multi-column rows into one unsplittable string). -P flags print
|
|
79
|
+
// no confirmation line (a \pset prelude would). 2>&1 merges the ERROR lines
|
|
80
|
+
// psql writes to stderr (ON_ERROR_STOP=0 → exit 0, errors only on stderr).
|
|
81
|
+
var TAB = "\t";
|
|
82
|
+
var _PSQL_BASE =
|
|
83
|
+
"psql -U blamejs -d blamejs_test -v ON_ERROR_STOP=0 " +
|
|
84
|
+
"-P footer=off -P null=" + NULL_SENTINEL + " -P 'fieldsep=" + TAB + "'";
|
|
85
|
+
function _psqlRaw(sql) {
|
|
86
|
+
return execFileSync(
|
|
87
|
+
"docker",
|
|
88
|
+
["exec", "-i", CONTAINER, "sh", "-c", _PSQL_BASE + " -At 2>&1"],
|
|
89
|
+
{ input: "SET bytea_output = 'hex';\n" + sql + "\n",
|
|
90
|
+
stdio: ["pipe", "pipe", "pipe"], maxBuffer: 64 * 1024 * 1024 }
|
|
91
|
+
).toString("utf8");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function _psql(sql) {
|
|
95
|
+
var out = _psqlRaw(sql);
|
|
96
|
+
if (/^ERROR:/m.test(out)) {
|
|
97
|
+
throw new Error("psql setup failed for [" + sql + "]:\n" + out);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
var BYTEA_PREFIX = "'" + "\\" + "x";
|
|
103
|
+
function _bindQ(sql, params) {
|
|
104
|
+
params = params || [];
|
|
105
|
+
var i = 0;
|
|
106
|
+
return sql.replace(/\?/g, function () {
|
|
107
|
+
if (i >= params.length) {
|
|
108
|
+
throw new Error("placeholder/param count mismatch in: " + sql);
|
|
109
|
+
}
|
|
110
|
+
var v = params[i++];
|
|
111
|
+
if (v === null || v === undefined) return "NULL";
|
|
112
|
+
if (Buffer.isBuffer(v)) return BYTEA_PREFIX + v.toString("hex") + "'::bytea";
|
|
113
|
+
if (typeof v === "number") return String(v);
|
|
114
|
+
if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
|
|
115
|
+
return "'" + String(v).replace(/'/g, "''") + "'";
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var _CMD_TAG_RE = /^(INSERT|UPDATE|DELETE|MERGE|SELECT|COPY|MOVE)\b(?:\s+\d+)*\s*$/;
|
|
120
|
+
|
|
121
|
+
function _parseError(out) {
|
|
122
|
+
var lines = out.split(/\r?\n/);
|
|
123
|
+
for (var i = 0; i < lines.length; i++) {
|
|
124
|
+
var em = /^ERROR:\s+([0-9A-Za-z]{5}):\s*(.*)$/.exec(lines[i]) ||
|
|
125
|
+
/^ERROR:\s+(.*)$/.exec(lines[i]);
|
|
126
|
+
if (em) {
|
|
127
|
+
var err = new Error("Postgres error: " + lines[i]);
|
|
128
|
+
if (em.length === 3) err.code = em[1];
|
|
129
|
+
return err;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function _psqlHeader(sql) {
|
|
136
|
+
return execFileSync(
|
|
137
|
+
"docker",
|
|
138
|
+
["exec", "-i", CONTAINER, "sh", "-c", _PSQL_BASE + " -A 2>&1"],
|
|
139
|
+
{ input: "SET bytea_output = 'hex';\n" + sql + "\n",
|
|
140
|
+
stdio: ["pipe", "pipe", "pipe"], maxBuffer: 64 * 1024 * 1024 }
|
|
141
|
+
).toString("utf8");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
var _BYTEA_COLUMNS = { nonce: true, payload: true, blobcol: true };
|
|
145
|
+
|
|
146
|
+
function _rowsFromHeaderBlock(out) {
|
|
147
|
+
var err = _parseError(out);
|
|
148
|
+
if (err) throw err;
|
|
149
|
+
var lines = out.split(/\r?\n/);
|
|
150
|
+
var data = [];
|
|
151
|
+
for (var i = 0; i < lines.length; i++) {
|
|
152
|
+
var ln = lines[i];
|
|
153
|
+
if (ln === "") continue;
|
|
154
|
+
if (_CMD_TAG_RE.test(ln) && ln.indexOf("\t") === -1) continue;
|
|
155
|
+
if (/^(BEGIN|COMMIT|ROLLBACK|SET|CREATE|DROP|ALTER)\b/.test(ln) &&
|
|
156
|
+
ln.indexOf("\t") === -1) continue;
|
|
157
|
+
data.push(ln);
|
|
158
|
+
}
|
|
159
|
+
if (data.length === 0) return [];
|
|
160
|
+
var headers = data[0].split("\t");
|
|
161
|
+
var rows = [];
|
|
162
|
+
for (var r = 1; r < data.length; r++) {
|
|
163
|
+
var cells = data[r].split("\t");
|
|
164
|
+
var row = {};
|
|
165
|
+
for (var c = 0; c < headers.length; c++) {
|
|
166
|
+
var hdr = headers[c];
|
|
167
|
+
var cell = cells[c];
|
|
168
|
+
if (cell === NULL_SENTINEL || cell === undefined) { row[hdr] = null; continue; }
|
|
169
|
+
if (_BYTEA_COLUMNS[hdr] === true) {
|
|
170
|
+
var hex = (cell.charAt(0) === "\\" && cell.charAt(1) === "x") ? cell.slice(2) : cell;
|
|
171
|
+
row[hdr] = Buffer.from(hex, "hex");
|
|
172
|
+
} else {
|
|
173
|
+
row[hdr] = cell;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
rows.push(row);
|
|
177
|
+
}
|
|
178
|
+
return rows;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function _affectedFromBlock(out) {
|
|
182
|
+
var err = _parseError(out);
|
|
183
|
+
if (err) throw err;
|
|
184
|
+
var lines = out.split(/\r?\n/);
|
|
185
|
+
var affected = 0;
|
|
186
|
+
for (var i = 0; i < lines.length; i++) {
|
|
187
|
+
if (_CMD_TAG_RE.test(lines[i])) {
|
|
188
|
+
var nums = lines[i].trim().split(/\s+/).slice(1).map(Number);
|
|
189
|
+
if (nums.length) affected = nums[nums.length - 1];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return affected;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function _isWrite(sql) {
|
|
196
|
+
return /^\s*(INSERT|UPDATE|DELETE|REPLACE|MERGE)\b/i.test(sql);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ---- node:sqlite-Statement-SHAPED adapter over real Postgres ----
|
|
200
|
+
function _makePgAdapter() {
|
|
201
|
+
return {
|
|
202
|
+
prepare: function (sql) {
|
|
203
|
+
return {
|
|
204
|
+
get: function () {
|
|
205
|
+
var params = Array.prototype.slice.call(arguments);
|
|
206
|
+
var rows = _rowsFromHeaderBlock(_psqlHeader(_bindQ(sql, params)));
|
|
207
|
+
return rows.length ? rows[0] : undefined;
|
|
208
|
+
},
|
|
209
|
+
all: function () {
|
|
210
|
+
var params = Array.prototype.slice.call(arguments);
|
|
211
|
+
return _rowsFromHeaderBlock(_psqlHeader(_bindQ(sql, params)));
|
|
212
|
+
},
|
|
213
|
+
run: function () {
|
|
214
|
+
var params = Array.prototype.slice.call(arguments);
|
|
215
|
+
var out = _psqlRaw(_bindQ(sql, params));
|
|
216
|
+
var changes = _isWrite(sql) ? _affectedFromBlock(out) : (function () {
|
|
217
|
+
var e = _parseError(out); if (e) throw e; return 0;
|
|
218
|
+
})();
|
|
219
|
+
return { changes: changes, lastInsertRowid: 0 };
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
exec: function (sql) {
|
|
224
|
+
var out = _psqlRaw(sql);
|
|
225
|
+
var e = _parseError(out);
|
|
226
|
+
if (e) throw e;
|
|
227
|
+
return out;
|
|
228
|
+
},
|
|
229
|
+
// The handle declares its dialect so the data layer (db-query /
|
|
230
|
+
// db-schema / migrations / seeders) emits Postgres-correct SQL: the
|
|
231
|
+
// single-row write resolves the PRIMARY KEY (`_id`) rather than the
|
|
232
|
+
// SQLite-only `rowid`, listColumns reads information_schema rather than
|
|
233
|
+
// `PRAGMA table_info`, and the lock timestamp is BIGINT (no 32-bit
|
|
234
|
+
// overflow). Without this the layer falls back to the SQLite dialect.
|
|
235
|
+
dialect: "postgres",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function _from(adapter, table, declared) {
|
|
240
|
+
return new Query(adapter, table, {
|
|
241
|
+
declaredColumns: declared || null,
|
|
242
|
+
columnGateMode: declared ? "reject" : "off",
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Run a block, recording an unexpected throw as a soft FAIL with the
|
|
247
|
+
// error text so a structural SQLite-ism surfaces with its PG error.
|
|
248
|
+
function _block(label, fn) {
|
|
249
|
+
try { fn(); return true; }
|
|
250
|
+
catch (e) {
|
|
251
|
+
soft(label + " (threw: " + ((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function run() {
|
|
257
|
+
var pg = await services.requireService("postgres");
|
|
258
|
+
if (!pg.ok) throw new Error("postgres unreachable: " + pg.reason);
|
|
259
|
+
|
|
260
|
+
var adapter = _makePgAdapter();
|
|
261
|
+
|
|
262
|
+
var T = "bjpg_orders";
|
|
263
|
+
var ALL_TABLES = [
|
|
264
|
+
'"' + T + '"', '"bjpg_things"', '"bjpg_widgets"', '"bjpg_seed_target"',
|
|
265
|
+
'"_blamejs_migrations"', '"_blamejs_migrations_lock"',
|
|
266
|
+
'"_blamejs_seeders"', '"_blamejs_seeders_lock"',
|
|
267
|
+
];
|
|
268
|
+
function _dropAll() {
|
|
269
|
+
_psql(ALL_TABLES.map(function (t) { return "DROP TABLE IF EXISTS " + t + " CASCADE;"; }).join("\n"));
|
|
270
|
+
}
|
|
271
|
+
_dropAll();
|
|
272
|
+
|
|
273
|
+
// ====================================================================
|
|
274
|
+
// 1. reconcileTable: CREATE TABLE IF NOT EXISTS + second-reconcile
|
|
275
|
+
// idempotence (no spurious ALTER / drift) on real Postgres.
|
|
276
|
+
// ====================================================================
|
|
277
|
+
var tableDef = {
|
|
278
|
+
name: T,
|
|
279
|
+
columns: { _id: "TEXT PRIMARY KEY", region: "TEXT", total: "BIGINT", note: "TEXT" },
|
|
280
|
+
};
|
|
281
|
+
var reconcileOk = _block(
|
|
282
|
+
"reconcileTable: first reconcile (CREATE TABLE IF NOT EXISTS) runs on real Postgres",
|
|
283
|
+
function () { dbSchema.reconcileTable(adapter, tableDef, { onDrift: "refuse" }); });
|
|
284
|
+
if (reconcileOk) {
|
|
285
|
+
soft("reconcileTable: first reconcile ran clean on Postgres", true);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// The CREATE itself is portable b.sql DDL; confirm it landed even if a
|
|
289
|
+
// later step in reconcileTable threw (so we can prove the failure point).
|
|
290
|
+
var tblPresent = _psql(
|
|
291
|
+
"SELECT count(*) FROM information_schema.tables WHERE table_name = '" + T + "';");
|
|
292
|
+
soft("reconcileTable: CREATE TABLE DDL landed the table on the server (portable b.sql DDL)",
|
|
293
|
+
/\b1\b/.test(tblPresent.trim()));
|
|
294
|
+
|
|
295
|
+
_block(
|
|
296
|
+
"reconcileTable: SECOND reconcile is idempotent on real Postgres " +
|
|
297
|
+
"(no spurious ALTER / duplicate-column / false drift)",
|
|
298
|
+
function () { dbSchema.reconcileTable(adapter, tableDef, { onDrift: "refuse" }); });
|
|
299
|
+
|
|
300
|
+
// reconcileIndex path (CREATE INDEX IF NOT EXISTS).
|
|
301
|
+
_block(
|
|
302
|
+
"reconcileTable: declared index (CREATE INDEX IF NOT EXISTS) runs on real Postgres",
|
|
303
|
+
function () {
|
|
304
|
+
dbSchema.reconcileTable(adapter,
|
|
305
|
+
{ name: T, columns: tableDef.columns, indexes: [{ columns: ["region"], name: "bjpg_orders_region_idx" }] },
|
|
306
|
+
{ onDrift: "ignore" });
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Ensure the table exists for the CRUD block regardless of reconcile
|
|
310
|
+
// outcome (create it directly if reconcile could not complete).
|
|
311
|
+
_psql('CREATE TABLE IF NOT EXISTS "' + T + '" ' +
|
|
312
|
+
'("_id" TEXT PRIMARY KEY, "region" TEXT, "total" BIGINT, "note" TEXT);');
|
|
313
|
+
|
|
314
|
+
// ====================================================================
|
|
315
|
+
// 2. db.from() Query CRUD end-to-end on real Postgres.
|
|
316
|
+
// ====================================================================
|
|
317
|
+
var declared = new Set(["_id", "region", "total", "note"]);
|
|
318
|
+
|
|
319
|
+
_block("db.from().insertOne runs on real Postgres", function () {
|
|
320
|
+
var ins = _from(adapter, T, declared).insertOne({ _id: "o-1", region: "eu", total: 100, note: "first" });
|
|
321
|
+
soft("db.from().insertOne returned the row with _id", ins && ins._id === "o-1");
|
|
322
|
+
});
|
|
323
|
+
_block("db.from().insertOne (rows 2,3) run on Postgres", function () {
|
|
324
|
+
_from(adapter, T, declared).insertOne({ _id: "o-2", region: "eu", total: 250, note: "second" });
|
|
325
|
+
_from(adapter, T, declared).insertOne({ _id: "o-3", region: "us", total: 70, note: "third" });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
_block("db.from().where().first runs on Postgres", function () {
|
|
329
|
+
var oneRow = _from(adapter, T, declared).where("_id", "o-1").first();
|
|
330
|
+
soft("db.from().where().first round-trips id", oneRow && oneRow._id === "o-1");
|
|
331
|
+
soft("db.from().first round-trips region", oneRow && oneRow.region === "eu");
|
|
332
|
+
soft("db.from(): BIGINT total coerces to a JS string on real Postgres (node-pg int8 default)",
|
|
333
|
+
oneRow && typeof oneRow.total === "string" && oneRow.total === "100");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
_block("db.from().where().orderBy().all runs on Postgres", function () {
|
|
337
|
+
var euRows = _from(adapter, T, declared).where("region", "eu").orderBy("_id", "asc").all();
|
|
338
|
+
soft("db.from().where().orderBy().all returns the eu rows in order",
|
|
339
|
+
euRows.length === 2 && euRows[0]._id === "o-1" && euRows[1]._id === "o-2");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
_block("db.from().count runs on Postgres", function () {
|
|
343
|
+
var total = _from(adapter, T, declared).count();
|
|
344
|
+
soft("db.from().count returns 3", Number(total) === 3);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
_block("db.from().updateOne (single-row, rowid sub-select) runs on real Postgres", function () {
|
|
348
|
+
var n = _from(adapter, T, declared).where("_id", "o-1").updateOne({ total: 999 });
|
|
349
|
+
soft("db.from().updateOne reported a change", n === true);
|
|
350
|
+
var after = _from(adapter, T, declared).where("_id", "o-1").first();
|
|
351
|
+
soft("db.from().updateOne persisted", after && after.total === "999");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
_block("db.from().updateMany (set-based) runs on real Postgres", function () {
|
|
355
|
+
_from(adapter, T, declared).where("region", "eu").updateMany({ note: "bulk" });
|
|
356
|
+
var bulk = _from(adapter, T, declared).where("_id", "o-2").first();
|
|
357
|
+
soft("db.from().updateMany persisted", bulk && bulk.note === "bulk");
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
_block("db.from().increment (COALESCE+?) runs on real Postgres", function () {
|
|
361
|
+
_from(adapter, T, declared).where("_id", "o-3").increment("total", 5);
|
|
362
|
+
var inc = _from(adapter, T, declared).where("_id", "o-3").first();
|
|
363
|
+
soft("db.from().increment persisted (70 + 5 = 75)", inc && inc.total === "75");
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
_block("db.from().deleteOne (single-row, rowid sub-select) runs on real Postgres", function () {
|
|
367
|
+
_from(adapter, T, declared).where("_id", "o-3").deleteOne();
|
|
368
|
+
var remain = _from(adapter, T, declared).count();
|
|
369
|
+
soft("db.from().deleteOne removed one row (3 -> 2)", Number(remain) === 2);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
_block("db.from().paginate runs on real Postgres", function () {
|
|
373
|
+
// Independent of prior write state: assert the envelope against the
|
|
374
|
+
// LIVE count rather than a fixed number (the single-row deleteOne may
|
|
375
|
+
// not have run on Postgres, so the row total varies by bug state).
|
|
376
|
+
var liveTotal = Number(_from(adapter, T, declared).count());
|
|
377
|
+
var page = _from(adapter, T, declared).paginate({ orderBy: "_id", limit: 1, offset: 0 });
|
|
378
|
+
soft("db.from().paginate envelope shape (items page-limited, total == live count)",
|
|
379
|
+
page && page.items.length === 1 && Number(page.total) === liveTotal && page.totalPages >= 1);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// ---- coercion fidelity: BYTEA -> Buffer, BIGINT>2^53 -> exact string ----
|
|
383
|
+
_psql('CREATE TABLE IF NOT EXISTS "bjpg_things" ' +
|
|
384
|
+
'("_id" TEXT PRIMARY KEY, "blobcol" BYTEA, "bignum" BIGINT);');
|
|
385
|
+
var thingsDeclared = new Set(["_id", "blobcol", "bignum"]);
|
|
386
|
+
_block("db.from().insertOne with BYTEA runs on real Postgres", function () {
|
|
387
|
+
_from(adapter, "bjpg_things", thingsDeclared)
|
|
388
|
+
.insertOne({ _id: "t-1", blobcol: Buffer.from([0xde, 0xad, 0xbe, 0xef]) });
|
|
389
|
+
var t = _from(adapter, "bjpg_things", thingsDeclared).where("_id", "t-1").first();
|
|
390
|
+
soft("BYTEA column round-trips to a Buffer on real Postgres",
|
|
391
|
+
t && Buffer.isBuffer(t.blobcol) && t.blobcol.toString("hex") === "deadbeef");
|
|
392
|
+
});
|
|
393
|
+
// A genuine > 2^53 BIGINT can't be expressed as a JS number literal (it
|
|
394
|
+
// rounds), so write the exact value via raw psql, then prove db.from()'s
|
|
395
|
+
// READER coerces it to the exact JS string a node-pg driver returns (the
|
|
396
|
+
// framework's reader concern — no float precision loss on the way out).
|
|
397
|
+
_block("db.from() reads a > 2^53 BIGINT back as an exact string on real Postgres", function () {
|
|
398
|
+
_psql('UPDATE "bjpg_things" SET "bignum" = 9007199254740993 WHERE "_id" = \'t-1\';');
|
|
399
|
+
var t = _from(adapter, "bjpg_things", thingsDeclared).where("_id", "t-1").first();
|
|
400
|
+
soft("BIGINT > 2^53 reads back as an exact string (node-pg int8 fidelity)",
|
|
401
|
+
t && t.bignum === "9007199254740993");
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// ====================================================================
|
|
405
|
+
// 3. migrations: composite-PK + CHECK DDL, run-once tracking, the lock.
|
|
406
|
+
// ====================================================================
|
|
407
|
+
var os = require("node:os");
|
|
408
|
+
var fs = require("node:fs");
|
|
409
|
+
var path = require("node:path");
|
|
410
|
+
var migDir = fs.mkdtempSync(path.join(os.tmpdir(), "bjpg-mig-"));
|
|
411
|
+
fs.writeFileSync(path.join(migDir, "0001-create-widgets.js"),
|
|
412
|
+
'module.exports = { description: "widgets",' +
|
|
413
|
+
' up: function (db) { db["exec"]("CREATE TABLE IF NOT EXISTS \\"bjpg_widgets\\" ' +
|
|
414
|
+
'(\\"k\\" TEXT, \\"v\\" TEXT, PRIMARY KEY (\\"k\\", \\"v\\"), ' +
|
|
415
|
+
'CHECK (char_length(\\"k\\") > 0))"); },' +
|
|
416
|
+
' down: function (db) { db["exec"]("DROP TABLE IF EXISTS \\"bjpg_widgets\\""); } };\n');
|
|
417
|
+
_psql('DROP TABLE IF EXISTS "bjpg_widgets";');
|
|
418
|
+
|
|
419
|
+
var mig = migrations.create({ db: adapter, dir: migDir });
|
|
420
|
+
var migRan = false;
|
|
421
|
+
await (async function () {
|
|
422
|
+
try {
|
|
423
|
+
var upResult = await mig.up();
|
|
424
|
+
migRan = true;
|
|
425
|
+
soft("migrations.up ran the migration on real Postgres", true);
|
|
426
|
+
soft("migrations.up applied 0001-create-widgets.js",
|
|
427
|
+
upResult.applied.indexOf("0001-create-widgets.js") !== -1);
|
|
428
|
+
} catch (e) {
|
|
429
|
+
soft("migrations.up runs on real Postgres (threw: " +
|
|
430
|
+
((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
431
|
+
}
|
|
432
|
+
})();
|
|
433
|
+
|
|
434
|
+
if (migRan) {
|
|
435
|
+
var widgetPresent = _psql(
|
|
436
|
+
"SELECT count(*) FROM information_schema.tables WHERE table_name = 'bjpg_widgets';");
|
|
437
|
+
soft("migrations: composite-PK + CHECK table created on the server", /\b1\b/.test(widgetPresent.trim()));
|
|
438
|
+
|
|
439
|
+
await (async function () {
|
|
440
|
+
try {
|
|
441
|
+
var up2 = await mig.up();
|
|
442
|
+
soft("migrations.up is run-once (second up skips the applied migration)",
|
|
443
|
+
up2.applied.length === 0 && up2.skipped.indexOf("0001-create-widgets.js") !== -1);
|
|
444
|
+
} catch (e) {
|
|
445
|
+
soft("migrations.up second run is run-once (threw: " +
|
|
446
|
+
((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
447
|
+
}
|
|
448
|
+
})();
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
var st = mig.status();
|
|
452
|
+
soft("migrations.status reports 0001 applied on Postgres",
|
|
453
|
+
st.applied.some(function (r) { return r.name === "0001-create-widgets.js"; }));
|
|
454
|
+
} catch (e) {
|
|
455
|
+
soft("migrations.status runs on Postgres (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// advisory lock: a held 'lock' row refuses a concurrent up().
|
|
459
|
+
var lockTbl = frameworkSchema.tableName(migrations.LOCK_TABLE);
|
|
460
|
+
await (async function () {
|
|
461
|
+
try {
|
|
462
|
+
_psql('INSERT INTO ' + safeSql.quoteIdentifier(lockTbl) +
|
|
463
|
+
' ("scope", "lockedAt", "lockedBy") VALUES (\'lock\', ' + Date.now() +
|
|
464
|
+
", 'other-proc') ON CONFLICT (\"scope\") DO NOTHING;");
|
|
465
|
+
var lockThrew = null;
|
|
466
|
+
try { await mig.up(); } catch (e) { lockThrew = e; }
|
|
467
|
+
soft("migrations: a held advisory lock refuses a concurrent up() on Postgres",
|
|
468
|
+
lockThrew !== null && /lock/i.test((lockThrew && lockThrew.message) || ""));
|
|
469
|
+
_psql('DELETE FROM ' + safeSql.quoteIdentifier(lockTbl) + " WHERE \"scope\" = 'lock';");
|
|
470
|
+
} catch (e) {
|
|
471
|
+
soft("migrations advisory-lock concurrency test ran on Postgres (threw: " +
|
|
472
|
+
((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
473
|
+
}
|
|
474
|
+
})();
|
|
475
|
+
|
|
476
|
+
// Stale-lock force-replace path. The DELETE+INSERT runs inside a
|
|
477
|
+
// transaction whose boundary keyword is dialect-aware: `BEGIN IMMEDIATE`
|
|
478
|
+
// is SQLite-only and a syntax error on Postgres, so the runner must emit
|
|
479
|
+
// a portable `BEGIN`. We plant a STALE lock row (far in the past) then
|
|
480
|
+
// run up({ staleAfterMs }) — the runner force-replaces the stale lock,
|
|
481
|
+
// applies the (already-applied) migration as a no-op skip, and releases.
|
|
482
|
+
await (async function () {
|
|
483
|
+
try {
|
|
484
|
+
var staleLockTbl = frameworkSchema.tableName(migrations.LOCK_TABLE);
|
|
485
|
+
_psql('DELETE FROM ' + safeSql.quoteIdentifier(staleLockTbl) + " WHERE \"scope\" = 'lock';");
|
|
486
|
+
_psql('INSERT INTO ' + safeSql.quoteIdentifier(staleLockTbl) +
|
|
487
|
+
' ("scope", "lockedAt", "lockedBy") VALUES (\'lock\', ' + (Date.now() - 3600000) +
|
|
488
|
+
", 'dead-proc') ON CONFLICT (\"scope\") DO NOTHING;");
|
|
489
|
+
var staleMig = migrations.create({ db: adapter, dir: migDir, staleAfterMs: 60000 });
|
|
490
|
+
var staleThrew = null;
|
|
491
|
+
try { await staleMig.up(); } catch (e) { staleThrew = e; }
|
|
492
|
+
soft("migrations: stale-lock force-replace uses a portable BEGIN (not BEGIN IMMEDIATE) on Postgres",
|
|
493
|
+
staleThrew === null);
|
|
494
|
+
// The dead-proc lock is gone; the runner replaced it and released.
|
|
495
|
+
var lockRows = _psql('SELECT count(*) FROM ' + safeSql.quoteIdentifier(staleLockTbl) +
|
|
496
|
+
" WHERE \"lockedBy\" = 'dead-proc';");
|
|
497
|
+
soft("migrations: the stale dead-proc lock was force-replaced + released on Postgres",
|
|
498
|
+
/\b0\b/.test(lockRows.trim()));
|
|
499
|
+
_psql('DELETE FROM ' + safeSql.quoteIdentifier(staleLockTbl) + " WHERE \"scope\" = 'lock';");
|
|
500
|
+
} catch (e) {
|
|
501
|
+
soft("migrations stale-lock-replace test ran on Postgres (threw: " +
|
|
502
|
+
((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
503
|
+
}
|
|
504
|
+
})();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ====================================================================
|
|
508
|
+
// 4. seeders: composite-PK registry, env scoping, run-once + rerunnable.
|
|
509
|
+
// ====================================================================
|
|
510
|
+
var seedDir = fs.mkdtempSync(path.join(os.tmpdir(), "bjpg-seed-"));
|
|
511
|
+
fs.mkdirSync(path.join(seedDir, "dev"));
|
|
512
|
+
_psql('DROP TABLE IF EXISTS "bjpg_seed_target";');
|
|
513
|
+
_psql('CREATE TABLE "bjpg_seed_target" ("id" TEXT PRIMARY KEY, "label" TEXT);');
|
|
514
|
+
fs.writeFileSync(path.join(seedDir, "dev", "0001-admin.js"),
|
|
515
|
+
'module.exports = { description: "admin",' +
|
|
516
|
+
' run: async function (db) { db.prepare("INSERT INTO \\"bjpg_seed_target\\" ' +
|
|
517
|
+
'(\\"id\\", \\"label\\") VALUES (?, ?)").run("admin", "Administrator"); } };\n');
|
|
518
|
+
|
|
519
|
+
var seed = seeders.create({ db: adapter, dir: seedDir });
|
|
520
|
+
var seedRan = false;
|
|
521
|
+
await (async function () {
|
|
522
|
+
try {
|
|
523
|
+
var seedResult = await seed.run({ env: "dev" });
|
|
524
|
+
seedRan = true;
|
|
525
|
+
soft("seeders.run applied the seed on real Postgres", true);
|
|
526
|
+
soft("seeders.run applied 0001-admin.js", seedResult.applied.indexOf("0001-admin.js") !== -1);
|
|
527
|
+
} catch (e) {
|
|
528
|
+
soft("seeders.run runs on real Postgres (threw: " +
|
|
529
|
+
((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
530
|
+
}
|
|
531
|
+
})();
|
|
532
|
+
|
|
533
|
+
if (seedRan) {
|
|
534
|
+
var seededRow = _psql("SELECT label FROM \"bjpg_seed_target\" WHERE id = 'admin';");
|
|
535
|
+
soft("seeders: the seed body actually wrote its row on Postgres", /Administrator/.test(seededRow));
|
|
536
|
+
|
|
537
|
+
await (async function () {
|
|
538
|
+
try {
|
|
539
|
+
var seed2 = await seed.run({ env: "dev" });
|
|
540
|
+
soft("seeders.run is run-once (second run skips the applied seed)",
|
|
541
|
+
seed2.applied.length === 0 && seed2.skipped.indexOf("0001-admin.js") !== -1);
|
|
542
|
+
} catch (e) {
|
|
543
|
+
soft("seeders.run second run is run-once (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
544
|
+
}
|
|
545
|
+
})();
|
|
546
|
+
|
|
547
|
+
try {
|
|
548
|
+
var sst = seed.status({ env: "dev" });
|
|
549
|
+
soft("seeders.status reports 0001-admin.js applied on Postgres",
|
|
550
|
+
sst.applied.some(function (r) { return r.name === "0001-admin.js"; }));
|
|
551
|
+
} catch (e) {
|
|
552
|
+
soft("seeders.status runs on Postgres (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// ---- teardown ----
|
|
557
|
+
try { fs.rmSync(migDir, { recursive: true, force: true }); } catch (_e) {}
|
|
558
|
+
try { fs.rmSync(seedDir, { recursive: true, force: true }); } catch (_e) {}
|
|
559
|
+
_dropAll();
|
|
560
|
+
|
|
561
|
+
// ---- summary ----
|
|
562
|
+
//
|
|
563
|
+
// The data layer is dialect-aware: with the handle declaring
|
|
564
|
+
// dialect: "postgres", every soft-check above MUST pass on real Postgres.
|
|
565
|
+
// The three formerly-SQLite-only constructs are resolved:
|
|
566
|
+
// - db-schema.listColumns reads information_schema.columns (not the
|
|
567
|
+
// SQLite-only PRAGMA table_info), so reconcileTable + its second-run
|
|
568
|
+
// idempotence work.
|
|
569
|
+
// - db-query single-row updateOne/deleteOne resolve the PRIMARY KEY
|
|
570
|
+
// (`_id`) sub-select instead of the SQLite-only `rowid`.
|
|
571
|
+
// - the migrations + seeders lock table types lockedAt as BIGINT (no
|
|
572
|
+
// 32-bit overflow of a Date.now() ms value), so the lock is
|
|
573
|
+
// acquirable and migrate.up / seed.run run end-to-end.
|
|
574
|
+
var failed = _results.filter(function (r) { return !r.ok; });
|
|
575
|
+
|
|
576
|
+
console.log("\n[db-layer-postgres] " +
|
|
577
|
+
(_results.length - failed.length) + "/" + _results.length + " checks passed");
|
|
578
|
+
if (failed.length) {
|
|
579
|
+
console.log("[db-layer-postgres] FAILURES:");
|
|
580
|
+
failed.forEach(function (r) { console.log(" - " + r.label); });
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Replay every recorded finding through the hard `check` so the file
|
|
584
|
+
// FAILS (and the runner reports it) when any data-layer op does not work
|
|
585
|
+
// on real Postgres.
|
|
586
|
+
for (var i = 0; i < _results.length; i++) {
|
|
587
|
+
check(_results[i].label, _results[i].ok);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
module.exports = { run: run };
|
|
592
|
+
|
|
593
|
+
if (require.main === module) {
|
|
594
|
+
run().then(
|
|
595
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
596
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
597
|
+
);
|
|
598
|
+
}
|