alepha 0.13.0 → 0.13.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/README.md +1 -1
- package/dist/api-files/index.d.ts +28 -91
- package/dist/api-files/index.js +10 -755
- package/dist/api-files/index.js.map +1 -1
- package/dist/api-jobs/index.d.ts +67 -67
- package/dist/api-jobs/index.js +13 -13
- package/dist/api-jobs/index.js.map +1 -1
- package/dist/api-notifications/index.d.ts +129 -146
- package/dist/api-notifications/index.js +17 -39
- package/dist/api-notifications/index.js.map +1 -1
- package/dist/api-parameters/index.d.ts +21 -22
- package/dist/api-parameters/index.js +22 -22
- package/dist/api-parameters/index.js.map +1 -1
- package/dist/api-users/index.d.ts +224 -2001
- package/dist/api-users/index.js +914 -4787
- package/dist/api-users/index.js.map +1 -1
- package/dist/api-verifications/index.d.ts +96 -96
- package/dist/batch/index.d.ts +13 -13
- package/dist/batch/index.js +8 -8
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +14 -14
- package/dist/bucket/index.js +12 -12
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/index.d.ts +11 -11
- package/dist/cache/index.js +9 -9
- package/dist/cache/index.js.map +1 -1
- package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
- package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
- package/dist/cli/index.d.ts +31 -37
- package/dist/cli/index.js +152 -83
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +19 -19
- package/dist/command/index.js +25 -25
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +218 -218
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +232 -232
- package/dist/core/index.js +218 -218
- package/dist/core/index.js.map +1 -1
- package/dist/core/{index.cjs → index.native.js} +304 -455
- package/dist/core/index.native.js.map +1 -0
- package/dist/datetime/index.d.ts +9 -9
- package/dist/datetime/index.js +7 -7
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +16 -16
- package/dist/email/index.js +80 -82
- package/dist/email/index.js.map +1 -1
- package/dist/file/index.js +1 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/index.d.ts +9 -9
- package/dist/lock/index.js +8 -8
- package/dist/lock/index.js.map +1 -1
- package/dist/lock-redis/index.js +3 -66
- package/dist/lock-redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +5 -5
- package/dist/logger/index.js +8 -8
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/index.browser.js +114 -114
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +219 -219
- package/dist/orm/index.js +46 -46
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/index.d.ts +25 -25
- package/dist/queue/index.js +20 -20
- package/dist/queue/index.js.map +1 -1
- package/dist/queue-redis/index.d.ts +2 -2
- package/dist/redis/index.d.ts +10 -10
- package/dist/retry/index.d.ts +20 -20
- package/dist/retry/index.js +9 -9
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +12 -12
- package/dist/scheduler/index.js +9 -9
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +53 -53
- package/dist/security/index.js +32 -32
- package/dist/security/index.js.map +1 -1
- package/dist/server/index.browser.js +1 -1
- package/dist/server/index.browser.js.map +1 -1
- package/dist/server/index.d.ts +101 -101
- package/dist/server/index.js +17 -17
- package/dist/server/index.js.map +1 -1
- package/dist/server-auth/index.browser.js +4 -982
- package/dist/server-auth/index.browser.js.map +1 -1
- package/dist/server-auth/index.d.ts +204 -785
- package/dist/server-auth/index.js +47 -1239
- package/dist/server-auth/index.js.map +1 -1
- package/dist/server-cache/index.d.ts +10 -10
- package/dist/server-cache/index.js +2 -2
- package/dist/server-cache/index.js.map +1 -1
- package/dist/server-compress/index.d.ts +4 -4
- package/dist/server-compress/index.js +1 -1
- package/dist/server-compress/index.js.map +1 -1
- package/dist/server-cookies/index.browser.js +8 -8
- package/dist/server-cookies/index.browser.js.map +1 -1
- package/dist/server-cookies/index.d.ts +17 -17
- package/dist/server-cookies/index.js +10 -10
- package/dist/server-cookies/index.js.map +1 -1
- package/dist/server-cors/index.d.ts +17 -17
- package/dist/server-cors/index.js +9 -9
- package/dist/server-cors/index.js.map +1 -1
- package/dist/server-health/index.d.ts +2 -2
- package/dist/server-helmet/index.d.ts +1 -1
- package/dist/server-links/index.browser.js +12 -12
- package/dist/server-links/index.browser.js.map +1 -1
- package/dist/server-links/index.d.ts +59 -251
- package/dist/server-links/index.js +23 -502
- package/dist/server-links/index.js.map +1 -1
- package/dist/server-metrics/index.d.ts +4 -4
- package/dist/server-metrics/index.js +170 -174
- package/dist/server-metrics/index.js.map +1 -1
- package/dist/server-multipart/index.d.ts +2 -2
- package/dist/server-proxy/index.d.ts +12 -12
- package/dist/server-proxy/index.js +10 -10
- package/dist/server-proxy/index.js.map +1 -1
- package/dist/server-rate-limit/index.d.ts +22 -22
- package/dist/server-rate-limit/index.js +12 -12
- package/dist/server-rate-limit/index.js.map +1 -1
- package/dist/server-security/index.d.ts +24 -24
- package/dist/server-security/index.js +15 -15
- package/dist/server-security/index.js.map +1 -1
- package/dist/server-static/index.d.ts +14 -14
- package/dist/server-static/index.js +8 -8
- package/dist/server-static/index.js.map +1 -1
- package/dist/server-swagger/index.d.ts +25 -184
- package/dist/server-swagger/index.js +21 -724
- package/dist/server-swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +14 -14
- package/dist/sms/index.js +9 -9
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +11 -11
- package/dist/thread/index.js +17 -17
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/index.d.ts +26 -26
- package/dist/topic/index.js +16 -16
- package/dist/topic/index.js.map +1 -1
- package/dist/topic-redis/index.d.ts +1 -1
- package/dist/vite/index.d.ts +3 -3
- package/dist/vite/index.js +12 -13
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +11 -11
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +51 -51
- package/dist/websocket/index.js +13 -13
- package/dist/websocket/index.js.map +1 -1
- package/package.json +62 -52
- package/src/api-files/services/FileService.ts +5 -7
- package/src/api-jobs/index.ts +1 -1
- package/src/api-jobs/{descriptors → primitives}/$job.ts +8 -8
- package/src/api-jobs/providers/JobProvider.ts +9 -9
- package/src/api-jobs/services/JobService.ts +5 -5
- package/src/api-notifications/index.ts +5 -15
- package/src/api-notifications/{descriptors → primitives}/$notification.ts +10 -10
- package/src/api-notifications/services/NotificationSenderService.ts +3 -3
- package/src/api-parameters/index.ts +1 -1
- package/src/api-parameters/{descriptors → primitives}/$config.ts +7 -12
- package/src/api-users/index.ts +1 -1
- package/src/api-users/{descriptors → primitives}/$userRealm.ts +8 -8
- package/src/api-users/providers/UserRealmProvider.ts +1 -1
- package/src/batch/index.ts +3 -3
- package/src/batch/{descriptors → primitives}/$batch.ts +13 -16
- package/src/bucket/index.ts +8 -8
- package/src/bucket/{descriptors → primitives}/$bucket.ts +8 -8
- package/src/bucket/providers/LocalFileStorageProvider.ts +3 -3
- package/src/cache/index.ts +4 -4
- package/src/cache/{descriptors → primitives}/$cache.ts +15 -15
- package/src/cli/apps/AlephaPackageBuilderCli.ts +30 -3
- package/src/cli/assets/appRouterTs.ts +9 -0
- package/src/cli/assets/indexHtml.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +10 -0
- package/src/cli/commands/CoreCommands.ts +6 -5
- package/src/cli/commands/DrizzleCommands.ts +69 -61
- package/src/cli/commands/VerifyCommands.ts +2 -2
- package/src/cli/commands/ViteCommands.ts +6 -1
- package/src/cli/services/ProjectUtils.ts +78 -41
- package/src/command/index.ts +5 -5
- package/src/command/{descriptors → primitives}/$command.ts +9 -12
- package/src/command/providers/CliProvider.ts +10 -10
- package/src/core/Alepha.ts +30 -33
- package/src/core/constants/KIND.ts +1 -1
- package/src/core/constants/OPTIONS.ts +1 -1
- package/src/core/helpers/{descriptor.ts → primitive.ts} +18 -18
- package/src/core/helpers/ref.ts +1 -1
- package/src/core/index.shared.ts +8 -8
- package/src/core/{descriptors → primitives}/$context.ts +5 -5
- package/src/core/{descriptors → primitives}/$hook.ts +4 -4
- package/src/core/{descriptors → primitives}/$inject.ts +2 -2
- package/src/core/{descriptors → primitives}/$module.ts +9 -9
- package/src/core/{descriptors → primitives}/$use.ts +2 -2
- package/src/core/providers/CodecManager.ts +1 -1
- package/src/core/providers/JsonSchemaCodec.ts +1 -1
- package/src/core/providers/StateManager.ts +2 -2
- package/src/datetime/index.ts +3 -3
- package/src/datetime/{descriptors → primitives}/$interval.ts +6 -6
- package/src/email/index.ts +4 -4
- package/src/email/{descriptors → primitives}/$email.ts +8 -8
- package/src/file/index.ts +1 -1
- package/src/lock/index.ts +3 -3
- package/src/lock/{descriptors → primitives}/$lock.ts +10 -10
- package/src/logger/index.ts +8 -8
- package/src/logger/{descriptors → primitives}/$logger.ts +2 -2
- package/src/logger/services/Logger.ts +1 -1
- package/src/orm/constants/PG_SYMBOLS.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.ts +8 -8
- package/src/orm/{descriptors → primitives}/$entity.ts +11 -11
- package/src/orm/{descriptors → primitives}/$repository.ts +2 -2
- package/src/orm/{descriptors → primitives}/$sequence.ts +8 -8
- package/src/orm/{descriptors → primitives}/$transaction.ts +4 -4
- package/src/orm/providers/DrizzleKitProvider.ts +1 -1
- package/src/orm/providers/PostgresTypeProvider.ts +3 -3
- package/src/orm/providers/RepositoryProvider.ts +4 -4
- package/src/orm/providers/drivers/DatabaseProvider.ts +7 -7
- package/src/orm/services/ModelBuilder.ts +9 -9
- package/src/orm/services/PgRelationManager.ts +2 -2
- package/src/orm/services/PostgresModelBuilder.ts +5 -5
- package/src/orm/services/Repository.ts +7 -7
- package/src/orm/services/SqliteModelBuilder.ts +5 -5
- package/src/queue/index.ts +7 -7
- package/src/queue/{descriptors → primitives}/$consumer.ts +15 -15
- package/src/queue/{descriptors → primitives}/$queue.ts +12 -12
- package/src/queue/providers/WorkerProvider.ts +7 -7
- package/src/retry/index.ts +3 -3
- package/src/retry/{descriptors → primitives}/$retry.ts +19 -17
- package/src/scheduler/index.ts +3 -3
- package/src/scheduler/{descriptors → primitives}/$scheduler.ts +9 -9
- package/src/scheduler/providers/CronProvider.ts +1 -1
- package/src/security/index.ts +9 -9
- package/src/security/{descriptors → primitives}/$permission.ts +7 -7
- package/src/security/{descriptors → primitives}/$realm.ts +6 -12
- package/src/security/{descriptors → primitives}/$role.ts +12 -12
- package/src/security/{descriptors → primitives}/$serviceAccount.ts +8 -8
- package/src/server/index.browser.ts +1 -1
- package/src/server/index.ts +14 -14
- package/src/server/{descriptors → primitives}/$action.ts +13 -13
- package/src/server/{descriptors → primitives}/$route.ts +9 -9
- package/src/server/providers/NodeHttpServerProvider.ts +2 -2
- package/src/server/services/HttpClient.ts +1 -1
- package/src/server-auth/index.browser.ts +1 -1
- package/src/server-auth/index.ts +6 -6
- package/src/server-auth/{descriptors → primitives}/$auth.ts +10 -10
- package/src/server-auth/{descriptors → primitives}/$authCredentials.ts +4 -4
- package/src/server-auth/{descriptors → primitives}/$authGithub.ts +4 -4
- package/src/server-auth/{descriptors → primitives}/$authGoogle.ts +4 -4
- package/src/server-auth/providers/ServerAuthProvider.ts +4 -4
- package/src/server-cache/providers/ServerCacheProvider.ts +7 -7
- package/src/server-compress/providers/ServerCompressProvider.ts +3 -3
- package/src/server-cookies/index.browser.ts +2 -2
- package/src/server-cookies/index.ts +5 -5
- package/src/server-cookies/{descriptors → primitives}/$cookie.browser.ts +12 -12
- package/src/server-cookies/{descriptors → primitives}/$cookie.ts +13 -13
- package/src/server-cookies/providers/ServerCookiesProvider.ts +4 -4
- package/src/server-cookies/services/CookieParser.ts +1 -1
- package/src/server-cors/index.ts +3 -3
- package/src/server-cors/{descriptors → primitives}/$cors.ts +11 -13
- package/src/server-cors/providers/ServerCorsProvider.ts +5 -5
- package/src/server-links/index.browser.ts +5 -5
- package/src/server-links/index.ts +9 -9
- package/src/server-links/{descriptors → primitives}/$remote.ts +11 -11
- package/src/server-links/providers/LinkProvider.ts +7 -7
- package/src/server-links/providers/{RemoteDescriptorProvider.ts → RemotePrimitiveProvider.ts} +6 -6
- package/src/server-links/providers/ServerLinksProvider.ts +3 -3
- package/src/server-proxy/index.ts +3 -3
- package/src/server-proxy/{descriptors → primitives}/$proxy.ts +8 -8
- package/src/server-proxy/providers/ServerProxyProvider.ts +4 -4
- package/src/server-rate-limit/index.ts +6 -6
- package/src/server-rate-limit/{descriptors → primitives}/$rateLimit.ts +13 -13
- package/src/server-rate-limit/providers/ServerRateLimitProvider.ts +5 -5
- package/src/server-security/index.ts +3 -3
- package/src/server-security/{descriptors → primitives}/$basicAuth.ts +13 -13
- package/src/server-security/providers/ServerBasicAuthProvider.ts +5 -5
- package/src/server-security/providers/ServerSecurityProvider.ts +4 -4
- package/src/server-static/index.ts +3 -3
- package/src/server-static/{descriptors → primitives}/$serve.ts +8 -10
- package/src/server-static/providers/ServerStaticProvider.ts +6 -6
- package/src/server-swagger/index.ts +5 -5
- package/src/server-swagger/{descriptors → primitives}/$swagger.ts +9 -9
- package/src/server-swagger/providers/ServerSwaggerProvider.ts +11 -10
- package/src/sms/index.ts +4 -4
- package/src/sms/{descriptors → primitives}/$sms.ts +8 -8
- package/src/thread/index.ts +3 -3
- package/src/thread/{descriptors → primitives}/$thread.ts +13 -13
- package/src/thread/providers/ThreadProvider.ts +7 -9
- package/src/topic/index.ts +5 -5
- package/src/topic/{descriptors → primitives}/$subscriber.ts +14 -14
- package/src/topic/{descriptors → primitives}/$topic.ts +10 -10
- package/src/topic/providers/TopicProvider.ts +4 -4
- package/src/vite/helpers/boot.ts +3 -3
- package/src/vite/tasks/copyAssets.ts +1 -1
- package/src/vite/tasks/generateSitemap.ts +3 -3
- package/src/vite/tasks/prerenderPages.ts +2 -2
- package/src/vite/tasks/runAlepha.ts +2 -2
- package/src/websocket/index.browser.ts +3 -3
- package/src/websocket/index.shared.ts +2 -2
- package/src/websocket/index.ts +4 -4
- package/src/websocket/interfaces/WebSocketInterfaces.ts +3 -3
- package/src/websocket/{descriptors → primitives}/$channel.ts +10 -10
- package/src/websocket/{descriptors → primitives}/$websocket.ts +8 -8
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +7 -7
- package/src/websocket/providers/WebSocketServerProvider.ts +3 -3
- package/src/websocket/services/WebSocketClient.ts +5 -5
- package/dist/api-files/index.cjs +0 -1293
- package/dist/api-files/index.cjs.map +0 -1
- package/dist/api-files/index.d.cts +0 -829
- package/dist/api-jobs/index.cjs +0 -274
- package/dist/api-jobs/index.cjs.map +0 -1
- package/dist/api-jobs/index.d.cts +0 -654
- package/dist/api-notifications/index.cjs +0 -380
- package/dist/api-notifications/index.cjs.map +0 -1
- package/dist/api-notifications/index.d.cts +0 -289
- package/dist/api-parameters/index.cjs +0 -66
- package/dist/api-parameters/index.cjs.map +0 -1
- package/dist/api-parameters/index.d.cts +0 -84
- package/dist/api-users/index.cjs +0 -6009
- package/dist/api-users/index.cjs.map +0 -1
- package/dist/api-users/index.d.cts +0 -4740
- package/dist/api-verifications/index.cjs +0 -407
- package/dist/api-verifications/index.cjs.map +0 -1
- package/dist/api-verifications/index.d.cts +0 -207
- package/dist/batch/index.cjs +0 -408
- package/dist/batch/index.cjs.map +0 -1
- package/dist/batch/index.d.cts +0 -330
- package/dist/bin/index.cjs +0 -17
- package/dist/bin/index.cjs.map +0 -1
- package/dist/bin/index.d.cts +0 -1
- package/dist/bucket/index.cjs +0 -303
- package/dist/bucket/index.cjs.map +0 -1
- package/dist/bucket/index.d.cts +0 -355
- package/dist/cache/index.cjs +0 -241
- package/dist/cache/index.cjs.map +0 -1
- package/dist/cache/index.d.cts +0 -202
- package/dist/cache-redis/index.cjs +0 -84
- package/dist/cache-redis/index.cjs.map +0 -1
- package/dist/cache-redis/index.d.cts +0 -40
- package/dist/cli/chunk-DSlc6foC.cjs +0 -43
- package/dist/cli/dist-BBPjuQ56.js +0 -2778
- package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
- package/dist/cli/index.cjs +0 -1241
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -422
- package/dist/command/index.cjs +0 -693
- package/dist/command/index.cjs.map +0 -1
- package/dist/command/index.d.cts +0 -340
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -1927
- package/dist/datetime/index.cjs +0 -318
- package/dist/datetime/index.cjs.map +0 -1
- package/dist/datetime/index.d.cts +0 -145
- package/dist/email/index.cjs +0 -10874
- package/dist/email/index.cjs.map +0 -1
- package/dist/email/index.d.cts +0 -186
- package/dist/fake/index.cjs +0 -34641
- package/dist/fake/index.cjs.map +0 -1
- package/dist/fake/index.d.cts +0 -74
- package/dist/file/index.cjs +0 -1212
- package/dist/file/index.cjs.map +0 -1
- package/dist/file/index.d.cts +0 -698
- package/dist/lock/index.cjs +0 -226
- package/dist/lock/index.cjs.map +0 -1
- package/dist/lock/index.d.cts +0 -361
- package/dist/lock-redis/index.cjs +0 -113
- package/dist/lock-redis/index.cjs.map +0 -1
- package/dist/lock-redis/index.d.cts +0 -24
- package/dist/logger/index.cjs +0 -521
- package/dist/logger/index.cjs.map +0 -1
- package/dist/logger/index.d.cts +0 -281
- package/dist/orm/index.cjs +0 -2986
- package/dist/orm/index.cjs.map +0 -1
- package/dist/orm/index.d.cts +0 -2213
- package/dist/queue/index.cjs +0 -1044
- package/dist/queue/index.cjs.map +0 -1
- package/dist/queue/index.d.cts +0 -1265
- package/dist/queue-redis/index.cjs +0 -873
- package/dist/queue-redis/index.cjs.map +0 -1
- package/dist/queue-redis/index.d.cts +0 -82
- package/dist/redis/index.cjs +0 -153
- package/dist/redis/index.cjs.map +0 -1
- package/dist/redis/index.d.cts +0 -82
- package/dist/retry/index.cjs +0 -146
- package/dist/retry/index.cjs.map +0 -1
- package/dist/retry/index.d.cts +0 -172
- package/dist/router/index.cjs +0 -111
- package/dist/router/index.cjs.map +0 -1
- package/dist/router/index.d.cts +0 -46
- package/dist/scheduler/index.cjs +0 -576
- package/dist/scheduler/index.cjs.map +0 -1
- package/dist/scheduler/index.d.cts +0 -145
- package/dist/security/index.cjs +0 -2402
- package/dist/security/index.cjs.map +0 -1
- package/dist/security/index.d.cts +0 -598
- package/dist/server/index.cjs +0 -1680
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.d.cts +0 -810
- package/dist/server-auth/index.cjs +0 -3146
- package/dist/server-auth/index.cjs.map +0 -1
- package/dist/server-auth/index.d.cts +0 -1164
- package/dist/server-cache/index.cjs +0 -252
- package/dist/server-cache/index.cjs.map +0 -1
- package/dist/server-cache/index.d.cts +0 -164
- package/dist/server-compress/index.cjs +0 -141
- package/dist/server-compress/index.cjs.map +0 -1
- package/dist/server-compress/index.d.cts +0 -38
- package/dist/server-cookies/index.cjs +0 -234
- package/dist/server-cookies/index.cjs.map +0 -1
- package/dist/server-cookies/index.d.cts +0 -144
- package/dist/server-cors/index.cjs +0 -201
- package/dist/server-cors/index.cjs.map +0 -1
- package/dist/server-cors/index.d.cts +0 -140
- package/dist/server-health/index.cjs +0 -62
- package/dist/server-health/index.cjs.map +0 -1
- package/dist/server-health/index.d.cts +0 -58
- package/dist/server-helmet/index.cjs +0 -131
- package/dist/server-helmet/index.cjs.map +0 -1
- package/dist/server-helmet/index.d.cts +0 -97
- package/dist/server-links/index.cjs +0 -992
- package/dist/server-links/index.cjs.map +0 -1
- package/dist/server-links/index.d.cts +0 -513
- package/dist/server-metrics/index.cjs +0 -4535
- package/dist/server-metrics/index.cjs.map +0 -1
- package/dist/server-metrics/index.d.cts +0 -35
- package/dist/server-multipart/index.cjs +0 -237
- package/dist/server-multipart/index.cjs.map +0 -1
- package/dist/server-multipart/index.d.cts +0 -50
- package/dist/server-proxy/index.cjs +0 -186
- package/dist/server-proxy/index.cjs.map +0 -1
- package/dist/server-proxy/index.d.cts +0 -234
- package/dist/server-rate-limit/index.cjs +0 -241
- package/dist/server-rate-limit/index.cjs.map +0 -1
- package/dist/server-rate-limit/index.d.cts +0 -183
- package/dist/server-security/index.cjs +0 -316
- package/dist/server-security/index.cjs.map +0 -1
- package/dist/server-security/index.d.cts +0 -173
- package/dist/server-static/index.cjs +0 -170
- package/dist/server-static/index.cjs.map +0 -1
- package/dist/server-static/index.d.cts +0 -121
- package/dist/server-swagger/index.cjs +0 -1021
- package/dist/server-swagger/index.cjs.map +0 -1
- package/dist/server-swagger/index.d.cts +0 -382
- package/dist/sms/index.cjs +0 -221
- package/dist/sms/index.cjs.map +0 -1
- package/dist/sms/index.d.cts +0 -130
- package/dist/thread/index.cjs +0 -350
- package/dist/thread/index.cjs.map +0 -1
- package/dist/thread/index.d.cts +0 -260
- package/dist/topic/index.cjs +0 -282
- package/dist/topic/index.cjs.map +0 -1
- package/dist/topic/index.d.cts +0 -523
- package/dist/topic-redis/index.cjs +0 -71
- package/dist/topic-redis/index.cjs.map +0 -1
- package/dist/topic-redis/index.d.cts +0 -42
- package/dist/vite/index.cjs +0 -1077
- package/dist/vite/index.cjs.map +0 -1
- package/dist/vite/index.d.cts +0 -542
- package/dist/websocket/index.cjs +0 -1117
- package/dist/websocket/index.cjs.map +0 -1
- package/dist/websocket/index.d.cts +0 -861
- package/src/api-notifications/providers/MemorySmsProvider.ts +0 -20
- package/src/api-notifications/providers/SmsProvider.ts +0 -8
- /package/src/core/{descriptors → primitives}/$atom.ts +0 -0
- /package/src/core/{descriptors → primitives}/$env.ts +0 -0
- /package/src/server-auth/{descriptors → primitives}/$authApple.ts +0 -0
- /package/src/server-links/{descriptors → primitives}/$client.ts +0 -0
package/dist/queue/index.cjs
DELETED
|
@@ -1,1044 +0,0 @@
|
|
|
1
|
-
let alepha = require("alepha");
|
|
2
|
-
let alepha_logger = require("alepha/logger");
|
|
3
|
-
let alepha_datetime = require("alepha/datetime");
|
|
4
|
-
|
|
5
|
-
//#region src/queue/descriptors/$consumer.ts
|
|
6
|
-
/**
|
|
7
|
-
* Creates a consumer descriptor to process messages from a specific queue.
|
|
8
|
-
*
|
|
9
|
-
* Provides a dedicated message consumer that connects to a queue and processes messages
|
|
10
|
-
* with custom handler logic, enabling scalable architectures where multiple consumers
|
|
11
|
-
* can process messages from the same queue.
|
|
12
|
-
*
|
|
13
|
-
* **Key Features**
|
|
14
|
-
* - Seamless integration with any $queue descriptor
|
|
15
|
-
* - Full type safety inherited from queue schema
|
|
16
|
-
* - Automatic worker management for background processing
|
|
17
|
-
* - Built-in error handling and retry mechanisms
|
|
18
|
-
* - Support for multiple consumers per queue for horizontal scaling
|
|
19
|
-
*
|
|
20
|
-
* **Common Use Cases**
|
|
21
|
-
* - Email sending and notification services
|
|
22
|
-
* - Image and media processing workers
|
|
23
|
-
* - Data synchronization and background jobs
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* class EmailService {
|
|
28
|
-
* emailQueue = $queue({
|
|
29
|
-
* name: "emails",
|
|
30
|
-
* schema: t.object({
|
|
31
|
-
* to: t.text(),
|
|
32
|
-
* subject: t.text(),
|
|
33
|
-
* body: t.text()
|
|
34
|
-
* })
|
|
35
|
-
* });
|
|
36
|
-
*
|
|
37
|
-
* emailConsumer = $consumer({
|
|
38
|
-
* queue: this.emailQueue,
|
|
39
|
-
* handler: async (message) => {
|
|
40
|
-
* const { to, subject, body } = message.payload;
|
|
41
|
-
* await this.sendEmail(to, subject, body);
|
|
42
|
-
* }
|
|
43
|
-
* });
|
|
44
|
-
*
|
|
45
|
-
* async sendWelcomeEmail(userEmail: string) {
|
|
46
|
-
* await this.emailQueue.push({
|
|
47
|
-
* to: userEmail,
|
|
48
|
-
* subject: "Welcome!",
|
|
49
|
-
* body: "Thanks for joining."
|
|
50
|
-
* });
|
|
51
|
-
* }
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
const $consumer = (options) => {
|
|
56
|
-
return (0, alepha.createDescriptor)(ConsumerDescriptor, options);
|
|
57
|
-
};
|
|
58
|
-
var ConsumerDescriptor = class extends alepha.Descriptor {};
|
|
59
|
-
$consumer[alepha.KIND] = ConsumerDescriptor;
|
|
60
|
-
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/queue/providers/QueueProvider.ts
|
|
63
|
-
/**
|
|
64
|
-
* Queue provider interface supporting both simple message-based and advanced job-based operations.
|
|
65
|
-
*
|
|
66
|
-
* The simple API (push/pop/popBlocking) is for basic fire-and-forget messaging.
|
|
67
|
-
* The job API provides crash recovery, retries, delayed jobs, priorities, and job history.
|
|
68
|
-
*/
|
|
69
|
-
var QueueProvider = class {
|
|
70
|
-
eventHandlers = /* @__PURE__ */ new Map();
|
|
71
|
-
on(event, handler) {
|
|
72
|
-
if (!this.eventHandlers.has(event)) this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
73
|
-
this.eventHandlers.get(event).add(handler);
|
|
74
|
-
return () => {
|
|
75
|
-
this.eventHandlers.get(event)?.delete(handler);
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Emit a queue event to all registered handlers.
|
|
80
|
-
*
|
|
81
|
-
* @param event The event to emit.
|
|
82
|
-
*/
|
|
83
|
-
async emit(event) {
|
|
84
|
-
const handlers = [...this.eventHandlers.get(event.type) ?? [], ...this.eventHandlers.get("*") ?? []];
|
|
85
|
-
await Promise.all(handlers.map((handler) => handler(event)));
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
//#endregion
|
|
90
|
-
//#region src/queue/providers/MemoryQueueProvider.ts
|
|
91
|
-
const DEFAULT_MAX_ATTEMPTS = 1;
|
|
92
|
-
const DEFAULT_LOCK_DURATION = 3e4;
|
|
93
|
-
const DEFAULT_BACKOFF_DELAY = 1e3;
|
|
94
|
-
const DEFAULT_BACKOFF_MAX_DELAY = 3e4;
|
|
95
|
-
/**
|
|
96
|
-
* In-memory queue provider with full job support.
|
|
97
|
-
*
|
|
98
|
-
* This provider stores all data in memory and is suitable for:
|
|
99
|
-
* - Development and testing
|
|
100
|
-
* - Single-instance applications
|
|
101
|
-
* - Scenarios where job persistence across restarts is not required
|
|
102
|
-
*/
|
|
103
|
-
var MemoryQueueProvider = class extends QueueProvider {
|
|
104
|
-
log = (0, alepha_logger.$logger)();
|
|
105
|
-
messageQueues = {};
|
|
106
|
-
messageWaiters = /* @__PURE__ */ new Set();
|
|
107
|
-
jobs = /* @__PURE__ */ new Map();
|
|
108
|
-
waiting = /* @__PURE__ */ new Map();
|
|
109
|
-
delayed = /* @__PURE__ */ new Map();
|
|
110
|
-
active = /* @__PURE__ */ new Map();
|
|
111
|
-
completed = /* @__PURE__ */ new Map();
|
|
112
|
-
failed = /* @__PURE__ */ new Map();
|
|
113
|
-
jobWaiters = /* @__PURE__ */ new Set();
|
|
114
|
-
jobIdCounter = 0;
|
|
115
|
-
async push(queue, ...messages) {
|
|
116
|
-
if (this.messageQueues[queue] == null) this.messageQueues[queue] = [];
|
|
117
|
-
for (const message of messages) {
|
|
118
|
-
const waiter = this.findMessageWaiter(queue);
|
|
119
|
-
if (waiter) {
|
|
120
|
-
this.removeMessageWaiter(waiter);
|
|
121
|
-
waiter.resolve({
|
|
122
|
-
queue,
|
|
123
|
-
message
|
|
124
|
-
});
|
|
125
|
-
} else this.messageQueues[queue].push(message);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async pop(queue) {
|
|
129
|
-
return this.messageQueues[queue]?.shift();
|
|
130
|
-
}
|
|
131
|
-
async popBlocking(queues, timeoutSeconds) {
|
|
132
|
-
for (const queue of queues) {
|
|
133
|
-
const message = this.messageQueues[queue]?.shift();
|
|
134
|
-
if (message) return {
|
|
135
|
-
queue,
|
|
136
|
-
message
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return new Promise((resolve) => {
|
|
140
|
-
const timer = setTimeout(() => {
|
|
141
|
-
this.removeMessageWaiter(waiter);
|
|
142
|
-
resolve(void 0);
|
|
143
|
-
}, timeoutSeconds * 1e3);
|
|
144
|
-
const waiter = {
|
|
145
|
-
queues: new Set(queues),
|
|
146
|
-
resolve: (result) => {
|
|
147
|
-
clearTimeout(timer);
|
|
148
|
-
resolve(result);
|
|
149
|
-
},
|
|
150
|
-
timer
|
|
151
|
-
};
|
|
152
|
-
this.messageWaiters.add(waiter);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
findMessageWaiter(queue) {
|
|
156
|
-
for (const waiter of this.messageWaiters) if (waiter.queues.has(queue)) return waiter;
|
|
157
|
-
}
|
|
158
|
-
removeMessageWaiter(waiter) {
|
|
159
|
-
clearTimeout(waiter.timer);
|
|
160
|
-
this.messageWaiters.delete(waiter);
|
|
161
|
-
}
|
|
162
|
-
generateJobId() {
|
|
163
|
-
return `job_${++this.jobIdCounter}_${Date.now()}`;
|
|
164
|
-
}
|
|
165
|
-
ensureQueueStructures(queue) {
|
|
166
|
-
if (!this.jobs.has(queue)) {
|
|
167
|
-
this.jobs.set(queue, /* @__PURE__ */ new Map());
|
|
168
|
-
this.waiting.set(queue, []);
|
|
169
|
-
this.delayed.set(queue, []);
|
|
170
|
-
this.active.set(queue, /* @__PURE__ */ new Set());
|
|
171
|
-
this.completed.set(queue, []);
|
|
172
|
-
this.failed.set(queue, []);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async addJob(queue, payload, options) {
|
|
176
|
-
this.ensureQueueStructures(queue);
|
|
177
|
-
const now = Date.now();
|
|
178
|
-
const delay = options?.delay ?? 0;
|
|
179
|
-
const isDelayed = delay > 0;
|
|
180
|
-
const job = {
|
|
181
|
-
id: this.generateJobId(),
|
|
182
|
-
queue,
|
|
183
|
-
payload,
|
|
184
|
-
options: {
|
|
185
|
-
priority: options?.priority ?? 0,
|
|
186
|
-
delay: options?.delay ?? 0,
|
|
187
|
-
maxAttempts: options?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
|
|
188
|
-
backoff: options?.backoff,
|
|
189
|
-
lockDuration: options?.lockDuration ?? DEFAULT_LOCK_DURATION,
|
|
190
|
-
removeOnComplete: options?.removeOnComplete,
|
|
191
|
-
removeOnFail: options?.removeOnFail
|
|
192
|
-
},
|
|
193
|
-
state: {
|
|
194
|
-
status: isDelayed ? "delayed" : "waiting",
|
|
195
|
-
attempts: 0,
|
|
196
|
-
createdAt: now,
|
|
197
|
-
availableAt: isDelayed ? now + delay : now
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
this.jobs.get(queue).set(job.id, job);
|
|
201
|
-
if (isDelayed) this.insertDelayed(queue, job);
|
|
202
|
-
else {
|
|
203
|
-
this.insertWaiting(queue, job);
|
|
204
|
-
this.notifyJobWaiters(queue);
|
|
205
|
-
}
|
|
206
|
-
this.log.debug(`Added job ${job.id} to queue ${queue}`, {
|
|
207
|
-
status: job.state.status,
|
|
208
|
-
priority: job.options.priority
|
|
209
|
-
});
|
|
210
|
-
if (!isDelayed) await this.emit({
|
|
211
|
-
type: "waiting",
|
|
212
|
-
queue,
|
|
213
|
-
jobId: job.id,
|
|
214
|
-
timestamp: now,
|
|
215
|
-
job
|
|
216
|
-
});
|
|
217
|
-
return job;
|
|
218
|
-
}
|
|
219
|
-
insertWaiting(queue, job) {
|
|
220
|
-
const waitingList = this.waiting.get(queue);
|
|
221
|
-
const priority = job.options.priority ?? 0;
|
|
222
|
-
let insertIndex = waitingList.length;
|
|
223
|
-
for (let i = 0; i < waitingList.length; i++) {
|
|
224
|
-
const existingJob = this.jobs.get(queue).get(waitingList[i]);
|
|
225
|
-
if (existingJob && (existingJob.options.priority ?? 0) > priority) {
|
|
226
|
-
insertIndex = i;
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
waitingList.splice(insertIndex, 0, job.id);
|
|
231
|
-
}
|
|
232
|
-
insertDelayed(queue, job) {
|
|
233
|
-
const delayedList = this.delayed.get(queue);
|
|
234
|
-
const availableAt = job.state.availableAt ?? 0;
|
|
235
|
-
let insertIndex = delayedList.length;
|
|
236
|
-
for (let i = 0; i < delayedList.length; i++) {
|
|
237
|
-
const existingJob = this.jobs.get(queue).get(delayedList[i]);
|
|
238
|
-
if (existingJob && (existingJob.state.availableAt ?? 0) > availableAt) {
|
|
239
|
-
insertIndex = i;
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
delayedList.splice(insertIndex, 0, job.id);
|
|
244
|
-
}
|
|
245
|
-
notifyJobWaiters(queue) {
|
|
246
|
-
for (const waiter of this.jobWaiters) if (waiter.queues.has(queue)) {
|
|
247
|
-
const result = this.tryAcquireJob(Array.from(waiter.queues), waiter.workerId);
|
|
248
|
-
if (result) {
|
|
249
|
-
this.removeJobWaiter(waiter);
|
|
250
|
-
waiter.resolve(result);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
removeJobWaiter(waiter) {
|
|
256
|
-
clearTimeout(waiter.timer);
|
|
257
|
-
this.jobWaiters.delete(waiter);
|
|
258
|
-
}
|
|
259
|
-
tryAcquireJob(queues, workerId) {
|
|
260
|
-
const now = Date.now();
|
|
261
|
-
for (const queue of queues) {
|
|
262
|
-
const waitingList = this.waiting.get(queue);
|
|
263
|
-
if (!waitingList || waitingList.length === 0) continue;
|
|
264
|
-
const jobId = waitingList.shift();
|
|
265
|
-
const job = this.jobs.get(queue).get(jobId);
|
|
266
|
-
if (!job) continue;
|
|
267
|
-
job.state.status = "active";
|
|
268
|
-
job.state.attempts += 1;
|
|
269
|
-
job.state.lockedBy = workerId;
|
|
270
|
-
job.state.lockedUntil = now + (job.options.lockDuration ?? DEFAULT_LOCK_DURATION);
|
|
271
|
-
job.state.processedAt = now;
|
|
272
|
-
this.active.get(queue).add(jobId);
|
|
273
|
-
this.log.debug(`Worker ${workerId} acquired job ${jobId}`, {
|
|
274
|
-
queue,
|
|
275
|
-
attempt: job.state.attempts
|
|
276
|
-
});
|
|
277
|
-
this.emit({
|
|
278
|
-
type: "active",
|
|
279
|
-
queue,
|
|
280
|
-
jobId,
|
|
281
|
-
timestamp: now,
|
|
282
|
-
workerId,
|
|
283
|
-
attempt: job.state.attempts
|
|
284
|
-
});
|
|
285
|
-
return {
|
|
286
|
-
queue,
|
|
287
|
-
job
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
async acquireJob(queues, workerId, timeoutSeconds) {
|
|
292
|
-
for (const queue of queues) this.ensureQueueStructures(queue);
|
|
293
|
-
const result = this.tryAcquireJob(queues, workerId);
|
|
294
|
-
if (result) return result;
|
|
295
|
-
return new Promise((resolve) => {
|
|
296
|
-
const timer = setTimeout(() => {
|
|
297
|
-
this.removeJobWaiter(waiter);
|
|
298
|
-
resolve(void 0);
|
|
299
|
-
}, timeoutSeconds * 1e3);
|
|
300
|
-
const waiter = {
|
|
301
|
-
queues: new Set(queues),
|
|
302
|
-
workerId,
|
|
303
|
-
resolve: (result$1) => {
|
|
304
|
-
clearTimeout(timer);
|
|
305
|
-
resolve(result$1);
|
|
306
|
-
},
|
|
307
|
-
timer
|
|
308
|
-
};
|
|
309
|
-
this.jobWaiters.add(waiter);
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
async completeJob(queue, jobId, result) {
|
|
313
|
-
const job = this.jobs.get(queue)?.get(jobId);
|
|
314
|
-
if (!job) {
|
|
315
|
-
this.log.warn(`Attempted to complete unknown job ${jobId}`);
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
const now = Date.now();
|
|
319
|
-
const duration = now - (job.state.processedAt ?? now);
|
|
320
|
-
this.active.get(queue)?.delete(jobId);
|
|
321
|
-
job.state.status = "completed";
|
|
322
|
-
job.state.completedAt = now;
|
|
323
|
-
job.state.result = result;
|
|
324
|
-
job.state.lockedBy = void 0;
|
|
325
|
-
job.state.lockedUntil = void 0;
|
|
326
|
-
const removeOnComplete = job.options.removeOnComplete;
|
|
327
|
-
if (removeOnComplete === true) {
|
|
328
|
-
this.jobs.get(queue)?.delete(jobId);
|
|
329
|
-
this.log.debug(`Job ${jobId} completed and removed`, {
|
|
330
|
-
queue,
|
|
331
|
-
result
|
|
332
|
-
});
|
|
333
|
-
} else {
|
|
334
|
-
this.completed.get(queue).unshift(jobId);
|
|
335
|
-
if (typeof removeOnComplete === "number" && removeOnComplete >= 0) await this.cleanJobs(queue, "completed", { maxCount: removeOnComplete });
|
|
336
|
-
this.log.debug(`Job ${jobId} completed`, {
|
|
337
|
-
queue,
|
|
338
|
-
result
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
await this.emit({
|
|
342
|
-
type: "completed",
|
|
343
|
-
queue,
|
|
344
|
-
jobId,
|
|
345
|
-
timestamp: now,
|
|
346
|
-
result,
|
|
347
|
-
duration
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
async failJob(queue, jobId, error, stackTrace) {
|
|
351
|
-
const job = this.jobs.get(queue)?.get(jobId);
|
|
352
|
-
if (!job) {
|
|
353
|
-
this.log.warn(`Attempted to fail unknown job ${jobId}`);
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
const now = Date.now();
|
|
357
|
-
this.active.get(queue)?.delete(jobId);
|
|
358
|
-
const maxAttempts = job.options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
359
|
-
if (job.state.attempts < maxAttempts) {
|
|
360
|
-
const backoffDelay = this.calculateBackoff(job);
|
|
361
|
-
job.state.status = "delayed";
|
|
362
|
-
job.state.availableAt = now + backoffDelay;
|
|
363
|
-
job.state.error = error;
|
|
364
|
-
job.state.stackTrace = stackTrace;
|
|
365
|
-
job.state.lockedBy = void 0;
|
|
366
|
-
job.state.lockedUntil = void 0;
|
|
367
|
-
this.insertDelayed(queue, job);
|
|
368
|
-
this.log.debug(`Job ${jobId} failed, will retry in ${backoffDelay}ms`, {
|
|
369
|
-
queue,
|
|
370
|
-
attempt: job.state.attempts,
|
|
371
|
-
maxAttempts,
|
|
372
|
-
error
|
|
373
|
-
});
|
|
374
|
-
await this.emit({
|
|
375
|
-
type: "retrying",
|
|
376
|
-
queue,
|
|
377
|
-
jobId,
|
|
378
|
-
timestamp: now,
|
|
379
|
-
error,
|
|
380
|
-
attempt: job.state.attempts + 1,
|
|
381
|
-
delay: backoffDelay
|
|
382
|
-
});
|
|
383
|
-
} else {
|
|
384
|
-
job.state.status = "failed";
|
|
385
|
-
job.state.failedAt = now;
|
|
386
|
-
job.state.error = error;
|
|
387
|
-
job.state.stackTrace = stackTrace;
|
|
388
|
-
job.state.lockedBy = void 0;
|
|
389
|
-
job.state.lockedUntil = void 0;
|
|
390
|
-
const removeOnFail = job.options.removeOnFail;
|
|
391
|
-
if (removeOnFail === true) {
|
|
392
|
-
this.jobs.get(queue)?.delete(jobId);
|
|
393
|
-
this.log.debug(`Job ${jobId} permanently failed and removed after ${job.state.attempts} attempts`, {
|
|
394
|
-
queue,
|
|
395
|
-
error
|
|
396
|
-
});
|
|
397
|
-
} else {
|
|
398
|
-
this.failed.get(queue).unshift(jobId);
|
|
399
|
-
if (typeof removeOnFail === "number" && removeOnFail >= 0) await this.cleanJobs(queue, "failed", { maxCount: removeOnFail });
|
|
400
|
-
this.log.debug(`Job ${jobId} permanently failed after ${job.state.attempts} attempts`, {
|
|
401
|
-
queue,
|
|
402
|
-
error
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
await this.emit({
|
|
406
|
-
type: "failed",
|
|
407
|
-
queue,
|
|
408
|
-
jobId,
|
|
409
|
-
timestamp: now,
|
|
410
|
-
error,
|
|
411
|
-
stackTrace,
|
|
412
|
-
attempts: job.state.attempts
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
calculateBackoff(job) {
|
|
417
|
-
const backoff = job.options.backoff;
|
|
418
|
-
const attempt = job.state.attempts;
|
|
419
|
-
if (!backoff) return DEFAULT_BACKOFF_DELAY;
|
|
420
|
-
const baseDelay = backoff.delay ?? DEFAULT_BACKOFF_DELAY;
|
|
421
|
-
const maxDelay = backoff.maxDelay ?? DEFAULT_BACKOFF_MAX_DELAY;
|
|
422
|
-
if (backoff.type === "fixed") return baseDelay;
|
|
423
|
-
const exponentialDelay = baseDelay * 2 ** (attempt - 1);
|
|
424
|
-
return Math.min(exponentialDelay, maxDelay);
|
|
425
|
-
}
|
|
426
|
-
async renewJobLock(queue, jobId, workerId) {
|
|
427
|
-
const job = this.jobs.get(queue)?.get(jobId);
|
|
428
|
-
if (!job || job.state.lockedBy !== workerId) return false;
|
|
429
|
-
job.state.lockedUntil = Date.now() + (job.options.lockDuration ?? DEFAULT_LOCK_DURATION);
|
|
430
|
-
return true;
|
|
431
|
-
}
|
|
432
|
-
async getJob(queue, jobId) {
|
|
433
|
-
return this.jobs.get(queue)?.get(jobId);
|
|
434
|
-
}
|
|
435
|
-
async getJobs(queue, status, options) {
|
|
436
|
-
const limit = options?.limit ?? 100;
|
|
437
|
-
const offset = options?.offset ?? 0;
|
|
438
|
-
let jobIds;
|
|
439
|
-
switch (status) {
|
|
440
|
-
case "waiting":
|
|
441
|
-
jobIds = this.waiting.get(queue) ?? [];
|
|
442
|
-
break;
|
|
443
|
-
case "delayed":
|
|
444
|
-
jobIds = this.delayed.get(queue) ?? [];
|
|
445
|
-
break;
|
|
446
|
-
case "active":
|
|
447
|
-
jobIds = Array.from(this.active.get(queue) ?? []);
|
|
448
|
-
break;
|
|
449
|
-
case "completed":
|
|
450
|
-
jobIds = this.completed.get(queue) ?? [];
|
|
451
|
-
break;
|
|
452
|
-
case "failed":
|
|
453
|
-
jobIds = this.failed.get(queue) ?? [];
|
|
454
|
-
break;
|
|
455
|
-
default: jobIds = [];
|
|
456
|
-
}
|
|
457
|
-
const jobsMap = this.jobs.get(queue);
|
|
458
|
-
if (!jobsMap) return [];
|
|
459
|
-
return jobIds.slice(offset, offset + limit).map((id) => jobsMap.get(id)).filter((job) => job !== void 0);
|
|
460
|
-
}
|
|
461
|
-
async getJobCounts(queue) {
|
|
462
|
-
return {
|
|
463
|
-
waiting: this.waiting.get(queue)?.length ?? 0,
|
|
464
|
-
delayed: this.delayed.get(queue)?.length ?? 0,
|
|
465
|
-
active: this.active.get(queue)?.size ?? 0,
|
|
466
|
-
completed: this.completed.get(queue)?.length ?? 0,
|
|
467
|
-
failed: this.failed.get(queue)?.length ?? 0
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
async promoteDelayedJobs(queue) {
|
|
471
|
-
const delayedList = this.delayed.get(queue);
|
|
472
|
-
if (!delayedList || delayedList.length === 0) return 0;
|
|
473
|
-
const now = Date.now();
|
|
474
|
-
let promoted = 0;
|
|
475
|
-
while (delayedList.length > 0) {
|
|
476
|
-
const jobId = delayedList[0];
|
|
477
|
-
const job = this.jobs.get(queue)?.get(jobId);
|
|
478
|
-
if (!job || (job.state.availableAt ?? 0) > now) break;
|
|
479
|
-
delayedList.shift();
|
|
480
|
-
job.state.status = "waiting";
|
|
481
|
-
this.insertWaiting(queue, job);
|
|
482
|
-
promoted++;
|
|
483
|
-
this.log.debug(`Promoted delayed job ${jobId}`, { queue });
|
|
484
|
-
this.emit({
|
|
485
|
-
type: "waiting",
|
|
486
|
-
queue,
|
|
487
|
-
jobId,
|
|
488
|
-
timestamp: now,
|
|
489
|
-
job
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
if (promoted > 0) this.notifyJobWaiters(queue);
|
|
493
|
-
return promoted;
|
|
494
|
-
}
|
|
495
|
-
async recoverStalledJobs(queue, stalledThresholdMs) {
|
|
496
|
-
const activeSet = this.active.get(queue);
|
|
497
|
-
if (!activeSet || activeSet.size === 0) return [];
|
|
498
|
-
const now = Date.now();
|
|
499
|
-
const stalledJobIds = [];
|
|
500
|
-
for (const jobId of activeSet) {
|
|
501
|
-
const job = this.jobs.get(queue)?.get(jobId);
|
|
502
|
-
if (!job) continue;
|
|
503
|
-
if ((job.state.lockedUntil ?? 0) + stalledThresholdMs < now) stalledJobIds.push(jobId);
|
|
504
|
-
}
|
|
505
|
-
for (const jobId of stalledJobIds) {
|
|
506
|
-
const job = this.jobs.get(queue).get(jobId);
|
|
507
|
-
const workerId = job.state.lockedBy;
|
|
508
|
-
activeSet.delete(jobId);
|
|
509
|
-
const maxAttempts = job.options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
510
|
-
const hasMoreAttempts = job.state.attempts < maxAttempts;
|
|
511
|
-
await this.emit({
|
|
512
|
-
type: "stalled",
|
|
513
|
-
queue,
|
|
514
|
-
jobId,
|
|
515
|
-
timestamp: now,
|
|
516
|
-
workerId,
|
|
517
|
-
willRetry: hasMoreAttempts
|
|
518
|
-
});
|
|
519
|
-
if (hasMoreAttempts) {
|
|
520
|
-
job.state.status = "waiting";
|
|
521
|
-
job.state.lockedBy = void 0;
|
|
522
|
-
job.state.lockedUntil = void 0;
|
|
523
|
-
job.state.error = "Job stalled (worker timeout)";
|
|
524
|
-
this.insertWaiting(queue, job);
|
|
525
|
-
this.log.warn(`Recovered stalled job ${jobId}, returning to waiting`, {
|
|
526
|
-
queue,
|
|
527
|
-
attempt: job.state.attempts
|
|
528
|
-
});
|
|
529
|
-
await this.emit({
|
|
530
|
-
type: "waiting",
|
|
531
|
-
queue,
|
|
532
|
-
jobId,
|
|
533
|
-
timestamp: now,
|
|
534
|
-
job
|
|
535
|
-
});
|
|
536
|
-
} else {
|
|
537
|
-
job.state.status = "failed";
|
|
538
|
-
job.state.failedAt = now;
|
|
539
|
-
job.state.lockedBy = void 0;
|
|
540
|
-
job.state.lockedUntil = void 0;
|
|
541
|
-
job.state.error = "Job stalled (worker timeout) - max attempts exceeded";
|
|
542
|
-
const removeOnFail = job.options.removeOnFail;
|
|
543
|
-
if (removeOnFail === true) this.jobs.get(queue)?.delete(jobId);
|
|
544
|
-
else {
|
|
545
|
-
this.failed.get(queue).unshift(jobId);
|
|
546
|
-
if (typeof removeOnFail === "number" && removeOnFail > 0) await this.cleanJobs(queue, "failed", { maxCount: removeOnFail });
|
|
547
|
-
}
|
|
548
|
-
this.log.warn(`Stalled job ${jobId} permanently failed`, {
|
|
549
|
-
queue,
|
|
550
|
-
attempts: job.state.attempts
|
|
551
|
-
});
|
|
552
|
-
await this.emit({
|
|
553
|
-
type: "failed",
|
|
554
|
-
queue,
|
|
555
|
-
jobId,
|
|
556
|
-
timestamp: now,
|
|
557
|
-
error: job.state.error,
|
|
558
|
-
attempts: job.state.attempts
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
if (stalledJobIds.length > 0) this.notifyJobWaiters(queue);
|
|
563
|
-
return stalledJobIds;
|
|
564
|
-
}
|
|
565
|
-
async cleanJobs(queue, status, options) {
|
|
566
|
-
const jobsList = status === "completed" ? this.completed.get(queue) : this.failed.get(queue);
|
|
567
|
-
if (!jobsList || jobsList.length === 0) return 0;
|
|
568
|
-
const jobsMap = this.jobs.get(queue);
|
|
569
|
-
if (!jobsMap) return 0;
|
|
570
|
-
const now = Date.now();
|
|
571
|
-
const maxAge = options?.maxAge;
|
|
572
|
-
const maxCount = options?.maxCount;
|
|
573
|
-
let removed = 0;
|
|
574
|
-
if (maxAge !== void 0) {
|
|
575
|
-
const cutoff = now - maxAge;
|
|
576
|
-
const toRemove = [];
|
|
577
|
-
for (const jobId of jobsList) {
|
|
578
|
-
const job = jobsMap.get(jobId);
|
|
579
|
-
if (job) {
|
|
580
|
-
const timestamp = status === "completed" ? job.state.completedAt : job.state.failedAt;
|
|
581
|
-
if (timestamp && timestamp < cutoff) toRemove.push(jobId);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
for (const jobId of toRemove) {
|
|
585
|
-
const idx = jobsList.indexOf(jobId);
|
|
586
|
-
if (idx !== -1) {
|
|
587
|
-
jobsList.splice(idx, 1);
|
|
588
|
-
jobsMap.delete(jobId);
|
|
589
|
-
removed++;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
if (maxCount !== void 0 && jobsList.length > maxCount) {
|
|
594
|
-
const toRemove = jobsList.splice(maxCount);
|
|
595
|
-
for (const jobId of toRemove) {
|
|
596
|
-
jobsMap.delete(jobId);
|
|
597
|
-
removed++;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
return removed;
|
|
601
|
-
}
|
|
602
|
-
async removeJob(queue, jobId) {
|
|
603
|
-
const job = this.jobs.get(queue)?.get(jobId);
|
|
604
|
-
if (!job) return;
|
|
605
|
-
const previousStatus = job.state.status;
|
|
606
|
-
switch (job.state.status) {
|
|
607
|
-
case "waiting": {
|
|
608
|
-
const list = this.waiting.get(queue);
|
|
609
|
-
const idx = list?.indexOf(jobId) ?? -1;
|
|
610
|
-
if (idx !== -1) list.splice(idx, 1);
|
|
611
|
-
break;
|
|
612
|
-
}
|
|
613
|
-
case "delayed": {
|
|
614
|
-
const list = this.delayed.get(queue);
|
|
615
|
-
const idx = list?.indexOf(jobId) ?? -1;
|
|
616
|
-
if (idx !== -1) list.splice(idx, 1);
|
|
617
|
-
break;
|
|
618
|
-
}
|
|
619
|
-
case "active":
|
|
620
|
-
this.active.get(queue)?.delete(jobId);
|
|
621
|
-
break;
|
|
622
|
-
case "completed": {
|
|
623
|
-
const list = this.completed.get(queue);
|
|
624
|
-
const idx = list?.indexOf(jobId) ?? -1;
|
|
625
|
-
if (idx !== -1) list.splice(idx, 1);
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
628
|
-
case "failed": {
|
|
629
|
-
const list = this.failed.get(queue);
|
|
630
|
-
const idx = list?.indexOf(jobId) ?? -1;
|
|
631
|
-
if (idx !== -1) list.splice(idx, 1);
|
|
632
|
-
break;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
this.jobs.get(queue)?.delete(jobId);
|
|
636
|
-
await this.emit({
|
|
637
|
-
type: "removed",
|
|
638
|
-
queue,
|
|
639
|
-
jobId,
|
|
640
|
-
timestamp: Date.now(),
|
|
641
|
-
previousStatus
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
cancelWaiters() {
|
|
645
|
-
for (const waiter of this.jobWaiters) {
|
|
646
|
-
clearTimeout(waiter.timer);
|
|
647
|
-
waiter.resolve(void 0);
|
|
648
|
-
}
|
|
649
|
-
this.jobWaiters.clear();
|
|
650
|
-
for (const waiter of this.messageWaiters) {
|
|
651
|
-
clearTimeout(waiter.timer);
|
|
652
|
-
waiter.resolve(void 0);
|
|
653
|
-
}
|
|
654
|
-
this.messageWaiters.clear();
|
|
655
|
-
}
|
|
656
|
-
};
|
|
657
|
-
|
|
658
|
-
//#endregion
|
|
659
|
-
//#region src/queue/descriptors/$queue.ts
|
|
660
|
-
/**
|
|
661
|
-
* Creates a queue descriptor for asynchronous message processing with background workers.
|
|
662
|
-
*
|
|
663
|
-
* The $queue descriptor enables powerful asynchronous communication patterns in your application.
|
|
664
|
-
* It provides type-safe message queuing with automatic worker processing, making it perfect for
|
|
665
|
-
* decoupling components and handling background tasks efficiently.
|
|
666
|
-
*
|
|
667
|
-
* **Background Processing**
|
|
668
|
-
* - Automatic worker threads for non-blocking message processing
|
|
669
|
-
* - Built-in retry mechanisms and error handling
|
|
670
|
-
* - Dead letter queues for failed message handling
|
|
671
|
-
* - Graceful shutdown and worker lifecycle management
|
|
672
|
-
*
|
|
673
|
-
* **Type Safety**
|
|
674
|
-
* - Full TypeScript support with schema validation using TypeBox
|
|
675
|
-
* - Type-safe message payloads with automatic inference
|
|
676
|
-
* - Runtime validation of all queued messages
|
|
677
|
-
* - Compile-time errors for invalid message structures
|
|
678
|
-
*
|
|
679
|
-
* **Storage Flexibility**
|
|
680
|
-
* - Memory provider for development and testing
|
|
681
|
-
* - Redis provider for production scalability and persistence
|
|
682
|
-
* - Custom provider support for specialized backends
|
|
683
|
-
* - Automatic failover and connection pooling
|
|
684
|
-
*
|
|
685
|
-
* **Performance & Scalability**
|
|
686
|
-
* - Batch processing support for high-throughput scenarios
|
|
687
|
-
* - Horizontal scaling with distributed queue backends
|
|
688
|
-
* - Configurable concurrency and worker pools
|
|
689
|
-
* - Efficient serialization and message routing
|
|
690
|
-
*
|
|
691
|
-
* **Reliability**
|
|
692
|
-
* - Message persistence across application restarts
|
|
693
|
-
* - Automatic retry with exponential backoff
|
|
694
|
-
* - Dead letter handling for permanently failed messages
|
|
695
|
-
* - Comprehensive logging and monitoring integration
|
|
696
|
-
*
|
|
697
|
-
* @example Basic notification queue
|
|
698
|
-
* ```typescript
|
|
699
|
-
* const emailQueue = $queue({
|
|
700
|
-
* name: "email-notifications",
|
|
701
|
-
* schema: t.object({
|
|
702
|
-
* to: t.text(),
|
|
703
|
-
* subject: t.text(),
|
|
704
|
-
* body: t.text(),
|
|
705
|
-
* priority: t.optional(t.enum(["high", "normal"]))
|
|
706
|
-
* }),
|
|
707
|
-
* handler: async (message) => {
|
|
708
|
-
* await emailService.send(message.payload);
|
|
709
|
-
* console.log(`Email sent to ${message.payload.to}`);
|
|
710
|
-
* }
|
|
711
|
-
* });
|
|
712
|
-
*
|
|
713
|
-
* // Push messages for background processing
|
|
714
|
-
* await emailQueue.push({
|
|
715
|
-
* to: "user@example.com",
|
|
716
|
-
* subject: "Welcome!",
|
|
717
|
-
* body: "Welcome to our platform",
|
|
718
|
-
* priority: "high"
|
|
719
|
-
* });
|
|
720
|
-
* ```
|
|
721
|
-
*
|
|
722
|
-
* @example Batch processing with Redis
|
|
723
|
-
* ```typescript
|
|
724
|
-
* const imageQueue = $queue({
|
|
725
|
-
* name: "image-processing",
|
|
726
|
-
* provider: RedisQueueProvider,
|
|
727
|
-
* schema: t.object({
|
|
728
|
-
* imageId: t.text(),
|
|
729
|
-
* operations: t.array(t.enum(["resize", "compress", "thumbnail"]))
|
|
730
|
-
* }),
|
|
731
|
-
* handler: async (message) => {
|
|
732
|
-
* for (const op of message.payload.operations) {
|
|
733
|
-
* await processImage(message.payload.imageId, op);
|
|
734
|
-
* }
|
|
735
|
-
* }
|
|
736
|
-
* });
|
|
737
|
-
*
|
|
738
|
-
* // Batch processing multiple images
|
|
739
|
-
* await imageQueue.push(
|
|
740
|
-
* { imageId: "img1", operations: ["resize", "thumbnail"] },
|
|
741
|
-
* { imageId: "img2", operations: ["compress"] },
|
|
742
|
-
* { imageId: "img3", operations: ["resize", "compress", "thumbnail"] }
|
|
743
|
-
* );
|
|
744
|
-
* ```
|
|
745
|
-
*
|
|
746
|
-
* @example Development with memory provider
|
|
747
|
-
* ```typescript
|
|
748
|
-
* const taskQueue = $queue({
|
|
749
|
-
* name: "dev-tasks",
|
|
750
|
-
* provider: "memory",
|
|
751
|
-
* schema: t.object({
|
|
752
|
-
* taskType: t.enum(["cleanup", "backup", "report"]),
|
|
753
|
-
* data: t.record(t.text(), t.any())
|
|
754
|
-
* }),
|
|
755
|
-
* handler: async (message) => {
|
|
756
|
-
* switch (message.payload.taskType) {
|
|
757
|
-
* case "cleanup":
|
|
758
|
-
* await performCleanup(message.payload.data);
|
|
759
|
-
* break;
|
|
760
|
-
* case "backup":
|
|
761
|
-
* await createBackup(message.payload.data);
|
|
762
|
-
* break;
|
|
763
|
-
* case "report":
|
|
764
|
-
* await generateReport(message.payload.data);
|
|
765
|
-
* break;
|
|
766
|
-
* }
|
|
767
|
-
* }
|
|
768
|
-
* });
|
|
769
|
-
* ```
|
|
770
|
-
*/
|
|
771
|
-
const $queue = (options) => {
|
|
772
|
-
return (0, alepha.createDescriptor)(QueueDescriptor, options);
|
|
773
|
-
};
|
|
774
|
-
var QueueDescriptor = class extends alepha.Descriptor {
|
|
775
|
-
log = (0, alepha_logger.$logger)();
|
|
776
|
-
provider = this.$provider();
|
|
777
|
-
async push(payloadOrFirst, optionsOrSecond, ...rest) {
|
|
778
|
-
if (optionsOrSecond != null && typeof optionsOrSecond === "object" && ("priority" in optionsOrSecond || "delay" in optionsOrSecond)) {
|
|
779
|
-
const payload = this.alepha.codec.decode(this.options.schema, payloadOrFirst);
|
|
780
|
-
await this.provider.addJob(this.name, payload, {
|
|
781
|
-
...this.getDefaultJobOptions(),
|
|
782
|
-
priority: optionsOrSecond.priority,
|
|
783
|
-
delay: optionsOrSecond.delay
|
|
784
|
-
});
|
|
785
|
-
this.log.debug(`Pushed job to queue ${this.name}`, {
|
|
786
|
-
payload,
|
|
787
|
-
options: optionsOrSecond
|
|
788
|
-
});
|
|
789
|
-
} else {
|
|
790
|
-
const payloads = optionsOrSecond != null ? [
|
|
791
|
-
payloadOrFirst,
|
|
792
|
-
optionsOrSecond,
|
|
793
|
-
...rest
|
|
794
|
-
] : [payloadOrFirst, ...rest];
|
|
795
|
-
await Promise.all(payloads.map((p) => {
|
|
796
|
-
const payload = this.alepha.codec.decode(this.options.schema, p);
|
|
797
|
-
return this.provider.addJob(this.name, payload, this.getDefaultJobOptions());
|
|
798
|
-
}));
|
|
799
|
-
this.log.debug(`Pushed ${payloads.length} job(s) to queue ${this.name}`, payloads);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
/**
|
|
803
|
-
* Get default job options from descriptor configuration.
|
|
804
|
-
*/
|
|
805
|
-
getDefaultJobOptions() {
|
|
806
|
-
return {
|
|
807
|
-
maxAttempts: this.options.maxAttempts,
|
|
808
|
-
backoff: this.options.backoff,
|
|
809
|
-
lockDuration: this.options.lockDuration,
|
|
810
|
-
removeOnComplete: this.options.removeOnComplete,
|
|
811
|
-
removeOnFail: this.options.removeOnFail
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
get name() {
|
|
815
|
-
return this.options.name || this.config.propertyKey;
|
|
816
|
-
}
|
|
817
|
-
$provider() {
|
|
818
|
-
if (!this.options.provider) return this.alepha.inject(QueueProvider);
|
|
819
|
-
if (this.options.provider === "memory") return this.alepha.inject(MemoryQueueProvider);
|
|
820
|
-
return this.alepha.inject(this.options.provider);
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
$queue[alepha.KIND] = QueueDescriptor;
|
|
824
|
-
|
|
825
|
-
//#endregion
|
|
826
|
-
//#region src/queue/providers/WorkerProvider.ts
|
|
827
|
-
const envSchema = alepha.t.object({
|
|
828
|
-
QUEUE_WORKER_BLOCKING_TIMEOUT: alepha.t.integer({ default: 5 }),
|
|
829
|
-
QUEUE_WORKER_CONCURRENCY: alepha.t.integer({ default: 1 }),
|
|
830
|
-
QUEUE_WORKER_LOCK_RENEWAL_INTERVAL: alepha.t.integer({ default: 1e4 }),
|
|
831
|
-
QUEUE_SCHEDULER_INTERVAL: alepha.t.integer({ default: 5e3 }),
|
|
832
|
-
QUEUE_STALLED_THRESHOLD: alepha.t.integer({ default: 5e3 })
|
|
833
|
-
});
|
|
834
|
-
var WorkerProvider = class {
|
|
835
|
-
log = (0, alepha_logger.$logger)();
|
|
836
|
-
env = (0, alepha.$env)(envSchema);
|
|
837
|
-
alepha = (0, alepha.$inject)(alepha.Alepha);
|
|
838
|
-
queueProvider = (0, alepha.$inject)(QueueProvider);
|
|
839
|
-
dateTime = (0, alepha.$inject)(alepha_datetime.DateTimeProvider);
|
|
840
|
-
workerPromises = [];
|
|
841
|
-
workersRunning = 0;
|
|
842
|
-
shouldStop = false;
|
|
843
|
-
consumers = [];
|
|
844
|
-
consumersByProvider = /* @__PURE__ */ new Map();
|
|
845
|
-
schedulerPromise;
|
|
846
|
-
schedulerRunning = false;
|
|
847
|
-
abortController;
|
|
848
|
-
workerId = `worker_${process.pid}_${Date.now()}`;
|
|
849
|
-
get isRunning() {
|
|
850
|
-
return this.workersRunning > 0;
|
|
851
|
-
}
|
|
852
|
-
start = (0, alepha.$hook)({
|
|
853
|
-
on: "start",
|
|
854
|
-
priority: "last",
|
|
855
|
-
handler: () => {
|
|
856
|
-
for (const queue of this.alepha.descriptors($queue)) {
|
|
857
|
-
const handler = queue.options.handler;
|
|
858
|
-
if (handler) this.consumers.push({
|
|
859
|
-
handler,
|
|
860
|
-
queue
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
for (const consumer of this.alepha.descriptors($consumer)) this.consumers.push(consumer.options);
|
|
864
|
-
for (const consumer of this.consumers) {
|
|
865
|
-
const provider = consumer.queue.provider;
|
|
866
|
-
const list = this.consumersByProvider.get(provider) ?? [];
|
|
867
|
-
list.push(consumer);
|
|
868
|
-
this.consumersByProvider.set(provider, list);
|
|
869
|
-
}
|
|
870
|
-
if (this.consumers.length > 0) {
|
|
871
|
-
this.startWorkers();
|
|
872
|
-
this.startScheduler();
|
|
873
|
-
this.log.debug(`Watching for ${this.consumers.length} queue${this.consumers.length > 1 ? "s" : ""} with ${this.env.QUEUE_WORKER_CONCURRENCY} worker${this.env.QUEUE_WORKER_CONCURRENCY > 1 ? "s" : ""}.`);
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
});
|
|
877
|
-
/**
|
|
878
|
-
* Start the workers.
|
|
879
|
-
* Each worker acquires jobs and processes them with proper lifecycle management.
|
|
880
|
-
*/
|
|
881
|
-
startWorkers() {
|
|
882
|
-
const workerToStart = this.env.QUEUE_WORKER_CONCURRENCY - this.workersRunning;
|
|
883
|
-
for (let i = 0; i < workerToStart; i++) {
|
|
884
|
-
this.workersRunning += 1;
|
|
885
|
-
const workerIndex = i;
|
|
886
|
-
const localWorkerId = `${this.workerId}_${workerIndex}`;
|
|
887
|
-
this.log.debug(`Starting worker n-${workerIndex}`);
|
|
888
|
-
const workerLoop = async () => {
|
|
889
|
-
while (!this.shouldStop) {
|
|
890
|
-
this.log.trace(`Worker n-${workerIndex} is waiting for jobs`);
|
|
891
|
-
const acquired = await this.acquireNextJob(localWorkerId);
|
|
892
|
-
if (acquired) await this.processJob(acquired, localWorkerId);
|
|
893
|
-
}
|
|
894
|
-
this.log.info(`Worker n-${workerIndex} has stopped`);
|
|
895
|
-
};
|
|
896
|
-
this.workerPromises.push(workerLoop().catch((e) => {
|
|
897
|
-
this.log.error(`Worker n-${workerIndex} has crashed`, e);
|
|
898
|
-
this.workersRunning -= 1;
|
|
899
|
-
}));
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
/**
|
|
903
|
-
* Start the scheduler for delayed job promotion and stalled job recovery.
|
|
904
|
-
*/
|
|
905
|
-
startScheduler() {
|
|
906
|
-
if (this.schedulerRunning) return;
|
|
907
|
-
this.schedulerRunning = true;
|
|
908
|
-
this.abortController = new AbortController();
|
|
909
|
-
this.log.debug("Starting scheduler");
|
|
910
|
-
const schedulerLoop = async () => {
|
|
911
|
-
while (!this.shouldStop) {
|
|
912
|
-
try {
|
|
913
|
-
await this.runSchedulerCycle();
|
|
914
|
-
} catch (e) {
|
|
915
|
-
this.log.error("Scheduler cycle failed", e);
|
|
916
|
-
}
|
|
917
|
-
await this.dateTime.wait(this.env.QUEUE_SCHEDULER_INTERVAL, { signal: this.abortController?.signal });
|
|
918
|
-
}
|
|
919
|
-
this.log.debug("Scheduler stopped");
|
|
920
|
-
};
|
|
921
|
-
this.schedulerPromise = schedulerLoop();
|
|
922
|
-
}
|
|
923
|
-
/**
|
|
924
|
-
* Run one cycle of the scheduler.
|
|
925
|
-
* Promotes delayed jobs and recovers stalled jobs.
|
|
926
|
-
*/
|
|
927
|
-
async runSchedulerCycle() {
|
|
928
|
-
for (const [provider, consumers] of this.consumersByProvider) {
|
|
929
|
-
const queues = new Set(consumers.map((c) => c.queue.name));
|
|
930
|
-
for (const queue of queues) {
|
|
931
|
-
const promoted = await provider.promoteDelayedJobs(queue);
|
|
932
|
-
if (promoted > 0) this.log.debug(`Promoted ${promoted} delayed jobs in queue ${queue}`);
|
|
933
|
-
const recovered = await provider.recoverStalledJobs(queue, this.env.QUEUE_STALLED_THRESHOLD);
|
|
934
|
-
if (recovered.length > 0) this.log.warn(`Recovered ${recovered.length} stalled jobs in queue ${queue}`);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
stop = (0, alepha.$hook)({
|
|
939
|
-
on: "stop",
|
|
940
|
-
handler: async () => {
|
|
941
|
-
if (this.consumers.length > 0) await this.stopWorkers();
|
|
942
|
-
}
|
|
943
|
-
});
|
|
944
|
-
/**
|
|
945
|
-
* Acquire the next available job from any provider.
|
|
946
|
-
*/
|
|
947
|
-
async acquireNextJob(localWorkerId) {
|
|
948
|
-
for (const [provider, consumers] of this.consumersByProvider) {
|
|
949
|
-
const queueNames = consumers.map((c) => c.queue.name);
|
|
950
|
-
const acquired = await provider.acquireJob(queueNames, localWorkerId, this.env.QUEUE_WORKER_BLOCKING_TIMEOUT);
|
|
951
|
-
if (acquired) {
|
|
952
|
-
const consumer = consumers.find((c) => c.queue.name === acquired.queue);
|
|
953
|
-
if (consumer) return {
|
|
954
|
-
acquired,
|
|
955
|
-
consumer,
|
|
956
|
-
provider
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
/**
|
|
962
|
-
* Process a job with proper lifecycle management.
|
|
963
|
-
* - Starts a lock renewal interval
|
|
964
|
-
* - Calls the handler
|
|
965
|
-
* - Marks job as completed or failed
|
|
966
|
-
*/
|
|
967
|
-
async processJob({ acquired, consumer, provider }, localWorkerId) {
|
|
968
|
-
const { queue, job } = acquired;
|
|
969
|
-
const lockRenewalInterval = this.dateTime.createInterval(async () => {
|
|
970
|
-
try {
|
|
971
|
-
if (!await provider.renewJobLock(queue, job.id, localWorkerId)) this.log.warn(`Failed to renew lock for job ${job.id}, lock may have been stolen`);
|
|
972
|
-
} catch (e) {
|
|
973
|
-
this.log.error(`Error renewing lock for job ${job.id}`, e);
|
|
974
|
-
}
|
|
975
|
-
}, this.env.QUEUE_WORKER_LOCK_RENEWAL_INTERVAL, true);
|
|
976
|
-
try {
|
|
977
|
-
const payload = this.alepha.codec.decode(consumer.queue.options.schema, job.payload);
|
|
978
|
-
await this.alepha.context.run(() => consumer.handler({ payload }));
|
|
979
|
-
await provider.completeJob(queue, job.id);
|
|
980
|
-
this.log.debug(`Job ${job.id} completed successfully`, { queue });
|
|
981
|
-
} catch (e) {
|
|
982
|
-
const error = e instanceof Error ? e.message : String(e);
|
|
983
|
-
const stackTrace = e instanceof Error ? e.stack : void 0;
|
|
984
|
-
await provider.failJob(queue, job.id, error, stackTrace);
|
|
985
|
-
this.log.error(`Job ${job.id} failed`, e);
|
|
986
|
-
} finally {
|
|
987
|
-
this.dateTime.clearInterval(lockRenewalInterval);
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
/**
|
|
991
|
-
* Stop the workers and scheduler.
|
|
992
|
-
*/
|
|
993
|
-
async stopWorkers() {
|
|
994
|
-
this.shouldStop = true;
|
|
995
|
-
this.workersRunning = 0;
|
|
996
|
-
this.schedulerRunning = false;
|
|
997
|
-
this.abortController?.abort();
|
|
998
|
-
for (const provider of this.consumersByProvider.keys()) provider.cancelWaiters();
|
|
999
|
-
this.log.trace("Stopping workers...");
|
|
1000
|
-
this.log.trace("Waiting for workers to finish...");
|
|
1001
|
-
const promises = [...this.workerPromises];
|
|
1002
|
-
if (this.schedulerPromise) promises.push(this.schedulerPromise);
|
|
1003
|
-
await Promise.all(promises);
|
|
1004
|
-
}
|
|
1005
|
-
};
|
|
1006
|
-
|
|
1007
|
-
//#endregion
|
|
1008
|
-
//#region src/queue/index.ts
|
|
1009
|
-
/**
|
|
1010
|
-
* Provides asynchronous message queuing and processing capabilities through declarative queue descriptors.
|
|
1011
|
-
*
|
|
1012
|
-
* The queue module enables reliable background job processing and message passing using the `$queue` descriptor
|
|
1013
|
-
* on class properties. It supports schema validation, automatic retries, and multiple queue backends for
|
|
1014
|
-
* building scalable, decoupled applications with robust error handling.
|
|
1015
|
-
*
|
|
1016
|
-
* @see {@link $queue}
|
|
1017
|
-
* @see {@link $consumer}
|
|
1018
|
-
* @module alepha.queue
|
|
1019
|
-
*/
|
|
1020
|
-
const AlephaQueue = (0, alepha.$module)({
|
|
1021
|
-
name: "alepha.queue",
|
|
1022
|
-
descriptors: [$queue, $consumer],
|
|
1023
|
-
services: [
|
|
1024
|
-
QueueProvider,
|
|
1025
|
-
MemoryQueueProvider,
|
|
1026
|
-
WorkerProvider
|
|
1027
|
-
],
|
|
1028
|
-
register: (alepha$1) => alepha$1.with({
|
|
1029
|
-
optional: true,
|
|
1030
|
-
provide: QueueProvider,
|
|
1031
|
-
use: MemoryQueueProvider
|
|
1032
|
-
}).with(WorkerProvider)
|
|
1033
|
-
});
|
|
1034
|
-
|
|
1035
|
-
//#endregion
|
|
1036
|
-
exports.$consumer = $consumer;
|
|
1037
|
-
exports.$queue = $queue;
|
|
1038
|
-
exports.AlephaQueue = AlephaQueue;
|
|
1039
|
-
exports.ConsumerDescriptor = ConsumerDescriptor;
|
|
1040
|
-
exports.MemoryQueueProvider = MemoryQueueProvider;
|
|
1041
|
-
exports.QueueDescriptor = QueueDescriptor;
|
|
1042
|
-
exports.QueueProvider = QueueProvider;
|
|
1043
|
-
exports.WorkerProvider = WorkerProvider;
|
|
1044
|
-
//# sourceMappingURL=index.cjs.map
|