@blamejs/blamejs-shop 0.4.56 → 0.4.57
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 +426 -366
- package/lib/vendor/blamejs/.github/codeql/codeql-config.yml +25 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/ci.yml +13 -13
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +8 -7
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +2 -2
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +1381 -4
- package/lib/vendor/blamejs/eslint.config.mjs +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +4 -1
- package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +5 -2
- package/lib/vendor/blamejs/examples/wiki/routes/pages.js +4 -1
- package/lib/vendor/blamejs/fuzz/guard-text.fuzz.js +20 -0
- package/lib/vendor/blamejs/index.js +2 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +7 -23
- package/lib/vendor/blamejs/lib/acme.js +6 -5
- package/lib/vendor/blamejs/lib/agent-event-bus.js +5 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +2 -5
- package/lib/vendor/blamejs/lib/agent-orchestrator.js +2 -5
- package/lib/vendor/blamejs/lib/agent-saga.js +3 -5
- package/lib/vendor/blamejs/lib/agent-tenant.js +2 -5
- package/lib/vendor/blamejs/lib/ai-adverse-decision.js +2 -15
- package/lib/vendor/blamejs/lib/ai-capability.js +1 -6
- package/lib/vendor/blamejs/lib/ai-dp.js +1 -5
- package/lib/vendor/blamejs/lib/ai-input.js +2 -2
- package/lib/vendor/blamejs/lib/ai-pref.js +3 -8
- package/lib/vendor/blamejs/lib/ai-quota.js +3 -14
- package/lib/vendor/blamejs/lib/api-key.js +37 -28
- package/lib/vendor/blamejs/lib/archive-adapters.js +2 -4
- package/lib/vendor/blamejs/lib/archive-entry-policy.js +32 -0
- package/lib/vendor/blamejs/lib/archive-read.js +5 -17
- package/lib/vendor/blamejs/lib/archive-tar-read.js +5 -16
- package/lib/vendor/blamejs/lib/archive.js +2 -10
- package/lib/vendor/blamejs/lib/arg-parser.js +7 -6
- package/lib/vendor/blamejs/lib/asyncapi-traits.js +2 -6
- package/lib/vendor/blamejs/lib/atomic-file.js +108 -31
- package/lib/vendor/blamejs/lib/audit-chain.js +133 -53
- package/lib/vendor/blamejs/lib/audit-daily-review.js +24 -14
- package/lib/vendor/blamejs/lib/audit-emit.js +82 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +257 -0
- package/lib/vendor/blamejs/lib/audit.js +84 -0
- package/lib/vendor/blamejs/lib/auth/access-lock.js +5 -27
- package/lib/vendor/blamejs/lib/auth/dpop.js +23 -35
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +9 -15
- package/lib/vendor/blamejs/lib/auth/jwt-external.js +26 -8
- package/lib/vendor/blamejs/lib/auth/jwt.js +13 -15
- package/lib/vendor/blamejs/lib/auth/lockout.js +6 -25
- package/lib/vendor/blamejs/lib/auth/oauth.js +67 -45
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +55 -32
- package/lib/vendor/blamejs/lib/auth/oid4vp.js +3 -2
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +6 -6
- package/lib/vendor/blamejs/lib/auth/passkey.js +3 -3
- package/lib/vendor/blamejs/lib/auth/password.js +7 -1
- package/lib/vendor/blamejs/lib/auth/saml.js +37 -27
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +2 -14
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +2 -14
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +1 -1
- package/lib/vendor/blamejs/lib/auth/status-list.js +7 -7
- package/lib/vendor/blamejs/lib/auth/step-up.js +6 -12
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +6 -37
- package/lib/vendor/blamejs/lib/backup/bundle.js +11 -18
- package/lib/vendor/blamejs/lib/backup/index.js +14 -47
- package/lib/vendor/blamejs/lib/bounded-map.js +112 -1
- package/lib/vendor/blamejs/lib/breach-deadline.js +1 -11
- package/lib/vendor/blamejs/lib/cache.js +7 -18
- package/lib/vendor/blamejs/lib/cbor.js +2 -1
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +8 -9
- package/lib/vendor/blamejs/lib/cert.js +3 -10
- package/lib/vendor/blamejs/lib/chain-writer.js +162 -47
- package/lib/vendor/blamejs/lib/cli.js +5 -1
- package/lib/vendor/blamejs/lib/client-hints.js +7 -9
- package/lib/vendor/blamejs/lib/cloud-events.js +40 -31
- package/lib/vendor/blamejs/lib/cluster-storage.js +2 -38
- package/lib/vendor/blamejs/lib/cms-codec.js +2 -1
- package/lib/vendor/blamejs/lib/codepoint-class.js +67 -1
- package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +1 -6
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +2 -4
- package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -11
- package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +21 -28
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +2 -14
- package/lib/vendor/blamejs/lib/compliance.js +2 -9
- package/lib/vendor/blamejs/lib/config-drift.js +2 -12
- package/lib/vendor/blamejs/lib/content-digest.js +10 -8
- package/lib/vendor/blamejs/lib/cookies.js +5 -11
- package/lib/vendor/blamejs/lib/cose.js +19 -11
- package/lib/vendor/blamejs/lib/cra-report.js +1 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +5 -10
- package/lib/vendor/blamejs/lib/crypto.js +120 -3
- package/lib/vendor/blamejs/lib/csp.js +235 -3
- package/lib/vendor/blamejs/lib/daemon.js +42 -41
- package/lib/vendor/blamejs/lib/data-act.js +19 -9
- package/lib/vendor/blamejs/lib/db-query.js +6 -40
- package/lib/vendor/blamejs/lib/db.js +34 -12
- package/lib/vendor/blamejs/lib/dbsc.js +5 -6
- package/lib/vendor/blamejs/lib/ddl-change-control.js +18 -14
- package/lib/vendor/blamejs/lib/deprecate.js +4 -5
- package/lib/vendor/blamejs/lib/did.js +6 -2
- package/lib/vendor/blamejs/lib/dsr.js +8 -12
- package/lib/vendor/blamejs/lib/external-db-migrate.js +21 -22
- package/lib/vendor/blamejs/lib/external-db.js +14 -26
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +12 -8
- package/lib/vendor/blamejs/lib/fdx.js +22 -18
- package/lib/vendor/blamejs/lib/file-upload.js +80 -66
- package/lib/vendor/blamejs/lib/flag-evaluation-context.js +5 -8
- package/lib/vendor/blamejs/lib/framework-error.js +12 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +19 -0
- package/lib/vendor/blamejs/lib/fsm.js +7 -12
- package/lib/vendor/blamejs/lib/gate-contract.js +869 -38
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +4 -13
- package/lib/vendor/blamejs/lib/graphql-federation.js +1 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +2 -1
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-archive.js +9 -30
- package/lib/vendor/blamejs/lib/guard-auth.js +23 -80
- package/lib/vendor/blamejs/lib/guard-cidr.js +20 -96
- package/lib/vendor/blamejs/lib/guard-csv.js +135 -196
- package/lib/vendor/blamejs/lib/guard-domain.js +23 -106
- package/lib/vendor/blamejs/lib/guard-dsn.js +16 -13
- package/lib/vendor/blamejs/lib/guard-email.js +46 -53
- package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +2 -1
- package/lib/vendor/blamejs/lib/guard-filename.js +12 -60
- package/lib/vendor/blamejs/lib/guard-graphql.js +28 -75
- package/lib/vendor/blamejs/lib/guard-html-wcag.js +15 -2
- package/lib/vendor/blamejs/lib/guard-html.js +65 -117
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +2 -1
- package/lib/vendor/blamejs/lib/guard-image.js +280 -77
- package/lib/vendor/blamejs/lib/guard-imap-command.js +8 -9
- package/lib/vendor/blamejs/lib/guard-json.js +87 -103
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +20 -88
- package/lib/vendor/blamejs/lib/guard-jwt.js +32 -114
- package/lib/vendor/blamejs/lib/guard-list-id.js +2 -7
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -7
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +5 -6
- package/lib/vendor/blamejs/lib/guard-mail-move.js +2 -1
- package/lib/vendor/blamejs/lib/guard-mail-query.js +5 -3
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +6 -7
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +5 -4
- package/lib/vendor/blamejs/lib/guard-markdown.js +76 -110
- package/lib/vendor/blamejs/lib/guard-message-id.js +5 -6
- package/lib/vendor/blamejs/lib/guard-mime.js +20 -99
- package/lib/vendor/blamejs/lib/guard-oauth.js +19 -73
- package/lib/vendor/blamejs/lib/guard-pdf.js +65 -72
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +12 -13
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +2 -1
- package/lib/vendor/blamejs/lib/guard-regex.js +24 -99
- package/lib/vendor/blamejs/lib/guard-saga-config.js +2 -1
- package/lib/vendor/blamejs/lib/guard-shell.js +22 -87
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +8 -11
- package/lib/vendor/blamejs/lib/guard-sql.js +15 -13
- package/lib/vendor/blamejs/lib/guard-stream-args.js +2 -1
- package/lib/vendor/blamejs/lib/guard-svg.js +95 -140
- package/lib/vendor/blamejs/lib/guard-template.js +23 -80
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +2 -1
- package/lib/vendor/blamejs/lib/guard-text.js +592 -0
- package/lib/vendor/blamejs/lib/guard-time.js +27 -95
- package/lib/vendor/blamejs/lib/guard-uuid.js +21 -93
- package/lib/vendor/blamejs/lib/guard-xml.js +76 -106
- package/lib/vendor/blamejs/lib/guard-yaml.js +24 -60
- package/lib/vendor/blamejs/lib/http-client-cache.js +5 -12
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +2 -4
- package/lib/vendor/blamejs/lib/http-client.js +8 -21
- package/lib/vendor/blamejs/lib/http-message-signature.js +3 -2
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +5 -7
- package/lib/vendor/blamejs/lib/i18n.js +83 -26
- package/lib/vendor/blamejs/lib/inbox.js +8 -8
- package/lib/vendor/blamejs/lib/incident-report.js +3 -21
- package/lib/vendor/blamejs/lib/ip-utils.js +49 -6
- package/lib/vendor/blamejs/lib/jobs.js +3 -2
- package/lib/vendor/blamejs/lib/keychain.js +6 -18
- package/lib/vendor/blamejs/lib/legal-hold.js +6 -15
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +44 -112
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +17 -14
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +16 -80
- package/lib/vendor/blamejs/lib/log-stream-webhook.js +20 -92
- package/lib/vendor/blamejs/lib/mail-arc-sign.js +8 -3
- package/lib/vendor/blamejs/lib/mail-arf.js +3 -2
- package/lib/vendor/blamejs/lib/mail-auth.js +40 -66
- package/lib/vendor/blamejs/lib/mail-bimi.js +19 -39
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
- package/lib/vendor/blamejs/lib/mail-dav.js +8 -35
- package/lib/vendor/blamejs/lib/mail-deploy.js +6 -9
- package/lib/vendor/blamejs/lib/mail-dkim.js +19 -38
- package/lib/vendor/blamejs/lib/mail-greylist.js +15 -26
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -3
- package/lib/vendor/blamejs/lib/mail-journal.js +2 -1
- package/lib/vendor/blamejs/lib/mail-mdn.js +4 -3
- package/lib/vendor/blamejs/lib/mail-rbl.js +5 -8
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -2
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +33 -20
- package/lib/vendor/blamejs/lib/mail-server-imap.js +32 -87
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +35 -51
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +29 -83
- package/lib/vendor/blamejs/lib/mail-server-mx.js +33 -74
- package/lib/vendor/blamejs/lib/mail-server-net.js +177 -0
- package/lib/vendor/blamejs/lib/mail-server-pop3.js +30 -83
- package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +30 -6
- package/lib/vendor/blamejs/lib/mail-server-submission.js +34 -73
- package/lib/vendor/blamejs/lib/mail-server-tls.js +89 -0
- package/lib/vendor/blamejs/lib/mail-spam-score.js +7 -8
- package/lib/vendor/blamejs/lib/mail-store.js +3 -2
- package/lib/vendor/blamejs/lib/mail.js +6 -11
- package/lib/vendor/blamejs/lib/markup-escape.js +31 -0
- package/lib/vendor/blamejs/lib/markup-tokenizer.js +54 -0
- package/lib/vendor/blamejs/lib/mcp-tool-registry.js +26 -23
- package/lib/vendor/blamejs/lib/mcp.js +1 -1
- package/lib/vendor/blamejs/lib/mdoc.js +11 -12
- package/lib/vendor/blamejs/lib/metrics.js +32 -30
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -23
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +11 -22
- package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -7
- package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +31 -48
- package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +7 -10
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +2 -10
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +13 -2
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -26
- package/lib/vendor/blamejs/lib/middleware/deny-response.js +19 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +7 -0
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +4 -20
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +20 -28
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +9 -1
- package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -6
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +12 -27
- package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -7
- package/lib/vendor/blamejs/lib/migrations.js +4 -13
- package/lib/vendor/blamejs/lib/mime-parse.js +34 -17
- package/lib/vendor/blamejs/lib/module-loader.js +44 -0
- package/lib/vendor/blamejs/lib/money.js +105 -0
- package/lib/vendor/blamejs/lib/mtls-ca.js +116 -36
- package/lib/vendor/blamejs/lib/network-byte-quota.js +4 -18
- package/lib/vendor/blamejs/lib/network-dane.js +8 -6
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +97 -6
- package/lib/vendor/blamejs/lib/network-dnssec.js +16 -16
- package/lib/vendor/blamejs/lib/network-heartbeat.js +3 -2
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +29 -41
- package/lib/vendor/blamejs/lib/network-tls.js +40 -42
- package/lib/vendor/blamejs/lib/nis2-report.js +1 -11
- package/lib/vendor/blamejs/lib/nonce-store.js +81 -3
- package/lib/vendor/blamejs/lib/numeric-bounds.js +22 -8
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +2 -6
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +5 -45
- package/lib/vendor/blamejs/lib/object-store/gcs.js +8 -71
- package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -5
- package/lib/vendor/blamejs/lib/object-store/http-request.js +128 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +2 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +9 -77
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -25
- package/lib/vendor/blamejs/lib/observability.js +95 -0
- package/lib/vendor/blamejs/lib/openapi-paths-builder.js +7 -3
- package/lib/vendor/blamejs/lib/otel-export.js +7 -10
- package/lib/vendor/blamejs/lib/outbox.js +43 -37
- package/lib/vendor/blamejs/lib/pagination.js +11 -15
- package/lib/vendor/blamejs/lib/parsers/safe-env.js +5 -4
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +10 -4
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +11 -9
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +2 -2
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +7 -6
- package/lib/vendor/blamejs/lib/pick.js +63 -5
- package/lib/vendor/blamejs/lib/pipl-cn.js +69 -59
- package/lib/vendor/blamejs/lib/privacy-pass.js +8 -6
- package/lib/vendor/blamejs/lib/problem-details.js +2 -5
- package/lib/vendor/blamejs/lib/pubsub.js +4 -8
- package/lib/vendor/blamejs/lib/redact.js +2 -1
- package/lib/vendor/blamejs/lib/render.js +4 -2
- package/lib/vendor/blamejs/lib/request-helpers.js +133 -6
- package/lib/vendor/blamejs/lib/restore.js +5 -22
- package/lib/vendor/blamejs/lib/retention.js +3 -5
- package/lib/vendor/blamejs/lib/safe-async.js +457 -0
- package/lib/vendor/blamejs/lib/safe-buffer.js +137 -6
- package/lib/vendor/blamejs/lib/safe-decompress.js +2 -1
- package/lib/vendor/blamejs/lib/safe-dns.js +2 -1
- package/lib/vendor/blamejs/lib/safe-ical.js +12 -26
- package/lib/vendor/blamejs/lib/safe-json.js +7 -8
- package/lib/vendor/blamejs/lib/safe-jsonpath.js +6 -5
- package/lib/vendor/blamejs/lib/safe-mime.js +25 -29
- package/lib/vendor/blamejs/lib/safe-redirect.js +2 -5
- package/lib/vendor/blamejs/lib/safe-schema.js +7 -6
- package/lib/vendor/blamejs/lib/safe-sql.js +126 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +12 -26
- package/lib/vendor/blamejs/lib/sandbox-worker.js +9 -2
- package/lib/vendor/blamejs/lib/sandbox.js +3 -2
- package/lib/vendor/blamejs/lib/scheduler.js +5 -12
- package/lib/vendor/blamejs/lib/sec-cyber.js +82 -37
- package/lib/vendor/blamejs/lib/seeders.js +4 -11
- package/lib/vendor/blamejs/lib/self-update.js +92 -69
- package/lib/vendor/blamejs/lib/session-device-binding.js +112 -66
- package/lib/vendor/blamejs/lib/session.js +194 -6
- package/lib/vendor/blamejs/lib/sql.js +225 -78
- package/lib/vendor/blamejs/lib/sse.js +6 -10
- package/lib/vendor/blamejs/lib/static.js +136 -98
- package/lib/vendor/blamejs/lib/storage.js +4 -2
- package/lib/vendor/blamejs/lib/structured-fields.js +275 -16
- package/lib/vendor/blamejs/lib/tenant-quota.js +2 -20
- package/lib/vendor/blamejs/lib/tsa.js +11 -15
- package/lib/vendor/blamejs/lib/validate-opts.js +154 -8
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +30 -48
- package/lib/vendor/blamejs/lib/vault-aad.js +3 -2
- package/lib/vendor/blamejs/lib/vc.js +2 -7
- package/lib/vendor/blamejs/lib/vex.js +35 -13
- package/lib/vendor/blamejs/lib/web-push-vapid.js +23 -20
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +706 -0
- package/lib/vendor/blamejs/lib/webhook.js +43 -18
- package/lib/vendor/blamejs/lib/websocket-channels.js +9 -7
- package/lib/vendor/blamejs/lib/websocket.js +7 -3
- package/lib/vendor/blamejs/lib/worm.js +2 -3
- package/lib/vendor/blamejs/lib/ws-client.js +8 -10
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.15.13.json +81 -0
- package/lib/vendor/blamejs/scripts/check-changelog-extract.js +1 -1
- package/lib/vendor/blamejs/test/00-primitives.js +136 -7
- package/lib/vendor/blamejs/test/10-state.js +9 -3
- package/lib/vendor/blamejs/test/30-chain.js +40 -0
- package/lib/vendor/blamejs/test/helpers/check.js +18 -0
- package/lib/vendor/blamejs/test/helpers/db.js +4 -0
- package/lib/vendor/blamejs/test/helpers/index.js +1 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +8 -1
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +1 -1
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +269 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +55 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-object-store-adapter.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/bounded-map.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/chain-writer-multichain.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +2788 -3214
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +63 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-resolve-opts.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +9 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract-content-gate.test.js +290 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +23 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-gate-disposition-coverage.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +184 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +54 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-text.test.js +365 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +20 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mime-parse.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-revocation.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-release.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-memory-getorinsert.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/session-valid-from.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/validate-opts-shape.test.js +235 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +374 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +75 -8
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Memory rate-limit backends — get-or-insert routing regression guard.
|
|
4
|
+
*
|
|
5
|
+
* Both in-memory backends keep a request-keyed Map (token buckets /
|
|
6
|
+
* fixed-window counters) and lazily create the per-key record on first
|
|
7
|
+
* sight. This pins the observable verdict behaviour across the
|
|
8
|
+
* insert path AND the existing-record path (token refill / window
|
|
9
|
+
* rollover) so the routing of those get-or-insert sites through
|
|
10
|
+
* b.boundedMap.getOrInsert stays behaviour-identical.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
var helpers = require("../helpers");
|
|
14
|
+
var b = helpers.b;
|
|
15
|
+
var check = helpers.check;
|
|
16
|
+
|
|
17
|
+
async function run() {
|
|
18
|
+
var rateLimitMod = b.middleware._modules.rateLimit;
|
|
19
|
+
|
|
20
|
+
// ---- token-bucket backend ----
|
|
21
|
+
var tb = rateLimitMod._memoryTokenBucketBackend({ burst: 3, refillPerSecond: 1 });
|
|
22
|
+
|
|
23
|
+
// First take on a brand-new key inserts the bucket and spends one token.
|
|
24
|
+
var t1 = tb.take("ip-a", 1);
|
|
25
|
+
check("token-bucket: first take allowed", t1.allowed === true);
|
|
26
|
+
check("token-bucket: first take reports full burst as limit", t1.limit === 3);
|
|
27
|
+
check("token-bucket: first take remaining = burst - 1", t1.remaining === 2);
|
|
28
|
+
|
|
29
|
+
// Subsequent takes on the SAME key hit the existing-bucket branch and
|
|
30
|
+
// keep decrementing (no refill in the same millisecond window).
|
|
31
|
+
var t2 = tb.take("ip-a", 1);
|
|
32
|
+
check("token-bucket: second take allowed", t2.allowed === true);
|
|
33
|
+
check("token-bucket: second take remaining decremented", t2.remaining === 1);
|
|
34
|
+
var t3 = tb.take("ip-a", 1);
|
|
35
|
+
check("token-bucket: third take allowed", t3.allowed === true);
|
|
36
|
+
var t4 = tb.take("ip-a", 1);
|
|
37
|
+
check("token-bucket: fourth take denied (bucket drained)", t4.allowed === false);
|
|
38
|
+
check("token-bucket: denied verdict has retryAfter", t4.retryAfter >= 1);
|
|
39
|
+
|
|
40
|
+
// A different key gets its own fresh bucket (independent insert).
|
|
41
|
+
var u1 = tb.take("ip-b", 1);
|
|
42
|
+
check("token-bucket: distinct key gets fresh bucket", u1.allowed === true && u1.remaining === 2);
|
|
43
|
+
|
|
44
|
+
// reset drops the key; the next take re-inserts a full bucket.
|
|
45
|
+
tb.reset("ip-a");
|
|
46
|
+
var t5 = tb.take("ip-a", 1);
|
|
47
|
+
check("token-bucket: after reset, bucket re-inserted full", t5.allowed === true && t5.remaining === 2);
|
|
48
|
+
tb.close();
|
|
49
|
+
|
|
50
|
+
// ---- fixed-window backend ----
|
|
51
|
+
var fw = rateLimitMod._memoryFixedWindowBackend({ max: 2, windowMs: 60000 });
|
|
52
|
+
|
|
53
|
+
// First take inserts the counter (count -> 1).
|
|
54
|
+
var f1 = fw.take("ip-c", 1);
|
|
55
|
+
check("fixed-window: first take allowed", f1.allowed === true);
|
|
56
|
+
check("fixed-window: first take limit = max", f1.limit === 2);
|
|
57
|
+
check("fixed-window: first take remaining = max - 1", f1.remaining === 1);
|
|
58
|
+
|
|
59
|
+
// Same window increments the existing counter.
|
|
60
|
+
var f2 = fw.take("ip-c", 1);
|
|
61
|
+
check("fixed-window: second take allowed (at max)", f2.allowed === true);
|
|
62
|
+
check("fixed-window: second take remaining 0", f2.remaining === 0);
|
|
63
|
+
var f3 = fw.take("ip-c", 1);
|
|
64
|
+
check("fixed-window: third take over max denied", f3.allowed === false);
|
|
65
|
+
check("fixed-window: denied verdict has retryAfter", f3.retryAfter >= 1);
|
|
66
|
+
|
|
67
|
+
// Distinct key inserts its own counter.
|
|
68
|
+
var g1 = fw.take("ip-d", 1);
|
|
69
|
+
check("fixed-window: distinct key gets fresh counter", g1.allowed === true && g1.remaining === 1);
|
|
70
|
+
|
|
71
|
+
// reset drops the key; next take re-inserts.
|
|
72
|
+
fw.reset("ip-c");
|
|
73
|
+
var f4 = fw.take("ip-c", 1);
|
|
74
|
+
check("fixed-window: after reset, counter re-inserted", f4.allowed === true && f4.remaining === 1);
|
|
75
|
+
fw.close();
|
|
76
|
+
|
|
77
|
+
// ---- fixed-window: window rollover re-seeds the existing key ----
|
|
78
|
+
// Drive a 1ms window so a real wall-clock advance rolls the window and
|
|
79
|
+
// exercises the "key present but window changed -> re-seed" branch.
|
|
80
|
+
var fwRoll = rateLimitMod._memoryFixedWindowBackend({ max: 1, windowMs: 1 });
|
|
81
|
+
var r1 = fwRoll.take("ip-e", 1);
|
|
82
|
+
check("fixed-window rollover: first take allowed", r1.allowed === true);
|
|
83
|
+
await helpers.waitUntil(function () {
|
|
84
|
+
// Once the wall clock advances past the 1ms window, a fresh take
|
|
85
|
+
// re-seeds count to 1 and is allowed again on the SAME key.
|
|
86
|
+
var v = fwRoll.take("ip-e", 1);
|
|
87
|
+
return v.allowed === true;
|
|
88
|
+
}, { timeoutMs: 5000, label: "fixed-window rollover: same key allowed in new window" });
|
|
89
|
+
check("fixed-window rollover: same key re-seeded in new window", true);
|
|
90
|
+
fwRoll.close();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { run: run };
|
|
94
|
+
|
|
95
|
+
if (require.main === module) {
|
|
96
|
+
run().then(
|
|
97
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
|
|
98
|
+
function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -323,6 +323,94 @@ async function run() {
|
|
|
323
323
|
testExtractBearerMultipleAuthHeaders();
|
|
324
324
|
testExtractBearerNonString();
|
|
325
325
|
testExtractBearerLeadingTrailingSpaces();
|
|
326
|
+
testMakeSkipMatcher();
|
|
327
|
+
testMakeResourceAuditEmitter();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function testMakeResourceAuditEmitter() {
|
|
331
|
+
var events = [];
|
|
332
|
+
var sink = { safeEmit: function (e) { events.push(e); } };
|
|
333
|
+
|
|
334
|
+
// idFor derives the resource id; no req → no actor.
|
|
335
|
+
var emit = b.requestHelpers.makeResourceAuditEmitter(sink, "auth.lockout",
|
|
336
|
+
function (key) { return "ns:" + key; });
|
|
337
|
+
check("makeResourceAuditEmitter: returns an emitter", typeof emit === "function");
|
|
338
|
+
emit("locked", "k1", "denied", { attempts: 3 }, null);
|
|
339
|
+
check("makeResourceAuditEmitter: event action/outcome",
|
|
340
|
+
events[0] && events[0].action === "locked" && events[0].outcome === "denied");
|
|
341
|
+
check("makeResourceAuditEmitter: resource kind + idFor-derived id",
|
|
342
|
+
events[0].resource && events[0].resource.kind === "auth.lockout" && events[0].resource.id === "ns:k1");
|
|
343
|
+
check("makeResourceAuditEmitter: metadata passed through", events[0].metadata.attempts === 3);
|
|
344
|
+
check("makeResourceAuditEmitter: no req → no actor", events[0].actor === undefined);
|
|
345
|
+
|
|
346
|
+
// default idFor = key verbatim; req → actor stamped.
|
|
347
|
+
var emit2 = b.requestHelpers.makeResourceAuditEmitter(sink, "session.device");
|
|
348
|
+
emit2("rotated", "tok-hash", "success", {}, { socket: { remoteAddress: "1.2.3.4" }, headers: {} });
|
|
349
|
+
check("makeResourceAuditEmitter: default idFor is the key verbatim", events[1].resource.id === "tok-hash");
|
|
350
|
+
check("makeResourceAuditEmitter: req → actor stamped", events[1].actor !== undefined);
|
|
351
|
+
|
|
352
|
+
// falsy sink → disabled (operator opted out).
|
|
353
|
+
var n = events.length;
|
|
354
|
+
b.requestHelpers.makeResourceAuditEmitter(null, "x")("a", "k", "o", {}, null);
|
|
355
|
+
check("makeResourceAuditEmitter: falsy sink disables emit", events.length === n);
|
|
356
|
+
|
|
357
|
+
// a throwing sink is swallowed (never breaks the request).
|
|
358
|
+
var threw = false;
|
|
359
|
+
try {
|
|
360
|
+
b.requestHelpers.makeResourceAuditEmitter({ safeEmit: function () { throw new Error("boom"); } }, "x")
|
|
361
|
+
("a", "k", "o", {}, null);
|
|
362
|
+
} catch (_e) { threw = true; }
|
|
363
|
+
check("makeResourceAuditEmitter: throwing sink is drop-silent", threw === false);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function testMakeSkipMatcher() {
|
|
367
|
+
var shouldSkip = b.requestHelpers.makeSkipMatcher(
|
|
368
|
+
{ skipPaths: ["/healthz", /^\/webhooks\//] }, "test.makeSkipMatcher");
|
|
369
|
+
check("makeSkipMatcher: returns a predicate", typeof shouldSkip === "function");
|
|
370
|
+
check("makeSkipMatcher: string-prefix match", shouldSkip({ pathname: "/healthz" }) === true);
|
|
371
|
+
check("makeSkipMatcher: regexp match", shouldSkip({ pathname: "/webhooks/stripe" }) === true);
|
|
372
|
+
check("makeSkipMatcher: non-matching path", shouldSkip({ pathname: "/account" }) === false);
|
|
373
|
+
check("makeSkipMatcher: falls back to req.url", shouldSkip({ url: "/healthz" }) === true);
|
|
374
|
+
check("makeSkipMatcher: falls back to req.originalUrl", shouldSkip({ originalUrl: "/healthz" }) === true);
|
|
375
|
+
check("makeSkipMatcher: missing path → '/' (no skip)", shouldSkip({}) === false);
|
|
376
|
+
|
|
377
|
+
// SEGMENT-BOUNDARY (not raw startsWith) — the guard-bypass fix. "/healthz"
|
|
378
|
+
// must NOT skip the sibling "/healthzzz", but MUST skip the descendant.
|
|
379
|
+
check("makeSkipMatcher: segment boundary — sibling NOT skipped", shouldSkip({ pathname: "/healthzzz" }) === false);
|
|
380
|
+
check("makeSkipMatcher: segment boundary — descendant skipped", shouldSkip({ pathname: "/healthz/ready" }) === true);
|
|
381
|
+
// Query string is stripped before matching (match on path, never the query).
|
|
382
|
+
check("makeSkipMatcher: query string stripped", shouldSkip({ url: "/healthz?ready=1" }) === true);
|
|
383
|
+
check("makeSkipMatcher: query can't fake a match", shouldSkip({ url: "/account?x=/healthz" }) === false);
|
|
384
|
+
|
|
385
|
+
// A string entry ending in "/" is itself a segment prefix.
|
|
386
|
+
var slashEntry = b.requestHelpers.makeSkipMatcher({ skipPaths: ["/api/"] }, "test.slash");
|
|
387
|
+
check("makeSkipMatcher: trailing-slash entry matches descendant", slashEntry({ pathname: "/api/v1" }) === true);
|
|
388
|
+
check("makeSkipMatcher: trailing-slash entry rejects sibling", slashEntry({ pathname: "/apixyz" }) === false);
|
|
389
|
+
|
|
390
|
+
// exact:true — whole-path equality only, no descendant.
|
|
391
|
+
var exactM = b.requestHelpers.makeSkipMatcher({ skipPaths: ["/foo"], exact: true }, "test.exact");
|
|
392
|
+
check("makeSkipMatcher: exact matches whole path", exactM({ pathname: "/foo" }) === true);
|
|
393
|
+
check("makeSkipMatcher: exact rejects descendant", exactM({ pathname: "/foo/bar" }) === false);
|
|
394
|
+
|
|
395
|
+
// skip(req) predicate composes; a throwing predicate fails CLOSED (keeps guard ON).
|
|
396
|
+
var withFn = b.requestHelpers.makeSkipMatcher(
|
|
397
|
+
{ skip: function (req) { return req.method === "OPTIONS"; } }, "test.skipFn");
|
|
398
|
+
check("makeSkipMatcher: skip predicate true", withFn({ method: "OPTIONS" }) === true);
|
|
399
|
+
check("makeSkipMatcher: skip predicate false", withFn({ method: "POST" }) === false);
|
|
400
|
+
var throwing = b.requestHelpers.makeSkipMatcher(
|
|
401
|
+
{ skip: function () { throw new Error("boom"); } }, "test.skipThrow");
|
|
402
|
+
check("makeSkipMatcher: throwing predicate fails closed (no skip)", throwing({}) === false);
|
|
403
|
+
|
|
404
|
+
// Build-time validation: a bad skipPaths entry dies at boot, not on first request.
|
|
405
|
+
check("makeSkipMatcher: non-array skipPaths throws",
|
|
406
|
+
(function () { try { b.requestHelpers.makeSkipMatcher({ skipPaths: "x" }); return false; }
|
|
407
|
+
catch (e) { return e instanceof TypeError; } })());
|
|
408
|
+
check("makeSkipMatcher: bad skipPaths entry throws",
|
|
409
|
+
(function () { try { b.requestHelpers.makeSkipMatcher({ skipPaths: [123] }); return false; }
|
|
410
|
+
catch (e) { return e instanceof TypeError; } })());
|
|
411
|
+
check("makeSkipMatcher: non-function skip throws",
|
|
412
|
+
(function () { try { b.requestHelpers.makeSkipMatcher({ skip: "x" }); return false; }
|
|
413
|
+
catch (e) { return e instanceof TypeError; } })());
|
|
326
414
|
}
|
|
327
415
|
|
|
328
416
|
module.exports = { run: run };
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* b.safeAsync.repeating + b.safeAsync.flushLoop — bounded-cadence + flush
|
|
4
4
|
* loop primitives that replace ad-hoc setInterval / setTimeout chains.
|
|
5
|
+
* Also covers b.safeAsync.makeBufferedEnqueue + b.safeAsync.makeDrainingClose
|
|
6
|
+
* + b.safeAsync.makeBatchDrain — the backpressure enqueue, graceful
|
|
7
|
+
* drain-then-close, and single-flight drain loop shared by the batching
|
|
8
|
+
* log-stream sinks.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
11
|
var helpers = require("../helpers");
|
|
@@ -151,6 +155,280 @@ async function run() {
|
|
|
151
155
|
rejectsF("bad ms (NaN)", function () { b.safeAsync.flushLoop(function () {}, NaN); }, /async\/bad-arg/);
|
|
152
156
|
rejectsF("bad ms (zero)", function () { b.safeAsync.flushLoop(function () {}, 0); }, /async\/bad-arg/);
|
|
153
157
|
rejectsF("bad ms (string)", function () { b.safeAsync.flushLoop(function () {}, "x"); }, /async\/bad-arg/);
|
|
158
|
+
|
|
159
|
+
// ---- makeBufferedEnqueue: backpressure enqueue for batching sinks ----
|
|
160
|
+
check("safeAsync.makeBufferedEnqueue is fn",
|
|
161
|
+
typeof b.safeAsync.makeBufferedEnqueue === "function");
|
|
162
|
+
|
|
163
|
+
// Partial batch defers to schedule; full batch fires flush; result shape.
|
|
164
|
+
var beBuf = [];
|
|
165
|
+
var flushes = 0, schedules = 0;
|
|
166
|
+
var beEnqueue = b.safeAsync.makeBufferedEnqueue(beBuf, {
|
|
167
|
+
batchSize: 3,
|
|
168
|
+
bufferLimit: 5,
|
|
169
|
+
// Drain on a microtask, mirroring the sinks' async _flush — the enqueue
|
|
170
|
+
// return value must see the pre-drain depth.
|
|
171
|
+
flush: function () { flushes += 1; return Promise.resolve().then(function () { beBuf.length = 0; }); },
|
|
172
|
+
schedule: function () { schedules += 1; },
|
|
173
|
+
});
|
|
174
|
+
var r1 = await beEnqueue({ n: 1 });
|
|
175
|
+
check("bufferedEnqueue: accepted result + queued depth", r1.accepted === true && r1.queued === 1);
|
|
176
|
+
check("bufferedEnqueue: partial batch schedules, no flush", schedules === 1 && flushes === 0);
|
|
177
|
+
await beEnqueue({ n: 2 });
|
|
178
|
+
var r3 = await beEnqueue({ n: 3 }); // batch full → flush, no schedule
|
|
179
|
+
check("bufferedEnqueue: full batch triggers flush", flushes === 1);
|
|
180
|
+
check("bufferedEnqueue: queued depth reported pre-flush", r3.queued === 3);
|
|
181
|
+
check("bufferedEnqueue: full-batch turn does not schedule", schedules === 2);
|
|
182
|
+
|
|
183
|
+
// Overflow: drop oldest once bufferLimit is reached, evicted record to onOverflow.
|
|
184
|
+
var ovBuf = [];
|
|
185
|
+
var ovDrops = [];
|
|
186
|
+
var ovEnqueue = b.safeAsync.makeBufferedEnqueue(ovBuf, {
|
|
187
|
+
batchSize: 100, // never flush via batch — isolate overflow
|
|
188
|
+
bufferLimit: 2,
|
|
189
|
+
flush: function () { return Promise.resolve(); },
|
|
190
|
+
schedule: function () {},
|
|
191
|
+
onOverflow: function (dropped) { ovDrops.push(dropped); },
|
|
192
|
+
});
|
|
193
|
+
await ovEnqueue({ id: "a" });
|
|
194
|
+
await ovEnqueue({ id: "b" });
|
|
195
|
+
await ovEnqueue({ id: "c" }); // len 2 >= limit 2 → evict "a", push "c"
|
|
196
|
+
check("bufferedEnqueue: overflow drops oldest",
|
|
197
|
+
ovBuf.length === 2 && ovBuf[0].id === "b" && ovBuf[1].id === "c");
|
|
198
|
+
check("bufferedEnqueue: onOverflow receives evicted record",
|
|
199
|
+
ovDrops.length === 1 && ovDrops[0].id === "a");
|
|
200
|
+
|
|
201
|
+
// onOverflow is optional — overflow without it must stay silent.
|
|
202
|
+
var noCbBuf = [];
|
|
203
|
+
var noCbEnqueue = b.safeAsync.makeBufferedEnqueue(noCbBuf, {
|
|
204
|
+
batchSize: 100, bufferLimit: 1,
|
|
205
|
+
flush: function () { return Promise.resolve(); },
|
|
206
|
+
schedule: function () {},
|
|
207
|
+
});
|
|
208
|
+
await noCbEnqueue({ x: 1 });
|
|
209
|
+
await noCbEnqueue({ x: 2 }); // overflow, no onOverflow → silent
|
|
210
|
+
check("bufferedEnqueue: overflow without onOverflow is silent",
|
|
211
|
+
noCbBuf.length === 1 && noCbBuf[0].x === 2);
|
|
212
|
+
|
|
213
|
+
// Config-time validation (TypeError) so sink-wiring typos surface at setup.
|
|
214
|
+
function beRejects(label, fn) {
|
|
215
|
+
var threw = null;
|
|
216
|
+
try { fn(); } catch (e) { threw = e; }
|
|
217
|
+
check("bufferedEnqueue: rejects " + label, threw instanceof TypeError);
|
|
218
|
+
}
|
|
219
|
+
var okOpts = { batchSize: 1, bufferLimit: 1, flush: function () {}, schedule: function () {} };
|
|
220
|
+
function withOpt(over) { return Object.assign({}, okOpts, over); }
|
|
221
|
+
beRejects("non-array buffer", function () { b.safeAsync.makeBufferedEnqueue({}, okOpts); });
|
|
222
|
+
beRejects("missing opts", function () { b.safeAsync.makeBufferedEnqueue([]); });
|
|
223
|
+
beRejects("bad batchSize", function () { b.safeAsync.makeBufferedEnqueue([], withOpt({ batchSize: 0 })); });
|
|
224
|
+
beRejects("bad bufferLimit", function () { b.safeAsync.makeBufferedEnqueue([], withOpt({ bufferLimit: -1 })); });
|
|
225
|
+
beRejects("non-fn flush", function () { b.safeAsync.makeBufferedEnqueue([], withOpt({ flush: null })); });
|
|
226
|
+
beRejects("non-fn schedule", function () { b.safeAsync.makeBufferedEnqueue([], withOpt({ schedule: null })); });
|
|
227
|
+
beRejects("non-fn onOverflow", function () { b.safeAsync.makeBufferedEnqueue([], withOpt({ onOverflow: 5 })); });
|
|
228
|
+
|
|
229
|
+
// ---- makeDrainingClose: graceful drain-then-close for batching sinks ----
|
|
230
|
+
check("safeAsync.makeDrainingClose is fn",
|
|
231
|
+
typeof b.safeAsync.makeDrainingClose === "function");
|
|
232
|
+
|
|
233
|
+
// Order is load-bearing: cancel → await inflight → flush → markClosed.
|
|
234
|
+
var seq = [];
|
|
235
|
+
var dcInflight = Promise.resolve().then(function () { seq.push("inflight"); });
|
|
236
|
+
var dcClose = b.safeAsync.makeDrainingClose({
|
|
237
|
+
scheduler: { cancel: function () { seq.push("cancel"); } },
|
|
238
|
+
getInflight: function () { return dcInflight; },
|
|
239
|
+
flush: function () { seq.push("flush"); return Promise.resolve(); },
|
|
240
|
+
markClosed: function () { seq.push("closed"); },
|
|
241
|
+
});
|
|
242
|
+
await dcClose();
|
|
243
|
+
check("drainingClose: drains in order cancel->inflight->flush->closed",
|
|
244
|
+
seq.join(",") === "cancel,inflight,flush,closed");
|
|
245
|
+
|
|
246
|
+
// No in-flight drain → still flushes + marks closed.
|
|
247
|
+
var seq2 = [];
|
|
248
|
+
var dcClose2 = b.safeAsync.makeDrainingClose({
|
|
249
|
+
scheduler: { cancel: function () { seq2.push("cancel"); } },
|
|
250
|
+
getInflight: function () { return null; },
|
|
251
|
+
flush: function () { seq2.push("flush"); return Promise.resolve(); },
|
|
252
|
+
markClosed: function () { seq2.push("closed"); },
|
|
253
|
+
});
|
|
254
|
+
await dcClose2();
|
|
255
|
+
check("drainingClose: no inflight still flushes + closes",
|
|
256
|
+
seq2.join(",") === "cancel,flush,closed");
|
|
257
|
+
|
|
258
|
+
// In-flight rejection is swallowed — close still flushes + marks closed.
|
|
259
|
+
var dcClosedFlag = false;
|
|
260
|
+
var dcClose3 = b.safeAsync.makeDrainingClose({
|
|
261
|
+
scheduler: { cancel: function () {} },
|
|
262
|
+
getInflight: function () { return Promise.reject(new Error("drain boom")); },
|
|
263
|
+
flush: function () { return Promise.resolve(); },
|
|
264
|
+
markClosed: function () { dcClosedFlag = true; },
|
|
265
|
+
});
|
|
266
|
+
await dcClose3();
|
|
267
|
+
check("drainingClose: in-flight rejection swallowed, close completes", dcClosedFlag === true);
|
|
268
|
+
|
|
269
|
+
// Config-time validation.
|
|
270
|
+
function dcRejects(label, fn) {
|
|
271
|
+
var threw = null;
|
|
272
|
+
try { fn(); } catch (e) { threw = e; }
|
|
273
|
+
check("drainingClose: rejects " + label, threw instanceof TypeError);
|
|
274
|
+
}
|
|
275
|
+
var okClose = {
|
|
276
|
+
scheduler: { cancel: function () {} }, getInflight: function () {},
|
|
277
|
+
flush: function () {}, markClosed: function () {},
|
|
278
|
+
};
|
|
279
|
+
function withClose(over) { return Object.assign({}, okClose, over); }
|
|
280
|
+
dcRejects("missing opts", function () { b.safeAsync.makeDrainingClose(); });
|
|
281
|
+
dcRejects("scheduler w/o cancel", function () { b.safeAsync.makeDrainingClose(withClose({ scheduler: {} })); });
|
|
282
|
+
dcRejects("non-fn getInflight", function () { b.safeAsync.makeDrainingClose(withClose({ getInflight: null })); });
|
|
283
|
+
dcRejects("non-fn flush", function () { b.safeAsync.makeDrainingClose(withClose({ flush: null })); });
|
|
284
|
+
dcRejects("non-fn markClosed", function () { b.safeAsync.makeDrainingClose(withClose({ markClosed: null })); });
|
|
285
|
+
|
|
286
|
+
// ---- makeBatchDrain: single-flight drain loop for batching sinks ----
|
|
287
|
+
check("safeAsync.makeBatchDrain is fn",
|
|
288
|
+
typeof b.safeAsync.makeBatchDrain === "function");
|
|
289
|
+
|
|
290
|
+
// Drains the whole buffer in batchSize chunks via sendBatch.
|
|
291
|
+
var bdBuf = [1, 2, 3, 4, 5];
|
|
292
|
+
var bdSent = [];
|
|
293
|
+
var bdDrain = b.safeAsync.makeBatchDrain({
|
|
294
|
+
buffer: bdBuf,
|
|
295
|
+
batchSize: 2,
|
|
296
|
+
scheduler: b.safeAsync.makeScheduledFlush(20, function () {}),
|
|
297
|
+
isClosed: function () { return false; },
|
|
298
|
+
sendBatch: function (batch) { bdSent.push(batch.slice()); return Promise.resolve(); },
|
|
299
|
+
onRetryExhausted: function () {},
|
|
300
|
+
});
|
|
301
|
+
await bdDrain.flush();
|
|
302
|
+
check("batchDrain: drains whole buffer in batchSize chunks",
|
|
303
|
+
bdBuf.length === 0 && bdSent.length === 3 &&
|
|
304
|
+
bdSent[0].join() === "1,2" && bdSent[2].join() === "5");
|
|
305
|
+
check("batchDrain: isInFlight false after drain", bdDrain.isInFlight() === false);
|
|
306
|
+
|
|
307
|
+
// retry-exhausted: a sendBatch throw reports + stops the loop, remainder kept.
|
|
308
|
+
var reBuf = [1, 2, 3, 4];
|
|
309
|
+
var reExhausted = null;
|
|
310
|
+
var reDrain = b.safeAsync.makeBatchDrain({
|
|
311
|
+
buffer: reBuf,
|
|
312
|
+
batchSize: 2,
|
|
313
|
+
scheduler: b.safeAsync.makeScheduledFlush(20, function () {}),
|
|
314
|
+
isClosed: function () { return false; },
|
|
315
|
+
sendBatch: function () { return Promise.reject(new Error("send boom")); },
|
|
316
|
+
onRetryExhausted: function (batch, e) { reExhausted = { n: batch.length, msg: e.message }; },
|
|
317
|
+
});
|
|
318
|
+
await reDrain.flush();
|
|
319
|
+
check("batchDrain: retry-exhausted reported, loop stops, remainder kept",
|
|
320
|
+
reExhausted && reExhausted.n === 2 && reExhausted.msg === "send boom" && reBuf.length === 2);
|
|
321
|
+
|
|
322
|
+
// beforeDrain failure drains the WHOLE buffer as a drop, no send attempted.
|
|
323
|
+
var bfBuf = [1, 2, 3];
|
|
324
|
+
var bfFailed = null, bfSends = 0;
|
|
325
|
+
var bfDrain = b.safeAsync.makeBatchDrain({
|
|
326
|
+
buffer: bfBuf,
|
|
327
|
+
batchSize: 2,
|
|
328
|
+
scheduler: b.safeAsync.makeScheduledFlush(20, function () {}),
|
|
329
|
+
isClosed: function () { return false; },
|
|
330
|
+
beforeDrain: function () { return Promise.reject(new Error("autocreate boom")); },
|
|
331
|
+
onBeforeDrainFail: function (records, e) { bfFailed = { n: records.length, msg: e.message }; },
|
|
332
|
+
sendBatch: function () { bfSends += 1; return Promise.resolve(); },
|
|
333
|
+
onRetryExhausted: function () {},
|
|
334
|
+
});
|
|
335
|
+
await bfDrain.flush();
|
|
336
|
+
check("batchDrain: beforeDrain failure drops whole buffer, no send",
|
|
337
|
+
bfFailed && bfFailed.n === 3 && bfFailed.msg === "autocreate boom" &&
|
|
338
|
+
bfSends === 0 && bfBuf.length === 0);
|
|
339
|
+
|
|
340
|
+
// Custom takeBatch governs the batch shape (byte-cap style — one per batch).
|
|
341
|
+
var tbBuf = [10, 20, 30];
|
|
342
|
+
var tbSent = [];
|
|
343
|
+
var tbDrain = b.safeAsync.makeBatchDrain({
|
|
344
|
+
buffer: tbBuf,
|
|
345
|
+
batchSize: 100,
|
|
346
|
+
scheduler: b.safeAsync.makeScheduledFlush(20, function () {}),
|
|
347
|
+
isClosed: function () { return false; },
|
|
348
|
+
takeBatch: function (buf) { return buf.splice(0, 1); },
|
|
349
|
+
sendBatch: function (batch) { tbSent.push(batch[0]); return Promise.resolve(); },
|
|
350
|
+
onRetryExhausted: function () {},
|
|
351
|
+
});
|
|
352
|
+
await tbDrain.flush();
|
|
353
|
+
check("batchDrain: custom takeBatch governs batch shape",
|
|
354
|
+
tbSent.join() === "10,20,30" && tbBuf.length === 0);
|
|
355
|
+
|
|
356
|
+
// Single-flight: concurrent flushes drain the buffer once (no double-send).
|
|
357
|
+
var sfBuf = [1, 2, 3];
|
|
358
|
+
var sfSent = 0;
|
|
359
|
+
var sfDrain = b.safeAsync.makeBatchDrain({
|
|
360
|
+
buffer: sfBuf,
|
|
361
|
+
batchSize: 1,
|
|
362
|
+
scheduler: b.safeAsync.makeScheduledFlush(20, function () {}),
|
|
363
|
+
isClosed: function () { return false; },
|
|
364
|
+
sendBatch: function () { sfSent += 1; return Promise.resolve(); },
|
|
365
|
+
onRetryExhausted: function () {},
|
|
366
|
+
});
|
|
367
|
+
check("batchDrain: isInFlight false before flush", sfDrain.isInFlight() === false);
|
|
368
|
+
var p1 = sfDrain.flush();
|
|
369
|
+
check("batchDrain: isInFlight true synchronously after flush()", sfDrain.isInFlight() === true);
|
|
370
|
+
var p2 = sfDrain.flush(); // in-flight → must not start a second drain
|
|
371
|
+
await Promise.all([p1, p2]);
|
|
372
|
+
check("batchDrain: concurrent flush drains buffer exactly once",
|
|
373
|
+
sfSent === 3 && sfBuf.length === 0 && sfDrain.isInFlight() === false);
|
|
374
|
+
|
|
375
|
+
// isClosed stops the loop between batches.
|
|
376
|
+
var clBuf = [1, 2, 3, 4];
|
|
377
|
+
var clSent = 0, clClosed = false;
|
|
378
|
+
var clDrain = b.safeAsync.makeBatchDrain({
|
|
379
|
+
buffer: clBuf,
|
|
380
|
+
batchSize: 1,
|
|
381
|
+
scheduler: b.safeAsync.makeScheduledFlush(20, function () {}),
|
|
382
|
+
isClosed: function () { return clClosed; },
|
|
383
|
+
sendBatch: function () { clSent += 1; clClosed = true; return Promise.resolve(); },
|
|
384
|
+
onRetryExhausted: function () {},
|
|
385
|
+
});
|
|
386
|
+
await clDrain.flush();
|
|
387
|
+
check("batchDrain: isClosed halts the loop after the current batch",
|
|
388
|
+
clSent === 1 && clBuf.length === 3);
|
|
389
|
+
|
|
390
|
+
// Config-time validation.
|
|
391
|
+
function bdRejects(label, fn) {
|
|
392
|
+
var threw = null;
|
|
393
|
+
try { fn(); } catch (e) { threw = e; }
|
|
394
|
+
check("batchDrain: rejects " + label, threw instanceof TypeError);
|
|
395
|
+
}
|
|
396
|
+
var okDrain = {
|
|
397
|
+
buffer: [], batchSize: 1, scheduler: { schedule: function () {} },
|
|
398
|
+
isClosed: function () {}, sendBatch: function () {}, onRetryExhausted: function () {},
|
|
399
|
+
};
|
|
400
|
+
function withDrain(over) { return Object.assign({}, okDrain, over); }
|
|
401
|
+
bdRejects("non-array buffer", function () { b.safeAsync.makeBatchDrain(withDrain({ buffer: {} })); });
|
|
402
|
+
bdRejects("bad batchSize", function () { b.safeAsync.makeBatchDrain(withDrain({ batchSize: 0 })); });
|
|
403
|
+
bdRejects("scheduler w/o schedule", function () { b.safeAsync.makeBatchDrain(withDrain({ scheduler: {} })); });
|
|
404
|
+
bdRejects("non-fn isClosed", function () { b.safeAsync.makeBatchDrain(withDrain({ isClosed: null })); });
|
|
405
|
+
bdRejects("non-fn sendBatch", function () { b.safeAsync.makeBatchDrain(withDrain({ sendBatch: null })); });
|
|
406
|
+
bdRejects("non-fn onRetryExhausted", function () { b.safeAsync.makeBatchDrain(withDrain({ onRetryExhausted: null })); });
|
|
407
|
+
bdRejects("non-fn takeBatch", function () { b.safeAsync.makeBatchDrain(withDrain({ takeBatch: 5 })); });
|
|
408
|
+
|
|
409
|
+
// makeBatchingSink — the top-level composer (buffered enqueue + batch
|
|
410
|
+
// drain + draining close). batchSize flush + final-drain-on-close: every
|
|
411
|
+
// emitted record reaches sendBatch, none stranded on shutdown.
|
|
412
|
+
var bsSent = [];
|
|
413
|
+
var bsSink = b.safeAsync.makeBatchingSink({
|
|
414
|
+
batchSize: 2,
|
|
415
|
+
bufferLimit: 100,
|
|
416
|
+
maxBatchAgeMs: 50,
|
|
417
|
+
sendBatch: function (batch) { bsSent.push(batch.slice()); return Promise.resolve(); },
|
|
418
|
+
});
|
|
419
|
+
await bsSink.emit({ message: "a" });
|
|
420
|
+
await bsSink.emit({ message: "b" });
|
|
421
|
+
await bsSink.emit({ message: "c" });
|
|
422
|
+
await bsSink.close();
|
|
423
|
+
var bsTotal = bsSent.reduce(function (n, batch) { return n + batch.length; }, 0);
|
|
424
|
+
check("makeBatchingSink batches and drains all records on close (3 in -> 3 out)",
|
|
425
|
+
bsTotal === 3 && bsSent.length >= 1);
|
|
426
|
+
|
|
427
|
+
// config-time validation: sendBatch is required.
|
|
428
|
+
var bsThrew = false;
|
|
429
|
+
try { b.safeAsync.makeBatchingSink({ batchSize: 2 }); }
|
|
430
|
+
catch (e) { bsThrew = /sendBatch/.test(e.message); }
|
|
431
|
+
check("makeBatchingSink requires opts.sendBatch", bsThrew);
|
|
154
432
|
}
|
|
155
433
|
|
|
156
434
|
module.exports = { run: run };
|
|
@@ -84,6 +84,43 @@ function run() {
|
|
|
84
84
|
check("indexAfterOpenTag: linear on 80K <body starts (< 500ms; regex was ~8.6s)", msIdx < 500);
|
|
85
85
|
check("indexAfterOpenTag: degenerate input → -1 (no terminated tag)",
|
|
86
86
|
sb.indexAfterOpenTag(bigBody, "body") === -1);
|
|
87
|
+
|
|
88
|
+
// ---- makeByteCoercer: bind toBuffer to a module's error contract ----
|
|
89
|
+
function FakeErr(code, message) { this.code = code; this.message = message; }
|
|
90
|
+
var coerce = sb.makeByteCoercer({
|
|
91
|
+
errorClass: FakeErr,
|
|
92
|
+
typeCode: "fake/bad-bytes",
|
|
93
|
+
messagePrefix: "fake: ",
|
|
94
|
+
messageSuffix: " must be bytes",
|
|
95
|
+
allowString: false,
|
|
96
|
+
});
|
|
97
|
+
check("makeByteCoercer: passes a Buffer through",
|
|
98
|
+
Buffer.isBuffer(coerce(Buffer.from([1, 2]), "field")) &&
|
|
99
|
+
coerce(Buffer.from([1, 2]), "field").length === 2);
|
|
100
|
+
check("makeByteCoercer: coerces a Uint8Array to Buffer",
|
|
101
|
+
Buffer.isBuffer(coerce(new Uint8Array([3, 4]), "field")));
|
|
102
|
+
var bcErr = null;
|
|
103
|
+
try { coerce("nope", "myField"); } catch (e) { bcErr = e; }
|
|
104
|
+
check("makeByteCoercer: type mismatch throws the bound class with interpolated message",
|
|
105
|
+
bcErr instanceof FakeErr && bcErr.code === "fake/bad-bytes" &&
|
|
106
|
+
bcErr.message === "fake: myField must be bytes");
|
|
107
|
+
|
|
108
|
+
// encoding variant: an encoded string is accepted + decoded.
|
|
109
|
+
var hexCoerce = sb.makeByteCoercer({
|
|
110
|
+
errorClass: FakeErr, typeCode: "fake/hex",
|
|
111
|
+
messagePrefix: "fake: ", messageSuffix: " must be hex", encoding: "hex",
|
|
112
|
+
});
|
|
113
|
+
check("makeByteCoercer: encoding accepts + decodes an encoded string",
|
|
114
|
+
hexCoerce("0102", "h").length === 2 && hexCoerce("0102", "h")[0] === 1);
|
|
115
|
+
|
|
116
|
+
// config-time validation.
|
|
117
|
+
function bcRejects(label, fn) {
|
|
118
|
+
var threw = null; try { fn(); } catch (e) { threw = e; }
|
|
119
|
+
check("makeByteCoercer: rejects " + label, threw && threw.code === "buffer/bad-arg");
|
|
120
|
+
}
|
|
121
|
+
bcRejects("missing opts", function () { sb.makeByteCoercer(); });
|
|
122
|
+
bcRejects("non-fn errorClass", function () { sb.makeByteCoercer({ errorClass: 5, typeCode: "x" }); });
|
|
123
|
+
bcRejects("empty typeCode", function () { sb.makeByteCoercer({ errorClass: FakeErr, typeCode: "" }); });
|
|
87
124
|
}
|
|
88
125
|
|
|
89
126
|
module.exports = { run: run };
|
|
@@ -23,7 +23,21 @@ function _throws(label, fn, codeRe) {
|
|
|
23
23
|
threw && (codeRe ? codeRe.test(threw.code || "") || codeRe.test(threw.message || "") : true));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function testByteCapMultibyte() {
|
|
27
|
+
// MAX_KEY_BYTES / MAX_EXPRESSION_BYTES (and opts.maxBytes) are BYTE caps.
|
|
28
|
+
var mb = String.fromCharCode(0x4e2d); // one 3-byte UTF-8 char
|
|
29
|
+
var t1 = null;
|
|
30
|
+
try { b.safeJsonPath.validateKey(mb.repeat(5), { maxBytes: 10 }); } catch (e) { t1 = e; }
|
|
31
|
+
check("safeJsonPath byte-cap: multibyte key over byte cap refused",
|
|
32
|
+
t1 && t1.code === "safe-jsonpath/key-too-long");
|
|
33
|
+
var t2 = null;
|
|
34
|
+
try { b.safeJsonPath.validateExpression("$." + mb.repeat(5), { maxBytes: 10 }); } catch (e) { t2 = e; }
|
|
35
|
+
check("safeJsonPath byte-cap: multibyte expression over byte cap refused",
|
|
36
|
+
t2 && t2.code === "safe-jsonpath/expression-too-long");
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
function run() {
|
|
40
|
+
testByteCapMultibyte();
|
|
27
41
|
// ---- validateKey ----
|
|
28
42
|
check("validateKey accepts plain key",
|
|
29
43
|
safeJsonPath.validateKey("role") === "role");
|
|
@@ -200,7 +200,26 @@ function testErrorClassExposed() {
|
|
|
200
200
|
typeof b.sandbox.SandboxError === "function");
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
async function testByteCapMultibyte() {
|
|
204
|
+
// maxBytes (input) and maxResultBytes (= maxBytes/4) are BYTE caps. A
|
|
205
|
+
// multibyte payload under the char count but over the byte cap must be refused.
|
|
206
|
+
var sandboxC = require("../../lib/constants");
|
|
207
|
+
var floor = sandboxC.BYTES.mib(4); // MIN_MAX_BYTES
|
|
208
|
+
var bigInput = String.fromCharCode(0x4e2d).repeat(1500000); // 1.5M chars / 4.5M UTF-8 bytes
|
|
209
|
+
var t1 = null;
|
|
210
|
+
try { await b.sandbox.run({ source: "return null;", input: bigInput, maxBytes: floor, timeoutMs: 10000 }); }
|
|
211
|
+
catch (e) { t1 = e; }
|
|
212
|
+
check("sandbox byte-cap: oversize multibyte input refused as input-too-large",
|
|
213
|
+
t1 && t1.code === "sandbox/input-too-large");
|
|
214
|
+
var t2 = null;
|
|
215
|
+
try { await b.sandbox.run({ source: "return String.fromCharCode(0x4e2d).repeat(400000);", maxBytes: floor, timeoutMs: 10000 }); }
|
|
216
|
+
catch (e) { t2 = e; }
|
|
217
|
+
check("sandbox byte-cap: oversize multibyte result refused as oversized-result",
|
|
218
|
+
t2 && t2.code === "sandbox/oversized-result");
|
|
219
|
+
}
|
|
220
|
+
|
|
203
221
|
async function run() {
|
|
222
|
+
await testByteCapMultibyte();
|
|
204
223
|
testErrorClassExposed();
|
|
205
224
|
await testHappyPath();
|
|
206
225
|
await testAllowedBuiltins();
|
|
@@ -310,6 +310,6 @@ module.exports = { run: run };
|
|
|
310
310
|
if (require.main === module) {
|
|
311
311
|
run().then(
|
|
312
312
|
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
|
|
313
|
-
function (e) { console.error("FAIL:"
|
|
313
|
+
function (e) { console.error("FAIL: " + helpers.formatErr(e)); process.exit(1); }
|
|
314
314
|
);
|
|
315
315
|
}
|
|
@@ -24,8 +24,9 @@ var helpers = require("../helpers");
|
|
|
24
24
|
var b = helpers.b;
|
|
25
25
|
var check = helpers.check;
|
|
26
26
|
|
|
27
|
+
var _tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-selfupdate-"));
|
|
27
28
|
function _tmp(name) {
|
|
28
|
-
return path.join(
|
|
29
|
+
return path.join(_tmpBase, Date.now() + "-" +
|
|
29
30
|
Math.random().toString(36).slice(2, 8) + "-" + name);
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -222,8 +222,26 @@ async function testUnbind() {
|
|
|
222
222
|
check("store cleared after unbind", !store.data.has(token));
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
function testNamespaceFingerprint() {
|
|
226
|
+
// The namespace-level b.sessionDeviceBinding.fingerprint(req, opts?) is the
|
|
227
|
+
// stateless device-hash helper (distinct from an instance's bound method):
|
|
228
|
+
// deterministic for identical request shape, divergent when a bound
|
|
229
|
+
// component changes.
|
|
230
|
+
check("b.sessionDeviceBinding.fingerprint is a function",
|
|
231
|
+
typeof b.sessionDeviceBinding.fingerprint === "function");
|
|
232
|
+
var fp1 = b.sessionDeviceBinding.fingerprint(_mockReq());
|
|
233
|
+
var fp2 = b.sessionDeviceBinding.fingerprint(_mockReq());
|
|
234
|
+
check("namespace fingerprint: returns a Buffer", Buffer.isBuffer(fp1));
|
|
235
|
+
check("namespace fingerprint: deterministic for identical requests", fp1.equals(fp2));
|
|
236
|
+
var other = _mockReq();
|
|
237
|
+
other.headers = Object.assign({}, other.headers, { "user-agent": "Totally-Different/9.9" });
|
|
238
|
+
var fp3 = b.sessionDeviceBinding.fingerprint(other);
|
|
239
|
+
check("namespace fingerprint: diverges when a bound component changes", !fp1.equals(fp3));
|
|
240
|
+
}
|
|
241
|
+
|
|
225
242
|
async function run() {
|
|
226
243
|
testSurface();
|
|
244
|
+
testNamespaceFingerprint();
|
|
227
245
|
testCreateRejectsBadOpts();
|
|
228
246
|
await testBindAndVerifyHappyPath();
|
|
229
247
|
await testVerifyDriftRefuses();
|
|
@@ -189,7 +189,7 @@ async function testPluggableStoreValidation() {
|
|
|
189
189
|
|
|
190
190
|
threw = false;
|
|
191
191
|
try { b.session.useStore("not-an-object"); }
|
|
192
|
-
catch (e) { threw = /must
|
|
192
|
+
catch (e) { threw = /must be an object exposing/.test(e.message) && e.code === "INVALID_ARG" && e.permanent === true; }
|
|
193
193
|
check("useStore: non-object refused", threw);
|
|
194
194
|
|
|
195
195
|
threw = false;
|