alepha 0.20.6 → 0.20.7
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/AGENTS.md +0 -1
- package/CLAUDE.md +0 -1
- package/assets/agents-template.md +0 -1
- package/dist/api/audits/index.browser.js +1 -0
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +370 -355
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +1 -0
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +1 -0
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +179 -170
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +1 -0
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +7 -0
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +271 -262
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +21 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +198 -192
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js +1 -0
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +246 -245
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/organizations/index.d.ts +100 -97
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/parameters/index.d.ts +323 -320
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/payments/index.d.ts +431 -376
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js +202 -87
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/subscriptions/index.d.ts +1695 -0
- package/dist/api/subscriptions/index.d.ts.map +1 -0
- package/dist/api/subscriptions/index.js +1919 -0
- package/dist/api/subscriptions/index.js.map +1 -0
- package/dist/api/users/index.d.ts +863 -847
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/verifications/index.d.ts +126 -125
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/bucket/index.d.ts +3 -2
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts +114 -4
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +181 -15
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +181 -15
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +20 -19
- package/dist/cache/database/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts +3 -2
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cli/core/index.d.ts +113 -129
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +75 -7
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +3 -2
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/platform/index.d.ts +346 -290
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +105 -6
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +12 -11
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/command/index.d.ts +5 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/core/index.browser.js +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +119 -118
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +1 -1
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.d.ts +3 -2
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/email/core/index.d.ts +3 -2
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/smtp/index.d.ts +7 -6
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/lock/core/index.d.ts +5 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +10 -9
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +9 -8
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +9 -3
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +31 -10
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +33 -14
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +31 -10
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +6 -5
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/queue/core/index.d.ts +5 -4
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts +3 -2
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/react/form/index.d.ts +5 -0
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +6 -4
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +2 -1
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/router/index.d.ts +206 -205
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/ui/index.d.ts +11 -11
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +3 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/security/index.browser.js +29 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +82 -35
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +56 -3
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +163 -158
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +16 -4
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/core/index.d.ts +35 -34
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/cors/index.d.ts +7 -6
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +16 -15
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/links/index.d.ts +51 -50
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts +6 -5
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +2 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts +3 -2
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/package.json +16 -32
- package/src/api/audits/entities/audits.ts +1 -0
- package/src/api/files/entities/files.ts +1 -0
- package/src/api/jobs/__tests__/$job.spec.ts +92 -40
- package/src/api/jobs/entities/jobExecutionEntity.ts +1 -0
- package/src/api/jobs/providers/JobProvider.ts +20 -5
- package/src/api/jobs/schemas/jobConfigAtom.ts +5 -0
- package/src/api/keys/entities/apiKeyEntity.ts +1 -0
- package/src/api/payments/controllers/MockCheckoutController.ts +146 -0
- package/src/api/payments/index.ts +3 -0
- package/src/api/payments/providers/MemoryPaymentProvider.ts +9 -4
- package/src/api/payments/providers/PaymentProvider.ts +25 -9
- package/src/api/payments/services/PaymentService.ts +3 -0
- package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
- package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
- package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
- package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
- package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
- package/src/api/subscriptions/entities/subscriptions.ts +68 -0
- package/src/api/subscriptions/index.ts +133 -0
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
- package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
- package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
- package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
- package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
- package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
- package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
- package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
- package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
- package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
- package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
- package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
- package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
- package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
- package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
- package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
- package/src/api/subscriptions/services/BillingService.ts +437 -0
- package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
- package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
- package/src/api/subscriptions/services/UsageService.ts +118 -0
- package/src/cache/core/__tests__/$cache.memory.spec.ts +450 -0
- package/src/cache/core/__tests__/$cache.swr.spec.ts +394 -0
- package/src/cache/core/index.ts +16 -0
- package/src/cache/core/primitives/$cache.ts +347 -21
- package/src/cli/core/tasks/BuildCloudflareTask.ts +16 -0
- package/src/cli/core/templates/agentMd.ts +39 -4
- package/src/cli/core/templates/biomeJson.ts +25 -1
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +2 -2
- package/src/cli/platform/__tests__/CloudflareAdapter.spec.ts +117 -0
- package/src/cli/platform/adapters/CloudflareAdapter.ts +104 -7
- package/src/cli/platform/atoms/platformOptions.ts +13 -0
- package/src/cli/platform/schemas/platform.ts +1 -0
- package/src/cli/platform/services/CloudflareApi.ts +61 -0
- package/src/cli/platform/services/PlatformOrchestrator.ts +9 -4
- package/src/core/__tests__/$module.spec.ts +2 -2
- package/src/core/primitives/$module.ts +4 -4
- package/src/mcp/providers/McpServerProvider.ts +1 -1
- package/src/orm/core/providers/DatabaseTypeProvider.ts +9 -3
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +1 -1
- package/src/orm/core/schemas/insertSchema.ts +10 -2
- package/src/orm/core/services/Repository.ts +27 -7
- package/src/react/form/hooks/useFormState.ts +8 -1
- package/src/react/form/index.ts +10 -1
- package/src/react/form/services/FormModel.ts +9 -3
- package/src/security/atoms/currentTenantAtom.ts +34 -0
- package/src/security/index.browser.ts +1 -0
- package/src/security/index.ts +12 -1
- package/src/security/primitives/$issuer.ts +17 -1
- package/src/security/providers/SecurityProvider.ts +37 -0
- package/src/server/auth/__tests__/validateRedirectUri.spec.ts +78 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +21 -5
- package/tsconfig.base.json +2 -1
- package/dist/react/websocket/index.d.ts +0 -117
- package/dist/react/websocket/index.d.ts.map +0 -1
- package/dist/react/websocket/index.js +0 -108
- package/dist/react/websocket/index.js.map +0 -1
- package/dist/websocket/index.browser.js +0 -848
- package/dist/websocket/index.browser.js.map +0 -1
- package/dist/websocket/index.d.ts +0 -876
- package/dist/websocket/index.d.ts.map +0 -1
- package/dist/websocket/index.js +0 -1185
- package/dist/websocket/index.js.map +0 -1
- package/src/react/websocket/hooks/useRoom.tsx +0 -251
- package/src/react/websocket/index.ts +0 -7
- package/src/websocket/__tests__/$channel.spec.ts +0 -30
- package/src/websocket/__tests__/$websocket-new.spec.ts +0 -195
- package/src/websocket/__tests__/RoomManager.spec.ts +0 -146
- package/src/websocket/__tests__/websocket-integration.spec.ts +0 -951
- package/src/websocket/errors/WebSocketError.ts +0 -34
- package/src/websocket/index.browser.ts +0 -25
- package/src/websocket/index.shared.ts +0 -8
- package/src/websocket/index.ts +0 -85
- package/src/websocket/interfaces/WebSocketInterfaces.ts +0 -252
- package/src/websocket/primitives/$channel.ts +0 -131
- package/src/websocket/primitives/$websocket.ts +0 -107
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +0 -617
- package/src/websocket/providers/WebSocketServerProvider.ts +0 -56
- package/src/websocket/services/RoomManager.ts +0 -160
- package/src/websocket/services/WebSocketClient.ts +0 -642
- package/src/websocket/services/WebSocketTopicService.ts +0 -108
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "alepha",
|
|
3
3
|
"description": "Easy-to-use modern TypeScript framework for building many kind of applications.",
|
|
4
4
|
"author": "Feunard",
|
|
5
|
-
"version": "0.20.
|
|
5
|
+
"version": "0.20.7",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=22.0.0"
|
|
@@ -29,15 +29,14 @@
|
|
|
29
29
|
"tsx": "^4.21.0",
|
|
30
30
|
"typebox": "^1.1.38",
|
|
31
31
|
"typescript": "^6.0.3",
|
|
32
|
-
"vite-bundle-analyzer": "^1.3.8"
|
|
33
|
-
"ws": "^8.20.0"
|
|
32
|
+
"vite-bundle-analyzer": "^1.3.8"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|
|
36
|
-
"@biomejs/biome": "^2.4.
|
|
37
|
-
"@cloudflare/workers-types": "^4.
|
|
35
|
+
"@biomejs/biome": "^2.4.15",
|
|
36
|
+
"@cloudflare/workers-types": "^4.20260510.1",
|
|
38
37
|
"@electric-sql/pglite": "^0.4.5",
|
|
39
38
|
"@faker-js/faker": "^10.4.0",
|
|
40
|
-
"@tailwindcss/vite": "^4.
|
|
39
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
41
40
|
"@testing-library/dom": "^10.4.1",
|
|
42
41
|
"@testing-library/react": "^16.3.2",
|
|
43
42
|
"@types/bun": "^1.3.13",
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
"@types/nodemailer": "^8.0.0",
|
|
46
45
|
"@types/react": "^19.2.14",
|
|
47
46
|
"@types/react-dom": "^19.2.3",
|
|
48
|
-
"@types/ws": "^8.18.1",
|
|
49
47
|
"cron-schedule": "^6.0.0",
|
|
50
48
|
"drizzle-kit": "^0.31.10",
|
|
51
49
|
"jose": "^6.2.3",
|
|
@@ -57,7 +55,7 @@
|
|
|
57
55
|
"react-dom": "^19.2.6",
|
|
58
56
|
"shadcn": "^4.7.0",
|
|
59
57
|
"swagger-ui-dist": "^5.32.5",
|
|
60
|
-
"tailwindcss": "^4.
|
|
58
|
+
"tailwindcss": "^4.3.0",
|
|
61
59
|
"tsdown": "^0.22.0",
|
|
62
60
|
"vite": "^8.0.11",
|
|
63
61
|
"vitest": "^4.1.5"
|
|
@@ -151,6 +149,11 @@
|
|
|
151
149
|
"import": "./dist/api/payments/index.js",
|
|
152
150
|
"default": "./dist/api/payments/index.js"
|
|
153
151
|
},
|
|
152
|
+
"./api/subscriptions": {
|
|
153
|
+
"types": "./dist/api/subscriptions/index.d.ts",
|
|
154
|
+
"import": "./dist/api/subscriptions/index.js",
|
|
155
|
+
"default": "./dist/api/subscriptions/index.js"
|
|
156
|
+
},
|
|
154
157
|
"./api/users": {
|
|
155
158
|
"types": "./dist/api/users/index.d.ts",
|
|
156
159
|
"react-native": "./dist/api/users/index.browser.js",
|
|
@@ -369,11 +372,6 @@
|
|
|
369
372
|
"import": "./dist/react/ui/index.js",
|
|
370
373
|
"default": "./dist/react/ui/index.js"
|
|
371
374
|
},
|
|
372
|
-
"./react/websocket": {
|
|
373
|
-
"types": "./dist/react/websocket/index.d.ts",
|
|
374
|
-
"import": "./dist/react/websocket/index.js",
|
|
375
|
-
"default": "./dist/react/websocket/index.js"
|
|
376
|
-
},
|
|
377
375
|
"./redis": {
|
|
378
376
|
"types": "./dist/redis/index.d.ts",
|
|
379
377
|
"bun": "./dist/redis/index.bun.js",
|
|
@@ -494,13 +492,6 @@
|
|
|
494
492
|
"import": "./dist/topic/redis/index.js",
|
|
495
493
|
"default": "./dist/topic/redis/index.js"
|
|
496
494
|
},
|
|
497
|
-
"./websocket": {
|
|
498
|
-
"types": "./dist/websocket/index.d.ts",
|
|
499
|
-
"react-native": "./dist/websocket/index.browser.js",
|
|
500
|
-
"browser": "./dist/websocket/index.browser.js",
|
|
501
|
-
"import": "./dist/websocket/index.js",
|
|
502
|
-
"default": "./dist/websocket/index.js"
|
|
503
|
-
},
|
|
504
495
|
"./tsconfig.base": "./tsconfig.base.json",
|
|
505
496
|
"./package.json": "./package.json"
|
|
506
497
|
},
|
|
@@ -561,6 +552,11 @@
|
|
|
561
552
|
"import": "./dist/api/payments/index.js",
|
|
562
553
|
"default": "./dist/api/payments/index.js"
|
|
563
554
|
},
|
|
555
|
+
"./api/subscriptions": {
|
|
556
|
+
"types": "./dist/api/subscriptions/index.d.ts",
|
|
557
|
+
"import": "./dist/api/subscriptions/index.js",
|
|
558
|
+
"default": "./dist/api/subscriptions/index.js"
|
|
559
|
+
},
|
|
564
560
|
"./api/users": {
|
|
565
561
|
"types": "./dist/api/users/index.d.ts",
|
|
566
562
|
"react-native": "./dist/api/users/index.browser.js",
|
|
@@ -779,11 +775,6 @@
|
|
|
779
775
|
"import": "./dist/react/ui/index.js",
|
|
780
776
|
"default": "./dist/react/ui/index.js"
|
|
781
777
|
},
|
|
782
|
-
"./react/websocket": {
|
|
783
|
-
"types": "./dist/react/websocket/index.d.ts",
|
|
784
|
-
"import": "./dist/react/websocket/index.js",
|
|
785
|
-
"default": "./dist/react/websocket/index.js"
|
|
786
|
-
},
|
|
787
778
|
"./redis": {
|
|
788
779
|
"types": "./dist/redis/index.d.ts",
|
|
789
780
|
"bun": "./dist/redis/index.bun.js",
|
|
@@ -904,13 +895,6 @@
|
|
|
904
895
|
"import": "./dist/topic/redis/index.js",
|
|
905
896
|
"default": "./dist/topic/redis/index.js"
|
|
906
897
|
},
|
|
907
|
-
"./websocket": {
|
|
908
|
-
"types": "./dist/websocket/index.d.ts",
|
|
909
|
-
"react-native": "./dist/websocket/index.browser.js",
|
|
910
|
-
"browser": "./dist/websocket/index.browser.js",
|
|
911
|
-
"import": "./dist/websocket/index.js",
|
|
912
|
-
"default": "./dist/websocket/index.js"
|
|
913
|
-
},
|
|
914
898
|
"./tsconfig.base": "./tsconfig.base.json",
|
|
915
899
|
"./package.json": "./package.json"
|
|
916
900
|
}
|
|
@@ -21,6 +21,29 @@ const makeApp = () =>
|
|
|
21
21
|
const makeAppDirect = () =>
|
|
22
22
|
Alepha.create().with(AlephaOrmPostgres).with(AlephaApiJobs);
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Poll `fn` until `predicate` returns true, or throw on timeout.
|
|
26
|
+
* Use this instead of `setTimeout(r, fixedMs)` — fixed sleeps race the
|
|
27
|
+
* in-memory queue under CI load and produce flaky failures.
|
|
28
|
+
*/
|
|
29
|
+
async function waitFor<T>(
|
|
30
|
+
fn: () => Promise<T> | T,
|
|
31
|
+
predicate: (v: T) => boolean,
|
|
32
|
+
{ timeout = 2000, interval = 10, label = "condition" } = {},
|
|
33
|
+
): Promise<T> {
|
|
34
|
+
const deadline = Date.now() + timeout;
|
|
35
|
+
let last: T = await fn();
|
|
36
|
+
while (Date.now() < deadline) {
|
|
37
|
+
if (predicate(last)) return last;
|
|
38
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
39
|
+
last = await fn();
|
|
40
|
+
}
|
|
41
|
+
if (predicate(last)) return last;
|
|
42
|
+
throw new Error(
|
|
43
|
+
`waitFor: ${label} not met within ${timeout}ms; last value: ${JSON.stringify(last)}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
24
47
|
describe("$job — registration validation", () => {
|
|
25
48
|
it("rejects jobs declaring both cron and schema", async ({ expect }) => {
|
|
26
49
|
const alepha = makeApp();
|
|
@@ -136,13 +159,12 @@ describe("$job — queue mode (outbox)", () => {
|
|
|
136
159
|
await alepha.start();
|
|
137
160
|
await app.work.push({ n: 42 });
|
|
138
161
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
162
|
+
const rows = await waitFor(
|
|
163
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
164
|
+
(r) => r.length === 0 && received !== undefined,
|
|
165
|
+
{ label: "row deleted on success" },
|
|
166
|
+
);
|
|
142
167
|
expect(received).toEqual({ n: 42 });
|
|
143
|
-
const rows = await app.executions.findMany({
|
|
144
|
-
where: { jobName: { eq: "App.work" } },
|
|
145
|
-
});
|
|
146
168
|
expect(rows).toHaveLength(0);
|
|
147
169
|
});
|
|
148
170
|
|
|
@@ -159,10 +181,11 @@ describe("$job — queue mode (outbox)", () => {
|
|
|
159
181
|
const app = alepha.inject(App);
|
|
160
182
|
await alepha.start();
|
|
161
183
|
await app.work.push({ n: 1 });
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
184
|
+
const rows = await waitFor(
|
|
185
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
186
|
+
(r) => r.length === 1 && r[0].status === "ok",
|
|
187
|
+
{ label: "row reaches status=ok" },
|
|
188
|
+
);
|
|
166
189
|
expect(rows).toHaveLength(1);
|
|
167
190
|
expect(rows[0].status).toBe("ok");
|
|
168
191
|
});
|
|
@@ -209,11 +232,12 @@ describe("$job — queue mode (outbox)", () => {
|
|
|
209
232
|
const app = alepha.inject(App);
|
|
210
233
|
await alepha.start();
|
|
211
234
|
await app.work.push({ v: 1 }, { delay: [1, "hour"] });
|
|
212
|
-
|
|
235
|
+
const rows = await waitFor(
|
|
236
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
237
|
+
(r) => r.length === 1 && r[0].status === "scheduled",
|
|
238
|
+
{ label: "row reaches status=scheduled" },
|
|
239
|
+
);
|
|
213
240
|
expect(calls).toBe(0);
|
|
214
|
-
const rows = await app.executions.findMany({
|
|
215
|
-
where: { jobName: { eq: "App.work" } },
|
|
216
|
-
});
|
|
217
241
|
expect(rows).toHaveLength(1);
|
|
218
242
|
expect(rows[0].status).toBe("scheduled");
|
|
219
243
|
expect(rows[0].scheduledAt).toBeTruthy();
|
|
@@ -265,10 +289,12 @@ describe("$job — queue mode (outbox)", () => {
|
|
|
265
289
|
const app = alepha.inject(App);
|
|
266
290
|
await alepha.start();
|
|
267
291
|
await app.work.push({ v: 1 });
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
292
|
+
const rows = await waitFor(
|
|
293
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
294
|
+
(r) =>
|
|
295
|
+
r.length === 1 && r[0].status === "scheduled" && r[0].attempt === 1,
|
|
296
|
+
{ label: "row rescheduled after first failure" },
|
|
297
|
+
);
|
|
272
298
|
expect(rows).toHaveLength(1);
|
|
273
299
|
expect(rows[0].status).toBe("scheduled");
|
|
274
300
|
expect(rows[0].attempt).toBe(1);
|
|
@@ -297,10 +323,11 @@ describe("$job — queue mode (outbox)", () => {
|
|
|
297
323
|
const app = alepha.inject(App);
|
|
298
324
|
await alepha.start();
|
|
299
325
|
await app.work.push({ v: 1 });
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
326
|
+
const rows = await waitFor(
|
|
327
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
328
|
+
(r) => r.length === 1 && r[0].status === "error",
|
|
329
|
+
{ label: "row reaches terminal status=error" },
|
|
330
|
+
);
|
|
304
331
|
expect(rows).toHaveLength(1);
|
|
305
332
|
expect(rows[0].status).toBe("error");
|
|
306
333
|
expect(rows[0].error).toBe("dead");
|
|
@@ -579,18 +606,31 @@ describe("$job — retry semantics", () => {
|
|
|
579
606
|
|
|
580
607
|
await app.work.push({ v: 1 });
|
|
581
608
|
|
|
582
|
-
// First attempt runs immediately
|
|
583
|
-
|
|
609
|
+
// First attempt runs immediately, then the row is rescheduled with attempt=1.
|
|
610
|
+
// Wait for that stable state before sweeping (sweep needs status=scheduled).
|
|
611
|
+
await waitFor(
|
|
612
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
613
|
+
(r) => r[0]?.status === "scheduled" && r[0]?.attempt === 1,
|
|
614
|
+
{ label: "row rescheduled after attempt 1" },
|
|
615
|
+
);
|
|
584
616
|
expect(attempts).toBe(1);
|
|
585
617
|
|
|
586
618
|
const provider = alepha.inject(JobProvider);
|
|
587
619
|
// Trigger sweeps manually — each one should claim and run the next attempt.
|
|
588
620
|
await (provider as any).sweep();
|
|
589
|
-
await
|
|
621
|
+
await waitFor(
|
|
622
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
623
|
+
(r) => r[0]?.status === "scheduled" && r[0]?.attempt === 2,
|
|
624
|
+
{ label: "row rescheduled after attempt 2" },
|
|
625
|
+
);
|
|
590
626
|
expect(attempts).toBe(2);
|
|
591
627
|
|
|
592
628
|
await (provider as any).sweep();
|
|
593
|
-
await
|
|
629
|
+
await waitFor(
|
|
630
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.work" } } }),
|
|
631
|
+
(r) => r[0]?.status === "error" && r[0]?.attempt === 3,
|
|
632
|
+
{ label: "row terminal after attempt 3" },
|
|
633
|
+
);
|
|
594
634
|
expect(attempts).toBe(3);
|
|
595
635
|
|
|
596
636
|
// After 3 attempts the row is terminal — no more retries.
|
|
@@ -640,11 +680,16 @@ describe("$job — cancel race", () => {
|
|
|
640
680
|
|
|
641
681
|
await app.slow.cancel(id);
|
|
642
682
|
|
|
643
|
-
//
|
|
644
|
-
//
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
683
|
+
// Confirm the row is `cancelled` and stays that way — without the guard,
|
|
684
|
+
// the handler's abort path would race to overwrite to `error`. Poll for a
|
|
685
|
+
// stable window so the (buggy) overwrite would be caught.
|
|
686
|
+
const settleDeadline = Date.now() + 300;
|
|
687
|
+
let final: any;
|
|
688
|
+
while (Date.now() < settleDeadline) {
|
|
689
|
+
final = await app.executions.findById(id);
|
|
690
|
+
if (final?.status !== "cancelled") break;
|
|
691
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
692
|
+
}
|
|
648
693
|
expect(final?.status).toBe("cancelled");
|
|
649
694
|
expect(final?.error).toBeFalsy();
|
|
650
695
|
});
|
|
@@ -695,11 +740,16 @@ describe("$job — cron + retry (outbox path)", () => {
|
|
|
695
740
|
|
|
696
741
|
// Sweep picks it up and runs attempt 2 → terminal.
|
|
697
742
|
await (provider as any).sweep();
|
|
698
|
-
await
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
743
|
+
await waitFor(
|
|
744
|
+
() => attempts,
|
|
745
|
+
(n) => n === 2,
|
|
746
|
+
{ label: "second attempt runs after cron sweep" },
|
|
747
|
+
);
|
|
748
|
+
const rows2 = await waitFor(
|
|
749
|
+
() => app.executions.findMany({ where: { jobName: { eq: "App.tick" } } }),
|
|
750
|
+
(r) => r[0]?.status === "error",
|
|
751
|
+
{ label: "row reaches terminal error" },
|
|
752
|
+
);
|
|
703
753
|
expect(rows2[0].status).toBe("error");
|
|
704
754
|
});
|
|
705
755
|
});
|
|
@@ -765,12 +815,14 @@ describe("$job — admin resource shape", () => {
|
|
|
765
815
|
await alepha.start();
|
|
766
816
|
|
|
767
817
|
const id = await app.work.push({ v: 1 });
|
|
768
|
-
// Wait for the handler to finish so the row is `ok` (record: all keeps it).
|
|
769
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
770
|
-
|
|
771
818
|
const { JobService } = await import("../services/JobService.ts");
|
|
772
819
|
const svc = alepha.inject(JobService);
|
|
773
|
-
|
|
820
|
+
// Wait for the handler to finish so the row is `ok` (record: all keeps it).
|
|
821
|
+
const resource = await waitFor(
|
|
822
|
+
() => svc.getExecution(id),
|
|
823
|
+
(r) => r?.status === "ok",
|
|
824
|
+
{ label: "execution reaches status=ok" },
|
|
825
|
+
);
|
|
774
826
|
expect(resource.priority).toBe("high");
|
|
775
827
|
expect(typeof resource.priority).toBe("string");
|
|
776
828
|
});
|
|
@@ -54,6 +54,7 @@ export const jobExecutionEntity = $entity({
|
|
|
54
54
|
}),
|
|
55
55
|
indexes: [
|
|
56
56
|
{ columns: ["jobName", "status", "scheduledAt"] },
|
|
57
|
+
{ columns: ["jobName", "status", "createdAt"] },
|
|
57
58
|
{ columns: ["jobName", "startedAt"] },
|
|
58
59
|
{ columns: ["jobName", "key"], unique: true },
|
|
59
60
|
],
|
|
@@ -112,7 +112,11 @@ export type JobEffectiveMode = "cron" | "queue" | "direct";
|
|
|
112
112
|
* - re-enqueue pending rows older than `staleThreshold`
|
|
113
113
|
* - mark crashed running rows as failed and apply retry policy
|
|
114
114
|
* - move `scheduled` rows with `scheduledAt <= now` to pending + dispatch
|
|
115
|
-
*
|
|
115
|
+
*
|
|
116
|
+
* Trim runs on its own cron (`trimCron`, default hourly):
|
|
117
|
+
* - per-job history trimmed beyond `keepLastSuccess` / `keepLastError`
|
|
118
|
+
* - decoupled from sweep because trim cost scales with job count, not
|
|
119
|
+
* retry latency — running it every sweep is wasted work for most apps.
|
|
116
120
|
*/
|
|
117
121
|
export class JobProvider {
|
|
118
122
|
protected readonly alepha = $inject(Alepha);
|
|
@@ -1127,9 +1131,6 @@ export class JobProvider {
|
|
|
1127
1131
|
await this.handleFailure(exec.id, reg, exec.attempt, err, "");
|
|
1128
1132
|
}
|
|
1129
1133
|
}
|
|
1130
|
-
|
|
1131
|
-
// 4. Trim ring buffer per job
|
|
1132
|
-
await this.trimRingBuffers();
|
|
1133
1134
|
} catch (e) {
|
|
1134
1135
|
this.log.error("Sweep failed", { error: e });
|
|
1135
1136
|
}
|
|
@@ -1188,7 +1189,7 @@ export class JobProvider {
|
|
|
1188
1189
|
try {
|
|
1189
1190
|
const rows = await this.executions.findMany({
|
|
1190
1191
|
where: { jobName: { eq: jobName }, status: { eq: status } },
|
|
1191
|
-
orderBy: { column: "
|
|
1192
|
+
orderBy: { column: "createdAt", direction: "desc" },
|
|
1192
1193
|
limit: keep + 50,
|
|
1193
1194
|
});
|
|
1194
1195
|
if (rows.length <= keep) return;
|
|
@@ -1251,6 +1252,20 @@ export class JobProvider {
|
|
|
1251
1252
|
},
|
|
1252
1253
|
true,
|
|
1253
1254
|
);
|
|
1255
|
+
|
|
1256
|
+
this.cronProvider.createCronJob(
|
|
1257
|
+
"api:jobs:trim",
|
|
1258
|
+
this.config.trimCron,
|
|
1259
|
+
async () => {
|
|
1260
|
+
if (this.stopping) return;
|
|
1261
|
+
try {
|
|
1262
|
+
await this.trimRingBuffers();
|
|
1263
|
+
} catch (e) {
|
|
1264
|
+
this.log.error("Trim failed", { error: e });
|
|
1265
|
+
}
|
|
1266
|
+
},
|
|
1267
|
+
true,
|
|
1268
|
+
);
|
|
1254
1269
|
},
|
|
1255
1270
|
});
|
|
1256
1271
|
|
|
@@ -8,6 +8,10 @@ export const jobConfig = $atom({
|
|
|
8
8
|
description:
|
|
9
9
|
"Cron expression for the sweep tick. Must be minute-granular at minimum (cron resolution). On Cloudflare Workers this expression is emitted into wrangler.jsonc by the build.",
|
|
10
10
|
}),
|
|
11
|
+
trimCron: t.text({
|
|
12
|
+
description:
|
|
13
|
+
"Cron expression for the ring-buffer trim tick (per-job keepLastSuccess/keepLastError enforcement). Decoupled from `sweepCron` because trim is bounded by job execution rate, not retry latency — running it every sweep is wasted work for most apps.",
|
|
14
|
+
}),
|
|
11
15
|
staleThreshold: t.integer({
|
|
12
16
|
description: "Pending age (ms) before the sweep re-dispatches it.",
|
|
13
17
|
}),
|
|
@@ -31,6 +35,7 @@ export const jobConfig = $atom({
|
|
|
31
35
|
}),
|
|
32
36
|
default: {
|
|
33
37
|
sweepCron: "*/5 * * * *",
|
|
38
|
+
trimCron: "0 * * * *",
|
|
34
39
|
staleThreshold: 300_000,
|
|
35
40
|
runTimeout: 1_800_000,
|
|
36
41
|
keepLastSuccess: 10,
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { $inject, t } from "alepha";
|
|
2
|
+
import { $route, type ServerReply } from "alepha/server";
|
|
3
|
+
import { MemoryPaymentProvider } from "../providers/MemoryPaymentProvider.ts";
|
|
4
|
+
import { PaymentProvider } from "../providers/PaymentProvider.ts";
|
|
5
|
+
import { PaymentService } from "../services/PaymentService.ts";
|
|
6
|
+
|
|
7
|
+
const FORBIDDEN_HTML =
|
|
8
|
+
"<!doctype html><meta charset=utf-8><title>Mock checkout</title>" +
|
|
9
|
+
"<p>Mock checkout is only available with the in-memory payment provider.</p>";
|
|
10
|
+
|
|
11
|
+
const escapeHtml = (s: string) =>
|
|
12
|
+
s.replace(/[&<>"']/g, (c) =>
|
|
13
|
+
c === "&"
|
|
14
|
+
? "&"
|
|
15
|
+
: c === "<"
|
|
16
|
+
? "<"
|
|
17
|
+
: c === ">"
|
|
18
|
+
? ">"
|
|
19
|
+
: c === '"'
|
|
20
|
+
? """
|
|
21
|
+
: "'",
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const formatAmount = (cents: number, currency: string) => {
|
|
25
|
+
const value = (cents / 100).toFixed(2);
|
|
26
|
+
return `${value} ${currency.toUpperCase()}`;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const renderPage = (opts: {
|
|
30
|
+
intentId: string;
|
|
31
|
+
amount: string;
|
|
32
|
+
returnUrl: string;
|
|
33
|
+
}) => `<!doctype html>
|
|
34
|
+
<html lang="en">
|
|
35
|
+
<head>
|
|
36
|
+
<meta charset="utf-8" />
|
|
37
|
+
<title>Mock checkout</title>
|
|
38
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
39
|
+
<style>
|
|
40
|
+
:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
|
|
41
|
+
body { display:flex; min-height:100vh; align-items:center; justify-content:center; margin:0; background:#f6f7f9; }
|
|
42
|
+
.card { background:#fff; padding:2rem; border-radius:12px; box-shadow:0 8px 24px rgba(0,0,0,.08); max-width:380px; width:100%; }
|
|
43
|
+
h1 { margin:0 0 .25rem; font-size:1.1rem; }
|
|
44
|
+
.badge { display:inline-block; background:#fef3c7; color:#92400e; font-size:.7rem; padding:2px 8px; border-radius:999px; margin-bottom:.75rem; }
|
|
45
|
+
.amount { font-size:2rem; font-weight:600; margin:.75rem 0 1.25rem; }
|
|
46
|
+
.row { display:flex; gap:.5rem; }
|
|
47
|
+
button { flex:1; padding:.75rem 1rem; border-radius:8px; border:0; font-size:.95rem; cursor:pointer; }
|
|
48
|
+
.pay { background:#16a34a; color:#fff; }
|
|
49
|
+
.cancel { background:#e5e7eb; color:#111; }
|
|
50
|
+
.meta { font-size:.75rem; color:#6b7280; margin-top:1rem; word-break:break-all; }
|
|
51
|
+
</style>
|
|
52
|
+
</head>
|
|
53
|
+
<body>
|
|
54
|
+
<div class="card">
|
|
55
|
+
<span class="badge">DEV — mock checkout</span>
|
|
56
|
+
<h1>Confirm payment</h1>
|
|
57
|
+
<div class="amount">${escapeHtml(opts.amount)}</div>
|
|
58
|
+
<div class="row">
|
|
59
|
+
<form method="post" action="/payments/mock-checkout/${opts.intentId}/cancel">
|
|
60
|
+
<input type="hidden" name="returnUrl" value="${escapeHtml(opts.returnUrl)}" />
|
|
61
|
+
<button type="submit" class="cancel">Cancel</button>
|
|
62
|
+
</form>
|
|
63
|
+
<form method="post" action="/payments/mock-checkout/${opts.intentId}/confirm">
|
|
64
|
+
<input type="hidden" name="returnUrl" value="${escapeHtml(opts.returnUrl)}" />
|
|
65
|
+
<button type="submit" class="pay">Pay</button>
|
|
66
|
+
</form>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="meta">intent: ${opts.intentId}</div>
|
|
69
|
+
</div>
|
|
70
|
+
</body>
|
|
71
|
+
</html>`;
|
|
72
|
+
|
|
73
|
+
const appendStatusParam = (returnUrl: string, status: "success" | "cancel") => {
|
|
74
|
+
try {
|
|
75
|
+
const url = new URL(returnUrl, "http://placeholder.local");
|
|
76
|
+
url.searchParams.set("booking", status);
|
|
77
|
+
return returnUrl.startsWith("http")
|
|
78
|
+
? url.toString()
|
|
79
|
+
: url.pathname + url.search + url.hash;
|
|
80
|
+
} catch {
|
|
81
|
+
return returnUrl;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export class MockCheckoutController {
|
|
86
|
+
protected readonly url = "/payments/mock-checkout";
|
|
87
|
+
protected readonly payments = $inject(PaymentService);
|
|
88
|
+
protected readonly provider = $inject(PaymentProvider);
|
|
89
|
+
|
|
90
|
+
protected isMemoryProvider() {
|
|
91
|
+
return this.provider instanceof MemoryPaymentProvider;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected forbidden(reply: ServerReply) {
|
|
95
|
+
reply.headers["content-type"] = "text/html; charset=utf-8";
|
|
96
|
+
reply.status = 403;
|
|
97
|
+
return FORBIDDEN_HTML;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public readonly mockCheckoutPage = $route({
|
|
101
|
+
method: "GET",
|
|
102
|
+
path: `${this.url}/:id`,
|
|
103
|
+
schema: {
|
|
104
|
+
params: t.object({ id: t.uuid() }),
|
|
105
|
+
query: t.object({ returnUrl: t.optional(t.text({ size: "rich" })) }),
|
|
106
|
+
},
|
|
107
|
+
handler: async ({ params, query, reply }) => {
|
|
108
|
+
if (!this.isMemoryProvider()) return this.forbidden(reply);
|
|
109
|
+
const intent = await this.payments.getIntent(params.id);
|
|
110
|
+
reply.headers["content-type"] = "text/html; charset=utf-8";
|
|
111
|
+
return renderPage({
|
|
112
|
+
intentId: intent.id,
|
|
113
|
+
amount: formatAmount(intent.amount, intent.currency),
|
|
114
|
+
returnUrl: query.returnUrl ?? "/",
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
public readonly mockCheckoutConfirm = $route({
|
|
120
|
+
method: "POST",
|
|
121
|
+
path: `${this.url}/:id/confirm`,
|
|
122
|
+
schema: {
|
|
123
|
+
params: t.object({ id: t.uuid() }),
|
|
124
|
+
body: t.object({ returnUrl: t.optional(t.text({ size: "rich" })) }),
|
|
125
|
+
},
|
|
126
|
+
handler: async ({ params, body, reply }) => {
|
|
127
|
+
if (!this.isMemoryProvider()) return this.forbidden(reply);
|
|
128
|
+
await this.payments.handleWebhookEvent(params.id, "captured");
|
|
129
|
+
reply.redirect(appendStatusParam(body.returnUrl ?? "/", "success"), 302);
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
public readonly mockCheckoutCancel = $route({
|
|
134
|
+
method: "POST",
|
|
135
|
+
path: `${this.url}/:id/cancel`,
|
|
136
|
+
schema: {
|
|
137
|
+
params: t.object({ id: t.uuid() }),
|
|
138
|
+
body: t.object({ returnUrl: t.optional(t.text({ size: "rich" })) }),
|
|
139
|
+
},
|
|
140
|
+
handler: async ({ params, body, reply }) => {
|
|
141
|
+
if (!this.isMemoryProvider()) return this.forbidden(reply);
|
|
142
|
+
await this.payments.handleWebhookEvent(params.id, "failed");
|
|
143
|
+
reply.redirect(appendStatusParam(body.returnUrl ?? "/", "cancel"), 302);
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $module } from "alepha";
|
|
2
2
|
import { AdminPaymentController } from "./controllers/AdminPaymentController.ts";
|
|
3
|
+
import { MockCheckoutController } from "./controllers/MockCheckoutController.ts";
|
|
3
4
|
import { PaymentController } from "./controllers/PaymentController.ts";
|
|
4
5
|
import { MemoryPaymentProvider } from "./providers/MemoryPaymentProvider.ts";
|
|
5
6
|
import { PaymentProvider } from "./providers/PaymentProvider.ts";
|
|
@@ -7,6 +8,7 @@ import { PaymentMethodService } from "./services/PaymentMethodService.ts";
|
|
|
7
8
|
import { PaymentService } from "./services/PaymentService.ts";
|
|
8
9
|
|
|
9
10
|
export * from "./controllers/AdminPaymentController.ts";
|
|
11
|
+
export * from "./controllers/MockCheckoutController.ts";
|
|
10
12
|
export * from "./controllers/PaymentController.ts";
|
|
11
13
|
export * from "./entities/paymentIntents.ts";
|
|
12
14
|
export * from "./entities/paymentMethods.ts";
|
|
@@ -67,6 +69,7 @@ export const AlephaApiPayments = $module({
|
|
|
67
69
|
services: [
|
|
68
70
|
AdminPaymentController,
|
|
69
71
|
PaymentController,
|
|
72
|
+
MockCheckoutController,
|
|
70
73
|
PaymentProvider,
|
|
71
74
|
PaymentService,
|
|
72
75
|
PaymentMethodService,
|
|
@@ -29,7 +29,12 @@ export class MemoryPaymentProvider implements PaymentProvider {
|
|
|
29
29
|
|
|
30
30
|
public async createSession(
|
|
31
31
|
intent: PaymentIntentEntity,
|
|
32
|
-
options: {
|
|
32
|
+
options: {
|
|
33
|
+
returnUrl: string;
|
|
34
|
+
authorize?: boolean;
|
|
35
|
+
stripeAccount?: string;
|
|
36
|
+
applicationFeeAmount?: number;
|
|
37
|
+
},
|
|
33
38
|
): Promise<CreateSessionResult> {
|
|
34
39
|
const providerRef = `mem_session_${randomUUID()}`;
|
|
35
40
|
const status = options.authorize ? "authorized" : "captured";
|
|
@@ -39,7 +44,7 @@ export class MemoryPaymentProvider implements PaymentProvider {
|
|
|
39
44
|
status,
|
|
40
45
|
});
|
|
41
46
|
return {
|
|
42
|
-
url: `/payments/mock-checkout/${intent.id}`,
|
|
47
|
+
url: `/payments/mock-checkout/${intent.id}?returnUrl=${encodeURIComponent(options.returnUrl)}`,
|
|
43
48
|
providerRef,
|
|
44
49
|
};
|
|
45
50
|
}
|
|
@@ -88,8 +93,8 @@ export class MemoryPaymentProvider implements PaymentProvider {
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
public async createPaymentMethod(
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
_userId: string,
|
|
97
|
+
_token: string,
|
|
93
98
|
): Promise<CreatePaymentMethodResult> {
|
|
94
99
|
const providerRef = `mem_pm_${randomUUID()}`;
|
|
95
100
|
const result: CreatePaymentMethodResult = {
|