payment-kit 1.29.1 → 1.29.3
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/api/dev.ts +41 -2
- package/api/hono.d.ts +42 -0
- package/api/node-sqlite.d.ts +12 -0
- package/api/src/bootstrap.ts +47 -0
- package/api/src/crons/base.ts +3 -3
- package/api/src/crons/currency.ts +1 -1
- package/api/src/crons/index.ts +41 -37
- package/api/src/crons/metering-subscription-detection.ts +1 -1
- package/api/src/crons/overdue-detection.ts +2 -2
- package/api/src/crons/retry-pending-events.ts +6 -0
- package/api/src/crons/tenant-fanout.ts +82 -0
- package/api/src/host-node/did-connect-runtime-node.ts +33 -0
- package/api/src/host-node/serve-static-arc.ts +68 -0
- package/api/src/host-node/serve-static.ts +41 -0
- package/api/src/index.ts +22 -161
- package/api/src/integrations/app-store/client.ts +3 -4
- package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
- package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
- package/api/src/integrations/arcblock/token.ts +21 -7
- package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
- package/api/src/integrations/google-play/handlers/voided.ts +2 -2
- package/api/src/integrations/google-play/verify.ts +3 -2
- package/api/src/integrations/iap-reconcile.ts +3 -5
- package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
- package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
- package/api/src/libs/archive/query.ts +19 -0
- package/api/src/libs/audit.ts +61 -4
- package/api/src/libs/auth.ts +247 -47
- package/api/src/libs/context.ts +89 -1
- package/api/src/libs/currency.ts +2 -2
- package/api/src/libs/dayjs.ts +8 -2
- package/api/src/libs/did-connect/runtime-did-connect-js.ts +88 -0
- package/api/src/libs/did-connect/tenant-identity.ts +221 -0
- package/api/src/libs/drivers/auth-storage.ts +118 -0
- package/api/src/libs/drivers/cron.ts +264 -0
- package/api/src/libs/drivers/db.ts +170 -0
- package/api/src/libs/drivers/identity.ts +142 -0
- package/api/src/libs/drivers/index.ts +40 -0
- package/api/src/libs/drivers/locks.ts +226 -0
- package/api/src/libs/drivers/migrate-runner.ts +70 -0
- package/api/src/libs/drivers/queue.ts +104 -0
- package/api/src/libs/drivers/secrets.ts +194 -0
- package/api/src/libs/env.ts +170 -54
- package/api/src/libs/exchange-rate/service.ts +7 -6
- package/api/src/libs/http-fetch-adapter.ts +60 -0
- package/api/src/libs/invoice.ts +1 -1
- package/api/src/libs/lock.ts +51 -47
- package/api/src/libs/logger.ts +48 -8
- package/api/src/libs/notification/index.ts +1 -1
- package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
- package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
- package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
- package/api/src/libs/overdraft-protection.ts +1 -1
- package/api/src/libs/payout.ts +1 -1
- package/api/src/libs/queue/index.ts +271 -52
- package/api/src/libs/queue/runtime.ts +175 -0
- package/api/src/libs/resource.ts +3 -3
- package/api/src/libs/secrets.ts +38 -0
- package/api/src/libs/session.ts +3 -2
- package/api/src/libs/subscription.ts +5 -5
- package/api/src/libs/tenant.ts +92 -0
- package/api/src/libs/url.ts +3 -3
- package/api/src/libs/util.ts +21 -13
- package/api/src/middlewares/hono/cdn.ts +63 -0
- package/api/src/middlewares/hono/context.ts +80 -0
- package/api/src/middlewares/hono/csrf.ts +83 -0
- package/api/src/middlewares/hono/fallback.ts +194 -0
- package/api/src/middlewares/hono/pipeline.ts +73 -0
- package/api/src/middlewares/hono/resource-mount.ts +42 -0
- package/api/src/middlewares/hono/resource.ts +63 -0
- package/api/src/middlewares/hono/security.ts +209 -0
- package/api/src/middlewares/hono/session.ts +114 -0
- package/api/src/middlewares/hono/xss.ts +61 -0
- package/api/src/queues/auto-recharge.ts +12 -10
- package/api/src/queues/checkout-session.ts +38 -21
- package/api/src/queues/credit-consume.ts +40 -36
- package/api/src/queues/credit-grant.ts +25 -18
- package/api/src/queues/credit-reconciliation.ts +7 -5
- package/api/src/queues/discount-status.ts +9 -6
- package/api/src/queues/event.ts +41 -11
- package/api/src/queues/exchange-rate-health.ts +49 -30
- package/api/src/queues/invoice.ts +18 -15
- package/api/src/queues/notification.ts +14 -7
- package/api/src/queues/payment.ts +64 -37
- package/api/src/queues/payout.ts +37 -21
- package/api/src/queues/refund.ts +36 -18
- package/api/src/queues/subscription.ts +83 -53
- package/api/src/queues/token-transfer.ts +15 -10
- package/api/src/queues/usage-record.ts +8 -5
- package/api/src/queues/vendors/commission.ts +7 -5
- package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
- package/api/src/queues/vendors/fulfillment.ts +4 -2
- package/api/src/queues/vendors/return-processor.ts +5 -3
- package/api/src/queues/vendors/return-scanner.ts +5 -4
- package/api/src/queues/vendors/status-check.ts +10 -7
- package/api/src/queues/webhook.ts +60 -32
- package/api/src/routes/connect/shared.ts +1 -2
- package/api/src/routes/connect/subscribe.ts +3 -3
- package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
- package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
- package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
- package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
- package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
- package/api/src/routes/hono/credit-tokens.ts +43 -0
- package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
- package/api/src/routes/{customers.ts → hono/customers.ts} +199 -224
- package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
- package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
- package/api/src/routes/{events.ts → hono/events.ts} +107 -71
- package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
- package/api/src/routes/hono/exchange-rates.ts +77 -0
- package/api/src/routes/hono/index.ts +115 -0
- package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
- package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
- package/api/src/routes/hono/integrations/stripe.ts +74 -0
- package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
- package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
- package/api/src/routes/hono/meters.ts +288 -0
- package/api/src/routes/hono/passports.ts +73 -0
- package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
- package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
- package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
- package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
- package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
- package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
- package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
- package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
- package/api/src/routes/{products.ts → hono/products.ts} +172 -159
- package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
- package/api/src/routes/hono/redirect.ts +24 -0
- package/api/src/routes/{refunds.ts → hono/refunds.ts} +98 -83
- package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
- package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
- package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
- package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
- package/api/src/routes/hono/tool.ts +69 -0
- package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
- package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
- package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
- package/api/src/routes/hono/webhook-endpoints.ts +126 -0
- package/api/src/service.ts +814 -0
- package/api/src/store/migrations/20230911-seeding.ts +2 -1
- package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
- package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
- package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
- package/api/src/store/models/auto-recharge-config.ts +22 -10
- package/api/src/store/models/checkout-session.ts +15 -14
- package/api/src/store/models/coupon.ts +29 -20
- package/api/src/store/models/credit-grant.ts +38 -29
- package/api/src/store/models/credit-transaction.ts +32 -21
- package/api/src/store/models/customer.ts +19 -17
- package/api/src/store/models/discount.ts +11 -2
- package/api/src/store/models/entitlement-grant.ts +21 -9
- package/api/src/store/models/entitlement-product.ts +21 -9
- package/api/src/store/models/entitlement.ts +19 -10
- package/api/src/store/models/event.ts +18 -9
- package/api/src/store/models/exchange-rate-provider.ts +17 -4
- package/api/src/store/models/invoice-item.ts +18 -9
- package/api/src/store/models/invoice.ts +16 -8
- package/api/src/store/models/meter-event.ts +27 -9
- package/api/src/store/models/meter.ts +31 -22
- package/api/src/store/models/payment-currency.ts +25 -8
- package/api/src/store/models/payment-intent.ts +15 -6
- package/api/src/store/models/payment-link.ts +15 -6
- package/api/src/store/models/payment-method.ts +38 -22
- package/api/src/store/models/payment-stat.ts +18 -9
- package/api/src/store/models/payout.ts +15 -6
- package/api/src/store/models/price-quote.ts +17 -8
- package/api/src/store/models/price.ts +24 -12
- package/api/src/store/models/pricing-table.ts +29 -20
- package/api/src/store/models/product-vendor.ts +20 -10
- package/api/src/store/models/product.ts +15 -6
- package/api/src/store/models/promotion-code.ts +14 -6
- package/api/src/store/models/refund.ts +15 -6
- package/api/src/store/models/revenue-snapshot.ts +21 -9
- package/api/src/store/models/setting.ts +18 -9
- package/api/src/store/models/setup-intent.ts +36 -27
- package/api/src/store/models/subscription-item.ts +21 -9
- package/api/src/store/models/subscription-schedule.ts +21 -9
- package/api/src/store/models/subscription.ts +21 -10
- package/api/src/store/models/tax-rate.ts +29 -21
- package/api/src/store/models/usage-record.ts +11 -2
- package/api/src/store/models/webhook-attempt.ts +18 -9
- package/api/src/store/models/webhook-endpoint.ts +18 -9
- package/api/src/store/scoped-core.ts +55 -0
- package/api/src/store/scoped.ts +247 -0
- package/api/src/store/sequelize.ts +82 -23
- package/api/src/store/sql-migrations.ts +20 -0
- package/api/src/store/tenant-backfill.ts +260 -0
- package/api/src/store/tenant-model.ts +124 -0
- package/api/src/store/tenant-tables.ts +50 -0
- package/api/tests/bootstrap/bootstrap.spec.ts +162 -0
- package/api/tests/crons/tenant-fanout.spec.ts +158 -0
- package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
- package/api/tests/fixtures/bare-query-violation.ts +13 -0
- package/api/tests/fixtures/core-env-violation.ts +10 -0
- package/api/tests/fixtures/host-read-violation.ts +19 -0
- package/api/tests/fixtures/tenants.ts +4 -0
- package/api/tests/integrations/iap-tenant.spec.ts +284 -0
- package/api/tests/libs/archive-query.spec.ts +26 -0
- package/api/tests/libs/audit-tenant.spec.ts +153 -0
- package/api/tests/libs/context.spec.ts +204 -0
- package/api/tests/libs/core-config.spec.ts +115 -0
- package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
- package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
- package/api/tests/libs/did-connect-runtime-js.spec.ts +98 -0
- package/api/tests/libs/did-connect-tenant-identity.spec.ts +159 -0
- package/api/tests/libs/lock-tenant.spec.ts +66 -0
- package/api/tests/libs/scoped.spec.ts +222 -0
- package/api/tests/libs/secrets-facade.spec.ts +52 -0
- package/api/tests/libs/service-host.spec.ts +37 -0
- package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
- package/api/tests/libs/tenant-middleware.spec.ts +42 -0
- package/api/tests/libs/tenant-scanner.spec.ts +120 -0
- package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
- package/api/tests/middlewares/hono/context.spec.ts +113 -0
- package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
- package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
- package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
- package/api/tests/middlewares/hono/security.spec.ts +181 -0
- package/api/tests/middlewares/hono/session.spec.ts +42 -0
- package/api/tests/middlewares/hono/xss.spec.ts +81 -0
- package/api/tests/models/tenant-backfill.spec.ts +287 -0
- package/api/tests/models/tenant-columns-model.spec.ts +46 -0
- package/api/tests/models/tenant-columns.spec.ts +161 -0
- package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
- package/api/tests/queues/credit-consume.spec.ts +8 -1
- package/api/tests/queues/event-tenant.spec.ts +292 -0
- package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
- package/api/tests/queues/queue-parity.spec.ts +249 -0
- package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
- package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
- package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
- package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
- package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
- package/api/tests/service/collapse.spec.ts +96 -0
- package/api/tests/service/didconnect-storage-slot.spec.ts +60 -0
- package/api/tests/service/fail-closed-http.spec.ts +79 -0
- package/api/tests/service/static-arc-handler.spec.ts +101 -0
- package/api/tests/service/static-externalized.spec.ts +48 -0
- package/api/tests/store/tenant-crosscut.spec.ts +202 -0
- package/api/tests/store/tenant-model-spike.spec.ts +177 -0
- package/api/tests/store/tenant-model.spec.ts +162 -0
- package/api/tests/store/tenant-residual.spec.ts +196 -0
- package/api/third.d.ts +4 -0
- package/blocklet.yml +1 -1
- package/cloudflare/MIGRATION-RUNBOOK.md +3 -8
- package/cloudflare/README.md +34 -27
- package/cloudflare/STAGING-MIGRATION-GUIDE.md +3 -15
- package/cloudflare/build.ts +33 -13
- package/cloudflare/cf-adapter.ts +419 -0
- package/cloudflare/did-connect-runtime.ts +96 -0
- package/cloudflare/did-connect-token-storage.ts +151 -0
- package/cloudflare/esbuild-cf-config.cjs +407 -0
- package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
- package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
- package/cloudflare/migrations/0008_schema_parity.sql +16 -0
- package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
- package/cloudflare/queue-runtime-mode.ts +13 -0
- package/cloudflare/run-build.js +33 -403
- package/cloudflare/scripts/cf-package-import-probe.mjs +90 -0
- package/cloudflare/scripts/didconnect-mock-smoke.mjs +140 -0
- package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
- package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
- package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
- package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
- package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
- package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
- package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +16 -1
- package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +18 -3
- package/cloudflare/shims/cron.ts +38 -158
- package/cloudflare/shims/events.ts +124 -0
- package/cloudflare/shims/fastq.ts +15 -1
- package/cloudflare/shims/nedb-storage.ts +16 -8
- package/cloudflare/shims/xss.ts +8 -0
- package/cloudflare/tenant-middleware.ts +36 -0
- package/cloudflare/tests/cf-adapter.spec.ts +244 -0
- package/cloudflare/tests/did-connect-token-storage.spec.ts +105 -0
- package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
- package/cloudflare/tests/worker-handler-gate.spec.ts +69 -0
- package/cloudflare/vite.config.ts +53 -45
- package/cloudflare/worker.ts +261 -448
- package/cloudflare/wrangler.json +0 -6
- package/cloudflare/wrangler.jsonc +0 -6
- package/cloudflare/wrangler.local-e2e.jsonc +25 -0
- package/cloudflare/wrangler.staging.json +0 -6
- package/jest.config.js +3 -1
- package/package.json +33 -38
- package/scripts/bootstrap-inject.ts +166 -0
- package/scripts/core-env-whitelist.json +1 -0
- package/scripts/e2e-12b-runtime.ts +149 -0
- package/scripts/e2e-core-config.ts +125 -0
- package/scripts/e2e-d1-tenancy.ts +116 -0
- package/scripts/e2e-d2-cron-queue.ts +139 -0
- package/scripts/e2e-d3-embedded-multi.ts +171 -0
- package/scripts/e2e-hono-s2.ts +125 -0
- package/scripts/e2e-hono-s3e.ts +135 -0
- package/scripts/e2e-hono-s4.ts +114 -0
- package/scripts/e2e-migration-contract.ts +100 -0
- package/scripts/e2e-s0.ts +61 -0
- package/scripts/e2e-s1.ts +107 -0
- package/scripts/e2e-s2.ts +178 -0
- package/scripts/e2e-s3.ts +110 -0
- package/scripts/e2e-s4.ts +191 -0
- package/scripts/e2e-s5.ts +139 -0
- package/scripts/e2e-s6.ts +127 -0
- package/scripts/e2e-tenant-model.ts +119 -0
- package/scripts/e2e-tenant-worker.ts +199 -0
- package/scripts/gen-sql-migrations.js +46 -0
- package/scripts/phase8-codemod.js +219 -0
- package/scripts/phase9a-env-getters-codemod.js +82 -0
- package/scripts/scan-core-env.js +109 -0
- package/scripts/scan-tenant-queries.js +235 -0
- package/scripts/schema-drift-guard.ts +210 -0
- package/scripts/tenant-scan-whitelist.json +1 -0
- package/src/app.tsx +2 -1
- package/src/env.d.ts +13 -1
- package/src/libs/service-host.ts +13 -0
- package/tsconfig.json +1 -1
- package/vite.arc.config.ts +159 -0
- package/api/src/libs/did-space.ts +0 -235
- package/api/src/libs/middleware.ts +0 -50
- package/api/src/libs/security.ts +0 -192
- package/api/src/queues/space.ts +0 -662
- package/api/src/routes/credit-tokens.ts +0 -38
- package/api/src/routes/exchange-rates.ts +0 -87
- package/api/src/routes/index.ts +0 -142
- package/api/src/routes/integrations/stripe.ts +0 -61
- package/api/src/routes/meters.ts +0 -274
- package/api/src/routes/passports.ts +0 -68
- package/api/src/routes/redirect.ts +0 -20
- package/api/src/routes/tool.ts +0 -65
- package/api/src/routes/webhook-endpoints.ts +0 -126
- package/api/tests/routes/credit-grants.spec.ts +0 -1261
- package/cloudflare/did-connect-auth.ts +0 -527
- package/cloudflare/shims/did-space-js.ts +0 -17
- package/cloudflare/shims/did-space.ts +0 -11
- package/cloudflare/shims/express-compat/index.ts +0 -80
- package/cloudflare/shims/express-compat/types.ts +0 -41
- package/cloudflare/shims/lock.ts +0 -115
- package/cloudflare/shims/queue.ts +0 -611
- package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
- package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* S3-CF (DID convergence) — local mock-AUTH_SERVICE smoke (TEST HARNESS ONLY).
|
|
4
|
+
*
|
|
5
|
+
* Runs the REAL built worker (dist/worker.js) in Miniflare with a MOCK AUTH_SERVICE
|
|
6
|
+
* that implements the production RPC contract (getInstanceAppIdentity) returning a
|
|
7
|
+
* deterministic TEST-ONLY appSk — no real secret, never in prod/wrangler config.
|
|
8
|
+
*
|
|
9
|
+
* The smoke goes through the REAL path (no handler/authenticator bypass):
|
|
10
|
+
* Host → tenant context → CF IdentityDriver → AUTH_SERVICE.getInstanceAppIdentity
|
|
11
|
+
* → resolveTenantIdentity → real @arcblock/did-connect-js authenticator
|
|
12
|
+
* → core buildConnectRoutesHono → tokenStorage.create (CF D1).
|
|
13
|
+
*
|
|
14
|
+
* Asserts the locally-verifiable integration facts:
|
|
15
|
+
* - /api/did/<action>/token is registered (not 404) and, WITH the mock, no longer
|
|
16
|
+
* fail-closes on getInstanceAppIdentity (the mock RPC is reached + appSk used);
|
|
17
|
+
* - a token row lands in the CF D1 _did_connect_tokens table, stamped with the
|
|
18
|
+
* tenant instanceDid.
|
|
19
|
+
* (The full wallet scan/auth handshake against a REAL AUTH_SERVICE stays a
|
|
20
|
+
* deploy/staging gate — see cloudflare/README.md.)
|
|
21
|
+
*
|
|
22
|
+
* node scripts/didconnect-mock-smoke.mjs
|
|
23
|
+
*/
|
|
24
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
25
|
+
import { dirname, join } from "node:path";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
|
|
28
|
+
import { Miniflare } from "miniflare";
|
|
29
|
+
|
|
30
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const cfDir = join(__dirname, "..");
|
|
32
|
+
const workerPath = join(cfDir, "dist", "worker.js");
|
|
33
|
+
|
|
34
|
+
if (!existsSync(workerPath)) {
|
|
35
|
+
console.error(`mock-smoke: ${workerPath} not found — run \`node run-build.js\` first.`);
|
|
36
|
+
process.exit(2);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Deterministic TEST-ONLY app signing key (ROLE_APPLICATION/ED25519/SHA3). NOT a
|
|
40
|
+
// real secret — generated once for the harness; isolation uses a second key.
|
|
41
|
+
const TEST_APP_SK =
|
|
42
|
+
"0xe86ad1043f2de80374ea9eb2ca5a0cdf0111126804cfb128e9e658cc44ae47694497dddaacf925929a19e8bc84a5059569983f307a4c67a694cca79c2001e634";
|
|
43
|
+
const INSTANCE_DID = "zMOCK_APP_INSTANCE";
|
|
44
|
+
|
|
45
|
+
const mockAuthScript = `
|
|
46
|
+
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
47
|
+
// Mock of the did-connect-service@4.0.3 AUTH_SERVICE RPC surface used on the DID path.
|
|
48
|
+
export class MockAuth extends WorkerEntrypoint {
|
|
49
|
+
async getInstanceAppIdentity(instanceDid) {
|
|
50
|
+
return { appSk: ${JSON.stringify(TEST_APP_SK)}, appInfo: { name: 'Mock Tenant', description: 'mock', icon: 'https://x/i.png' } };
|
|
51
|
+
}
|
|
52
|
+
async resolveInstanceDidForHost(host) { return null; }
|
|
53
|
+
async getAppEk(instanceDid) { return null; }
|
|
54
|
+
async resolveIdentity() { return null; }
|
|
55
|
+
}
|
|
56
|
+
export default { fetch() { return new Response('mock-auth'); } };
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const mf = new Miniflare({
|
|
60
|
+
workers: [
|
|
61
|
+
{
|
|
62
|
+
name: "payment",
|
|
63
|
+
modules: true,
|
|
64
|
+
modulesRoot: join(cfDir, "dist"),
|
|
65
|
+
scriptPath: workerPath,
|
|
66
|
+
compatibilityDate: "2024-12-01",
|
|
67
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
68
|
+
d1Databases: { DB: "payment-mock-smoke" },
|
|
69
|
+
serviceBindings: { AUTH_SERVICE: { name: "mock-auth", entrypoint: "MockAuth" } },
|
|
70
|
+
bindings: {
|
|
71
|
+
APP_NAME: "Payment Kit",
|
|
72
|
+
APP_PID: INSTANCE_DID,
|
|
73
|
+
APP_URL: "http://localhost",
|
|
74
|
+
PAYMENT_LIVEMODE: "false",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "mock-auth",
|
|
79
|
+
modules: true,
|
|
80
|
+
script: mockAuthScript,
|
|
81
|
+
compatibilityDate: "2024-12-01",
|
|
82
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
let failed = false;
|
|
88
|
+
const log = (...a) => console.log(...a);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const db = await mf.getD1Database("DB", "payment");
|
|
92
|
+
await db.exec(
|
|
93
|
+
"CREATE TABLE IF NOT EXISTS _did_connect_tokens (token TEXT PRIMARY KEY, data TEXT NOT NULL, expires_at INTEGER NOT NULL)",
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// 1) healthz
|
|
97
|
+
const health = await mf.dispatchFetch("http://localhost/api/healthz");
|
|
98
|
+
log("=== healthz ===", health.status, await health.text());
|
|
99
|
+
|
|
100
|
+
// 2) /api/did/payment/token through the mock AUTH_SERVICE
|
|
101
|
+
const res = await mf.dispatchFetch("http://localhost/api/did/payment/token", {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: { "content-type": "application/json" },
|
|
104
|
+
body: "{}",
|
|
105
|
+
});
|
|
106
|
+
const body = await res.text();
|
|
107
|
+
log("=== POST /api/did/payment/token ===", res.status);
|
|
108
|
+
log(body.slice(0, 800));
|
|
109
|
+
|
|
110
|
+
if (body.includes("getInstanceAppIdentity unavailable")) {
|
|
111
|
+
console.error("FAIL: still fail-closed — the mock AUTH_SERVICE RPC was not reached");
|
|
112
|
+
failed = true;
|
|
113
|
+
} else {
|
|
114
|
+
log("PASS: past the fail-closed gate — mock getInstanceAppIdentity reached + appSk resolved");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 3) a token row landed in D1, stamped with the tenant instanceDid
|
|
118
|
+
const { results } = await db.prepare("SELECT token, data FROM _did_connect_tokens").all();
|
|
119
|
+
log("=== _did_connect_tokens rows ===", JSON.stringify(results, null, 2));
|
|
120
|
+
const tagged = (results || []).some((r) => {
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(r.data).instanceDid === INSTANCE_DID;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
if (tagged) {
|
|
128
|
+
log(`PASS: a token row is stamped with instanceDid=${INSTANCE_DID}`);
|
|
129
|
+
} else {
|
|
130
|
+
console.error("FAIL: no token row stamped with the tenant instanceDid");
|
|
131
|
+
failed = true;
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.error("mock-smoke error:", err?.stack || err?.message || err);
|
|
135
|
+
failed = true;
|
|
136
|
+
} finally {
|
|
137
|
+
await mf.dispose();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
process.exit(failed ? 1 : 0);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// CF shim for @blocklet/sdk/lib/util/asset-host-transformer.
|
|
2
|
+
//
|
|
3
|
+
// DEAD on the CF path: the only importer is the cdn middleware (node-shell only —
|
|
4
|
+
// HTML CDN-URL rewriting on the full node app shell). The worker serves JSON API
|
|
5
|
+
// routes through a LITE app-shell, so cdn never executes. A pass-through stub is
|
|
6
|
+
// enough to resolve the bundled-but-unreachable node code.
|
|
7
|
+
export class AssetHostTransformer {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor, no-empty-function
|
|
9
|
+
constructor(_assetHost?: string) {}
|
|
10
|
+
|
|
11
|
+
transform(html: string, _assetHost?: string): string {
|
|
12
|
+
return html;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
transformBuffer(body: Buffer | Uint8Array, _assetHost?: string): Buffer | Uint8Array {
|
|
16
|
+
return body;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default { AssetHostTransformer };
|
|
@@ -4,5 +4,12 @@ export { env };
|
|
|
4
4
|
export const Events = {};
|
|
5
5
|
export const events = { on: () => {}, emit: () => {} };
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Phase 4 (express→hono): the fallback middleware (SPA serving) reads these. It is
|
|
8
|
+
// node-shell only — DEAD on the CF worker (which never serves the SPA) — so a
|
|
9
|
+
// resolving stub is enough. getBlockletSettings also backs sessionMiddleware's
|
|
10
|
+
// blacklist check, which is gated off on CF (enableBlacklist: false).
|
|
11
|
+
export const getBlockletSettings = (): any => ({ enableBlacklist: false, theme: {} });
|
|
12
|
+
export const getBlockletJs = (..._args: any[]): string => '';
|
|
13
|
+
|
|
14
|
+
const config = { env, Events, events, getBlockletSettings, getBlockletJs };
|
|
8
15
|
export default config;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// CF shim for @blocklet/sdk/lib/util/login.
|
|
2
|
+
//
|
|
3
|
+
// Reached on the LIVE CF path: sessionMiddleware (used by many resource routes)
|
|
4
|
+
// calls isLoginToken/isAccessKey to classify the bearer token. These are pure
|
|
5
|
+
// string-format checks — copied verbatim from the upstream so behavior matches.
|
|
6
|
+
export const isLoginToken = (token: unknown): boolean =>
|
|
7
|
+
typeof token === 'string' && token.split('.').length === 3;
|
|
8
|
+
|
|
9
|
+
export const isAccessKey = (token: unknown): boolean =>
|
|
10
|
+
typeof token === 'string' && token.split('.').length === 1 && token.startsWith('blocklet-');
|
|
11
|
+
|
|
12
|
+
export default { isLoginToken, isAccessKey };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// CF shim for @blocklet/sdk/lib/util/service-api.
|
|
2
|
+
//
|
|
3
|
+
// The only caller on the CF path is sessionMiddleware's login-token blacklist
|
|
4
|
+
// check, which is gated by `blockletSettings.enableBlacklist` (off on the CF
|
|
5
|
+
// worker — the worker resolves identity via AUTH_SERVICE RPC, not the blacklist
|
|
6
|
+
// endpoint). The old express-compat CF path never ran this check at all, so a
|
|
7
|
+
// stub that reports "valid" preserves the worker's (no-blacklist) behavior and
|
|
8
|
+
// never wrongly blocks a token if the setting is somehow on.
|
|
9
|
+
const serviceApi = {
|
|
10
|
+
post: async (_url: string, _body?: unknown) => ({ data: { valid: true } }),
|
|
11
|
+
get: async (_url: string) => ({ data: {} }),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default serviceApi;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// @blocklet/sdk/lib/middlewares/session shim
|
|
2
|
-
// Auth is resolved in Hono middleware layer via AUTH_SERVICE RPC
|
|
3
|
-
//
|
|
2
|
+
// Auth is resolved in the Hono middleware layer via AUTH_SERVICE RPC, then injected
|
|
3
|
+
// as x-user-* request headers by the worker's /api/* dispatcher (worker.ts) — the
|
|
4
|
+
// native authenticate()/sessionMiddleware read those. This express-middleware shim
|
|
5
|
+
// is inert (the old express-compat mountExpressRoutes path it served is gone).
|
|
4
6
|
export default function sessionMiddleware(_options?: any) {
|
|
5
7
|
return (_req: any, _res: any, next: any) => next();
|
|
6
8
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// CF shim for @blocklet/sdk/lib/util/constants.
|
|
2
|
+
//
|
|
3
|
+
// Reached via the fallback middleware (node-shell SPA serving — DEAD on the CF
|
|
4
|
+
// worker, which never serves the SPA). SERVICE_PREFIX carries its real upstream
|
|
5
|
+
// value so the bundled-but-unreachable node code is byte-faithful.
|
|
6
|
+
export const SERVICE_PREFIX = '/.well-known/service';
|
|
7
|
+
|
|
8
|
+
export default { SERVICE_PREFIX };
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
// CF FAIL-FAST shim for @blocklet/sdk/lib/wallet-authenticator.
|
|
2
|
+
//
|
|
3
|
+
// S3-CF (DID convergence): see wallet-handler.ts. On workerd the @blocklet/sdk
|
|
4
|
+
// WalletAuthenticator is a no-op — it cannot sign DID-Connect sessions/certs. CF
|
|
5
|
+
// (and arc-node embedded) MUST inject the real @arcblock/did-connect-js runtime.
|
|
6
|
+
// Throw on construct so a host that forgot to inject fails loudly instead of
|
|
7
|
+
// producing an authenticator that silently signs nothing.
|
|
8
|
+
const FORBIDDEN =
|
|
9
|
+
'CF must inject a DID-Connect runtime via setDidConnectRuntime(createCloudflareDidConnectRuntime); ' +
|
|
10
|
+
'the @blocklet/sdk wallet-authenticator shim is forbidden on workerd';
|
|
11
|
+
|
|
1
12
|
export class WalletAuthenticator {
|
|
2
|
-
constructor(_opts?: any) {
|
|
13
|
+
constructor(_opts?: any) {
|
|
14
|
+
throw new Error(`[did-connect] WalletAuthenticator: ${FORBIDDEN}`);
|
|
15
|
+
}
|
|
3
16
|
}
|
|
17
|
+
|
|
18
|
+
export default { WalletAuthenticator };
|
|
@@ -1,6 +1,21 @@
|
|
|
1
|
+
// CF FAIL-FAST shim for @blocklet/sdk/lib/wallet-handler.
|
|
2
|
+
//
|
|
3
|
+
// S3-CF (DID convergence): on workerd the @blocklet/sdk wallet wrapper is NOT a
|
|
4
|
+
// working DID-Connect implementation. CF (and arc-node embedded) MUST inject the
|
|
5
|
+
// real @arcblock/did-connect-js runtime via setDidConnectRuntime
|
|
6
|
+
// (createCloudflareDidConnectRuntime). This used to be a silent no-op stub whose
|
|
7
|
+
// `attach()` registered ZERO routes — a CF host that forgot to inject would serve
|
|
8
|
+
// no DID-Connect routes and fail only at request time. It now throws on construct
|
|
9
|
+
// so the misconfiguration fails loudly (a spec asserts the AUTH_SERVICE runtime
|
|
10
|
+
// never reaches this path).
|
|
11
|
+
const FORBIDDEN =
|
|
12
|
+
'CF must inject a DID-Connect runtime via setDidConnectRuntime(createCloudflareDidConnectRuntime); ' +
|
|
13
|
+
'the @blocklet/sdk wallet-handler shim is forbidden on workerd';
|
|
14
|
+
|
|
1
15
|
export class WalletHandlers {
|
|
2
|
-
constructor(_opts?: any) {
|
|
3
|
-
|
|
4
|
-
// TODO: implement DID Connect for CF
|
|
16
|
+
constructor(_opts?: any) {
|
|
17
|
+
throw new Error(`[did-connect] WalletHandlers: ${FORBIDDEN}`);
|
|
5
18
|
}
|
|
6
19
|
}
|
|
20
|
+
|
|
21
|
+
export default { WalletHandlers };
|
package/cloudflare/shims/cron.ts
CHANGED
|
@@ -1,182 +1,62 @@
|
|
|
1
|
-
// @abtnode/cron shim for CF Cron Triggers
|
|
1
|
+
// @abtnode/cron shim for CF Cron Triggers (Phase 9, W2-1b).
|
|
2
2
|
//
|
|
3
|
-
// The real @abtnode/cron exports { init } where init({
|
|
4
|
-
// In CF Workers
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
// The real @abtnode/cron exports { init } where init({ jobs, onError }) registers
|
|
4
|
+
// jobs. In CF Workers we store the jobs and execute due ones from scheduled().
|
|
5
|
+
// The cron-expression matcher + registry now live in the shared cron driver
|
|
6
|
+
// (api/src/libs/drivers/cron.ts) so embedded and worker agree on when a job is
|
|
7
|
+
// due; this file is the thin worker adapter that keeps the @abtnode/cron API.
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
createCronRegistry,
|
|
11
|
+
setCronDriver,
|
|
12
|
+
getCronDriver,
|
|
13
|
+
matchesCron,
|
|
14
|
+
shouldRunInWindow,
|
|
15
|
+
} from '../../api/src/libs/drivers/cron';
|
|
16
|
+
|
|
17
|
+
// D2: crons/index.ts now registers through getCronDriver() instead of importing
|
|
18
|
+
// @abtnode/cron directly. On CF this shim is the active cron driver, so make the
|
|
19
|
+
// cf-cron registry the global driver at module load — BEFORE worker.ts calls
|
|
20
|
+
// crons.init(). That keeps crons.init()'s register() and this shim's runAll() on
|
|
21
|
+
// the SAME passive cf-cron registry (host drives runDue from scheduled(); no
|
|
22
|
+
// @abtnode/cron self-scheduling timer is ever created in the frozen isolate).
|
|
23
|
+
const registry = createCronRegistry('cf-cron');
|
|
24
|
+
setCronDriver(registry);
|
|
15
25
|
|
|
16
26
|
type InitOptions = {
|
|
17
27
|
context?: any;
|
|
18
|
-
jobs:
|
|
28
|
+
jobs: Array<{ name: string; time: string; fn: () => Promise<any> | any; options?: { runOnInit?: boolean } }>;
|
|
19
29
|
onError?: (error: Error, name: string) => void;
|
|
20
30
|
};
|
|
21
31
|
|
|
22
|
-
// Singleton storage for registered cron jobs
|
|
23
|
-
const registeredJobs: CronJob[] = [];
|
|
24
|
-
let onErrorHandler: ((error: Error, name: string) => void) | undefined;
|
|
25
|
-
|
|
26
|
-
// --- Cron expression matcher ---
|
|
27
|
-
// Supports 6-field format: second minute hour dayOfMonth month dayOfWeek
|
|
28
|
-
// Supports: numbers, *, */N, ranges (1-5), lists (1,3,5)
|
|
29
|
-
|
|
30
|
-
function parseField(field: string, min: number, max: number): number[] | null {
|
|
31
|
-
// null means "match all"
|
|
32
|
-
if (field === '*') return null;
|
|
33
|
-
|
|
34
|
-
const values = new Set<number>();
|
|
35
|
-
|
|
36
|
-
for (const part of field.split(',')) {
|
|
37
|
-
// */N — every N
|
|
38
|
-
const stepMatch = part.match(/^\*\/(\d+)$/);
|
|
39
|
-
if (stepMatch) {
|
|
40
|
-
const step = parseInt(stepMatch[1], 10);
|
|
41
|
-
for (let i = min; i <= max; i += step) {
|
|
42
|
-
values.add(i);
|
|
43
|
-
}
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// N-M — range
|
|
48
|
-
const rangeMatch = part.match(/^(\d+)-(\d+)$/);
|
|
49
|
-
if (rangeMatch) {
|
|
50
|
-
const from = parseInt(rangeMatch[1], 10);
|
|
51
|
-
const to = parseInt(rangeMatch[2], 10);
|
|
52
|
-
for (let i = from; i <= to; i++) {
|
|
53
|
-
values.add(i);
|
|
54
|
-
}
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// N — single value
|
|
59
|
-
const num = parseInt(part, 10);
|
|
60
|
-
if (!isNaN(num)) {
|
|
61
|
-
values.add(num);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return values.size > 0 ? Array.from(values) : null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Check if a date matches a 6-field cron expression.
|
|
70
|
-
* Returns true if the date's minute/hour/day/month/weekday match.
|
|
71
|
-
* Seconds field is ignored (CF triggers are minute-level).
|
|
72
|
-
*/
|
|
73
|
-
function matchesCron(cronExpr: string, date: Date): boolean {
|
|
74
|
-
const fields = cronExpr.trim().split(/\s+/);
|
|
75
|
-
if (fields.length < 5) return true; // Can't parse — run it
|
|
76
|
-
|
|
77
|
-
// 6-field: sec min hour dom month dow
|
|
78
|
-
// 5-field: min hour dom month dow
|
|
79
|
-
const offset = fields.length >= 6 ? 1 : 0;
|
|
80
|
-
|
|
81
|
-
const minuteField = parseField(fields[offset], 0, 59);
|
|
82
|
-
const hourField = parseField(fields[offset + 1], 0, 23);
|
|
83
|
-
const domField = parseField(fields[offset + 2], 1, 31);
|
|
84
|
-
const monthField = parseField(fields[offset + 3], 0, 11); // cron months are 1-12, JS is 0-11
|
|
85
|
-
const dowField = parseField(fields[offset + 4], 0, 6);
|
|
86
|
-
|
|
87
|
-
const m = date.getUTCMinutes();
|
|
88
|
-
const h = date.getUTCHours();
|
|
89
|
-
const dom = date.getUTCDate();
|
|
90
|
-
const month = date.getUTCMonth() + 1; // JS 0-based → cron 1-based
|
|
91
|
-
const dow = date.getUTCDay(); // 0=Sunday
|
|
92
|
-
|
|
93
|
-
if (minuteField && !minuteField.includes(m)) return false;
|
|
94
|
-
if (hourField && !hourField.includes(h)) return false;
|
|
95
|
-
if (domField && !domField.includes(dom)) return false;
|
|
96
|
-
if (monthField && !monthField.includes(month)) return false;
|
|
97
|
-
if (dowField && !dowField.includes(dow)) return false;
|
|
98
|
-
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Check if a cron expression should fire at the given date (minute-level match).
|
|
103
|
-
//
|
|
104
|
-
// History: this helper previously used a 5-minute look-ahead window, under the
|
|
105
|
-
// assumption that CF Cron Triggers fire every 5 minutes. Once the deploy config
|
|
106
|
-
// switched to every-minute cron, that window made every stepped expression fire
|
|
107
|
-
// 5x more often than intended, driving CF Queues past the free-tier daily cap
|
|
108
|
-
// (2026-04-17 incident). With CF Scheduled firing every minute, we only check
|
|
109
|
-
// the current minute — each cron expression triggers at its designed frequency.
|
|
110
|
-
// See docs/cf-queues-ops-alert-analysis.md § 改动 B.
|
|
111
|
-
function shouldRunInWindow(cronExpr: string, date: Date): boolean {
|
|
112
|
-
return matchesCron(cronExpr, date);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// --- Cron shim API ---
|
|
116
|
-
|
|
117
32
|
function init(options: InitOptions) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for (const job of options.jobs || []) {
|
|
122
|
-
if (job.name && job.time && typeof job.fn === 'function') {
|
|
123
|
-
registeredJobs.push(job);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Skip runOnInit jobs in CF Workers — they'll run on the next matching trigger
|
|
128
|
-
// (running them at module init time would block the request and may exceed CPU limits)
|
|
129
|
-
|
|
33
|
+
registry.register(options.jobs || [], options.onError);
|
|
34
|
+
// runOnInit jobs are skipped in CF Workers — they run on the next matching
|
|
35
|
+
// trigger (running them at module init would block the request / risk CPU limits)
|
|
130
36
|
return {
|
|
131
37
|
addJob(name: string, time: string, fn: Function, opts?: any) {
|
|
132
|
-
|
|
38
|
+
registry.addJob(name, time, fn as any, opts);
|
|
39
|
+
},
|
|
40
|
+
start() {
|
|
41
|
+
/* no-op — CF scheduled() drives runAll */
|
|
133
42
|
},
|
|
134
|
-
start() { /* no-op */ },
|
|
135
43
|
};
|
|
136
44
|
}
|
|
137
45
|
|
|
138
|
-
// Called by worker.ts scheduled handler
|
|
139
|
-
//
|
|
140
|
-
// intended trigger minute) instead of relying on wall-clock at execution time.
|
|
141
|
-
// CF may deliver scheduled events with a small delay that crosses a minute
|
|
142
|
-
// boundary; matching on `scheduledTime` keeps exact-minute cron reliable.
|
|
46
|
+
// Called by worker.ts scheduled handler. Accepts an optional `now` so the caller
|
|
47
|
+
// can pass `event.scheduledTime` (the intended trigger minute).
|
|
143
48
|
async function runAll(now: Date = new Date()) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
for (const job of registeredJobs) {
|
|
148
|
-
if (shouldRunInWindow(job.time, now)) {
|
|
149
|
-
matched.push(job.name);
|
|
150
|
-
try {
|
|
151
|
-
await job.fn();
|
|
152
|
-
} catch (err: any) {
|
|
153
|
-
console.error(`[Cron] ${job.name} failed:`, err?.message || err);
|
|
154
|
-
onErrorHandler?.(err, job.name);
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
skipped.push(job.name);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
console.log(`[Cron] Ran ${matched.length} jobs: [${matched.join(', ')}]. Skipped ${skipped.length}.`);
|
|
49
|
+
const { ran, skipped } = await getCronDriver().runDue(now);
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.log(`[Cron] Ran ${ran.length} jobs: [${ran.join(', ')}]. Skipped ${skipped.length}.`);
|
|
162
52
|
}
|
|
163
53
|
|
|
164
54
|
async function runJob(name: string) {
|
|
165
|
-
|
|
166
|
-
if (job) {
|
|
167
|
-
try {
|
|
168
|
-
await job.fn();
|
|
169
|
-
} catch (err: any) {
|
|
170
|
-
console.error(`[Cron] ${name} failed:`, err?.message || err);
|
|
171
|
-
onErrorHandler?.(err, name);
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
console.warn(`[Cron] Job ${name} not found`);
|
|
175
|
-
}
|
|
55
|
+
await getCronDriver().runJob(name);
|
|
176
56
|
}
|
|
177
57
|
|
|
178
58
|
function getJobNames(): string[] {
|
|
179
|
-
return
|
|
59
|
+
return getCronDriver().getJobNames();
|
|
180
60
|
}
|
|
181
61
|
|
|
182
62
|
// Export matching @abtnode/cron API: default export is { init }
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// EventEmitter shim for CF Workers.
|
|
2
|
+
//
|
|
3
|
+
// workerd's nodejs_compat provides `node:events` for ESM `import from 'events'`,
|
|
4
|
+
// but the esbuild banner's globalThis.require polyfill (build.ts) does NOT list
|
|
5
|
+
// `events`, so CJS `const { EventEmitter } = require('events')` resolves to an
|
|
6
|
+
// empty object and `class X extends EventEmitter` throws at global init
|
|
7
|
+
// ("Class extends value undefined"). Two core drivers hit this:
|
|
8
|
+
// - api/src/libs/drivers/locks.ts (MemoryLock)
|
|
9
|
+
// - api/src/libs/drivers/auth-storage.ts (DbAuthStorage extends EventEmitter)
|
|
10
|
+
//
|
|
11
|
+
// Aliasing `events` to this shim (build.ts) makes BOTH the CJS-require and
|
|
12
|
+
// ESM-import forms resolve to one deterministic implementation, removing the
|
|
13
|
+
// fragile split between nodejs_compat (ESM) and the banner (CJS). Only the core
|
|
14
|
+
// queue/lock/auth-storage event buses use it — emit / on / once / removeListener
|
|
15
|
+
// are the methods exercised (verified by grep), so a compact but correct
|
|
16
|
+
// implementation suffices.
|
|
17
|
+
|
|
18
|
+
type Listener = (...args: any[]) => void;
|
|
19
|
+
|
|
20
|
+
export class EventEmitter {
|
|
21
|
+
private _events: Map<string | symbol, Listener[]> = new Map();
|
|
22
|
+
|
|
23
|
+
private _maxListeners = 10;
|
|
24
|
+
|
|
25
|
+
addListener(event: string | symbol, listener: Listener): this {
|
|
26
|
+
return this.on(event, listener);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
on(event: string | symbol, listener: Listener): this {
|
|
30
|
+
const list = this._events.get(event);
|
|
31
|
+
if (list) {
|
|
32
|
+
list.push(listener);
|
|
33
|
+
} else {
|
|
34
|
+
this._events.set(event, [listener]);
|
|
35
|
+
}
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
prependListener(event: string | symbol, listener: Listener): this {
|
|
40
|
+
const list = this._events.get(event);
|
|
41
|
+
if (list) {
|
|
42
|
+
list.unshift(listener);
|
|
43
|
+
} else {
|
|
44
|
+
this._events.set(event, [listener]);
|
|
45
|
+
}
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
once(event: string | symbol, listener: Listener): this {
|
|
50
|
+
const wrapper = (...args: any[]) => {
|
|
51
|
+
this.removeListener(event, wrapper);
|
|
52
|
+
listener.apply(this, args);
|
|
53
|
+
};
|
|
54
|
+
// keep a handle to the original so removeListener(event, listener) still works
|
|
55
|
+
(wrapper as any).listener = listener;
|
|
56
|
+
return this.on(event, wrapper);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
prependOnceListener(event: string | symbol, listener: Listener): this {
|
|
60
|
+
const wrapper = (...args: any[]) => {
|
|
61
|
+
this.removeListener(event, wrapper);
|
|
62
|
+
listener.apply(this, args);
|
|
63
|
+
};
|
|
64
|
+
(wrapper as any).listener = listener;
|
|
65
|
+
return this.prependListener(event, wrapper);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
removeListener(event: string | symbol, listener: Listener): this {
|
|
69
|
+
const list = this._events.get(event);
|
|
70
|
+
if (!list) return this;
|
|
71
|
+
const idx = list.findIndex((l) => l === listener || (l as any).listener === listener);
|
|
72
|
+
if (idx >= 0) {
|
|
73
|
+
list.splice(idx, 1);
|
|
74
|
+
if (list.length === 0) this._events.delete(event);
|
|
75
|
+
}
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
off(event: string | symbol, listener: Listener): this {
|
|
80
|
+
return this.removeListener(event, listener);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
removeAllListeners(event?: string | symbol): this {
|
|
84
|
+
if (event === undefined) {
|
|
85
|
+
this._events.clear();
|
|
86
|
+
} else {
|
|
87
|
+
this._events.delete(event);
|
|
88
|
+
}
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
emit(event: string | symbol, ...args: any[]): boolean {
|
|
93
|
+
const list = this._events.get(event);
|
|
94
|
+
if (!list || list.length === 0) return false;
|
|
95
|
+
// copy so once()-removals during iteration don't skip listeners
|
|
96
|
+
for (const listener of [...list]) {
|
|
97
|
+
listener.apply(this, args);
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
listeners(event: string | symbol): Listener[] {
|
|
103
|
+
return [...(this._events.get(event) || [])];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
listenerCount(event: string | symbol): number {
|
|
107
|
+
return this._events.get(event)?.length || 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
eventNames(): (string | symbol)[] {
|
|
111
|
+
return [...this._events.keys()];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setMaxListeners(n: number): this {
|
|
115
|
+
this._maxListeners = n;
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getMaxListeners(): number {
|
|
120
|
+
return this._maxListeners;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default EventEmitter;
|
|
@@ -12,7 +12,21 @@
|
|
|
12
12
|
|
|
13
13
|
type Task = { data: any; cb?: Function };
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Phase 9 (W2-1b): faithful fastq executor driver for the worker.
|
|
16
|
+
//
|
|
17
|
+
// Match the real fastq calling convention. The Node queue engine
|
|
18
|
+
// (api/src/libs/queue) calls `fastq(workerFn, concurrency)` (2-arg form);
|
|
19
|
+
// real fastq detects a function first arg and shifts (context => null). The
|
|
20
|
+
// previous shim assumed the 3-arg `fastq(context, worker, concurrency)` form,
|
|
21
|
+
// so the 2-arg call landed `worker` on `context` and every job execution threw
|
|
22
|
+
// "worker is not a function". This shift makes the shim a drop-in executor so
|
|
23
|
+
// the SAME engine runs identically on Node (real fastq) and the worker (shim).
|
|
24
|
+
export default function fastq(context: any, worker: Function, concurrency: number) {
|
|
25
|
+
if (typeof context === 'function') {
|
|
26
|
+
concurrency = worker as unknown as number;
|
|
27
|
+
worker = context;
|
|
28
|
+
context = null;
|
|
29
|
+
}
|
|
16
30
|
const pending: Task[] = [];
|
|
17
31
|
let running = false;
|
|
18
32
|
let drainFn: (() => void) | null = null;
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
// NeDB → D1 storage shim for DID Connect sessions
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
// NeDB → D1 storage shim for DID Connect sessions (Phase 8, W2-1a).
|
|
2
|
+
//
|
|
3
|
+
// Was a no-op stub (DID Connect state silently dropped in the worker). Now a
|
|
4
|
+
// thin worker adapter: it builds a D1 db driver from the bound D1 database and
|
|
5
|
+
// delegates to the real, contract-tested DbAuthStorage. The `{ dbPath }` option
|
|
6
|
+
// is ignored — persistence is the D1 binding, not a disk file.
|
|
7
|
+
|
|
8
|
+
import { createD1DbDriver, DbAuthStorage } from '../../api/src/libs/drivers';
|
|
9
|
+
|
|
10
|
+
import { getDB } from './sequelize-d1/model';
|
|
11
|
+
|
|
12
|
+
export default class AuthStorage extends DbAuthStorage {
|
|
13
|
+
constructor(_opts?: any) {
|
|
14
|
+
// lazy getter — the D1 binding is set per request, not at module import
|
|
15
|
+
super(createD1DbDriver(() => getDB()));
|
|
16
|
+
}
|
|
9
17
|
}
|