payment-kit 1.29.0 → 1.29.2
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 +36 -0
- package/api/src/crons/base.ts +3 -3
- package/api/src/crons/currency.ts +1 -1
- package/api/src/crons/index.ts +27 -24
- 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/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 +99 -38
- package/api/src/libs/context.ts +78 -1
- package/api/src/libs/currency.ts +2 -2
- package/api/src/libs/dayjs.ts +8 -2
- 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 +81 -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 +50 -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 +259 -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 +73 -0
- package/api/src/middlewares/hono/csrf.ts +72 -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 +214 -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 +17 -12
- 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 +12 -4
- 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 +41 -28
- package/api/src/queues/payout.ts +9 -5
- package/api/src/queues/refund.ts +18 -12
- 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} +193 -223
- 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} +96 -80
- 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 +667 -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 +66 -22
- 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/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/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/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 +236 -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/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/README.md +26 -6
- package/cloudflare/build.ts +28 -13
- package/cloudflare/did-connect-auth.ts +0 -217
- package/cloudflare/docs/2026-06-10-bundle-size-analysis.md +288 -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 +31 -56
- 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/util-csrf.ts +13 -0
- package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
- 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/node-fetch.ts +35 -0
- package/cloudflare/shims/xss.ts +8 -0
- package/cloudflare/tenant-middleware.ts +36 -0
- package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
- package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
- package/cloudflare/worker.ts +204 -433
- package/cloudflare/wrangler.local-e2e.jsonc +26 -0
- package/jest.config.js +3 -1
- package/package.json +33 -38
- 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/env.d.ts +13 -1
- package/tsconfig.json +1 -1
- 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/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,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
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// node-fetch shim for CF Workers — forwards to the runtime's native fetch.
|
|
2
|
+
// node-fetch is pulled in transitively by @apple/app-store-server-library (IAP
|
|
3
|
+
// JWS verification). On Node it drags in a polyfill chain that is dead weight on
|
|
4
|
+
// Workers (which has native fetch/URL/TextDecoder):
|
|
5
|
+
// encoding (iconv-lite CJK code tables) 481KB + tr46 (IDNA map) 259KB
|
|
6
|
+
// + whatwg-url 22KB + node-fetch 19KB ≈ 781KB, never executed at runtime.
|
|
7
|
+
// Forwarding node-fetch → native fetch drops that whole chain (~754KB raw).
|
|
8
|
+
// Only implements the node-fetch v2 export surface actually referenced.
|
|
9
|
+
|
|
10
|
+
export default globalThis.fetch.bind(globalThis);
|
|
11
|
+
|
|
12
|
+
export const Headers = globalThis.Headers;
|
|
13
|
+
export const Request = globalThis.Request;
|
|
14
|
+
export const Response = globalThis.Response;
|
|
15
|
+
export const FormData = globalThis.FormData;
|
|
16
|
+
export const Blob = globalThis.Blob;
|
|
17
|
+
|
|
18
|
+
export class FetchError extends Error {
|
|
19
|
+
type: string;
|
|
20
|
+
constructor(message: string, type = 'system') {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'FetchError';
|
|
23
|
+
this.type = type;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class AbortError extends Error {
|
|
28
|
+
type = 'aborted';
|
|
29
|
+
constructor(message: string) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'AbortError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const isRedirect = (code: number) => [301, 302, 303, 307, 308].includes(code);
|
package/cloudflare/shims/xss.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
export function xss(_opts?: any) {
|
|
2
2
|
return (_req: any, _res: any, next: any) => next();
|
|
3
3
|
}
|
|
4
|
+
|
|
5
|
+
// Phase 4 (express→hono): the hono xss middleware (LITE app-shell on CF) calls
|
|
6
|
+
// initSanitize(...)(body) to produce sanitizedBody. The old express-compat worker
|
|
7
|
+
// path set req.body WITHOUT xss sanitization, so an identity sanitizer preserves
|
|
8
|
+
// the worker's exact behavior (the node host uses the real @blocklet/xss).
|
|
9
|
+
export function initSanitize(_opts?: any) {
|
|
10
|
+
return <T>(body: T): T => body;
|
|
11
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Phase 7 (W1′): worker tenant-context wiring.
|
|
2
|
+
//
|
|
3
|
+
// The CF worker's `*` middleware only set up DB/env — it never established a
|
|
4
|
+
// tenant context, so every TenantModel query in the worker fell back to the
|
|
5
|
+
// single-mode default. This Hono middleware mirrors the Express
|
|
6
|
+
// `contextMiddleware`: it resolves the request's raw Host to a tenant via the
|
|
7
|
+
// SINGLE-POINT resolver (`resolveTenantForHost`) and wraps the downstream chain
|
|
8
|
+
// in `context.withTenant` so the resolved tenant flows through AsyncLocalStorage
|
|
9
|
+
// into every handler/query.
|
|
10
|
+
//
|
|
11
|
+
// single mode: resolves to the deployment app DID (standalone worker unchanged).
|
|
12
|
+
// multi mode : unknown/missing Host -> 400 fail-closed, no default-tenant
|
|
13
|
+
// fallback, and the handler never runs.
|
|
14
|
+
|
|
15
|
+
import type { Context, Next } from 'hono';
|
|
16
|
+
|
|
17
|
+
import { context } from '../api/src/libs/context';
|
|
18
|
+
import { TenantError, TENANT_HOST_UNRESOLVED } from '../api/src/libs/tenant';
|
|
19
|
+
import { resolveTenantForHost } from '../api/src/libs/drivers/identity';
|
|
20
|
+
|
|
21
|
+
export function tenantMiddleware() {
|
|
22
|
+
return async (c: Context, next: Next) => {
|
|
23
|
+
let instanceDid: string;
|
|
24
|
+
try {
|
|
25
|
+
// raw Host header only — never a proxy header (X-Forwarded-Host); the CF
|
|
26
|
+
// route is responsible for ensuring it cannot be forged by the client.
|
|
27
|
+
instanceDid = await resolveTenantForHost(c.req.header('host'));
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (err instanceof TenantError && err.code === TENANT_HOST_UNRESOLVED) {
|
|
30
|
+
return c.json({ error: { code: err.code, message: err.message } }, 400);
|
|
31
|
+
}
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
return context.withTenant(instanceDid, () => next());
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Phase 7 (W1′): worker `withTenant` wiring. The CF worker had no tenant
|
|
2
|
+
// context — its `*` middleware only `setDB`. This drives the real Hono tenant
|
|
3
|
+
// middleware (the same factory mounted in worker.ts) over a tiny Hono app so
|
|
4
|
+
// the actual Host -> instanceDid resolution, multi-mode fail-closed 4xx, and
|
|
5
|
+
// `context.withTenant` ALS wrapping are exercised end to end.
|
|
6
|
+
|
|
7
|
+
import { Hono } from 'hono';
|
|
8
|
+
|
|
9
|
+
import { tenantMiddleware } from '../tenant-middleware';
|
|
10
|
+
import { getInstanceDid, context } from '../../api/src/libs/context';
|
|
11
|
+
import { getDefaultInstanceDid } from '../../api/src/libs/tenant';
|
|
12
|
+
import { setIdentityDriver, createDefaultIdentityDriver, type IdentityDriver } from '../../api/src/libs/drivers/identity';
|
|
13
|
+
|
|
14
|
+
const TENANT_A = 'did:abt:zHOSTA';
|
|
15
|
+
const TENANT_B = 'did:abt:zHOSTB';
|
|
16
|
+
|
|
17
|
+
// Build a Hono app wired exactly like worker.ts: tenant middleware on /api/*,
|
|
18
|
+
// then a handler that reports the tenant resolved from the ALS context.
|
|
19
|
+
function buildApp() {
|
|
20
|
+
const app = new Hono();
|
|
21
|
+
app.use('/api/*', tenantMiddleware());
|
|
22
|
+
app.get('/api/whoami', (c) => c.json({ tenant: getInstanceDid() }));
|
|
23
|
+
// /health is OUTSIDE /api/* — must never require a tenant (infra health check)
|
|
24
|
+
app.get('/health', (c) => c.json({ status: 'ok' }));
|
|
25
|
+
return app;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function get(app: Hono, path: string, headers: Record<string, string> = {}) {
|
|
29
|
+
const res = await app.request(path, { headers });
|
|
30
|
+
let body: any;
|
|
31
|
+
try {
|
|
32
|
+
body = await res.json();
|
|
33
|
+
} catch {
|
|
34
|
+
body = await res.text();
|
|
35
|
+
}
|
|
36
|
+
return { status: res.status, body };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ORIGINAL_MODE = process.env.PAYMENT_TENANT_MODE;
|
|
40
|
+
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
if (ORIGINAL_MODE === undefined) delete process.env.PAYMENT_TENANT_MODE;
|
|
43
|
+
else process.env.PAYMENT_TENANT_MODE = ORIGINAL_MODE;
|
|
44
|
+
setIdentityDriver(createDefaultIdentityDriver());
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('single mode (standalone worker legacy) — unchanged', () => {
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
process.env.PAYMENT_TENANT_MODE = 'single';
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('any host resolves to the deployment app DID', async () => {
|
|
53
|
+
const expected = getDefaultInstanceDid();
|
|
54
|
+
const app = buildApp();
|
|
55
|
+
const r1 = await get(app, '/api/whoami', { Host: 'anything.example.com' });
|
|
56
|
+
expect(r1.status).toBe(200);
|
|
57
|
+
expect(r1.body.tenant).toBe(expected);
|
|
58
|
+
const r2 = await get(app, '/api/whoami', { Host: 'other.example.com' });
|
|
59
|
+
expect(r2.body.tenant).toBe(expected); // default regardless of host
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('a request with no Host still resolves (single-mode default, no crash)', async () => {
|
|
63
|
+
const expected = getDefaultInstanceDid();
|
|
64
|
+
const app = buildApp();
|
|
65
|
+
const r = await get(app, '/api/whoami');
|
|
66
|
+
expect(r.status).toBe(200);
|
|
67
|
+
expect(r.body.tenant).toBe(expected);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('multi mode — Host maps to tenant, fail-closed on unknown', () => {
|
|
72
|
+
const identity: IdentityDriver = {
|
|
73
|
+
resolveInstanceDidForHost(host) {
|
|
74
|
+
if (host === 'a.example.com') return TENANT_A;
|
|
75
|
+
if (host === 'b.example.com') return TENANT_B;
|
|
76
|
+
return null;
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
process.env.PAYMENT_TENANT_MODE = 'multi';
|
|
82
|
+
setIdentityDriver(identity);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Happy path — two hosts see two tenants (data isolation)
|
|
86
|
+
it('two hosts resolve to two tenants', async () => {
|
|
87
|
+
const app = buildApp();
|
|
88
|
+
const a = await get(app, '/api/whoami', { Host: 'a.example.com' });
|
|
89
|
+
const b = await get(app, '/api/whoami', { Host: 'b.example.com' });
|
|
90
|
+
expect(a.body.tenant).toBe(TENANT_A);
|
|
91
|
+
expect(b.body.tenant).toBe(TENANT_B);
|
|
92
|
+
expect(a.body.tenant).not.toBe(b.body.tenant);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Bad input — missing Host fails closed (no default-tenant fallback)
|
|
96
|
+
it('missing Host fails closed with 4xx (no APP_PID fallback)', async () => {
|
|
97
|
+
const app = buildApp();
|
|
98
|
+
const r = await get(app, '/api/whoami');
|
|
99
|
+
expect(r.status).toBe(400);
|
|
100
|
+
expect(r.body.error.code).toBe('TENANT_HOST_UNRESOLVED');
|
|
101
|
+
expect(r.body.tenant).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Data leak — unknown (unregistered) host must never fall into a tenant
|
|
105
|
+
it('unknown host fails closed with 4xx + error code', async () => {
|
|
106
|
+
const app = buildApp();
|
|
107
|
+
const r = await get(app, '/api/whoami', { Host: 'unknown.example.com' });
|
|
108
|
+
expect(r.status).toBe(400);
|
|
109
|
+
expect(r.body.error.code).toBe('TENANT_HOST_UNRESOLVED');
|
|
110
|
+
expect(r.body.tenant).toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Security — a forged X-Forwarded-Host must not change resolution (raw Host only)
|
|
114
|
+
it('SECURITY: X-Forwarded-Host cannot override the raw Host', async () => {
|
|
115
|
+
const app = buildApp();
|
|
116
|
+
const r = await get(app, '/api/whoami', { Host: 'a.example.com', 'X-Forwarded-Host': 'b.example.com' });
|
|
117
|
+
expect(r.body.tenant).toBe(TENANT_A);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('SECURITY: X-Forwarded-Host pointing at a known tenant cannot rescue an unknown raw Host', async () => {
|
|
121
|
+
const app = buildApp();
|
|
122
|
+
const r = await get(app, '/api/whoami', { Host: 'unknown.example.com', 'X-Forwarded-Host': 'a.example.com' });
|
|
123
|
+
expect(r.status).toBe(400);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Data loss — a 4xx-rejected request must never reach the route handler
|
|
127
|
+
it('a 4xx-rejected request never runs the handler (no write)', async () => {
|
|
128
|
+
const app = new Hono();
|
|
129
|
+
app.use('/api/*', tenantMiddleware());
|
|
130
|
+
let handlerRuns = 0;
|
|
131
|
+
app.get('/api/whoami', (c) => {
|
|
132
|
+
handlerRuns += 1;
|
|
133
|
+
return c.json({ tenant: getInstanceDid() });
|
|
134
|
+
});
|
|
135
|
+
const r = await get(app, '/api/whoami', { Host: 'unknown.example.com' });
|
|
136
|
+
expect(r.status).toBe(400);
|
|
137
|
+
expect(handlerRuns).toBe(0);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ALS propagation — the handler runs INSIDE the withTenant context
|
|
141
|
+
it('the handler observes the resolved tenant via context ALS', async () => {
|
|
142
|
+
const app = new Hono();
|
|
143
|
+
app.use('/api/*', tenantMiddleware());
|
|
144
|
+
let seen: string | undefined;
|
|
145
|
+
app.get('/api/whoami', (c) => {
|
|
146
|
+
seen = context.getInstanceDid();
|
|
147
|
+
return c.json({ ok: true });
|
|
148
|
+
});
|
|
149
|
+
await get(app, '/api/whoami', { Host: 'b.example.com' });
|
|
150
|
+
expect(seen).toBe(TENANT_B);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// /health is outside /api/* — tenant middleware must not gate it
|
|
154
|
+
it('/health is reachable without a Host even in multi mode', async () => {
|
|
155
|
+
const app = buildApp();
|
|
156
|
+
const r = await get(app, '/health');
|
|
157
|
+
expect(r.status).toBe(200);
|
|
158
|
+
expect(r.body.status).toBe('ok');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Phase 12c HARD GATE (scan): the CF worker is a host adapter. It must mount the
|
|
2
|
+
// embedded service's resource-route surface (svc.http.resourceRoutes) and NEVER
|
|
3
|
+
// access svc.handler — whose lazy getter builds the node-only Express app shell
|
|
4
|
+
// that does not belong under workerd. It also must not import the deleted legacy
|
|
5
|
+
// shims/queue.ts duplicate engine. This statically scans the worker source so a
|
|
6
|
+
// regression fails the suite (the runtime Proxy in ensurePaymentService is the
|
|
7
|
+
// second line of defense).
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
const workerSrc = fs.readFileSync(path.join(__dirname, '../worker.ts'), 'utf8');
|
|
12
|
+
|
|
13
|
+
describe('Phase 12c — worker host-adapter hard gate', () => {
|
|
14
|
+
it('mounts the resource-route surface (svc.http.resourceRoutes)', () => {
|
|
15
|
+
expect(workerSrc).toMatch(/service\.http\.resourceRoutes/);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('never reads .handler on the payment service', () => {
|
|
19
|
+
const offending = workerSrc
|
|
20
|
+
.split('\n')
|
|
21
|
+
.map((line, n) => ({ line: line.trim(), n: n + 1 }))
|
|
22
|
+
// ignore comment lines — only actual code may not read svc.handler
|
|
23
|
+
.filter(({ line }) => !line.startsWith('//') && !line.startsWith('*') && !line.startsWith('/*'))
|
|
24
|
+
.filter(({ line }) => /\b(service|paymentService|svc)\.handler\b/.test(line))
|
|
25
|
+
// the hard-gate trap string + the Proxy guard line are the gate itself
|
|
26
|
+
.filter(({ line }) => !/forbidden in the CF worker/.test(line) && !/prop === 'handler'/.test(line));
|
|
27
|
+
expect(offending).toEqual([]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('installs the runtime hard-gate Proxy that throws on handler access', () => {
|
|
31
|
+
expect(workerSrc).toMatch(/new Proxy\(svc,/);
|
|
32
|
+
expect(workerSrc).toMatch(/svc\.handler is forbidden in the CF worker/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('does not import the deleted legacy shims/queue engine', () => {
|
|
36
|
+
expect(workerSrc).not.toMatch(/from '\.\/shims\/queue'/);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('drives the core queue runtime surface instead', () => {
|
|
40
|
+
expect(workerSrc).toMatch(/from '\.\.\/api\/src\/libs\/queue\/runtime'/);
|
|
41
|
+
expect(workerSrc).toMatch(/dispatchDueJobs\(\)/);
|
|
42
|
+
expect(workerSrc).toMatch(/flushQueueWork\(\)/);
|
|
43
|
+
});
|
|
44
|
+
});
|