@blamejs/blamejs-shop 0.4.30 → 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 +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/checkout.js +8 -0
- package/lib/order.js +71 -11
- 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,549 @@
|
|
|
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 MySQL container.
|
|
6
|
+
*
|
|
7
|
+
* Same intent as db-layer-postgres.test.js: those four modules were built
|
|
8
|
+
* for node:sqlite (synchronous) and compose every statement through b.sql
|
|
9
|
+
* with { dialect: "sqlite" } — `?` placeholders + DOUBLE-QUOTED identifiers
|
|
10
|
+
* — with NO per-dialect translation (clusterStorage, the advertised
|
|
11
|
+
* rewrite layer, is bypassed by these single-node modules). Host smoke
|
|
12
|
+
* only ever runs them on sqlite.
|
|
13
|
+
*
|
|
14
|
+
* MySQL-specific faithfulness in the adapter:
|
|
15
|
+
* - b.sql emits `"camelCase"` double-quoted identifiers. MySQL's default
|
|
16
|
+
* sql_mode treats `"..."` as a STRING literal, not an identifier, so a
|
|
17
|
+
* real MySQL driver must run with ANSI_QUOTES enabled for the
|
|
18
|
+
* framework's quote-by-construction SQL to parse at all. Each docker-
|
|
19
|
+
* exec mysql call is a fresh connection, so every statement is prefixed
|
|
20
|
+
* with `SET SESSION sql_mode=...,ANSI_QUOTES`.
|
|
21
|
+
* - `?` placeholders fold into the SQL as quoted literals at the adapter
|
|
22
|
+
* boundary (the mysql CLI has no bind protocol); every value here is
|
|
23
|
+
* operator-controlled.
|
|
24
|
+
* - run() returns { changes } from ROW_COUNT() issued in the SAME mysql
|
|
25
|
+
* invocation as the write (ROW_COUNT is connection-scoped).
|
|
26
|
+
* - BIGINT comes back as a JS string in --batch mode (matching the
|
|
27
|
+
* mysql2 BIGINT-as-string default the framework readers expect).
|
|
28
|
+
*
|
|
29
|
+
* The custom `check` helper is fail-fast, so this file records every
|
|
30
|
+
* assertion through a local soft-check (all divergences surface in one
|
|
31
|
+
* run) and ends with WORKING-path + KNOWN-BUG gate assertions so the file
|
|
32
|
+
* is a stable green coverage gate that flips red the moment a lib is fixed.
|
|
33
|
+
*
|
|
34
|
+
* Tables/migration-rows are namespaced (bjml_*) in a dedicated bjml_test
|
|
35
|
+
* database + dropped in setup + teardown so a concurrent test can't collide.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
var execFileSync = require("node:child_process").execFileSync;
|
|
39
|
+
var helpers = require("../helpers");
|
|
40
|
+
var check = helpers.check;
|
|
41
|
+
var services = require("../helpers/services");
|
|
42
|
+
|
|
43
|
+
var safeSql = require("../../lib/safe-sql");
|
|
44
|
+
var frameworkSchema = require("../../lib/framework-schema");
|
|
45
|
+
var dbSchema = require("../../lib/db-schema");
|
|
46
|
+
var migrations = require("../../lib/migrations");
|
|
47
|
+
var seeders = require("../../lib/seeders");
|
|
48
|
+
var { Query } = require("../../lib/db-query");
|
|
49
|
+
|
|
50
|
+
var CONTAINER = "blamejs-test-mysql";
|
|
51
|
+
var DB_NAME = "bjml_test";
|
|
52
|
+
// ANSI_QUOTES makes MySQL honor the b.sql double-quoted identifiers; without
|
|
53
|
+
// it every "col" parses as a string literal and the SQL is meaningless.
|
|
54
|
+
// PIPES_AS_CONCAT off-by-default is fine (we emit no `||`). Prefixed to every
|
|
55
|
+
// statement batch since each docker-exec is a fresh connection.
|
|
56
|
+
var SQLMODE = "SET SESSION sql_mode=CONCAT(@@sql_mode,',ANSI_QUOTES');\n";
|
|
57
|
+
|
|
58
|
+
var _results = [];
|
|
59
|
+
function soft(label, cond) {
|
|
60
|
+
_results.push({ label: label, ok: !!cond });
|
|
61
|
+
console.log((cond ? " ok " : " FAIL ") + label);
|
|
62
|
+
return !!cond;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---- shared synchronous docker-exec mysql ----
|
|
66
|
+
// SQL travels on stdin (never argv → no shell parsing). --batch gives
|
|
67
|
+
// TAB-separated, header-bearing output; --raw disables escaping so a
|
|
68
|
+
// value round-trips byte-faithfully. stderr captured + merged so a SQL
|
|
69
|
+
// error surfaces with its message.
|
|
70
|
+
function _mysqlRaw(sql, opts) {
|
|
71
|
+
opts = opts || {};
|
|
72
|
+
var db = opts.noDb ? [] : [DB_NAME];
|
|
73
|
+
var args = ["exec", "-i", CONTAINER, "mysql", "-uroot", "-pblamejs_test_root",
|
|
74
|
+
"--batch", "--raw"].concat(db);
|
|
75
|
+
try {
|
|
76
|
+
var out = execFileSync("docker", args,
|
|
77
|
+
{ input: (opts.noMode ? "" : SQLMODE) + sql + "\n",
|
|
78
|
+
stdio: ["pipe", "pipe", "pipe"], maxBuffer: 64 * 1024 * 1024 });
|
|
79
|
+
return { ok: true, out: out.toString("utf8") };
|
|
80
|
+
} catch (e) {
|
|
81
|
+
var stderr = e.stderr ? e.stderr.toString("utf8") : "";
|
|
82
|
+
return { ok: false, out: (e.stdout ? e.stdout.toString("utf8") : ""), err: stderr || (e.message || String(e)) };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// One-shot for setup / teardown / out-of-band assertions; throws on error.
|
|
87
|
+
function _mysql(sql, opts) {
|
|
88
|
+
var r = _mysqlRaw(sql, opts);
|
|
89
|
+
if (!r.ok) throw new Error("mysql setup failed for [" + sql.slice(0, 120) + "]: " + _clean(r.err));
|
|
90
|
+
return r.out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function _clean(s) {
|
|
94
|
+
return String(s || "").split(/\r?\n/)
|
|
95
|
+
.filter(function (l) { return l && l.indexOf("[Warning] World-writable") === -1; })
|
|
96
|
+
.join(" ").slice(0, 220);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var BLOB_HEX = "0x";
|
|
100
|
+
function _bindQ(sql, params) {
|
|
101
|
+
params = params || [];
|
|
102
|
+
var i = 0;
|
|
103
|
+
return sql.replace(/\?/g, function () {
|
|
104
|
+
if (i >= params.length) throw new Error("placeholder/param count mismatch in: " + sql);
|
|
105
|
+
var v = params[i++];
|
|
106
|
+
if (v === null || v === undefined) return "NULL";
|
|
107
|
+
if (Buffer.isBuffer(v)) return BLOB_HEX + (v.length ? v.toString("hex") : "00");
|
|
108
|
+
if (typeof v === "number") return String(v);
|
|
109
|
+
if (typeof v === "boolean") return v ? "1" : "0";
|
|
110
|
+
return "'" + String(v).replace(/\\/g, "\\\\").replace(/'/g, "''") + "'";
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var _BLOB_COLUMNS = { blobcol: true, payload: true, nonce: true };
|
|
115
|
+
|
|
116
|
+
// Parse a --batch block: drop the warning line(s), header row first, then
|
|
117
|
+
// data rows. The SQLMODE `SET` produces no output. Multiple statements in
|
|
118
|
+
// one batch each emit their own header+rows; we parse the LAST result set
|
|
119
|
+
// (the SELECT we care about), since reads issue exactly one SELECT.
|
|
120
|
+
function _parseSelect(out) {
|
|
121
|
+
var lines = out.split(/\r?\n/).filter(function (l) {
|
|
122
|
+
return l.length > 0 && l.indexOf("[Warning] World-writable") === -1;
|
|
123
|
+
});
|
|
124
|
+
if (lines.length === 0) return [];
|
|
125
|
+
var headers = lines[0].split("\t");
|
|
126
|
+
var rows = [];
|
|
127
|
+
for (var i = 1; i < lines.length; i++) {
|
|
128
|
+
var cells = lines[i].split("\t");
|
|
129
|
+
var row = {};
|
|
130
|
+
for (var c = 0; c < headers.length; c++) {
|
|
131
|
+
var hdr = headers[c];
|
|
132
|
+
var cell = cells[c];
|
|
133
|
+
if (cell === "NULL" || cell === undefined) { row[hdr] = null; continue; }
|
|
134
|
+
if (_BLOB_COLUMNS[hdr] === true) {
|
|
135
|
+
// --raw emits binary bytes verbatim; recover them as a Buffer.
|
|
136
|
+
row[hdr] = Buffer.from(cell, "binary");
|
|
137
|
+
} else {
|
|
138
|
+
row[hdr] = cell;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
rows.push(row);
|
|
142
|
+
}
|
|
143
|
+
return rows;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function _isWrite(sql) {
|
|
147
|
+
return /^\s*(INSERT|UPDATE|DELETE|REPLACE|MERGE)\b/i.test(sql);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ---- node:sqlite-Statement-SHAPED adapter over real MySQL ----
|
|
151
|
+
function _makeMysqlAdapter() {
|
|
152
|
+
return {
|
|
153
|
+
prepare: function (sql) {
|
|
154
|
+
return {
|
|
155
|
+
get: function () {
|
|
156
|
+
var params = Array.prototype.slice.call(arguments);
|
|
157
|
+
var r = _mysqlRaw(_bindQ(sql, params));
|
|
158
|
+
if (!r.ok) throw new Error("MySQL error: " + _clean(r.err));
|
|
159
|
+
var rows = _parseSelect(r.out);
|
|
160
|
+
return rows.length ? rows[0] : undefined;
|
|
161
|
+
},
|
|
162
|
+
all: function () {
|
|
163
|
+
var params = Array.prototype.slice.call(arguments);
|
|
164
|
+
var r = _mysqlRaw(_bindQ(sql, params));
|
|
165
|
+
if (!r.ok) throw new Error("MySQL error: " + _clean(r.err));
|
|
166
|
+
return _parseSelect(r.out);
|
|
167
|
+
},
|
|
168
|
+
run: function () {
|
|
169
|
+
var params = Array.prototype.slice.call(arguments);
|
|
170
|
+
var bound = _bindQ(sql, params);
|
|
171
|
+
// ROW_COUNT() in the SAME connection/batch as the write reports
|
|
172
|
+
// affectedRows (it is connection-scoped).
|
|
173
|
+
var r = _mysqlRaw(bound + ";\nSELECT ROW_COUNT() AS rc;");
|
|
174
|
+
if (!r.ok) throw new Error("MySQL error: " + _clean(r.err));
|
|
175
|
+
var changes = 0;
|
|
176
|
+
if (_isWrite(sql)) {
|
|
177
|
+
var rows = _parseSelect(r.out);
|
|
178
|
+
// The LAST result set is the ROW_COUNT() select.
|
|
179
|
+
var last = rows.length ? rows[rows.length - 1] : null;
|
|
180
|
+
if (last && last.rc !== undefined && last.rc !== null) changes = Number(last.rc);
|
|
181
|
+
}
|
|
182
|
+
return { changes: changes, lastInsertRowid: 0 };
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
exec: function (sql) {
|
|
187
|
+
var r = _mysqlRaw(sql);
|
|
188
|
+
if (!r.ok) throw new Error("MySQL error: " + _clean(r.err));
|
|
189
|
+
return r.out;
|
|
190
|
+
},
|
|
191
|
+
// The handle declares its dialect so the data layer emits MySQL-correct
|
|
192
|
+
// SQL: BACKTICK-quoted identifiers (the framework SQL no longer relies
|
|
193
|
+
// on ANSI_QUOTES — backticks work in either sql_mode), the single-row
|
|
194
|
+
// write resolves the PRIMARY KEY in a prior SELECT then writes
|
|
195
|
+
// `WHERE pk = ?` (MySQL rejects a subquery referencing the UPDATE/DELETE
|
|
196
|
+
// target — error 1093), listColumns reads information_schema, and the
|
|
197
|
+
// migrations/seeders registry/lock tables use VARCHAR(191) for key text
|
|
198
|
+
// columns + BIGINT for the ms-epoch lock timestamp.
|
|
199
|
+
dialect: "mysql",
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function _from(adapter, table, declared) {
|
|
204
|
+
return new Query(adapter, table, {
|
|
205
|
+
declaredColumns: declared || null,
|
|
206
|
+
columnGateMode: declared ? "reject" : "off",
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function _block(label, fn) {
|
|
211
|
+
try { fn(); return true; }
|
|
212
|
+
catch (e) {
|
|
213
|
+
soft(label + " (threw: " + ((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function run() {
|
|
219
|
+
var mysqlSvc = await services.requireService("mysql");
|
|
220
|
+
if (!mysqlSvc.ok) throw new Error("mysql unreachable: " + mysqlSvc.reason);
|
|
221
|
+
|
|
222
|
+
// Dedicated database so we never touch other suites' fixtures.
|
|
223
|
+
_mysql("CREATE DATABASE IF NOT EXISTS " + DB_NAME + ";", { noDb: true, noMode: true });
|
|
224
|
+
|
|
225
|
+
var adapter = _makeMysqlAdapter();
|
|
226
|
+
|
|
227
|
+
var T = "bjml_orders";
|
|
228
|
+
var ALL_TABLES = [
|
|
229
|
+
'"' + T + '"', '"bjml_things"', '"bjml_widgets"', '"bjml_seed_target"',
|
|
230
|
+
'"_blamejs_migrations"', '"_blamejs_migrations_lock"',
|
|
231
|
+
'"_blamejs_seeders"', '"_blamejs_seeders_lock"',
|
|
232
|
+
];
|
|
233
|
+
function _dropAll() {
|
|
234
|
+
_mysql(ALL_TABLES.map(function (t) { return "DROP TABLE IF EXISTS " + t + ";"; }).join("\n"));
|
|
235
|
+
}
|
|
236
|
+
_dropAll();
|
|
237
|
+
|
|
238
|
+
// ====================================================================
|
|
239
|
+
// 1. reconcileTable: CREATE TABLE IF NOT EXISTS + second-reconcile
|
|
240
|
+
// idempotence on real MySQL.
|
|
241
|
+
// ====================================================================
|
|
242
|
+
// region is declared VARCHAR (not TEXT) because the test indexes it
|
|
243
|
+
// below: MySQL refuses an unbounded TEXT/BLOB column in an index without
|
|
244
|
+
// a prefix length (error 1170). On MySQL an operator who wants an indexed
|
|
245
|
+
// string column declares it VARCHAR — that is an operator-schema choice
|
|
246
|
+
// the framework honors, not something reconcileIndex can guess a length
|
|
247
|
+
// for. The Postgres sibling uses TEXT (Postgres indexes TEXT directly).
|
|
248
|
+
var tableDef = {
|
|
249
|
+
name: T,
|
|
250
|
+
columns: { _id: "VARCHAR(64) PRIMARY KEY", region: "VARCHAR(64)", total: "BIGINT", note: "TEXT" },
|
|
251
|
+
};
|
|
252
|
+
var reconcileOk = _block(
|
|
253
|
+
"reconcileTable: first reconcile (CREATE TABLE IF NOT EXISTS) runs on real MySQL",
|
|
254
|
+
function () { dbSchema.reconcileTable(adapter, tableDef, { onDrift: "refuse" }); });
|
|
255
|
+
if (reconcileOk) soft("reconcileTable: first reconcile ran clean on MySQL", true);
|
|
256
|
+
|
|
257
|
+
var tblPresent = _mysql(
|
|
258
|
+
"SELECT count(*) AS n FROM information_schema.tables WHERE table_schema='" +
|
|
259
|
+
DB_NAME + "' AND table_name='" + T + "';");
|
|
260
|
+
soft("reconcileTable: CREATE TABLE DDL landed the table on the server (portable b.sql DDL)",
|
|
261
|
+
/\b1\b/.test(tblPresent));
|
|
262
|
+
|
|
263
|
+
_block(
|
|
264
|
+
"reconcileTable: SECOND reconcile is idempotent on real MySQL " +
|
|
265
|
+
"(no spurious ALTER / duplicate-column / false drift)",
|
|
266
|
+
function () { dbSchema.reconcileTable(adapter, tableDef, { onDrift: "refuse" }); });
|
|
267
|
+
|
|
268
|
+
_block(
|
|
269
|
+
"reconcileTable: declared index (CREATE INDEX IF NOT EXISTS) runs on real MySQL",
|
|
270
|
+
function () {
|
|
271
|
+
dbSchema.reconcileTable(adapter,
|
|
272
|
+
{ name: T, columns: tableDef.columns, indexes: [{ columns: ["region"], name: "bjml_orders_region_idx" }] },
|
|
273
|
+
{ onDrift: "ignore" });
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Ensure the table exists for the CRUD block regardless of reconcile.
|
|
277
|
+
_mysql('CREATE TABLE IF NOT EXISTS "' + T + '" ' +
|
|
278
|
+
'("_id" VARCHAR(64) PRIMARY KEY, "region" VARCHAR(64), "total" BIGINT, "note" TEXT);');
|
|
279
|
+
|
|
280
|
+
// ====================================================================
|
|
281
|
+
// 2. db.from() Query CRUD end-to-end on real MySQL.
|
|
282
|
+
// ====================================================================
|
|
283
|
+
var declared = new Set(["_id", "region", "total", "note"]);
|
|
284
|
+
|
|
285
|
+
_block("db.from().insertOne runs on real MySQL", function () {
|
|
286
|
+
var ins = _from(adapter, T, declared).insertOne({ _id: "o-1", region: "eu", total: 100, note: "first" });
|
|
287
|
+
soft("db.from().insertOne returned the row with _id", ins && ins._id === "o-1");
|
|
288
|
+
});
|
|
289
|
+
_block("db.from().insertOne (rows 2,3) run on MySQL", function () {
|
|
290
|
+
_from(adapter, T, declared).insertOne({ _id: "o-2", region: "eu", total: 250, note: "second" });
|
|
291
|
+
_from(adapter, T, declared).insertOne({ _id: "o-3", region: "us", total: 70, note: "third" });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
_block("db.from().where().first runs on MySQL", function () {
|
|
295
|
+
var oneRow = _from(adapter, T, declared).where("_id", "o-1").first();
|
|
296
|
+
soft("db.from().where().first round-trips id", oneRow && oneRow._id === "o-1");
|
|
297
|
+
soft("db.from().first round-trips region", oneRow && oneRow.region === "eu");
|
|
298
|
+
soft("db.from(): BIGINT total coerces to a JS string on real MySQL (mysql2 bigint-as-string default)",
|
|
299
|
+
oneRow && typeof oneRow.total === "string" && oneRow.total === "100");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
_block("db.from().where().orderBy().all runs on MySQL", function () {
|
|
303
|
+
var euRows = _from(adapter, T, declared).where("region", "eu").orderBy("_id", "asc").all();
|
|
304
|
+
soft("db.from().where().orderBy().all returns the eu rows in order",
|
|
305
|
+
euRows.length === 2 && euRows[0]._id === "o-1" && euRows[1]._id === "o-2");
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
_block("db.from().count runs on MySQL", function () {
|
|
309
|
+
var total = _from(adapter, T, declared).count();
|
|
310
|
+
soft("db.from().count returns 3", Number(total) === 3);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
_block("db.from().updateOne (single-row, PK resolve-then-write) runs on real MySQL", function () {
|
|
314
|
+
var n = _from(adapter, T, declared).where("_id", "o-1").updateOne({ total: 999 });
|
|
315
|
+
soft("db.from().updateOne reported a change", n === true);
|
|
316
|
+
var after = _from(adapter, T, declared).where("_id", "o-1").first();
|
|
317
|
+
soft("db.from().updateOne persisted", after && after.total === "999");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
_block("db.from().updateMany (set-based) runs on real MySQL", function () {
|
|
321
|
+
_from(adapter, T, declared).where("region", "eu").updateMany({ note: "bulk" });
|
|
322
|
+
var bulk = _from(adapter, T, declared).where("_id", "o-2").first();
|
|
323
|
+
soft("db.from().updateMany persisted", bulk && bulk.note === "bulk");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
_block("db.from().increment (COALESCE+?) runs on real MySQL", function () {
|
|
327
|
+
_from(adapter, T, declared).where("_id", "o-3").increment("total", 5);
|
|
328
|
+
var inc = _from(adapter, T, declared).where("_id", "o-3").first();
|
|
329
|
+
soft("db.from().increment persisted (70 + 5 = 75)", inc && inc.total === "75");
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
_block("db.from().deleteOne (single-row, PK resolve-then-write) runs on real MySQL", function () {
|
|
333
|
+
_from(adapter, T, declared).where("_id", "o-3").deleteOne();
|
|
334
|
+
var remain = _from(adapter, T, declared).count();
|
|
335
|
+
soft("db.from().deleteOne removed one row (3 -> 2)", Number(remain) === 2);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
_block("db.from().paginate runs on real MySQL", function () {
|
|
339
|
+
var liveTotal = Number(_from(adapter, T, declared).count());
|
|
340
|
+
var page = _from(adapter, T, declared).paginate({ orderBy: "_id", limit: 1, offset: 0 });
|
|
341
|
+
soft("db.from().paginate envelope shape (items page-limited, total == live count)",
|
|
342
|
+
page && page.items.length === 1 && Number(page.total) === liveTotal && page.totalPages >= 1);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// ---- coercion fidelity: BIGINT > 2^53 reads back as exact string ----
|
|
346
|
+
_mysql('CREATE TABLE IF NOT EXISTS "bjml_things" ' +
|
|
347
|
+
'("_id" VARCHAR(64) PRIMARY KEY, "bignum" BIGINT);');
|
|
348
|
+
var thingsDeclared = new Set(["_id", "bignum"]);
|
|
349
|
+
_block("db.from() reads a > 2^53 BIGINT back as an exact string on real MySQL", function () {
|
|
350
|
+
_mysql('INSERT INTO "bjml_things" ("_id","bignum") VALUES (\'t-1\', 9007199254740993);');
|
|
351
|
+
var t = _from(adapter, "bjml_things", thingsDeclared).where("_id", "t-1").first();
|
|
352
|
+
soft("BIGINT > 2^53 reads back as an exact string (mysql2 bigint fidelity)",
|
|
353
|
+
t && t.bignum === "9007199254740993");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// ====================================================================
|
|
357
|
+
// 3. migrations: composite-PK + CHECK DDL, run-once tracking, the lock.
|
|
358
|
+
// ====================================================================
|
|
359
|
+
var os = require("node:os");
|
|
360
|
+
var fs = require("node:fs");
|
|
361
|
+
var path = require("node:path");
|
|
362
|
+
var migDir = fs.mkdtempSync(path.join(os.tmpdir(), "bjml-mig-"));
|
|
363
|
+
fs.writeFileSync(path.join(migDir, "0001-create-widgets.js"),
|
|
364
|
+
'module.exports = { description: "widgets",' +
|
|
365
|
+
' up: function (db) { db["exec"]("CREATE TABLE IF NOT EXISTS \\"bjml_widgets\\" ' +
|
|
366
|
+
'(\\"k\\" VARCHAR(64), \\"v\\" VARCHAR(64), PRIMARY KEY (\\"k\\", \\"v\\"), ' +
|
|
367
|
+
'CHECK (CHAR_LENGTH(\\"k\\") > 0))"); },' +
|
|
368
|
+
' down: function (db) { db["exec"]("DROP TABLE IF EXISTS \\"bjml_widgets\\""); } };\n');
|
|
369
|
+
_mysql('DROP TABLE IF EXISTS "bjml_widgets";');
|
|
370
|
+
|
|
371
|
+
var mig = migrations.create({ db: adapter, dir: migDir });
|
|
372
|
+
var migRan = false;
|
|
373
|
+
await (async function () {
|
|
374
|
+
try {
|
|
375
|
+
var upResult = await mig.up();
|
|
376
|
+
migRan = true;
|
|
377
|
+
soft("migrations.up ran the migration on real MySQL", true);
|
|
378
|
+
soft("migrations.up applied 0001-create-widgets.js",
|
|
379
|
+
upResult.applied.indexOf("0001-create-widgets.js") !== -1);
|
|
380
|
+
} catch (e) {
|
|
381
|
+
soft("migrations.up runs on real MySQL (threw: " +
|
|
382
|
+
((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
385
|
+
|
|
386
|
+
if (migRan) {
|
|
387
|
+
var widgetPresent = _mysql(
|
|
388
|
+
"SELECT count(*) AS n FROM information_schema.tables WHERE table_schema='" +
|
|
389
|
+
DB_NAME + "' AND table_name='bjml_widgets';");
|
|
390
|
+
soft("migrations: composite-PK + CHECK table created on the server", /\b1\b/.test(widgetPresent));
|
|
391
|
+
|
|
392
|
+
await (async function () {
|
|
393
|
+
try {
|
|
394
|
+
var up2 = await mig.up();
|
|
395
|
+
soft("migrations.up is run-once (second up skips the applied migration)",
|
|
396
|
+
up2.applied.length === 0 && up2.skipped.indexOf("0001-create-widgets.js") !== -1);
|
|
397
|
+
} catch (e) {
|
|
398
|
+
soft("migrations.up second run is run-once (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
399
|
+
}
|
|
400
|
+
})();
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
var st = mig.status();
|
|
404
|
+
soft("migrations.status reports 0001 applied on MySQL",
|
|
405
|
+
st.applied.some(function (r) { return r.name === "0001-create-widgets.js"; }));
|
|
406
|
+
} catch (e) {
|
|
407
|
+
soft("migrations.status runs on MySQL (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
var lockTbl = frameworkSchema.tableName(migrations.LOCK_TABLE);
|
|
411
|
+
await (async function () {
|
|
412
|
+
try {
|
|
413
|
+
_mysql('INSERT INTO ' + safeSql.quoteIdentifier(lockTbl, "sqlite") +
|
|
414
|
+
' ("scope", "lockedAt", "lockedBy") VALUES (\'lock\', ' + Date.now() +
|
|
415
|
+
", 'other-proc');");
|
|
416
|
+
var lockThrew = null;
|
|
417
|
+
try { await mig.up(); } catch (e) { lockThrew = e; }
|
|
418
|
+
soft("migrations: a held advisory lock refuses a concurrent up() on MySQL",
|
|
419
|
+
lockThrew !== null && /lock/i.test((lockThrew && lockThrew.message) || ""));
|
|
420
|
+
_mysql('DELETE FROM ' + safeSql.quoteIdentifier(lockTbl, "sqlite") + " WHERE \"scope\" = 'lock';");
|
|
421
|
+
} catch (e) {
|
|
422
|
+
soft("migrations advisory-lock concurrency test ran on MySQL (threw: " +
|
|
423
|
+
((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
424
|
+
}
|
|
425
|
+
})();
|
|
426
|
+
|
|
427
|
+
// Stale-lock force-replace path. The DELETE+INSERT runs inside a
|
|
428
|
+
// transaction whose boundary keyword is dialect-aware: `BEGIN IMMEDIATE`
|
|
429
|
+
// is SQLite-only and a syntax error on MySQL, so the runner must emit a
|
|
430
|
+
// portable `BEGIN`. Plant a STALE lock row (far in the past) then run
|
|
431
|
+
// up({ staleAfterMs }) — the runner force-replaces it, skips the
|
|
432
|
+
// already-applied migration, and releases.
|
|
433
|
+
await (async function () {
|
|
434
|
+
try {
|
|
435
|
+
_mysql('DELETE FROM ' + safeSql.quoteIdentifier(lockTbl, "sqlite") + " WHERE \"scope\" = 'lock';");
|
|
436
|
+
_mysql('INSERT INTO ' + safeSql.quoteIdentifier(lockTbl, "sqlite") +
|
|
437
|
+
' ("scope", "lockedAt", "lockedBy") VALUES (\'lock\', ' + (Date.now() - 3600000) +
|
|
438
|
+
", 'dead-proc');");
|
|
439
|
+
var staleMig = migrations.create({ db: adapter, dir: migDir, staleAfterMs: 60000 });
|
|
440
|
+
var staleThrew = null;
|
|
441
|
+
try { await staleMig.up(); } catch (e) { staleThrew = e; }
|
|
442
|
+
soft("migrations: stale-lock force-replace uses a portable BEGIN (not BEGIN IMMEDIATE) on MySQL",
|
|
443
|
+
staleThrew === null);
|
|
444
|
+
var deadRows = _mysql('SELECT count(*) AS "n" FROM ' + safeSql.quoteIdentifier(lockTbl, "sqlite") +
|
|
445
|
+
" WHERE \"lockedBy\" = 'dead-proc';");
|
|
446
|
+
soft("migrations: the stale dead-proc lock was force-replaced + released on MySQL",
|
|
447
|
+
/\b0\b/.test(deadRows));
|
|
448
|
+
_mysql('DELETE FROM ' + safeSql.quoteIdentifier(lockTbl, "sqlite") + " WHERE \"scope\" = 'lock';");
|
|
449
|
+
} catch (e) {
|
|
450
|
+
soft("migrations stale-lock-replace test ran on MySQL (threw: " +
|
|
451
|
+
((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
452
|
+
}
|
|
453
|
+
})();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ====================================================================
|
|
457
|
+
// 4. seeders: composite-PK registry, env scoping, run-once + rerunnable.
|
|
458
|
+
// ====================================================================
|
|
459
|
+
var seedDir = fs.mkdtempSync(path.join(os.tmpdir(), "bjml-seed-"));
|
|
460
|
+
fs.mkdirSync(path.join(seedDir, "dev"));
|
|
461
|
+
_mysql('DROP TABLE IF EXISTS "bjml_seed_target";');
|
|
462
|
+
_mysql('CREATE TABLE "bjml_seed_target" ("id" VARCHAR(64) PRIMARY KEY, "label" TEXT);');
|
|
463
|
+
fs.writeFileSync(path.join(seedDir, "dev", "0001-admin.js"),
|
|
464
|
+
'module.exports = { description: "admin",' +
|
|
465
|
+
' run: async function (db) { db.prepare("INSERT INTO \\"bjml_seed_target\\" ' +
|
|
466
|
+
'(\\"id\\", \\"label\\") VALUES (?, ?)").run("admin", "Administrator"); } };\n');
|
|
467
|
+
|
|
468
|
+
var seed = seeders.create({ db: adapter, dir: seedDir });
|
|
469
|
+
var seedRan = false;
|
|
470
|
+
await (async function () {
|
|
471
|
+
try {
|
|
472
|
+
var seedResult = await seed.run({ env: "dev" });
|
|
473
|
+
seedRan = true;
|
|
474
|
+
soft("seeders.run applied the seed on real MySQL", true);
|
|
475
|
+
soft("seeders.run applied 0001-admin.js", seedResult.applied.indexOf("0001-admin.js") !== -1);
|
|
476
|
+
} catch (e) {
|
|
477
|
+
soft("seeders.run runs on real MySQL (threw: " +
|
|
478
|
+
((e && e.message) || String(e)).replace(/\s+/g, " ").slice(0, 200) + ")", false);
|
|
479
|
+
}
|
|
480
|
+
})();
|
|
481
|
+
|
|
482
|
+
if (seedRan) {
|
|
483
|
+
var seededRow = _mysql('SELECT label FROM "bjml_seed_target" WHERE id = \'admin\';');
|
|
484
|
+
soft("seeders: the seed body actually wrote its row on MySQL", /Administrator/.test(seededRow));
|
|
485
|
+
|
|
486
|
+
await (async function () {
|
|
487
|
+
try {
|
|
488
|
+
var seed2 = await seed.run({ env: "dev" });
|
|
489
|
+
soft("seeders.run is run-once (second run skips the applied seed)",
|
|
490
|
+
seed2.applied.length === 0 && seed2.skipped.indexOf("0001-admin.js") !== -1);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
soft("seeders.run second run is run-once (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
493
|
+
}
|
|
494
|
+
})();
|
|
495
|
+
|
|
496
|
+
try {
|
|
497
|
+
var sst = seed.status({ env: "dev" });
|
|
498
|
+
soft("seeders.status reports 0001-admin.js applied on MySQL",
|
|
499
|
+
sst.applied.some(function (r) { return r.name === "0001-admin.js"; }));
|
|
500
|
+
} catch (e) {
|
|
501
|
+
soft("seeders.status runs on MySQL (threw: " + ((e && e.message) || String(e)).slice(0, 120) + ")", false);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// ---- teardown ----
|
|
506
|
+
try { fs.rmSync(migDir, { recursive: true, force: true }); } catch (_e) {}
|
|
507
|
+
try { fs.rmSync(seedDir, { recursive: true, force: true }); } catch (_e) {}
|
|
508
|
+
_dropAll();
|
|
509
|
+
|
|
510
|
+
// ---- summary ----
|
|
511
|
+
//
|
|
512
|
+
// The data layer is dialect-aware: with the handle declaring
|
|
513
|
+
// dialect: "mysql", every soft-check above MUST pass on real MySQL. The
|
|
514
|
+
// formerly-SQLite-only / MySQL-incompatible constructs are resolved:
|
|
515
|
+
// - db-schema.listColumns reads information_schema.columns (not the
|
|
516
|
+
// SQLite-only PRAGMA table_info), and reconcileTable / reconcileIndex
|
|
517
|
+
// emit backtick-quoted DDL (MySQL has no CREATE INDEX IF NOT EXISTS —
|
|
518
|
+
// a duplicate-index re-run is swallowed for idempotence).
|
|
519
|
+
// - db-query single-row updateOne/deleteOne resolve the PRIMARY KEY in a
|
|
520
|
+
// prior SELECT then write `WHERE pk = ?` (MySQL error 1093 forbids a
|
|
521
|
+
// subquery on the UPDATE/DELETE target table).
|
|
522
|
+
// - the migrations + seeders registry/lock tables use VARCHAR(191) for
|
|
523
|
+
// their key text columns (MySQL refuses unbounded TEXT in a key,
|
|
524
|
+
// error 1170) and BIGINT for the ms-epoch lock timestamp (a 32-bit
|
|
525
|
+
// INT overflows, error 1264).
|
|
526
|
+
var failed = _results.filter(function (r) { return !r.ok; });
|
|
527
|
+
|
|
528
|
+
console.log("\n[db-layer-mysql] " +
|
|
529
|
+
(_results.length - failed.length) + "/" + _results.length + " checks passed");
|
|
530
|
+
if (failed.length) {
|
|
531
|
+
console.log("[db-layer-mysql] FAILURES:");
|
|
532
|
+
failed.forEach(function (r) { console.log(" - " + r.label); });
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Replay every recorded finding through the hard `check` so the file
|
|
536
|
+
// FAILS when any data-layer op does not work on real MySQL.
|
|
537
|
+
for (var i = 0; i < _results.length; i++) {
|
|
538
|
+
check(_results[i].label, _results[i].ok);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
module.exports = { run: run };
|
|
543
|
+
|
|
544
|
+
if (require.main === module) {
|
|
545
|
+
run().then(
|
|
546
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
547
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
548
|
+
);
|
|
549
|
+
}
|