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/thread/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["name: string","maxPoolSize: number","idleTimeout: number","script: string","instance: ThreadInstance","message: ThreadMessage"],"sources":["../../src/thread/descriptors/$thread.ts","../../src/thread/providers/ThreadProvider.ts","../../src/thread/index.ts"],"sourcesContent":["import { cpus } from \"node:os\";\nimport { MessageChannel, type MessagePort, Worker } from \"node:worker_threads\";\nimport type { TSchema } from \"alepha\";\nimport { createDescriptor, Descriptor, KIND, TypeBoxValue } from \"alepha\";\n\n/**\n * Creates a worker thread descriptor for offloading CPU-intensive tasks to separate threads.\n *\n * This descriptor enables you to run JavaScript code in Node.js worker threads, allowing you to\n * leverage multiple CPU cores and avoid blocking the main event loop. It provides a pool-based\n * approach with intelligent thread reuse and automatic lifecycle management.\n *\n * **Key Features**\n *\n * - **Thread Pool Management**: Automatically manages a pool of worker threads with configurable limits\n * - **Thread Reuse**: Reuses existing threads to avoid expensive initialization overhead\n * - **Idle Cleanup**: Automatically terminates unused threads after a configurable timeout\n * - **Type-Safe Communication**: Optional TypeBox schema validation for data passed to threads\n * - **CPU-Aware Defaults**: Pool size defaults to CPU count × 2 for optimal performance\n * - **Error Handling**: Proper error propagation and thread cleanup on failures\n *\n * **Use Cases**\n *\n * Perfect for CPU-intensive tasks that would otherwise block the main thread:\n * - Image/video processing\n * - Data transformation and analysis\n * - Cryptographic operations\n * - Heavy computations and algorithms\n * - Background data processing\n *\n * @example\n * **Basic thread usage:**\n * ```ts\n * import { $thread } from \"alepha/thread\";\n *\n * class DataProcessor {\n * heavyComputation = $thread({\n * name: \"compute\",\n * handler: async () => {\n * // This runs in a separate worker thread\n * let result = 0;\n * for (let i = 0; i < 1000000; i++) {\n * result += Math.sqrt(i);\n * }\n * return { result, timestamp: Date.now() };\n * }\n * });\n *\n * async processData() {\n * // Execute in worker thread without blocking main thread\n * const result = await this.heavyComputation.execute();\n * console.log(`Computation result: ${result.result}`);\n * }\n * }\n * ```\n *\n * @example\n * **Configured thread pool with custom settings:**\n * ```ts\n * class ImageProcessor {\n * imageProcessor = $thread({\n * name: \"image-processing\",\n * maxPoolSize: 4, // Limit to 4 concurrent threads\n * idleTimeout: 30000, // Clean up idle threads after 30 seconds\n * handler: async () => {\n * // CPU-intensive image processing logic\n * return await processImageData();\n * }\n * });\n * }\n * ```\n *\n * @example\n * **Thread with data validation:**\n * ```ts\n * import { t } from \"alepha\";\n *\n * class CryptoService {\n * encrypt = $thread({\n * name: \"encryption\",\n * handler: async () => {\n * // Perform encryption operations\n * return await encryptData();\n * }\n * });\n *\n * async encryptSensitiveData(data: { text: string; key: string }) {\n * // Validate input data before sending to thread\n * const schema = t.object({\n * text: t.text(),\n * key: t.text()\n * });\n *\n * return await this.encrypt.execute(data, schema);\n * }\n * }\n * ```\n *\n * @example\n * **Parallel processing with multiple threads:**\n * ```ts\n * class BatchProcessor {\n * processor = $thread({\n * name: \"batch-worker\",\n * maxPoolSize: 8, // Allow up to 8 concurrent workers\n * handler: async () => {\n * return await processBatchItem();\n * }\n * });\n *\n * async processBatch(items: any[]) {\n * // Process multiple items in parallel across different threads\n * const promises = items.map(() => this.processor.execute());\n * const results = await Promise.all(promises);\n * return results;\n * }\n * }\n * ```\n */\nexport const $thread = (options: ThreadDescriptorOptions): ThreadDescriptor => {\n return createDescriptor(ThreadDescriptor, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ThreadDescriptorOptions {\n /**\n * Unique name for this thread worker.\n *\n * Used for:\n * - Thread pool identification (threads with same name share the same pool)\n * - Logging and debugging\n * - Error messages and stack traces\n *\n * If not provided, defaults to the property key of the descriptor.\n *\n * @example \"image-processor\"\n * @example \"crypto-worker\"\n * @example \"data-analysis\"\n */\n name?: string;\n\n /**\n * The function to execute in the worker thread.\n *\n * This function:\n * - Runs in a separate Node.js worker thread\n * - Should contain the CPU-intensive logic\n * - Can be async and return any serializable data\n * - Has access to standard Node.js APIs and modules\n * - Cannot directly access the main thread's memory or variables\n *\n * **Important**: The handler function is serialized and sent to the worker thread,\n * so it cannot reference variables from the parent scope (closures won't work).\n * All required data must be passed via the `execute()` method.\n *\n * @example\n * ```ts\n * handler: async () => {\n * // CPU-intensive work here\n * const result = performComplexCalculation();\n * return { result, completed: Date.now() };\n * }\n * ```\n */\n handler: () => any | Promise<any>;\n\n /**\n * Maximum number of worker threads in the pool.\n *\n * Controls how many threads can run concurrently for this named thread worker.\n * When all threads are busy, additional `execute()` calls will queue until a thread becomes available.\n *\n * **Default**: `cpus().length * 2` (number of CPU cores × 2)\n *\n * **Guidelines**:\n * - For CPU-bound tasks: Set to number of CPU cores\n * - For I/O-bound tasks in workers: Can be higher (2x CPU cores)\n * - For memory-intensive tasks: Set lower to avoid memory pressure\n * - For short-lived tasks: Can be higher for better throughput\n *\n * @default cpus().length * 2\n * @example 4 // Limit to 4 concurrent threads\n * @example 1 // Single worker thread (sequential processing)\n * @example 16 // High concurrency for lightweight tasks\n */\n maxPoolSize?: number;\n\n /**\n * Time in milliseconds before idle worker threads are terminated.\n *\n * When a worker thread has been idle (not executing any tasks) for this duration,\n * it will be automatically terminated to free up system resources. New threads\n * will be created as needed when new tasks are submitted.\n *\n * **Default**: 60000 (60 seconds)\n *\n * **Considerations**:\n * - Shorter timeouts: Save memory but increase thread creation overhead\n * - Longer timeouts: Keep threads ready but consume more memory\n * - Very short timeouts may cause constant thread creation/destruction\n *\n * @default 60000\n * @example 30000 // 30 seconds\n * @example 120000 // 2 minutes\n * @example 5000 // 5 seconds (for very memory-constrained environments)\n */\n idleTimeout?: number;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ThreadDescriptor extends Descriptor<ThreadDescriptorOptions> {\n protected readonly script = process.argv[1];\n static readonly globalPool = new Map<string, ThreadPool>();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public get maxPoolSize(): number {\n return this.options.maxPoolSize || cpus().length * 2;\n }\n\n public get idleTimeout(): number {\n return this.options.idleTimeout || 60000; // 1 minute default\n }\n\n private getPool(): ThreadPool {\n if (!ThreadDescriptor.globalPool.has(this.name)) {\n ThreadDescriptor.globalPool.set(\n this.name,\n new ThreadPool(\n this.name,\n this.maxPoolSize,\n this.idleTimeout,\n this.script,\n ),\n );\n }\n return ThreadDescriptor.globalPool.get(this.name)!;\n }\n\n public async execute<T = any>(data?: any, schema?: TSchema): Promise<T> {\n if (schema && data) {\n try {\n TypeBoxValue.Decode(schema, data);\n } catch (error) {\n throw new Error(\n `Invalid data: ${error instanceof Error ? error.message : error}`,\n );\n }\n }\n\n const pool = this.getPool();\n return await pool.execute<T>(data);\n }\n\n public async create(): Promise<void> {\n const pool = this.getPool();\n await pool.warmUp();\n }\n\n public async terminate(): Promise<void> {\n const pool = this.getPool();\n await pool.terminate();\n ThreadDescriptor.globalPool.delete(this.name);\n }\n}\n\n$thread[KIND] = ThreadDescriptor;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ninterface ThreadMessage<T = any> {\n id: string;\n type: \"execute\" | \"response\" | \"error\";\n data?: T;\n error?: string;\n}\n\ninterface ThreadInstance {\n worker: Worker;\n port: MessagePort;\n busy: boolean;\n lastUsed: number;\n pendingMessages: Map<\n string,\n { resolve: (value: any) => void; reject: (error: Error) => void }\n >;\n}\n\nclass ThreadPool {\n private instances: ThreadInstance[] = [];\n private queue: Array<{\n data: any;\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n }> = [];\n private idleTimer?: NodeJS.Timeout;\n\n constructor(\n private readonly name: string,\n private readonly maxPoolSize: number,\n private readonly idleTimeout: number,\n private readonly script: string,\n ) {}\n\n async warmUp(): Promise<void> {\n if (this.instances.length === 0) {\n await this.createInstance();\n }\n }\n\n private async createInstance(): Promise<ThreadInstance> {\n const { port1, port2 } = new MessageChannel();\n\n const worker = new Worker(this.script, {\n env: {\n ...process.env,\n ALEPHA_WORKER: this.name,\n APP_NAME: \"WORKER\",\n },\n workerData: { port: port2 },\n transferList: [port2],\n });\n\n const instance: ThreadInstance = {\n worker,\n port: port1,\n busy: false,\n lastUsed: Date.now(),\n pendingMessages: new Map(),\n };\n\n instance.port.on(\"message\", (message: ThreadMessage) => {\n if (message.type === \"response\" || message.type === \"error\") {\n const pending = instance.pendingMessages.get(message.id);\n if (pending) {\n instance.pendingMessages.delete(message.id);\n instance.busy = false;\n instance.lastUsed = Date.now();\n\n if (message.type === \"error\") {\n pending.reject(new Error(message.error));\n } else {\n pending.resolve(message.data);\n }\n\n this.processQueue();\n }\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error(\"Thread initialization timeout\")),\n 5000,\n );\n\n worker.once(\"online\", () => {\n clearTimeout(timeout);\n resolve();\n });\n\n worker.once(\"error\", (error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n\n this.instances.push(instance);\n this.resetIdleTimer();\n\n return instance;\n }\n\n async execute<T = any>(data?: any): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n this.queue.push({ data, resolve, reject });\n this.processQueue();\n });\n }\n\n private async processQueue(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n let instance = this.instances.find((i) => !i.busy);\n\n if (!instance && this.instances.length < this.maxPoolSize) {\n try {\n instance = await this.createInstance();\n } catch (error) {\n const { reject } = this.queue.shift()!;\n reject(\n error instanceof Error\n ? error\n : new Error(\"Failed to create thread instance\"),\n );\n return;\n }\n }\n\n if (!instance) {\n return; // Wait for an instance to become available\n }\n\n const { data, resolve, reject } = this.queue.shift()!;\n const messageId = `${Date.now()}-${Math.random()}`;\n\n instance.busy = true;\n instance.pendingMessages.set(messageId, { resolve, reject });\n\n const message: ThreadMessage = {\n id: messageId,\n type: \"execute\",\n data,\n };\n\n instance.port.postMessage(message);\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n }\n\n this.idleTimer = setTimeout(() => {\n this.cleanupIdleInstances();\n }, this.idleTimeout);\n }\n\n private cleanupIdleInstances(): void {\n const now = Date.now();\n const instancesToRemove = this.instances.filter(\n (instance) =>\n !instance.busy && now - instance.lastUsed > this.idleTimeout,\n );\n\n for (const instance of instancesToRemove) {\n const index = this.instances.indexOf(instance);\n if (index > -1) {\n this.instances.splice(index, 1);\n instance.port.close();\n void instance.worker.terminate();\n }\n }\n\n if (this.instances.length > 0) {\n this.resetIdleTimer();\n }\n }\n\n async terminate(): Promise<void> {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n }\n\n await Promise.all(\n this.instances.map(async (instance) => {\n instance.port.close();\n await instance.worker.terminate();\n }),\n );\n\n this.instances = [];\n this.queue = [];\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n","import { parentPort, workerData } from \"node:worker_threads\";\nimport { $env, $hook, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $thread } from \"../descriptors/$thread.ts\";\n\ninterface ThreadMessage<T = any> {\n id: string;\n type: \"execute\" | \"response\" | \"error\" | \"ready\";\n data?: T;\n error?: string;\n}\n\nexport class ThreadProvider {\n protected readonly log = $logger();\n protected readonly env = $env(\n t.object({\n ALEPHA_WORKER: t.optional(t.text()),\n }),\n );\n\n protected readonly ready = $hook({\n on: \"ready\",\n handler: async (alepha) => {\n const worker = this.env.ALEPHA_WORKER;\n if (!worker) {\n return;\n }\n\n const threads = alepha.descriptors($thread);\n const threadDescriptor = threads.find((thread) => thread.name === worker);\n\n if (!threadDescriptor) {\n this.log.error(`Thread not found: ${worker}`);\n return;\n }\n\n this.log.info(`Thread ready: ${threadDescriptor.name}`);\n\n // Use the message channel port from worker data if available, fallback to parentPort\n const communicationPort = workerData?.port || parentPort;\n\n if (!communicationPort) {\n this.log.error(\"No communication port available\");\n return;\n }\n\n // Set up message handling\n communicationPort.on(\"message\", async (message: ThreadMessage) => {\n if (message.type === \"execute\") {\n try {\n this.log.debug(\n `Executing thread handler: ${threadDescriptor.name}`,\n );\n const result = await threadDescriptor.options.handler();\n\n communicationPort.postMessage({\n id: message.id,\n type: \"response\",\n data: result,\n } as ThreadMessage);\n } catch (error) {\n this.log.error(`Thread execution error: ${error}`);\n\n communicationPort.postMessage({\n id: message.id,\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n } as ThreadMessage);\n }\n }\n });\n\n // Signal that the worker is ready\n communicationPort.postMessage({ type: \"ready\" } as ThreadMessage);\n },\n });\n\n public static async cleanup(): Promise<void> {\n if (parentPort) {\n parentPort.removeAllListeners();\n }\n }\n}\n","import { $module, Alepha } from \"alepha\";\nimport { $thread } from \"./descriptors/$thread.ts\";\nimport { ThreadProvider } from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$thread.ts\";\nexport * from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Alepha {\n isWorkerThread(): boolean;\n }\n}\n\nAlepha.prototype.isWorkerThread = function (this: Alepha): boolean {\n return !!this.env.ALEPHA_WORKER;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Simple interface for managing worker threads in Alepha.\n *\n * @see {@link $thread}\n * @module alepha.thread\n */\nexport const AlephaThread = $module({\n name: \"alepha.thread\",\n descriptors: [$thread],\n services: [ThreadProvider],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["name: string","maxPoolSize: number","idleTimeout: number","script: string","instance: ThreadInstance","message: ThreadMessage"],"sources":["../../src/thread/primitives/$thread.ts","../../src/thread/providers/ThreadProvider.ts","../../src/thread/index.ts"],"sourcesContent":["import { cpus } from \"node:os\";\nimport { MessageChannel, type MessagePort, Worker } from \"node:worker_threads\";\nimport type { TSchema } from \"alepha\";\nimport { createPrimitive, KIND, Primitive, TypeBoxValue } from \"alepha\";\n\n/**\n * Creates a worker thread primitive for offloading CPU-intensive tasks to separate threads.\n *\n * This primitive enables you to run JavaScript code in Node.js worker threads, allowing you to\n * leverage multiple CPU cores and avoid blocking the main event loop. It provides a pool-based\n * approach with intelligent thread reuse and automatic lifecycle management.\n *\n * **Key Features**\n *\n * - **Thread Pool Management**: Automatically manages a pool of worker threads with configurable limits\n * - **Thread Reuse**: Reuses existing threads to avoid expensive initialization overhead\n * - **Idle Cleanup**: Automatically terminates unused threads after a configurable timeout\n * - **Type-Safe Communication**: Optional TypeBox schema validation for data passed to threads\n * - **CPU-Aware Defaults**: Pool size defaults to CPU count × 2 for optimal performance\n * - **Error Handling**: Proper error propagation and thread cleanup on failures\n *\n * **Use Cases**\n *\n * Perfect for CPU-intensive tasks that would otherwise block the main thread:\n * - Image/video processing\n * - Data transformation and analysis\n * - Cryptographic operations\n * - Heavy computations and algorithms\n * - Background data processing\n *\n * @example\n * **Basic thread usage:**\n * ```ts\n * import { $thread } from \"alepha/thread\";\n *\n * class DataProcessor {\n * heavyComputation = $thread({\n * name: \"compute\",\n * handler: async () => {\n * // This runs in a separate worker thread\n * let result = 0;\n * for (let i = 0; i < 1000000; i++) {\n * result += Math.sqrt(i);\n * }\n * return { result, timestamp: Date.now() };\n * }\n * });\n *\n * async processData() {\n * // Execute in worker thread without blocking main thread\n * const result = await this.heavyComputation.execute();\n * console.log(`Computation result: ${result.result}`);\n * }\n * }\n * ```\n *\n * @example\n * **Configured thread pool with custom settings:**\n * ```ts\n * class ImageProcessor {\n * imageProcessor = $thread({\n * name: \"image-processing\",\n * maxPoolSize: 4, // Limit to 4 concurrent threads\n * idleTimeout: 30000, // Clean up idle threads after 30 seconds\n * handler: async () => {\n * // CPU-intensive image processing logic\n * return await processImageData();\n * }\n * });\n * }\n * ```\n *\n * @example\n * **Thread with data validation:**\n * ```ts\n * import { t } from \"alepha\";\n *\n * class CryptoService {\n * encrypt = $thread({\n * name: \"encryption\",\n * handler: async () => {\n * // Perform encryption operations\n * return await encryptData();\n * }\n * });\n *\n * async encryptSensitiveData(data: { text: string; key: string }) {\n * // Validate input data before sending to thread\n * const schema = t.object({\n * text: t.text(),\n * key: t.text()\n * });\n *\n * return await this.encrypt.execute(data, schema);\n * }\n * }\n * ```\n *\n * @example\n * **Parallel processing with multiple threads:**\n * ```ts\n * class BatchProcessor {\n * processor = $thread({\n * name: \"batch-worker\",\n * maxPoolSize: 8, // Allow up to 8 concurrent workers\n * handler: async () => {\n * return await processBatchItem();\n * }\n * });\n *\n * async processBatch(items: any[]) {\n * // Process multiple items in parallel across different threads\n * const promises = items.map(() => this.processor.execute());\n * const results = await Promise.all(promises);\n * return results;\n * }\n * }\n * ```\n */\nexport const $thread = (options: ThreadPrimitiveOptions): ThreadPrimitive => {\n return createPrimitive(ThreadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ThreadPrimitiveOptions {\n /**\n * Unique name for this thread worker.\n *\n * Used for:\n * - Thread pool identification (threads with same name share the same pool)\n * - Logging and debugging\n * - Error messages and stack traces\n *\n * If not provided, defaults to the property key of the primitive.\n *\n * @example \"image-processor\"\n * @example \"crypto-worker\"\n * @example \"data-analysis\"\n */\n name?: string;\n\n /**\n * The function to execute in the worker thread.\n *\n * This function:\n * - Runs in a separate Node.js worker thread\n * - Should contain the CPU-intensive logic\n * - Can be async and return any serializable data\n * - Has access to standard Node.js APIs and modules\n * - Cannot directly access the main thread's memory or variables\n *\n * **Important**: The handler function is serialized and sent to the worker thread,\n * so it cannot reference variables from the parent scope (closures won't work).\n * All required data must be passed via the `execute()` method.\n *\n * @example\n * ```ts\n * handler: async () => {\n * // CPU-intensive work here\n * const result = performComplexCalculation();\n * return { result, completed: Date.now() };\n * }\n * ```\n */\n handler: () => any | Promise<any>;\n\n /**\n * Maximum number of worker threads in the pool.\n *\n * Controls how many threads can run concurrently for this named thread worker.\n * When all threads are busy, additional `execute()` calls will queue until a thread becomes available.\n *\n * **Default**: `cpus().length * 2` (number of CPU cores × 2)\n *\n * **Guidelines**:\n * - For CPU-bound tasks: Set to number of CPU cores\n * - For I/O-bound tasks in workers: Can be higher (2x CPU cores)\n * - For memory-intensive tasks: Set lower to avoid memory pressure\n * - For short-lived tasks: Can be higher for better throughput\n *\n * @default cpus().length * 2\n * @example 4 // Limit to 4 concurrent threads\n * @example 1 // Single worker thread (sequential processing)\n * @example 16 // High concurrency for lightweight tasks\n */\n maxPoolSize?: number;\n\n /**\n * Time in milliseconds before idle worker threads are terminated.\n *\n * When a worker thread has been idle (not executing any tasks) for this duration,\n * it will be automatically terminated to free up system resources. New threads\n * will be created as needed when new tasks are submitted.\n *\n * **Default**: 60000 (60 seconds)\n *\n * **Considerations**:\n * - Shorter timeouts: Save memory but increase thread creation overhead\n * - Longer timeouts: Keep threads ready but consume more memory\n * - Very short timeouts may cause constant thread creation/destruction\n *\n * @default 60000\n * @example 30000 // 30 seconds\n * @example 120000 // 2 minutes\n * @example 5000 // 5 seconds (for very memory-constrained environments)\n */\n idleTimeout?: number;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ThreadPrimitive extends Primitive<ThreadPrimitiveOptions> {\n protected readonly script = process.argv[1];\n static readonly globalPool = new Map<string, ThreadPool>();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public get maxPoolSize(): number {\n return this.options.maxPoolSize || cpus().length * 2;\n }\n\n public get idleTimeout(): number {\n return this.options.idleTimeout || 60000; // 1 minute default\n }\n\n private getPool(): ThreadPool {\n if (!ThreadPrimitive.globalPool.has(this.name)) {\n ThreadPrimitive.globalPool.set(\n this.name,\n new ThreadPool(\n this.name,\n this.maxPoolSize,\n this.idleTimeout,\n this.script,\n ),\n );\n }\n return ThreadPrimitive.globalPool.get(this.name)!;\n }\n\n public async execute<T = any>(data?: any, schema?: TSchema): Promise<T> {\n if (schema && data) {\n try {\n TypeBoxValue.Decode(schema, data);\n } catch (error) {\n throw new Error(\n `Invalid data: ${error instanceof Error ? error.message : error}`,\n );\n }\n }\n\n const pool = this.getPool();\n return await pool.execute<T>(data);\n }\n\n public async create(): Promise<void> {\n const pool = this.getPool();\n await pool.warmUp();\n }\n\n public async terminate(): Promise<void> {\n const pool = this.getPool();\n await pool.terminate();\n ThreadPrimitive.globalPool.delete(this.name);\n }\n}\n\n$thread[KIND] = ThreadPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ninterface ThreadMessage<T = any> {\n id: string;\n type: \"execute\" | \"response\" | \"error\";\n data?: T;\n error?: string;\n}\n\ninterface ThreadInstance {\n worker: Worker;\n port: MessagePort;\n busy: boolean;\n lastUsed: number;\n pendingMessages: Map<\n string,\n { resolve: (value: any) => void; reject: (error: Error) => void }\n >;\n}\n\nclass ThreadPool {\n private instances: ThreadInstance[] = [];\n private queue: Array<{\n data: any;\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n }> = [];\n private idleTimer?: NodeJS.Timeout;\n\n constructor(\n private readonly name: string,\n private readonly maxPoolSize: number,\n private readonly idleTimeout: number,\n private readonly script: string,\n ) {}\n\n async warmUp(): Promise<void> {\n if (this.instances.length === 0) {\n await this.createInstance();\n }\n }\n\n private async createInstance(): Promise<ThreadInstance> {\n const { port1, port2 } = new MessageChannel();\n\n const worker = new Worker(this.script, {\n env: {\n ...process.env,\n ALEPHA_WORKER: this.name,\n APP_NAME: \"WORKER\",\n },\n workerData: { port: port2 },\n transferList: [port2],\n });\n\n const instance: ThreadInstance = {\n worker,\n port: port1,\n busy: false,\n lastUsed: Date.now(),\n pendingMessages: new Map(),\n };\n\n instance.port.on(\"message\", (message: ThreadMessage) => {\n if (message.type === \"response\" || message.type === \"error\") {\n const pending = instance.pendingMessages.get(message.id);\n if (pending) {\n instance.pendingMessages.delete(message.id);\n instance.busy = false;\n instance.lastUsed = Date.now();\n\n if (message.type === \"error\") {\n pending.reject(new Error(message.error));\n } else {\n pending.resolve(message.data);\n }\n\n this.processQueue();\n }\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error(\"Thread initialization timeout\")),\n 5000,\n );\n\n worker.once(\"online\", () => {\n clearTimeout(timeout);\n resolve();\n });\n\n worker.once(\"error\", (error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n\n this.instances.push(instance);\n this.resetIdleTimer();\n\n return instance;\n }\n\n async execute<T = any>(data?: any): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n this.queue.push({ data, resolve, reject });\n this.processQueue();\n });\n }\n\n private async processQueue(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n let instance = this.instances.find((i) => !i.busy);\n\n if (!instance && this.instances.length < this.maxPoolSize) {\n try {\n instance = await this.createInstance();\n } catch (error) {\n const { reject } = this.queue.shift()!;\n reject(\n error instanceof Error\n ? error\n : new Error(\"Failed to create thread instance\"),\n );\n return;\n }\n }\n\n if (!instance) {\n return; // Wait for an instance to become available\n }\n\n const { data, resolve, reject } = this.queue.shift()!;\n const messageId = `${Date.now()}-${Math.random()}`;\n\n instance.busy = true;\n instance.pendingMessages.set(messageId, { resolve, reject });\n\n const message: ThreadMessage = {\n id: messageId,\n type: \"execute\",\n data,\n };\n\n instance.port.postMessage(message);\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n }\n\n this.idleTimer = setTimeout(() => {\n this.cleanupIdleInstances();\n }, this.idleTimeout);\n }\n\n private cleanupIdleInstances(): void {\n const now = Date.now();\n const instancesToRemove = this.instances.filter(\n (instance) =>\n !instance.busy && now - instance.lastUsed > this.idleTimeout,\n );\n\n for (const instance of instancesToRemove) {\n const index = this.instances.indexOf(instance);\n if (index > -1) {\n this.instances.splice(index, 1);\n instance.port.close();\n void instance.worker.terminate();\n }\n }\n\n if (this.instances.length > 0) {\n this.resetIdleTimer();\n }\n }\n\n async terminate(): Promise<void> {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n }\n\n await Promise.all(\n this.instances.map(async (instance) => {\n instance.port.close();\n await instance.worker.terminate();\n }),\n );\n\n this.instances = [];\n this.queue = [];\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n","import { parentPort, workerData } from \"node:worker_threads\";\nimport { $env, $hook, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $thread } from \"../primitives/$thread.ts\";\n\ninterface ThreadMessage<T = any> {\n id: string;\n type: \"execute\" | \"response\" | \"error\" | \"ready\";\n data?: T;\n error?: string;\n}\n\nexport class ThreadProvider {\n protected readonly log = $logger();\n protected readonly env = $env(\n t.object({\n ALEPHA_WORKER: t.optional(t.text()),\n }),\n );\n\n protected readonly ready = $hook({\n on: \"ready\",\n handler: async (alepha) => {\n const worker = this.env.ALEPHA_WORKER;\n if (!worker) {\n return;\n }\n\n const threads = alepha.primitives($thread);\n const threadPrimitive = threads.find((thread) => thread.name === worker);\n\n if (!threadPrimitive) {\n this.log.error(`Thread not found: ${worker}`);\n return;\n }\n\n this.log.info(`Thread ready: ${threadPrimitive.name}`);\n\n // Use the message channel port from worker data if available, fallback to parentPort\n const communicationPort = workerData?.port || parentPort;\n\n if (!communicationPort) {\n this.log.error(\"No communication port available\");\n return;\n }\n\n // Set up message handling\n communicationPort.on(\"message\", async (message: ThreadMessage) => {\n if (message.type === \"execute\") {\n try {\n this.log.debug(`Executing thread handler: ${threadPrimitive.name}`);\n const result = await threadPrimitive.options.handler();\n\n communicationPort.postMessage({\n id: message.id,\n type: \"response\",\n data: result,\n } as ThreadMessage);\n } catch (error) {\n this.log.error(`Thread execution error: ${error}`);\n\n communicationPort.postMessage({\n id: message.id,\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n } as ThreadMessage);\n }\n }\n });\n\n // Signal that the worker is ready\n communicationPort.postMessage({ type: \"ready\" } as ThreadMessage);\n },\n });\n\n public static async cleanup(): Promise<void> {\n if (parentPort) {\n parentPort.removeAllListeners();\n }\n }\n}\n","import { $module, Alepha } from \"alepha\";\nimport { $thread } from \"./primitives/$thread.ts\";\nimport { ThreadProvider } from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$thread.ts\";\nexport * from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Alepha {\n isWorkerThread(): boolean;\n }\n}\n\nAlepha.prototype.isWorkerThread = function (this: Alepha): boolean {\n return !!this.env.ALEPHA_WORKER;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Simple interface for managing worker threads in Alepha.\n *\n * @see {@link $thread}\n * @module alepha.thread\n */\nexport const AlephaThread = $module({\n name: \"alepha.thread\",\n primitives: [$thread],\n services: [ThreadProvider],\n});\n"],"mappings":"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"}
|
package/dist/topic/index.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import * as alepha1 from "alepha";
|
|
2
|
-
import { Alepha,
|
|
2
|
+
import { Alepha, KIND, Primitive, Service, Static, TSchema } from "alepha";
|
|
3
3
|
import { DateTimeProvider, DurationLike } from "alepha/datetime";
|
|
4
4
|
import * as alepha_logger0 from "alepha/logger";
|
|
5
5
|
|
|
6
|
+
//#region src/topic/errors/TopicTimeoutError.d.ts
|
|
7
|
+
declare class TopicTimeoutError extends Error {
|
|
8
|
+
readonly topic: string;
|
|
9
|
+
readonly timeout: number;
|
|
10
|
+
constructor(topic: string, timeout: number);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
6
13
|
//#region src/topic/providers/TopicProvider.d.ts
|
|
7
14
|
/**
|
|
8
15
|
* Base class for topic providers.
|
|
@@ -37,9 +44,9 @@ declare abstract class TopicProvider {
|
|
|
37
44
|
type SubscribeCallback = (message: string) => Promise<void> | void;
|
|
38
45
|
type UnSubscribeFn = () => Promise<void>;
|
|
39
46
|
//#endregion
|
|
40
|
-
//#region src/topic/
|
|
47
|
+
//#region src/topic/primitives/$topic.d.ts
|
|
41
48
|
/**
|
|
42
|
-
* Creates a topic
|
|
49
|
+
* Creates a topic primitive for publish/subscribe messaging and event-driven architecture.
|
|
43
50
|
*
|
|
44
51
|
* Enables decoupled communication through a pub/sub pattern where publishers send messages
|
|
45
52
|
* and multiple subscribers receive them. Supports type-safe messages, real-time delivery,
|
|
@@ -77,10 +84,10 @@ type UnSubscribeFn = () => Promise<void>;
|
|
|
77
84
|
* ```
|
|
78
85
|
*/
|
|
79
86
|
declare const $topic: {
|
|
80
|
-
<T extends TopicMessageSchema>(options:
|
|
81
|
-
[KIND]: typeof
|
|
87
|
+
<T extends TopicMessageSchema>(options: TopicPrimitiveOptions<T>): TopicPrimitive<T>;
|
|
88
|
+
[KIND]: typeof TopicPrimitive;
|
|
82
89
|
};
|
|
83
|
-
interface
|
|
90
|
+
interface TopicPrimitiveOptions<T extends TopicMessageSchema> {
|
|
84
91
|
/**
|
|
85
92
|
* Unique name identifier for the topic.
|
|
86
93
|
*
|
|
@@ -255,7 +262,7 @@ interface TopicDescriptorOptions<T extends TopicMessageSchema> {
|
|
|
255
262
|
*/
|
|
256
263
|
handler?: TopicHandler<T>;
|
|
257
264
|
}
|
|
258
|
-
declare class
|
|
265
|
+
declare class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<TopicPrimitiveOptions<T>> {
|
|
259
266
|
protected readonly log: alepha_logger0.Logger;
|
|
260
267
|
protected readonly dateTimeProvider: DateTimeProvider;
|
|
261
268
|
readonly provider: TopicProvider;
|
|
@@ -280,16 +287,16 @@ interface TopicMessageSchema {
|
|
|
280
287
|
}
|
|
281
288
|
type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (message: TopicMessage<T>) => unknown;
|
|
282
289
|
//#endregion
|
|
283
|
-
//#region src/topic/
|
|
290
|
+
//#region src/topic/primitives/$subscriber.d.ts
|
|
284
291
|
/**
|
|
285
|
-
* Creates a subscriber
|
|
292
|
+
* Creates a subscriber primitive to listen for messages from a specific topic.
|
|
286
293
|
*
|
|
287
294
|
* Provides a dedicated message subscriber that connects to a topic and processes messages
|
|
288
295
|
* with custom handler logic, enabling scalable pub/sub architectures where multiple
|
|
289
296
|
* subscribers can react to the same events independently.
|
|
290
297
|
*
|
|
291
298
|
* **Key Features**
|
|
292
|
-
* - Seamless integration with any $topic
|
|
299
|
+
* - Seamless integration with any $topic primitive
|
|
293
300
|
* - Full type safety inherited from topic schema
|
|
294
301
|
* - Real-time message delivery when events are published
|
|
295
302
|
* - Error isolation between subscribers
|
|
@@ -337,12 +344,12 @@ type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (message:
|
|
|
337
344
|
* ```
|
|
338
345
|
*/
|
|
339
346
|
declare const $subscriber: {
|
|
340
|
-
<T extends TopicMessageSchema>(options:
|
|
341
|
-
[KIND]: typeof
|
|
347
|
+
<T extends TopicMessageSchema>(options: SubscriberPrimitiveOptions<T>): SubscriberPrimitive<T>;
|
|
348
|
+
[KIND]: typeof SubscriberPrimitive;
|
|
342
349
|
};
|
|
343
|
-
interface
|
|
350
|
+
interface SubscriberPrimitiveOptions<T extends TopicMessageSchema> {
|
|
344
351
|
/**
|
|
345
|
-
* The topic
|
|
352
|
+
* The topic primitive that this subscriber will listen to for messages.
|
|
346
353
|
*
|
|
347
354
|
* This establishes the connection between the subscriber and its source topic:
|
|
348
355
|
* - The subscriber inherits the topic's message schema for type safety
|
|
@@ -369,12 +376,12 @@ interface SubscriberDescriptorOptions<T extends TopicMessageSchema> {
|
|
|
369
376
|
*
|
|
370
377
|
* // Then, create a subscriber for that topic
|
|
371
378
|
* activitySubscriber = $subscriber({
|
|
372
|
-
* topic: this.userEvents, // Reference the topic
|
|
379
|
+
* topic: this.userEvents, // Reference the topic primitive
|
|
373
380
|
* handler: async (message) => { } // Process messages here
|
|
374
381
|
* });
|
|
375
382
|
* ```
|
|
376
383
|
*/
|
|
377
|
-
topic:
|
|
384
|
+
topic: TopicPrimitive<T>;
|
|
378
385
|
/**
|
|
379
386
|
* Message handler function that processes individual messages from the topic.
|
|
380
387
|
*
|
|
@@ -471,20 +478,13 @@ interface SubscriberDescriptorOptions<T extends TopicMessageSchema> {
|
|
|
471
478
|
*/
|
|
472
479
|
handler: TopicHandler<T>;
|
|
473
480
|
}
|
|
474
|
-
declare class
|
|
475
|
-
//#endregion
|
|
476
|
-
//#region src/topic/errors/TopicTimeoutError.d.ts
|
|
477
|
-
declare class TopicTimeoutError extends Error {
|
|
478
|
-
readonly topic: string;
|
|
479
|
-
readonly timeout: number;
|
|
480
|
-
constructor(topic: string, timeout: number);
|
|
481
|
-
}
|
|
481
|
+
declare class SubscriberPrimitive<T extends TopicMessageSchema> extends Primitive<SubscriberPrimitiveOptions<T>> {}
|
|
482
482
|
//#endregion
|
|
483
483
|
//#region src/topic/providers/MemoryTopicProvider.d.ts
|
|
484
484
|
declare class MemoryTopicProvider extends TopicProvider {
|
|
485
485
|
protected readonly log: alepha_logger0.Logger;
|
|
486
486
|
protected readonly subscriptions: Record<string, SubscribeCallback[]>;
|
|
487
|
-
protected readonly start: alepha1.
|
|
487
|
+
protected readonly start: alepha1.HookPrimitive<"start">;
|
|
488
488
|
/**
|
|
489
489
|
* Publish a message to a topic.
|
|
490
490
|
*
|
|
@@ -519,5 +519,5 @@ declare class MemoryTopicProvider extends TopicProvider {
|
|
|
519
519
|
*/
|
|
520
520
|
declare const AlephaTopic: alepha1.Service<alepha1.Module>;
|
|
521
521
|
//#endregion
|
|
522
|
-
export { $subscriber, $topic, AlephaTopic, MemoryTopicProvider, SubscribeCallback,
|
|
522
|
+
export { $subscriber, $topic, AlephaTopic, MemoryTopicProvider, SubscribeCallback, SubscriberPrimitive, SubscriberPrimitiveOptions, TopicHandler, TopicMessage, TopicMessageSchema, TopicPrimitive, TopicPrimitiveOptions, TopicProvider, TopicTimeoutError, TopicWaitOptions, UnSubscribeFn };
|
|
523
523
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/topic/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { $hook, $inject, $module, Alepha,
|
|
1
|
+
import { $hook, $inject, $module, Alepha, KIND, Primitive, createPrimitive } from "alepha";
|
|
2
2
|
import { DateTimeProvider } from "alepha/datetime";
|
|
3
3
|
import { $logger } from "alepha/logger";
|
|
4
4
|
|
|
5
|
-
//#region src/topic/
|
|
5
|
+
//#region src/topic/primitives/$subscriber.ts
|
|
6
6
|
/**
|
|
7
|
-
* Creates a subscriber
|
|
7
|
+
* Creates a subscriber primitive to listen for messages from a specific topic.
|
|
8
8
|
*
|
|
9
9
|
* Provides a dedicated message subscriber that connects to a topic and processes messages
|
|
10
10
|
* with custom handler logic, enabling scalable pub/sub architectures where multiple
|
|
11
11
|
* subscribers can react to the same events independently.
|
|
12
12
|
*
|
|
13
13
|
* **Key Features**
|
|
14
|
-
* - Seamless integration with any $topic
|
|
14
|
+
* - Seamless integration with any $topic primitive
|
|
15
15
|
* - Full type safety inherited from topic schema
|
|
16
16
|
* - Real-time message delivery when events are published
|
|
17
17
|
* - Error isolation between subscribers
|
|
@@ -59,10 +59,10 @@ import { $logger } from "alepha/logger";
|
|
|
59
59
|
* ```
|
|
60
60
|
*/
|
|
61
61
|
const $subscriber = (options) => {
|
|
62
|
-
return
|
|
62
|
+
return createPrimitive(SubscriberPrimitive, options);
|
|
63
63
|
};
|
|
64
|
-
var
|
|
65
|
-
$subscriber[KIND] =
|
|
64
|
+
var SubscriberPrimitive = class extends Primitive {};
|
|
65
|
+
$subscriber[KIND] = SubscriberPrimitive;
|
|
66
66
|
|
|
67
67
|
//#endregion
|
|
68
68
|
//#region src/topic/errors/TopicTimeoutError.ts
|
|
@@ -88,13 +88,13 @@ var TopicProvider = class {
|
|
|
88
88
|
*/
|
|
89
89
|
subscribers() {
|
|
90
90
|
const handlers = [];
|
|
91
|
-
const topics = this.alepha.
|
|
91
|
+
const topics = this.alepha.primitives($topic);
|
|
92
92
|
for (const topic of topics) {
|
|
93
93
|
if (topic.provider !== this) continue;
|
|
94
94
|
const handler = topic.options.handler;
|
|
95
95
|
if (handler && topic.provider === this) handlers.push(() => topic.subscribe(handler));
|
|
96
96
|
}
|
|
97
|
-
const subscribers = this.alepha.
|
|
97
|
+
const subscribers = this.alepha.primitives($subscriber);
|
|
98
98
|
for (const subscriber of subscribers) {
|
|
99
99
|
if (subscriber.options.topic.provider !== this) continue;
|
|
100
100
|
handlers.push(() => subscriber.options.topic.subscribe(subscriber.options.handler));
|
|
@@ -155,9 +155,9 @@ var MemoryTopicProvider = class extends TopicProvider {
|
|
|
155
155
|
};
|
|
156
156
|
|
|
157
157
|
//#endregion
|
|
158
|
-
//#region src/topic/
|
|
158
|
+
//#region src/topic/primitives/$topic.ts
|
|
159
159
|
/**
|
|
160
|
-
* Creates a topic
|
|
160
|
+
* Creates a topic primitive for publish/subscribe messaging and event-driven architecture.
|
|
161
161
|
*
|
|
162
162
|
* Enables decoupled communication through a pub/sub pattern where publishers send messages
|
|
163
163
|
* and multiple subscribers receive them. Supports type-safe messages, real-time delivery,
|
|
@@ -195,9 +195,9 @@ var MemoryTopicProvider = class extends TopicProvider {
|
|
|
195
195
|
* ```
|
|
196
196
|
*/
|
|
197
197
|
const $topic = (options) => {
|
|
198
|
-
return
|
|
198
|
+
return createPrimitive(TopicPrimitive, options);
|
|
199
199
|
};
|
|
200
|
-
var
|
|
200
|
+
var TopicPrimitive = class extends Primitive {
|
|
201
201
|
log = $logger();
|
|
202
202
|
dateTimeProvider = $inject(DateTimeProvider);
|
|
203
203
|
provider = this.$provider();
|
|
@@ -246,7 +246,7 @@ var TopicDescriptor = class extends Descriptor {
|
|
|
246
246
|
return { payload: this.alepha.codec.decode(this.options.schema.payload, payload) };
|
|
247
247
|
}
|
|
248
248
|
};
|
|
249
|
-
$topic[KIND] =
|
|
249
|
+
$topic[KIND] = TopicPrimitive;
|
|
250
250
|
|
|
251
251
|
//#endregion
|
|
252
252
|
//#region src/topic/index.ts
|
|
@@ -261,7 +261,7 @@ $topic[KIND] = TopicDescriptor;
|
|
|
261
261
|
*/
|
|
262
262
|
const AlephaTopic = $module({
|
|
263
263
|
name: "alepha.topic",
|
|
264
|
-
|
|
264
|
+
primitives: [$topic, $subscriber],
|
|
265
265
|
services: [TopicProvider, MemoryTopicProvider],
|
|
266
266
|
register: (alepha) => alepha.with({
|
|
267
267
|
optional: true,
|
|
@@ -271,5 +271,5 @@ const AlephaTopic = $module({
|
|
|
271
271
|
});
|
|
272
272
|
|
|
273
273
|
//#endregion
|
|
274
|
-
export { $subscriber, $topic, AlephaTopic, MemoryTopicProvider,
|
|
274
|
+
export { $subscriber, $topic, AlephaTopic, MemoryTopicProvider, SubscriberPrimitive, TopicPrimitive, TopicProvider, TopicTimeoutError };
|
|
275
275
|
//# sourceMappingURL=index.js.map
|
package/dist/topic/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["handlers: Array<() => Promise<unknown>>","ref: { timeout?: Timeout }"],"sources":["../../src/topic/descriptors/$subscriber.ts","../../src/topic/errors/TopicTimeoutError.ts","../../src/topic/providers/TopicProvider.ts","../../src/topic/providers/MemoryTopicProvider.ts","../../src/topic/descriptors/$topic.ts","../../src/topic/index.ts"],"sourcesContent":["import { createDescriptor, Descriptor, KIND } from \"alepha\";\nimport type {\n TopicDescriptor,\n TopicHandler,\n TopicMessageSchema,\n} from \"./$topic.ts\";\n\n/**\n * Creates a subscriber descriptor to listen for messages from a specific topic.\n *\n * Provides a dedicated message subscriber that connects to a topic and processes messages\n * with custom handler logic, enabling scalable pub/sub architectures where multiple\n * subscribers can react to the same events independently.\n *\n * **Key Features**\n * - Seamless integration with any $topic descriptor\n * - Full type safety inherited from topic schema\n * - Real-time message delivery when events are published\n * - Error isolation between subscribers\n * - Support for multiple independent subscribers per topic\n *\n * **Common Use Cases**\n * - Notification services and audit logging\n * - Analytics and metrics collection\n * - Data synchronization and real-time UI updates\n *\n * @example\n * ```ts\n * class UserActivityService {\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * }\n * });\n *\n * activityLogger = $subscriber({\n * topic: this.userEvents,\n * handler: async (message) => {\n * const { userId, action, timestamp } = message.payload;\n * await this.auditLogger.log({\n * userId,\n * action,\n * timestamp\n * });\n * }\n * });\n *\n * async trackUserLogin(userId: string) {\n * await this.userEvents.publish({\n * userId,\n * action: \"login\",\n * timestamp: Date.now()\n * });\n * }\n * }\n * ```\n */\nexport const $subscriber = <T extends TopicMessageSchema>(\n options: SubscriberDescriptorOptions<T>,\n): SubscriberDescriptor<T> => {\n return createDescriptor(SubscriberDescriptor<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SubscriberDescriptorOptions<T extends TopicMessageSchema> {\n /**\n * The topic descriptor that this subscriber will listen to for messages.\n *\n * This establishes the connection between the subscriber and its source topic:\n * - The subscriber inherits the topic's message schema for type safety\n * - Messages published to the topic will be automatically delivered to this subscriber\n * - Multiple subscribers can listen to the same topic independently\n * - The subscriber will use the topic's provider and configuration settings\n *\n * **Topic Integration Benefits**:\n * - Type safety: Subscriber handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the subscriber\n * - Real-time delivery: Messages are delivered immediately upon publication\n * - Error isolation: Subscriber errors don't affect the topic or other subscribers\n * - Monitoring: Topic metrics include subscriber processing statistics\n *\n * @example\n * ```ts\n * // First, define a topic\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({ userId: t.text(), action: t.text() })\n * }\n * });\n *\n * // Then, create a subscriber for that topic\n * activitySubscriber = $subscriber({\n * topic: this.userEvents, // Reference the topic descriptor\n * handler: async (message) => { } // Process messages here\n * });\n * ```\n */\n topic: TopicDescriptor<T>;\n\n /**\n * Message handler function that processes individual messages from the topic.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected topic\n * - Executes immediately when messages are published to the topic\n * - Should implement the core business logic for reacting to these events\n * - Runs independently of other subscribers to the same topic\n * - Should handle errors gracefully to avoid affecting other subscribers\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible for reliability\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging and monitoring\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Implement circuit breakers for resilience with external systems\n * - Monitor error rates and patterns for system health\n * - Consider implementing retry logic for temporary failures\n *\n * **Performance Considerations**:\n * - Keep handler execution time minimal for high-throughput topics\n * - Use background queues for heavy processing triggered by events\n * - Implement batching for efficiency when processing many similar events\n * - Consider async processing patterns for non-critical operations\n *\n * @param message - The topic message with validated payload and optional headers\n * @param message.payload - The typed message data based on the topic's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, eventType, timestamp } = message.payload;\n *\n * try {\n * // Log event receipt\n * this.logger.info(`Processing ${eventType} event for user ${userId}`, {\n * timestamp,\n * userId,\n * eventType\n * });\n *\n * // Perform event-specific processing\n * switch (eventType) {\n * case 'user.login':\n * await this.updateLastLogin(userId, timestamp);\n * await this.sendWelcomeNotification(userId);\n * break;\n * case 'user.logout':\n * await this.updateSessionDuration(userId, timestamp);\n * break;\n * case 'user.purchase':\n * await this.updateRewardsPoints(userId, message.payload.purchaseAmount);\n * await this.triggerRecommendations(userId);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * // Update analytics\n * await this.analytics.track(eventType, {\n * userId,\n * timestamp,\n * source: 'topic-subscriber'\n * });\n *\n * this.logger.info(`Successfully processed ${eventType} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} for user ${userId}`, {\n * error: error.message,\n * stack: error.stack,\n * userId,\n * eventType,\n * timestamp\n * });\n *\n * // Optionally send to error tracking service\n * await this.errorTracker.captureException(error, {\n * context: { userId, eventType, timestamp },\n * tags: { component: 'topic-subscriber' }\n * });\n * }\n * }\n * ```\n */\n handler: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class SubscriberDescriptor<\n T extends TopicMessageSchema,\n> extends Descriptor<SubscriberDescriptorOptions<T>> {}\n\n$subscriber[KIND] = SubscriberDescriptor;\n","export class TopicTimeoutError extends Error {\n public readonly topic: string;\n public readonly timeout: number;\n\n constructor(topic: string, timeout: number) {\n super(`Timeout of ${timeout}ms exceeded for topic ${topic}`);\n this.timeout = timeout;\n this.topic = topic;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $subscriber } from \"../descriptors/$subscriber.ts\";\nimport { $topic } from \"../descriptors/$topic.ts\";\n\n/**\n * Base class for topic providers.\n */\nexport abstract class TopicProvider {\n protected readonly alepha = $inject(Alepha);\n\n /**\n * Publish a message to a topic.\n *\n * @param topic - The topic to publish to.\n * @param message - The message to publish.\n */\n public abstract publish(topic: string, message: string): Promise<void>;\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback - The callback to call when a message is received.\n */\n public abstract subscribe(\n topic: string,\n callback: SubscribeCallback,\n ): Promise<UnSubscribeFn>;\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public abstract unsubscribe(topic: string): Promise<void>;\n\n /**\n * Returns the list of $subscribers for this provider.\n */\n protected subscribers(): Array<() => Promise<unknown>> {\n const handlers: Array<() => Promise<unknown>> = [];\n\n const topics = this.alepha.descriptors($topic);\n\n for (const topic of topics) {\n if (topic.provider !== this) {\n continue;\n }\n\n const handler = topic.options.handler;\n if (handler && topic.provider === this) {\n handlers.push(() => topic.subscribe(handler));\n }\n }\n\n const subscribers = this.alepha.descriptors($subscriber);\n for (const subscriber of subscribers) {\n if (subscriber.options.topic.provider !== this) {\n continue;\n }\n\n handlers.push(() =>\n subscriber.options.topic.subscribe(subscriber.options.handler),\n );\n }\n\n return handlers;\n }\n}\n\nexport type SubscribeCallback = (message: string) => Promise<void> | void;\n\nexport type UnSubscribeFn = () => Promise<void>;\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type UnSubscribeFn,\n} from \"./TopicProvider.ts\";\n\nexport class MemoryTopicProvider extends TopicProvider {\n protected readonly log = $logger();\n protected readonly subscriptions: Record<string, SubscribeCallback[]> = {};\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n /**\n * Publish a message to a topic.\n *\n * @param topic\n * @param message\n */\n public async publish(topic: string, message: string): Promise<void> {\n if (!this.subscriptions[topic]) {\n return;\n }\n\n for (const callback of this.subscriptions[topic]) {\n await callback(message);\n }\n }\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback\n */\n\n public async subscribe(\n topic: string,\n callback: SubscribeCallback,\n ): Promise<UnSubscribeFn> {\n if (!this.subscriptions[topic]) {\n this.subscriptions[topic] = [];\n }\n\n this.subscriptions[topic].push(callback);\n\n return async () => {\n const callbacks = this.subscriptions[topic];\n if (!callbacks) {\n return;\n }\n\n this.subscriptions[topic] = callbacks.filter((cb) => cb !== callback);\n if (this.subscriptions[topic].length === 0) {\n delete this.subscriptions[topic];\n }\n };\n }\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public async unsubscribe(topic: string): Promise<void> {\n delete this.subscriptions[topic];\n }\n}\n","import {\n $inject,\n createDescriptor,\n Descriptor,\n KIND,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport {\n DateTimeProvider,\n type DurationLike,\n type Timeout,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { TopicTimeoutError } from \"../errors/TopicTimeoutError.ts\";\nimport { MemoryTopicProvider } from \"../providers/MemoryTopicProvider.ts\";\nimport {\n TopicProvider,\n type UnSubscribeFn,\n} from \"../providers/TopicProvider.ts\";\n\n/**\n * Creates a topic descriptor for publish/subscribe messaging and event-driven architecture.\n *\n * Enables decoupled communication through a pub/sub pattern where publishers send messages\n * and multiple subscribers receive them. Supports type-safe messages, real-time delivery,\n * event filtering, and pluggable backends (memory, Redis, custom providers).\n *\n * **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication\n *\n * @example\n * ```ts\n * class NotificationService {\n * userActivity = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * },\n * handler: async (message) => {\n * console.log(`User ${message.payload.userId}: ${message.payload.action}`);\n * }\n * });\n *\n * async trackLogin(userId: string) {\n * await this.userActivity.publish({ userId, action: \"login\", timestamp: Date.now() });\n * }\n *\n * async subscribeToEvents() {\n * await this.userActivity.subscribe(async (message) => {\n * // Additional subscriber logic\n * });\n * }\n * }\n * ```\n */\nexport const $topic = <T extends TopicMessageSchema>(\n options: TopicDescriptorOptions<T>,\n): TopicDescriptor<T> => {\n return createDescriptor(TopicDescriptor<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicDescriptorOptions<T extends TopicMessageSchema> {\n /**\n * Unique name identifier for the topic.\n *\n * This name is used for:\n * - Topic identification across the pub/sub system\n * - Message routing between publishers and subscribers\n * - Logging and debugging topic-related operations\n * - Provider-specific topic management (channels, keys, etc.)\n *\n * If not provided, defaults to the property key where the topic is declared.\n *\n * **Naming Conventions**:\n * - Use descriptive, hierarchical names: \"user.activity\", \"order.events\"\n * - Avoid spaces and special characters\n * - Consider using dot notation for categorization\n * - Keep names concise but meaningful\n *\n * @example \"user-activity\"\n * @example \"chat.messages\"\n * @example \"system.health.checks\"\n * @example \"payment.webhooks\"\n */\n name?: string;\n\n /**\n * Human-readable description of the topic's purpose and usage.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and understanding\n * - Monitoring dashboards and admin interfaces\n * - Team communication about system architecture\n *\n * **Description Best Practices**:\n * - Explain what events/messages this topic handles\n * - Mention key use cases and subscribers\n * - Include any important timing or ordering guarantees\n * - Note any special processing requirements\n *\n * @example \"Real-time user activity events for analytics and notifications\"\n * @example \"Order lifecycle events from creation to delivery\"\n * @example \"Chat messages broadcast to all room participants\"\n * @example \"System health checks and service status updates\"\n */\n description?: string;\n\n /**\n * Topic provider configuration for message storage and delivery.\n *\n * Options:\n * - **\"memory\"**: In-memory provider (default for development, lost on restart)\n * - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)\n * - **undefined**: Uses the default topic provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use Redis or message brokers for persistence and scalability\n * - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication\n * - **High-throughput**: Use specialized providers with connection pooling\n * - **Real-time**: Ensure provider supports low-latency message delivery\n *\n * **Provider Capabilities**:\n * - Message persistence and durability\n * - Subscriber management and connection handling\n * - Message ordering and delivery guarantees\n * - Horizontal scaling and load distribution\n *\n * @default Uses injected TopicProvider\n * @example \"memory\"\n * @example RedisTopicProvider\n * @example RabbitMQTopicProvider\n */\n provider?: \"memory\" | Service<TopicProvider>;\n\n /**\n * TypeBox schema defining the structure of messages published to this topic.\n *\n * The schema must include:\n * - **payload**: Required schema for the main message data\n * - **headers**: Optional schema for message metadata\n *\n * This schema:\n * - Validates all messages published to the topic\n * - Provides full TypeScript type inference for subscribers\n * - Ensures type safety between publishers and subscribers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep payload schemas focused and cohesive\n * - Use optional fields for data that might not always be present\n * - Include timestamp fields for event ordering\n * - Consider versioning for schema evolution\n * - Use union types for different event types in the same topic\n *\n * @example\n * ```ts\n * {\n * payload: t.object({\n * eventId: t.text(),\n * eventType: t.enum([\"created\", \"updated\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.number(),\n * userId: t.optional(t.text())\n * }),\n * headers: t.optional(t.object({\n * source: t.text(),\n * correlationId: t.text()\n * }))\n * }\n * ```\n */\n schema: T;\n\n /**\n * Default subscriber handler function that processes messages published to this topic.\n *\n * This handler:\n * - Automatically subscribes when the topic is initialized\n * - Receives all messages published to the topic\n * - Runs for every message without additional subscription setup\n * - Can be supplemented with additional subscribers via `subscribe()` method\n * - Should handle errors gracefully to avoid breaking other subscribers\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Consider implementing circuit breakers for resilience\n * - Monitor error rates and patterns for system health\n *\n * @param message - The topic message with validated payload and headers\n * @param message.payload - The typed message data based on the schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { eventType, data, timestamp } = message.payload;\n *\n * try {\n * // Log message receipt\n * this.logger.info(`Processing ${eventType} event`, { timestamp, data });\n *\n * // Process based on event type\n * switch (eventType) {\n * case \"created\":\n * await this.handleCreation(data);\n * break;\n * case \"updated\":\n * await this.handleUpdate(data);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * this.logger.info(`Successfully processed ${eventType} event`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} event`, {\n * error: error.message,\n * eventType,\n * timestamp,\n * data\n * });\n * }\n * }\n * ```\n */\n handler?: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class TopicDescriptor<T extends TopicMessageSchema> extends Descriptor<\n TopicDescriptorOptions<T>\n> {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n public readonly provider = this.$provider();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public async publish(payload: TopicMessage<T>[\"payload\"]): Promise<void> {\n await this.provider.publish(\n this.name,\n JSON.stringify({\n payload: this.alepha.codec.encode(this.options.schema.payload, payload),\n }),\n );\n }\n\n public async subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn> {\n return this.provider.subscribe(this.name, async (message) => {\n try {\n await handler(this.parseMessage(message));\n } catch (error) {\n this.log.error(\"Message processing has failed\", error);\n }\n });\n }\n\n public async wait(\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n const filter = options.filter ?? (() => true);\n\n return new Promise((resolve, reject) => {\n const ref: { timeout?: Timeout } = {};\n\n (async () => {\n const clear = await this.provider.subscribe(this.name, (raw) => {\n const message = this.parseMessage(raw);\n if (!filter(message)) {\n return;\n }\n\n ref.timeout?.clear();\n clear();\n resolve(message);\n });\n\n const timeoutDuration = options.timeout ?? [10, \"seconds\"];\n\n ref.timeout = this.dateTimeProvider.createTimeout(() => {\n clear();\n reject(\n new TopicTimeoutError(\n this.name,\n this.dateTimeProvider.duration(timeoutDuration).asMilliseconds(),\n ),\n );\n }, timeoutDuration);\n })();\n });\n }\n\n protected $provider(): TopicProvider {\n if (!this.options.provider) {\n return this.alepha.inject(TopicProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryTopicProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n\n protected parseMessage(message: string): TopicMessage<T> {\n const { payload } = JSON.parse(message);\n return {\n payload: this.alepha.codec.decode(\n this.options.schema.payload,\n payload,\n ) as TopicMessage<T>[\"payload\"],\n };\n }\n}\n\n$topic[KIND] = TopicDescriptor;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicMessage<T extends TopicMessageSchema> {\n payload: Static<T[\"payload\"]>;\n}\n\nexport interface TopicWaitOptions<T extends TopicMessageSchema> {\n timeout?: DurationLike;\n filter?: (message: { payload: Static<T[\"payload\"]> }) => boolean;\n}\n\nexport interface TopicMessageSchema {\n payload: TSchema;\n}\n\nexport type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (\n message: TopicMessage<T>,\n) => unknown;\n","import { $module, type Alepha } from \"alepha\";\nimport { $subscriber } from \"./descriptors/$subscriber.ts\";\nimport { $topic } from \"./descriptors/$topic.ts\";\nimport { MemoryTopicProvider } from \"./providers/MemoryTopicProvider.ts\";\nimport { TopicProvider } from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$subscriber.ts\";\nexport * from \"./descriptors/$topic.ts\";\nexport * from \"./errors/TopicTimeoutError.ts\";\nexport * from \"./providers/MemoryTopicProvider.ts\";\nexport * from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Generic interface for pub/sub messaging.\n * Gives you the ability to create topics and subscribers.\n * This module provides only a memory implementation of the topic provider.\n *\n * @see {@link $topic}\n * @see {@link $subscriber}\n * @module alepha.topic\n */\nexport const AlephaTopic = $module({\n name: \"alepha.topic\",\n descriptors: [$topic, $subscriber],\n services: [TopicProvider, MemoryTopicProvider],\n register: (alepha: Alepha) =>\n alepha.with({\n optional: true,\n provide: TopicProvider,\n use: MemoryTopicProvider,\n }),\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["handlers: Array<() => Promise<unknown>>","ref: { timeout?: Timeout }"],"sources":["../../src/topic/primitives/$subscriber.ts","../../src/topic/errors/TopicTimeoutError.ts","../../src/topic/providers/TopicProvider.ts","../../src/topic/providers/MemoryTopicProvider.ts","../../src/topic/primitives/$topic.ts","../../src/topic/index.ts"],"sourcesContent":["import { createPrimitive, KIND, Primitive } from \"alepha\";\nimport type {\n TopicHandler,\n TopicMessageSchema,\n TopicPrimitive,\n} from \"./$topic.ts\";\n\n/**\n * Creates a subscriber primitive to listen for messages from a specific topic.\n *\n * Provides a dedicated message subscriber that connects to a topic and processes messages\n * with custom handler logic, enabling scalable pub/sub architectures where multiple\n * subscribers can react to the same events independently.\n *\n * **Key Features**\n * - Seamless integration with any $topic primitive\n * - Full type safety inherited from topic schema\n * - Real-time message delivery when events are published\n * - Error isolation between subscribers\n * - Support for multiple independent subscribers per topic\n *\n * **Common Use Cases**\n * - Notification services and audit logging\n * - Analytics and metrics collection\n * - Data synchronization and real-time UI updates\n *\n * @example\n * ```ts\n * class UserActivityService {\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * }\n * });\n *\n * activityLogger = $subscriber({\n * topic: this.userEvents,\n * handler: async (message) => {\n * const { userId, action, timestamp } = message.payload;\n * await this.auditLogger.log({\n * userId,\n * action,\n * timestamp\n * });\n * }\n * });\n *\n * async trackUserLogin(userId: string) {\n * await this.userEvents.publish({\n * userId,\n * action: \"login\",\n * timestamp: Date.now()\n * });\n * }\n * }\n * ```\n */\nexport const $subscriber = <T extends TopicMessageSchema>(\n options: SubscriberPrimitiveOptions<T>,\n): SubscriberPrimitive<T> => {\n return createPrimitive(SubscriberPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SubscriberPrimitiveOptions<T extends TopicMessageSchema> {\n /**\n * The topic primitive that this subscriber will listen to for messages.\n *\n * This establishes the connection between the subscriber and its source topic:\n * - The subscriber inherits the topic's message schema for type safety\n * - Messages published to the topic will be automatically delivered to this subscriber\n * - Multiple subscribers can listen to the same topic independently\n * - The subscriber will use the topic's provider and configuration settings\n *\n * **Topic Integration Benefits**:\n * - Type safety: Subscriber handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the subscriber\n * - Real-time delivery: Messages are delivered immediately upon publication\n * - Error isolation: Subscriber errors don't affect the topic or other subscribers\n * - Monitoring: Topic metrics include subscriber processing statistics\n *\n * @example\n * ```ts\n * // First, define a topic\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({ userId: t.text(), action: t.text() })\n * }\n * });\n *\n * // Then, create a subscriber for that topic\n * activitySubscriber = $subscriber({\n * topic: this.userEvents, // Reference the topic primitive\n * handler: async (message) => { } // Process messages here\n * });\n * ```\n */\n topic: TopicPrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the topic.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected topic\n * - Executes immediately when messages are published to the topic\n * - Should implement the core business logic for reacting to these events\n * - Runs independently of other subscribers to the same topic\n * - Should handle errors gracefully to avoid affecting other subscribers\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible for reliability\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging and monitoring\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Implement circuit breakers for resilience with external systems\n * - Monitor error rates and patterns for system health\n * - Consider implementing retry logic for temporary failures\n *\n * **Performance Considerations**:\n * - Keep handler execution time minimal for high-throughput topics\n * - Use background queues for heavy processing triggered by events\n * - Implement batching for efficiency when processing many similar events\n * - Consider async processing patterns for non-critical operations\n *\n * @param message - The topic message with validated payload and optional headers\n * @param message.payload - The typed message data based on the topic's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, eventType, timestamp } = message.payload;\n *\n * try {\n * // Log event receipt\n * this.logger.info(`Processing ${eventType} event for user ${userId}`, {\n * timestamp,\n * userId,\n * eventType\n * });\n *\n * // Perform event-specific processing\n * switch (eventType) {\n * case 'user.login':\n * await this.updateLastLogin(userId, timestamp);\n * await this.sendWelcomeNotification(userId);\n * break;\n * case 'user.logout':\n * await this.updateSessionDuration(userId, timestamp);\n * break;\n * case 'user.purchase':\n * await this.updateRewardsPoints(userId, message.payload.purchaseAmount);\n * await this.triggerRecommendations(userId);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * // Update analytics\n * await this.analytics.track(eventType, {\n * userId,\n * timestamp,\n * source: 'topic-subscriber'\n * });\n *\n * this.logger.info(`Successfully processed ${eventType} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} for user ${userId}`, {\n * error: error.message,\n * stack: error.stack,\n * userId,\n * eventType,\n * timestamp\n * });\n *\n * // Optionally send to error tracking service\n * await this.errorTracker.captureException(error, {\n * context: { userId, eventType, timestamp },\n * tags: { component: 'topic-subscriber' }\n * });\n * }\n * }\n * ```\n */\n handler: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class SubscriberPrimitive<\n T extends TopicMessageSchema,\n> extends Primitive<SubscriberPrimitiveOptions<T>> {}\n\n$subscriber[KIND] = SubscriberPrimitive;\n","export class TopicTimeoutError extends Error {\n public readonly topic: string;\n public readonly timeout: number;\n\n constructor(topic: string, timeout: number) {\n super(`Timeout of ${timeout}ms exceeded for topic ${topic}`);\n this.timeout = timeout;\n this.topic = topic;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $subscriber } from \"../primitives/$subscriber.ts\";\nimport { $topic } from \"../primitives/$topic.ts\";\n\n/**\n * Base class for topic providers.\n */\nexport abstract class TopicProvider {\n protected readonly alepha = $inject(Alepha);\n\n /**\n * Publish a message to a topic.\n *\n * @param topic - The topic to publish to.\n * @param message - The message to publish.\n */\n public abstract publish(topic: string, message: string): Promise<void>;\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback - The callback to call when a message is received.\n */\n public abstract subscribe(\n topic: string,\n callback: SubscribeCallback,\n ): Promise<UnSubscribeFn>;\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public abstract unsubscribe(topic: string): Promise<void>;\n\n /**\n * Returns the list of $subscribers for this provider.\n */\n protected subscribers(): Array<() => Promise<unknown>> {\n const handlers: Array<() => Promise<unknown>> = [];\n\n const topics = this.alepha.primitives($topic);\n\n for (const topic of topics) {\n if (topic.provider !== this) {\n continue;\n }\n\n const handler = topic.options.handler;\n if (handler && topic.provider === this) {\n handlers.push(() => topic.subscribe(handler));\n }\n }\n\n const subscribers = this.alepha.primitives($subscriber);\n for (const subscriber of subscribers) {\n if (subscriber.options.topic.provider !== this) {\n continue;\n }\n\n handlers.push(() =>\n subscriber.options.topic.subscribe(subscriber.options.handler),\n );\n }\n\n return handlers;\n }\n}\n\nexport type SubscribeCallback = (message: string) => Promise<void> | void;\n\nexport type UnSubscribeFn = () => Promise<void>;\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type UnSubscribeFn,\n} from \"./TopicProvider.ts\";\n\nexport class MemoryTopicProvider extends TopicProvider {\n protected readonly log = $logger();\n protected readonly subscriptions: Record<string, SubscribeCallback[]> = {};\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n /**\n * Publish a message to a topic.\n *\n * @param topic\n * @param message\n */\n public async publish(topic: string, message: string): Promise<void> {\n if (!this.subscriptions[topic]) {\n return;\n }\n\n for (const callback of this.subscriptions[topic]) {\n await callback(message);\n }\n }\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback\n */\n\n public async subscribe(\n topic: string,\n callback: SubscribeCallback,\n ): Promise<UnSubscribeFn> {\n if (!this.subscriptions[topic]) {\n this.subscriptions[topic] = [];\n }\n\n this.subscriptions[topic].push(callback);\n\n return async () => {\n const callbacks = this.subscriptions[topic];\n if (!callbacks) {\n return;\n }\n\n this.subscriptions[topic] = callbacks.filter((cb) => cb !== callback);\n if (this.subscriptions[topic].length === 0) {\n delete this.subscriptions[topic];\n }\n };\n }\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public async unsubscribe(topic: string): Promise<void> {\n delete this.subscriptions[topic];\n }\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport {\n DateTimeProvider,\n type DurationLike,\n type Timeout,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { TopicTimeoutError } from \"../errors/TopicTimeoutError.ts\";\nimport { MemoryTopicProvider } from \"../providers/MemoryTopicProvider.ts\";\nimport {\n TopicProvider,\n type UnSubscribeFn,\n} from \"../providers/TopicProvider.ts\";\n\n/**\n * Creates a topic primitive for publish/subscribe messaging and event-driven architecture.\n *\n * Enables decoupled communication through a pub/sub pattern where publishers send messages\n * and multiple subscribers receive them. Supports type-safe messages, real-time delivery,\n * event filtering, and pluggable backends (memory, Redis, custom providers).\n *\n * **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication\n *\n * @example\n * ```ts\n * class NotificationService {\n * userActivity = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * },\n * handler: async (message) => {\n * console.log(`User ${message.payload.userId}: ${message.payload.action}`);\n * }\n * });\n *\n * async trackLogin(userId: string) {\n * await this.userActivity.publish({ userId, action: \"login\", timestamp: Date.now() });\n * }\n *\n * async subscribeToEvents() {\n * await this.userActivity.subscribe(async (message) => {\n * // Additional subscriber logic\n * });\n * }\n * }\n * ```\n */\nexport const $topic = <T extends TopicMessageSchema>(\n options: TopicPrimitiveOptions<T>,\n): TopicPrimitive<T> => {\n return createPrimitive(TopicPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicPrimitiveOptions<T extends TopicMessageSchema> {\n /**\n * Unique name identifier for the topic.\n *\n * This name is used for:\n * - Topic identification across the pub/sub system\n * - Message routing between publishers and subscribers\n * - Logging and debugging topic-related operations\n * - Provider-specific topic management (channels, keys, etc.)\n *\n * If not provided, defaults to the property key where the topic is declared.\n *\n * **Naming Conventions**:\n * - Use descriptive, hierarchical names: \"user.activity\", \"order.events\"\n * - Avoid spaces and special characters\n * - Consider using dot notation for categorization\n * - Keep names concise but meaningful\n *\n * @example \"user-activity\"\n * @example \"chat.messages\"\n * @example \"system.health.checks\"\n * @example \"payment.webhooks\"\n */\n name?: string;\n\n /**\n * Human-readable description of the topic's purpose and usage.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and understanding\n * - Monitoring dashboards and admin interfaces\n * - Team communication about system architecture\n *\n * **Description Best Practices**:\n * - Explain what events/messages this topic handles\n * - Mention key use cases and subscribers\n * - Include any important timing or ordering guarantees\n * - Note any special processing requirements\n *\n * @example \"Real-time user activity events for analytics and notifications\"\n * @example \"Order lifecycle events from creation to delivery\"\n * @example \"Chat messages broadcast to all room participants\"\n * @example \"System health checks and service status updates\"\n */\n description?: string;\n\n /**\n * Topic provider configuration for message storage and delivery.\n *\n * Options:\n * - **\"memory\"**: In-memory provider (default for development, lost on restart)\n * - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)\n * - **undefined**: Uses the default topic provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use Redis or message brokers for persistence and scalability\n * - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication\n * - **High-throughput**: Use specialized providers with connection pooling\n * - **Real-time**: Ensure provider supports low-latency message delivery\n *\n * **Provider Capabilities**:\n * - Message persistence and durability\n * - Subscriber management and connection handling\n * - Message ordering and delivery guarantees\n * - Horizontal scaling and load distribution\n *\n * @default Uses injected TopicProvider\n * @example \"memory\"\n * @example RedisTopicProvider\n * @example RabbitMQTopicProvider\n */\n provider?: \"memory\" | Service<TopicProvider>;\n\n /**\n * TypeBox schema defining the structure of messages published to this topic.\n *\n * The schema must include:\n * - **payload**: Required schema for the main message data\n * - **headers**: Optional schema for message metadata\n *\n * This schema:\n * - Validates all messages published to the topic\n * - Provides full TypeScript type inference for subscribers\n * - Ensures type safety between publishers and subscribers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep payload schemas focused and cohesive\n * - Use optional fields for data that might not always be present\n * - Include timestamp fields for event ordering\n * - Consider versioning for schema evolution\n * - Use union types for different event types in the same topic\n *\n * @example\n * ```ts\n * {\n * payload: t.object({\n * eventId: t.text(),\n * eventType: t.enum([\"created\", \"updated\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.number(),\n * userId: t.optional(t.text())\n * }),\n * headers: t.optional(t.object({\n * source: t.text(),\n * correlationId: t.text()\n * }))\n * }\n * ```\n */\n schema: T;\n\n /**\n * Default subscriber handler function that processes messages published to this topic.\n *\n * This handler:\n * - Automatically subscribes when the topic is initialized\n * - Receives all messages published to the topic\n * - Runs for every message without additional subscription setup\n * - Can be supplemented with additional subscribers via `subscribe()` method\n * - Should handle errors gracefully to avoid breaking other subscribers\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Consider implementing circuit breakers for resilience\n * - Monitor error rates and patterns for system health\n *\n * @param message - The topic message with validated payload and headers\n * @param message.payload - The typed message data based on the schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { eventType, data, timestamp } = message.payload;\n *\n * try {\n * // Log message receipt\n * this.logger.info(`Processing ${eventType} event`, { timestamp, data });\n *\n * // Process based on event type\n * switch (eventType) {\n * case \"created\":\n * await this.handleCreation(data);\n * break;\n * case \"updated\":\n * await this.handleUpdate(data);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * this.logger.info(`Successfully processed ${eventType} event`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} event`, {\n * error: error.message,\n * eventType,\n * timestamp,\n * data\n * });\n * }\n * }\n * ```\n */\n handler?: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<\n TopicPrimitiveOptions<T>\n> {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n public readonly provider = this.$provider();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public async publish(payload: TopicMessage<T>[\"payload\"]): Promise<void> {\n await this.provider.publish(\n this.name,\n JSON.stringify({\n payload: this.alepha.codec.encode(this.options.schema.payload, payload),\n }),\n );\n }\n\n public async subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn> {\n return this.provider.subscribe(this.name, async (message) => {\n try {\n await handler(this.parseMessage(message));\n } catch (error) {\n this.log.error(\"Message processing has failed\", error);\n }\n });\n }\n\n public async wait(\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n const filter = options.filter ?? (() => true);\n\n return new Promise((resolve, reject) => {\n const ref: { timeout?: Timeout } = {};\n\n (async () => {\n const clear = await this.provider.subscribe(this.name, (raw) => {\n const message = this.parseMessage(raw);\n if (!filter(message)) {\n return;\n }\n\n ref.timeout?.clear();\n clear();\n resolve(message);\n });\n\n const timeoutDuration = options.timeout ?? [10, \"seconds\"];\n\n ref.timeout = this.dateTimeProvider.createTimeout(() => {\n clear();\n reject(\n new TopicTimeoutError(\n this.name,\n this.dateTimeProvider.duration(timeoutDuration).asMilliseconds(),\n ),\n );\n }, timeoutDuration);\n })();\n });\n }\n\n protected $provider(): TopicProvider {\n if (!this.options.provider) {\n return this.alepha.inject(TopicProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryTopicProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n\n protected parseMessage(message: string): TopicMessage<T> {\n const { payload } = JSON.parse(message);\n return {\n payload: this.alepha.codec.decode(\n this.options.schema.payload,\n payload,\n ) as TopicMessage<T>[\"payload\"],\n };\n }\n}\n\n$topic[KIND] = TopicPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicMessage<T extends TopicMessageSchema> {\n payload: Static<T[\"payload\"]>;\n}\n\nexport interface TopicWaitOptions<T extends TopicMessageSchema> {\n timeout?: DurationLike;\n filter?: (message: { payload: Static<T[\"payload\"]> }) => boolean;\n}\n\nexport interface TopicMessageSchema {\n payload: TSchema;\n}\n\nexport type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (\n message: TopicMessage<T>,\n) => unknown;\n","import { $module, type Alepha } from \"alepha\";\nimport { $subscriber } from \"./primitives/$subscriber.ts\";\nimport { $topic } from \"./primitives/$topic.ts\";\nimport { MemoryTopicProvider } from \"./providers/MemoryTopicProvider.ts\";\nimport { TopicProvider } from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/TopicTimeoutError.ts\";\nexport * from \"./primitives/$subscriber.ts\";\nexport * from \"./primitives/$topic.ts\";\nexport * from \"./providers/MemoryTopicProvider.ts\";\nexport * from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Generic interface for pub/sub messaging.\n * Gives you the ability to create topics and subscribers.\n * This module provides only a memory implementation of the topic provider.\n *\n * @see {@link $topic}\n * @see {@link $subscriber}\n * @module alepha.topic\n */\nexport const AlephaTopic = $module({\n name: \"alepha.topic\",\n primitives: [$topic, $subscriber],\n services: [TopicProvider, MemoryTopicProvider],\n register: (alepha: Alepha) =>\n alepha.with({\n optional: true,\n provide: TopicProvider,\n use: MemoryTopicProvider,\n }),\n});\n"],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ declare class RedisTopicProvider extends TopicProvider {
|
|
|
13
13
|
protected readonly redisProvider: RedisProvider;
|
|
14
14
|
protected readonly redisSubscriberProvider: RedisSubscriberProvider;
|
|
15
15
|
protected readonly log: alepha_logger0.Logger;
|
|
16
|
-
protected readonly start: alepha1.
|
|
16
|
+
protected readonly start: alepha1.HookPrimitive<"start">;
|
|
17
17
|
prefix(queue: string): string;
|
|
18
18
|
/**
|
|
19
19
|
* Publish a message to a topic.
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -277,10 +277,10 @@ interface GenerateSitemapOptions {
|
|
|
277
277
|
template?: string;
|
|
278
278
|
}
|
|
279
279
|
/**
|
|
280
|
-
* Generate sitemap.xml from Alepha page
|
|
280
|
+
* Generate sitemap.xml from Alepha page primitives.
|
|
281
281
|
*
|
|
282
282
|
* This task loads the built Alepha application,
|
|
283
|
-
* queries all page
|
|
283
|
+
* queries all page primitives, and generates a sitemap.xml
|
|
284
284
|
* containing URLs for all accessible pages.
|
|
285
285
|
*/
|
|
286
286
|
declare function generateSitemap(opts: GenerateSitemapOptions): Promise<string>;
|
|
@@ -350,7 +350,7 @@ interface PrerenderPagesResult {
|
|
|
350
350
|
* Pre-render static pages defined in the Alepha application.
|
|
351
351
|
*
|
|
352
352
|
* This task loads the built Alepha application, queries all page
|
|
353
|
-
*
|
|
353
|
+
* primitives with `static: true`, and generates static HTML files
|
|
354
354
|
* for each page. Supports pages with parameterized routes via
|
|
355
355
|
* `static.entries` configuration.
|
|
356
356
|
*/
|
package/dist/vite/index.js
CHANGED
|
@@ -39,23 +39,22 @@ const getServerEntry = async (root = process.cwd(), explicitEntry) => {
|
|
|
39
39
|
throw new AlephaError(`Explicit server entry file "${explicitEntry}" not found.`);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
"src/index.server.ts",
|
|
42
|
+
const maybeEntry = [
|
|
44
43
|
"src/main.server.ts",
|
|
45
44
|
"src/server-entry.ts",
|
|
46
|
-
"src/index.server.tsx",
|
|
47
45
|
"src/main.server.tsx",
|
|
48
46
|
"src/server-entry.tsx",
|
|
49
47
|
"src/main.ts",
|
|
50
48
|
"src/main.tsx"
|
|
51
|
-
]
|
|
49
|
+
];
|
|
50
|
+
for (const entry of maybeEntry) try {
|
|
52
51
|
const path$1 = join(root, entry).replace(/\\/g, "/");
|
|
53
52
|
await access(path$1);
|
|
54
53
|
return path$1;
|
|
55
54
|
} catch {}
|
|
56
55
|
const clientEntry = await getClientEntry(root);
|
|
57
56
|
if (clientEntry) return clientEntry;
|
|
58
|
-
throw new AlephaError(`Could not find a server entry file
|
|
57
|
+
throw new AlephaError(`Could not find a server entry file. List of supported entry file: ${maybeEntry.join(", ")}`);
|
|
59
58
|
};
|
|
60
59
|
/**
|
|
61
60
|
* Extract first module script src from HTML.
|
|
@@ -402,7 +401,7 @@ const importAlepha = async (entry, options) => {
|
|
|
402
401
|
*/
|
|
403
402
|
async function copyAssets(opts) {
|
|
404
403
|
const root = process.cwd();
|
|
405
|
-
const assets = (await importAlepha(opts.entry)).
|
|
404
|
+
const assets = (await importAlepha(opts.entry)).store.get("alepha.build.assets");
|
|
406
405
|
if (!assets || assets.length === 0) return;
|
|
407
406
|
const require = createRequire(join(root, opts.entry));
|
|
408
407
|
const buildAssetsDir = join(root, `${opts.distDir}/assets`);
|
|
@@ -478,10 +477,10 @@ CMD ["${command}", "index.js"]
|
|
|
478
477
|
//#endregion
|
|
479
478
|
//#region src/vite/tasks/generateSitemap.ts
|
|
480
479
|
/**
|
|
481
|
-
* Generate sitemap.xml from Alepha page
|
|
480
|
+
* Generate sitemap.xml from Alepha page primitives.
|
|
482
481
|
*
|
|
483
482
|
* This task loads the built Alepha application,
|
|
484
|
-
* queries all page
|
|
483
|
+
* queries all page primitives, and generates a sitemap.xml
|
|
485
484
|
* containing URLs for all accessible pages.
|
|
486
485
|
*/
|
|
487
486
|
async function generateSitemap(opts) {
|
|
@@ -493,7 +492,7 @@ async function generateSitemap(opts) {
|
|
|
493
492
|
return generateSitemapFromAlepha(alepha, opts.baseUrl);
|
|
494
493
|
}
|
|
495
494
|
function generateSitemapFromAlepha(alepha, baseUrl) {
|
|
496
|
-
const pages = alepha.
|
|
495
|
+
const pages = alepha.primitives("page");
|
|
497
496
|
const urls = [];
|
|
498
497
|
for (const page of pages) {
|
|
499
498
|
const options = page.options;
|
|
@@ -586,7 +585,7 @@ export default async function (req, res) {
|
|
|
586
585
|
* Pre-render static pages defined in the Alepha application.
|
|
587
586
|
*
|
|
588
587
|
* This task loads the built Alepha application, queries all page
|
|
589
|
-
*
|
|
588
|
+
* primitives with `static: true`, and generates static HTML files
|
|
590
589
|
* for each page. Supports pages with parameterized routes via
|
|
591
590
|
* `static.entries` configuration.
|
|
592
591
|
*/
|
|
@@ -600,7 +599,7 @@ async function prerenderPages(opts) {
|
|
|
600
599
|
}
|
|
601
600
|
async function prerenderFromAlepha(alepha, dist, compress) {
|
|
602
601
|
let count = 0;
|
|
603
|
-
const pages = alepha.
|
|
602
|
+
const pages = alepha.primitives("page");
|
|
604
603
|
for (const page of pages) {
|
|
605
604
|
const options = page.options;
|
|
606
605
|
if (options.children) continue;
|
|
@@ -663,7 +662,7 @@ var AlephaRunner = class {
|
|
|
663
662
|
*/
|
|
664
663
|
isSsrEnabled() {
|
|
665
664
|
if (!this.state.app) return false;
|
|
666
|
-
return this.state.app.
|
|
665
|
+
return this.state.app.store.get("alepha.react.server.ssr") ?? false;
|
|
667
666
|
}
|
|
668
667
|
/**
|
|
669
668
|
* Check if app is started.
|
|
@@ -712,7 +711,7 @@ var AlephaRunner = class {
|
|
|
712
711
|
this.state.log("[DEBUG] No app found - skip starting");
|
|
713
712
|
return;
|
|
714
713
|
}
|
|
715
|
-
this.state.app.
|
|
714
|
+
this.state.app.store.set("alepha.node.server", server.httpServer);
|
|
716
715
|
await this.state.app.start();
|
|
717
716
|
this.state.started = true;
|
|
718
717
|
process.env = { ...before };
|