@webiny/api-event-handler-standalone 0.0.0-unstable.0156a4c312

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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/bulkActions/bulkActionsServer.d.ts +6 -0
  4. package/bulkActions/bulkActionsServer.js +26 -0
  5. package/bulkActions/bulkActionsServer.js.map +1 -0
  6. package/config/abstractions.d.ts +36 -0
  7. package/config/abstractions.js +5 -0
  8. package/config/abstractions.js.map +1 -0
  9. package/config/bakedSecrets.d.ts +8 -0
  10. package/config/bakedSecrets.js +17 -0
  11. package/config/bakedSecrets.js.map +1 -0
  12. package/config/readBuildParams.d.ts +10 -0
  13. package/config/readBuildParams.js +13 -0
  14. package/config/readBuildParams.js.map +1 -0
  15. package/config/readStandaloneConfig.d.ts +12 -0
  16. package/config/readStandaloneConfig.js +56 -0
  17. package/config/readStandaloneConfig.js.map +1 -0
  18. package/createWebinyApiHandler.d.ts +15 -0
  19. package/createWebinyApiHandler.js +130 -0
  20. package/createWebinyApiHandler.js.map +1 -0
  21. package/crypto/StandaloneEncryptionConfig.d.ts +17 -0
  22. package/crypto/StandaloneEncryptionConfig.js +20 -0
  23. package/crypto/StandaloneEncryptionConfig.js.map +1 -0
  24. package/crypto/StandaloneHashingConfig.d.ts +16 -0
  25. package/crypto/StandaloneHashingConfig.js +20 -0
  26. package/crypto/StandaloneHashingConfig.js.map +1 -0
  27. package/crypto/feature.d.ts +5 -0
  28. package/crypto/feature.js +13 -0
  29. package/crypto/feature.js.map +1 -0
  30. package/handlers/NodeHttpAssumePermissionsDecorator.d.ts +19 -0
  31. package/handlers/NodeHttpAssumePermissionsDecorator.js +23 -0
  32. package/handlers/NodeHttpAssumePermissionsDecorator.js.map +1 -0
  33. package/handlers/NodeHttpIdentityLoaderDecorator.d.ts +32 -0
  34. package/handlers/NodeHttpIdentityLoaderDecorator.js +45 -0
  35. package/handlers/NodeHttpIdentityLoaderDecorator.js.map +1 -0
  36. package/handlers/NodeHttpTenantLoaderDecorator.d.ts +24 -0
  37. package/handlers/NodeHttpTenantLoaderDecorator.js +29 -0
  38. package/handlers/NodeHttpTenantLoaderDecorator.js.map +1 -0
  39. package/index.d.ts +5 -0
  40. package/index.js +4 -0
  41. package/package.json +53 -0
  42. package/publicUrls/ConfigPublicUrls.d.ts +13 -0
  43. package/publicUrls/ConfigPublicUrls.js +22 -0
  44. package/publicUrls/ConfigPublicUrls.js.map +1 -0
  45. package/publicUrls/feature.d.ts +4 -0
  46. package/publicUrls/feature.js +11 -0
  47. package/publicUrls/feature.js.map +1 -0
  48. package/scheduler/RecoverScheduledActionsHandler.d.ts +31 -0
  49. package/scheduler/RecoverScheduledActionsHandler.js +81 -0
  50. package/scheduler/RecoverScheduledActionsHandler.js.map +1 -0
  51. package/scheduler/RunScheduledActionOnceDecorator.d.ts +37 -0
  52. package/scheduler/RunScheduledActionOnceDecorator.js +91 -0
  53. package/scheduler/RunScheduledActionOnceDecorator.js.map +1 -0
  54. package/scheduler/ScheduledActionRecoverEventHandler.d.ts +14 -0
  55. package/scheduler/ScheduledActionRecoverEventHandler.js +5 -0
  56. package/scheduler/ScheduledActionRecoverEventHandler.js.map +1 -0
  57. package/scheduler/ScheduledActionRecoverEventType.d.ts +17 -0
  58. package/scheduler/ScheduledActionRecoverEventType.js +18 -0
  59. package/scheduler/ScheduledActionRecoverEventType.js.map +1 -0
  60. package/scheduler/abstractions/SchedulerSingleton.d.ts +10 -0
  61. package/scheduler/abstractions/SchedulerSingleton.js +5 -0
  62. package/scheduler/abstractions/SchedulerSingleton.js.map +1 -0
  63. package/scheduler/schedulerServer.d.ts +25 -0
  64. package/scheduler/schedulerServer.js +89 -0
  65. package/scheduler/schedulerServer.js.map +1 -0
  66. package/websockets/createWebsocketsAuthenticator.d.ts +24 -0
  67. package/websockets/createWebsocketsAuthenticator.js +22 -0
  68. package/websockets/createWebsocketsAuthenticator.js.map +1 -0
@@ -0,0 +1,14 @@
1
+ import { Abstraction } from "@webiny/di";
2
+ import type { IEventHandler } from "@webiny/event-handler-core";
3
+ import type { IScheduledActionRecoverEvent } from "./ScheduledActionRecoverEventType.js";
4
+ export interface IScheduledActionRecoverResult {
5
+ recovered: number;
6
+ failedTenants: string[];
7
+ }
8
+ export interface IScheduledActionRecoverEventHandler extends IEventHandler<IScheduledActionRecoverEvent, IScheduledActionRecoverResult> {
9
+ }
10
+ export declare const ScheduledActionRecoverEventHandler: Abstraction<IScheduledActionRecoverEventHandler>;
11
+ export declare namespace ScheduledActionRecoverEventHandler {
12
+ type Interface = IScheduledActionRecoverEventHandler;
13
+ type Result = IScheduledActionRecoverResult;
14
+ }
@@ -0,0 +1,5 @@
1
+ import { Abstraction } from "@webiny/di";
2
+ const ScheduledActionRecoverEventHandler = new Abstraction("ScheduledActionRecoverEventHandler");
3
+ export { ScheduledActionRecoverEventHandler };
4
+
5
+ //# sourceMappingURL=ScheduledActionRecoverEventHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler/ScheduledActionRecoverEventHandler.js","sources":["../../src/scheduler/ScheduledActionRecoverEventHandler.ts"],"sourcesContent":["import { Abstraction } from \"@webiny/di\";\nimport type { IEventHandler } from \"@webiny/event-handler-core\";\nimport type { IScheduledActionRecoverEvent } from \"./ScheduledActionRecoverEventType.js\";\n\nexport interface IScheduledActionRecoverResult {\n recovered: number;\n // Tenants whose schedules could not be read or re-armed; the others were still recovered.\n failedTenants: string[];\n}\n\nexport interface IScheduledActionRecoverEventHandler extends IEventHandler<\n IScheduledActionRecoverEvent,\n IScheduledActionRecoverResult\n> {}\n\nexport const ScheduledActionRecoverEventHandler =\n new Abstraction<IScheduledActionRecoverEventHandler>(\"ScheduledActionRecoverEventHandler\");\n\nexport namespace ScheduledActionRecoverEventHandler {\n export type Interface = IScheduledActionRecoverEventHandler;\n export type Result = IScheduledActionRecoverResult;\n}\n"],"names":["ScheduledActionRecoverEventHandler","Abstraction"],"mappings":";AAeO,MAAMA,qCACT,IAAIC,YAAiD"}
@@ -0,0 +1,17 @@
1
+ import type { IEventType } from "@webiny/event-handler-core";
2
+ export declare const SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER = "WebinyScheduledActionRecover";
3
+ /**
4
+ * Asks the scheduler to re-arm the root tenant's pending scheduled actions. Dispatched once at
5
+ * boot: Bree keeps its timers in memory, so a restart loses every schedule that has not fired yet.
6
+ */
7
+ export interface IScheduledActionRecoverEvent {
8
+ [SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER]: true;
9
+ }
10
+ declare class ScheduledActionRecoverEventTypeImpl implements IEventType<IScheduledActionRecoverEvent> {
11
+ canHandle(event: any): event is IScheduledActionRecoverEvent;
12
+ getHandlerAbstraction(): import("@webiny/di").Abstraction<import("./ScheduledActionRecoverEventHandler.js").IScheduledActionRecoverEventHandler>;
13
+ }
14
+ export declare const ScheduledActionRecoverEventType: typeof ScheduledActionRecoverEventTypeImpl & {
15
+ __abstraction: import("@webiny/di").Abstraction<IEventType<any>>;
16
+ };
17
+ export {};
@@ -0,0 +1,18 @@
1
+ import { EventType } from "@webiny/event-handler-core";
2
+ import { ScheduledActionRecoverEventHandler } from "./ScheduledActionRecoverEventHandler.js";
3
+ const SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER = "WebinyScheduledActionRecover";
4
+ class ScheduledActionRecoverEventTypeImpl {
5
+ canHandle(event) {
6
+ return event?.[SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER] === true;
7
+ }
8
+ getHandlerAbstraction() {
9
+ return ScheduledActionRecoverEventHandler;
10
+ }
11
+ }
12
+ const ScheduledActionRecoverEventType = EventType.createImplementation({
13
+ implementation: ScheduledActionRecoverEventTypeImpl,
14
+ dependencies: []
15
+ });
16
+ export { SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER, ScheduledActionRecoverEventType };
17
+
18
+ //# sourceMappingURL=ScheduledActionRecoverEventType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler/ScheduledActionRecoverEventType.js","sources":["../../src/scheduler/ScheduledActionRecoverEventType.ts"],"sourcesContent":["import { EventType } from \"@webiny/event-handler-core\";\nimport type { IEventType } from \"@webiny/event-handler-core\";\nimport { ScheduledActionRecoverEventHandler } from \"./ScheduledActionRecoverEventHandler.js\";\n\nexport const SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER = \"WebinyScheduledActionRecover\";\n\n/**\n * Asks the scheduler to re-arm the root tenant's pending scheduled actions. Dispatched once at\n * boot: Bree keeps its timers in memory, so a restart loses every schedule that has not fired yet.\n */\nexport interface IScheduledActionRecoverEvent {\n [SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER]: true;\n}\n\nclass ScheduledActionRecoverEventTypeImpl implements IEventType<IScheduledActionRecoverEvent> {\n canHandle(event: any): event is IScheduledActionRecoverEvent {\n return event?.[SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER] === true;\n }\n\n getHandlerAbstraction() {\n return ScheduledActionRecoverEventHandler;\n }\n}\n\nexport const ScheduledActionRecoverEventType = EventType.createImplementation({\n implementation: ScheduledActionRecoverEventTypeImpl,\n dependencies: []\n});\n"],"names":["SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER","ScheduledActionRecoverEventTypeImpl","event","ScheduledActionRecoverEventHandler","ScheduledActionRecoverEventType","EventType"],"mappings":";;AAIO,MAAMA,4CAA4C;AAUzD,MAAMC;IACF,UAAUC,KAAU,EAAyC;QACzD,OAAOA,OAAO,CAACF,0CAA0C,KAAK;IAClE;IAEA,wBAAwB;QACpB,OAAOG;IACX;AACJ;AAEO,MAAMC,kCAAkCC,UAAU,oBAAoB,CAAC;IAC1E,gBAAgBJ;IAChB,cAAc,EAAE;AACpB"}
@@ -0,0 +1,10 @@
1
+ import type { BreeSchedulerService } from "@webiny/api-scheduler-standalone";
2
+ /**
3
+ * DI token for the single root Bree scheduler instance. Distinct from the `SchedulerService`
4
+ * abstraction (create/update/delete/exists): this exposes the concrete service so the recover route +
5
+ * boot step can call `recover()`, which isn't part of the SchedulerService contract.
6
+ */
7
+ export declare const SchedulerSingleton: import("@webiny/di").Abstraction<BreeSchedulerService>;
8
+ export declare namespace SchedulerSingleton {
9
+ type Interface = BreeSchedulerService;
10
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const SchedulerSingleton = createAbstraction("SchedulerServer/Singleton");
3
+ export { SchedulerSingleton };
4
+
5
+ //# sourceMappingURL=SchedulerSingleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler/abstractions/SchedulerSingleton.js","sources":["../../../src/scheduler/abstractions/SchedulerSingleton.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { BreeSchedulerService } from \"@webiny/api-scheduler-standalone\";\n\n/**\n * DI token for the single root Bree scheduler instance. Distinct from the `SchedulerService`\n * abstraction (create/update/delete/exists): this exposes the concrete service so the recover route +\n * boot step can call `recover()`, which isn't part of the SchedulerService contract.\n */\nexport const SchedulerSingleton = createAbstraction<BreeSchedulerService>(\n \"SchedulerServer/Singleton\"\n);\n\nexport namespace SchedulerSingleton {\n export type Interface = BreeSchedulerService;\n}\n"],"names":["SchedulerSingleton","createAbstraction"],"mappings":";AAQO,MAAMA,qBAAqBC,kBAC9B"}
@@ -0,0 +1,25 @@
1
+ import type { Container } from "@webiny/di";
2
+ /**
3
+ * ROOT wiring for the standalone (Bree, in-process) scheduler. Unlike AWS (per-request EventBridge
4
+ * binding), the server holds ONE long-lived Bree instance for all tenants, started once at boot —
5
+ * the counterpart of the WebSockets connection manager. Registered as `SchedulerService` so
6
+ * per-request create/update/delete (during GraphQL mutations) manipulate that single live timer set.
7
+ *
8
+ * When a timer fires (outside any request), the singleton dispatches a `ScheduledActionEvent`. The
9
+ * handler is the one AWS runs for an EventBridge Scheduler invocation, in a fresh request container.
10
+ */
11
+ export declare function registerSchedulerServer(rootContainer: Container): void;
12
+ /**
13
+ * Boot step (onServer): start the timers, then re-arm the persisted schedules, at boot and then every
14
+ * minute. Recovery runs in the background: it executes overdue actions one by one, and the server
15
+ * should not wait for that before it starts listening. A failure is logged and never crashes startup.
16
+ *
17
+ * Running it every minute is what makes several processes work. Each holds its own timers, so an
18
+ * action scheduled or moved through one process reaches the others' timers within a minute, and an
19
+ * action whose process died is still fired, as an overdue one. `RunScheduledActionOnceDecorator`
20
+ * keeps the processes from all running it. Runs never overlap.
21
+ *
22
+ * Returns a function for the shutdown: it stops the timers and recovery, then waits up to
23
+ * `timeoutMs` for actions already running.
24
+ */
25
+ export declare function startSchedulerServer(rootContainer: Container): Promise<(timeoutMs: number) => Promise<void>>;
@@ -0,0 +1,89 @@
1
+ import { EventDispatcher, SCHEDULED_ACTION_EVENT_IDENTIFIER, ScheduledActionEventType } from "@webiny/event-handler-core";
2
+ import { SchedulerService } from "@webiny/api-scheduler/shared/abstractions.js";
3
+ import { ScheduledActionLambdaHandler } from "@webiny/api-scheduler";
4
+ import { BreeSchedulerService } from "@webiny/api-scheduler-standalone";
5
+ import { SchedulerSingleton } from "./abstractions/SchedulerSingleton.js";
6
+ import { SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER, ScheduledActionRecoverEventType } from "./ScheduledActionRecoverEventType.js";
7
+ import { RecoverScheduledActionsHandler } from "./RecoverScheduledActionsHandler.js";
8
+ import { RunScheduledActionOnceDecorator } from "./RunScheduledActionOnceDecorator.js";
9
+ import { Logger } from "@webiny/api-core/features/logger/index.js";
10
+ function registerSchedulerServer(rootContainer) {
11
+ const dispatcher = rootContainer.resolve(EventDispatcher);
12
+ const logger = rootContainer.resolve(Logger);
13
+ const service = new BreeSchedulerService({
14
+ logger,
15
+ runAction: async (id, namespace, tenant)=>{
16
+ const event = {
17
+ [SCHEDULED_ACTION_EVENT_IDENTIFIER]: {
18
+ id,
19
+ namespace,
20
+ tenant,
21
+ scheduleFor: new Date().toISOString()
22
+ }
23
+ };
24
+ try {
25
+ await dispatcher.dispatch(event);
26
+ } catch (err) {
27
+ logger.error({
28
+ error: err,
29
+ id,
30
+ tenant
31
+ }, "[scheduler] scheduled action failed");
32
+ }
33
+ }
34
+ });
35
+ rootContainer.registerInstance(SchedulerSingleton, service);
36
+ rootContainer.registerInstance(SchedulerService, service);
37
+ rootContainer.register(ScheduledActionEventType);
38
+ rootContainer.register(ScheduledActionLambdaHandler);
39
+ rootContainer.registerDecorator(RunScheduledActionOnceDecorator);
40
+ rootContainer.register(ScheduledActionRecoverEventType);
41
+ rootContainer.register(RecoverScheduledActionsHandler);
42
+ }
43
+ const RECOVER_EVERY_MS = 60000;
44
+ async function recoverPendingSchedules(dispatcher, logger, atBoot) {
45
+ const event = {
46
+ [SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER]: true
47
+ };
48
+ try {
49
+ const result = await dispatcher.dispatch(event);
50
+ if (atBoot) logger.info({
51
+ recovered: result.recovered
52
+ }, "[scheduler] boot recovery re-armed pending actions");
53
+ if (result.failedTenants.length > 0) logger.error({
54
+ tenants: result.failedTenants
55
+ }, "[scheduler] recovery failed for some tenants");
56
+ } catch (err) {
57
+ logger.error({
58
+ error: err
59
+ }, "[scheduler] recovery failed");
60
+ }
61
+ }
62
+ async function startSchedulerServer(rootContainer) {
63
+ const scheduler = rootContainer.resolve(SchedulerSingleton);
64
+ await scheduler.start();
65
+ const dispatcher = rootContainer.resolve(EventDispatcher);
66
+ const logger = rootContainer.resolve(Logger);
67
+ let recovering = false;
68
+ const recover = async (atBoot)=>{
69
+ if (recovering) return;
70
+ recovering = true;
71
+ try {
72
+ await recoverPendingSchedules(dispatcher, logger, atBoot);
73
+ } finally{
74
+ recovering = false;
75
+ }
76
+ };
77
+ recover(true);
78
+ const everyMinute = setInterval(()=>{
79
+ recover(false);
80
+ }, RECOVER_EVERY_MS);
81
+ return async (timeoutMs)=>{
82
+ clearInterval(everyMinute);
83
+ await scheduler.stop();
84
+ await scheduler.waitForRunning(timeoutMs);
85
+ };
86
+ }
87
+ export { registerSchedulerServer, startSchedulerServer };
88
+
89
+ //# sourceMappingURL=schedulerServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler/schedulerServer.js","sources":["../../src/scheduler/schedulerServer.ts"],"sourcesContent":["import type { Container } from \"@webiny/di\";\nimport { EventDispatcher } from \"@webiny/event-handler-core\";\nimport { ScheduledActionEventType } from \"@webiny/event-handler-core\";\nimport { SCHEDULED_ACTION_EVENT_IDENTIFIER } from \"@webiny/event-handler-core\";\nimport type { IScheduledActionEvent } from \"@webiny/event-handler-core\";\nimport { SchedulerService } from \"@webiny/api-scheduler/shared/abstractions.js\";\nimport { ScheduledActionLambdaHandler } from \"@webiny/api-scheduler\";\nimport { BreeSchedulerService } from \"@webiny/api-scheduler-standalone\";\nimport { SchedulerSingleton } from \"./abstractions/SchedulerSingleton.js\";\nimport { ScheduledActionRecoverEventType } from \"./ScheduledActionRecoverEventType.js\";\nimport { SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER } from \"./ScheduledActionRecoverEventType.js\";\nimport type { IScheduledActionRecoverEvent } from \"./ScheduledActionRecoverEventType.js\";\nimport { RecoverScheduledActionsHandler } from \"./RecoverScheduledActionsHandler.js\";\nimport { ScheduledActionRecoverEventHandler } from \"./ScheduledActionRecoverEventHandler.js\";\nimport { RunScheduledActionOnceDecorator } from \"./RunScheduledActionOnceDecorator.js\";\nimport { Logger } from \"@webiny/api-core/features/logger/index.js\";\n\n/**\n * ROOT wiring for the standalone (Bree, in-process) scheduler. Unlike AWS (per-request EventBridge\n * binding), the server holds ONE long-lived Bree instance for all tenants, started once at boot —\n * the counterpart of the WebSockets connection manager. Registered as `SchedulerService` so\n * per-request create/update/delete (during GraphQL mutations) manipulate that single live timer set.\n *\n * When a timer fires (outside any request), the singleton dispatches a `ScheduledActionEvent`. The\n * handler is the one AWS runs for an EventBridge Scheduler invocation, in a fresh request container.\n */\nexport function registerSchedulerServer(rootContainer: Container): void {\n const dispatcher = rootContainer.resolve(EventDispatcher);\n const logger = rootContainer.resolve(Logger);\n\n const service = new BreeSchedulerService({\n logger,\n runAction: async (id, namespace, tenant) => {\n /*\n * `scheduleFor` is the time it fired. The event type needs one to recognise the event,\n * and nothing downstream reads it.\n */\n const event: IScheduledActionEvent = {\n [SCHEDULED_ACTION_EVENT_IDENTIFIER]: {\n id,\n namespace,\n tenant,\n scheduleFor: new Date().toISOString()\n }\n };\n try {\n await dispatcher.dispatch(event);\n } catch (err) {\n logger.error({ error: err, id, tenant }, \"[scheduler] scheduled action failed\");\n }\n }\n });\n\n // Register the ONE Bree instance under TWO tokens — two views of the same object:\n //\n // - SchedulerService: the hosting-agnostic contract (create/update/delete/exists) that AWS\n // implements too. This is what per-request GraphQL mutations resolve to schedule/reschedule.\n // - SchedulerSingleton: typed as the CONCRETE BreeSchedulerService, so it also exposes the\n // methods that aren't on that contract — start() and recover(). Those are Bree-only: AWS's\n // EventBridge is managed infra (nothing to start) and persists its own schedules (nothing to\n // recover), so they don't belong on the shared interface. The boot step + recover handler\n // resolve this token precisely because they need start()/recover().\n //\n // registerInstance (not register) because it's a single live object holding all tenants' timers —\n // every caller must get the SAME instance, not a per-scope construction. Registered at root, so\n // per-request child containers inherit it via the parent chain; nothing registers a per-request\n // SchedulerService default that would shadow it (unlike e.g. the WS transport's Null default).\n rootContainer.registerInstance(SchedulerSingleton, service);\n rootContainer.registerInstance(SchedulerService, service);\n\n rootContainer.register(ScheduledActionEventType);\n rootContainer.register(ScheduledActionLambdaHandler);\n // Every process holds timers for the same actions; this lets only one of them run each action.\n rootContainer.registerDecorator(RunScheduledActionOnceDecorator);\n\n rootContainer.register(ScheduledActionRecoverEventType);\n rootContainer.register(RecoverScheduledActionsHandler);\n}\n\n// How often each process re-arms pending actions and fires overdue ones.\nconst RECOVER_EVERY_MS = 60_000;\n\nasync function recoverPendingSchedules(\n dispatcher: EventDispatcher.Interface,\n logger: Logger.Interface,\n atBoot: boolean\n): Promise<void> {\n const event: IScheduledActionRecoverEvent = {\n [SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER]: true\n };\n\n try {\n const result = await dispatcher.dispatch<ScheduledActionRecoverEventHandler.Result>(event);\n if (atBoot) {\n logger.info(\n { recovered: result.recovered },\n \"[scheduler] boot recovery re-armed pending actions\"\n );\n }\n if (result.failedTenants.length > 0) {\n logger.error(\n { tenants: result.failedTenants },\n \"[scheduler] recovery failed for some tenants\"\n );\n }\n } catch (err) {\n logger.error({ error: err }, \"[scheduler] recovery failed\");\n }\n}\n\n/**\n * Boot step (onServer): start the timers, then re-arm the persisted schedules, at boot and then every\n * minute. Recovery runs in the background: it executes overdue actions one by one, and the server\n * should not wait for that before it starts listening. A failure is logged and never crashes startup.\n *\n * Running it every minute is what makes several processes work. Each holds its own timers, so an\n * action scheduled or moved through one process reaches the others' timers within a minute, and an\n * action whose process died is still fired, as an overdue one. `RunScheduledActionOnceDecorator`\n * keeps the processes from all running it. Runs never overlap.\n *\n * Returns a function for the shutdown: it stops the timers and recovery, then waits up to\n * `timeoutMs` for actions already running.\n */\nexport async function startSchedulerServer(\n rootContainer: Container\n): Promise<(timeoutMs: number) => Promise<void>> {\n const scheduler = rootContainer.resolve(SchedulerSingleton);\n await scheduler.start();\n\n const dispatcher = rootContainer.resolve(EventDispatcher);\n const logger = rootContainer.resolve(Logger);\n let recovering = false;\n const recover = async (atBoot: boolean) => {\n if (recovering) {\n return;\n }\n recovering = true;\n try {\n await recoverPendingSchedules(dispatcher, logger, atBoot);\n } finally {\n recovering = false;\n }\n };\n\n void recover(true);\n const everyMinute = setInterval(() => {\n void recover(false);\n }, RECOVER_EVERY_MS);\n\n return async (timeoutMs: number) => {\n clearInterval(everyMinute);\n await scheduler.stop();\n await scheduler.waitForRunning(timeoutMs);\n };\n}\n"],"names":["registerSchedulerServer","rootContainer","dispatcher","EventDispatcher","logger","Logger","service","BreeSchedulerService","id","namespace","tenant","event","SCHEDULED_ACTION_EVENT_IDENTIFIER","Date","err","SchedulerSingleton","SchedulerService","ScheduledActionEventType","ScheduledActionLambdaHandler","RunScheduledActionOnceDecorator","ScheduledActionRecoverEventType","RecoverScheduledActionsHandler","RECOVER_EVERY_MS","recoverPendingSchedules","atBoot","SCHEDULED_ACTION_RECOVER_EVENT_IDENTIFIER","result","startSchedulerServer","scheduler","recovering","recover","everyMinute","setInterval","timeoutMs","clearInterval"],"mappings":";;;;;;;;;AA0BO,SAASA,wBAAwBC,aAAwB;IAC5D,MAAMC,aAAaD,cAAc,OAAO,CAACE;IACzC,MAAMC,SAASH,cAAc,OAAO,CAACI;IAErC,MAAMC,UAAU,IAAIC,qBAAqB;QACrCH;QACA,WAAW,OAAOI,IAAIC,WAAWC;YAK7B,MAAMC,QAA+B;gBACjC,CAACC,kCAAkC,EAAE;oBACjCJ;oBACAC;oBACAC;oBACA,aAAa,IAAIG,OAAO,WAAW;gBACvC;YACJ;YACA,IAAI;gBACA,MAAMX,WAAW,QAAQ,CAACS;YAC9B,EAAE,OAAOG,KAAK;gBACVV,OAAO,KAAK,CAAC;oBAAE,OAAOU;oBAAKN;oBAAIE;gBAAO,GAAG;YAC7C;QACJ;IACJ;IAgBAT,cAAc,gBAAgB,CAACc,oBAAoBT;IACnDL,cAAc,gBAAgB,CAACe,kBAAkBV;IAEjDL,cAAc,QAAQ,CAACgB;IACvBhB,cAAc,QAAQ,CAACiB;IAEvBjB,cAAc,iBAAiB,CAACkB;IAEhClB,cAAc,QAAQ,CAACmB;IACvBnB,cAAc,QAAQ,CAACoB;AAC3B;AAGA,MAAMC,mBAAmB;AAEzB,eAAeC,wBACXrB,UAAqC,EACrCE,MAAwB,EACxBoB,MAAe;IAEf,MAAMb,QAAsC;QACxC,CAACc,0CAA0C,EAAE;IACjD;IAEA,IAAI;QACA,MAAMC,SAAS,MAAMxB,WAAW,QAAQ,CAA4CS;QACpF,IAAIa,QACApB,OAAO,IAAI,CACP;YAAE,WAAWsB,OAAO,SAAS;QAAC,GAC9B;QAGR,IAAIA,OAAO,aAAa,CAAC,MAAM,GAAG,GAC9BtB,OAAO,KAAK,CACR;YAAE,SAASsB,OAAO,aAAa;QAAC,GAChC;IAGZ,EAAE,OAAOZ,KAAK;QACVV,OAAO,KAAK,CAAC;YAAE,OAAOU;QAAI,GAAG;IACjC;AACJ;AAeO,eAAea,qBAClB1B,aAAwB;IAExB,MAAM2B,YAAY3B,cAAc,OAAO,CAACc;IACxC,MAAMa,UAAU,KAAK;IAErB,MAAM1B,aAAaD,cAAc,OAAO,CAACE;IACzC,MAAMC,SAASH,cAAc,OAAO,CAACI;IACrC,IAAIwB,aAAa;IACjB,MAAMC,UAAU,OAAON;QACnB,IAAIK,YACA;QAEJA,aAAa;QACb,IAAI;YACA,MAAMN,wBAAwBrB,YAAYE,QAAQoB;QACtD,SAAU;YACNK,aAAa;QACjB;IACJ;IAEKC,QAAQ;IACb,MAAMC,cAAcC,YAAY;QACvBF,QAAQ;IACjB,GAAGR;IAEH,OAAO,OAAOW;QACVC,cAAcH;QACd,MAAMH,UAAU,IAAI;QACpB,MAAMA,UAAU,cAAc,CAACK;IACnC;AACJ"}
@@ -0,0 +1,24 @@
1
+ import type { Container } from "@webiny/di";
2
+ import { type RegisterApiRequestStackConfig } from "@webiny/api-event-handler-core";
3
+ import type { WebsocketsConnectionAuthenticator } from "@webiny/api-websockets-standalone";
4
+ /**
5
+ * Builds the `authenticate` callback the server WebSocket upgrade acceptor uses to turn a
6
+ * connection's `?token` JWT into an identity.
7
+ *
8
+ * Each new WebSocket connection (the HTTP upgrade handshake) is authenticated from its `?token` JWT
9
+ * so the connection is registered under the real identity — targeted server→client sends need this,
10
+ * since SendToIdentity matches connections by identity id. The catch is that AuthenticationContext
11
+ * lives in the per-request stack (registered by ApiCoreFeature, not registerRootStorage), so we can't
12
+ * resolve it from the root container. Instead we spin up a short-lived request-scoped child container,
13
+ * the same way createHandler does for each HTTP request, and resolve it there. We only need the core
14
+ * stack, not the transports, because this is just the token→identity step the HTTP stack already runs
15
+ * via RequestIdentityLoader — and that step doesn't depend on the tenant (the HTTP stack resolves
16
+ * identity before tenant), so there's no request or tenant state to set up. Connections are rare (one
17
+ * per admin session), so a fresh child per connection is cheap enough.
18
+ *
19
+ * This is also why the WebSocket server takes `authenticate` as a plain callback instead of resolving
20
+ * auth itself: AuthenticationContext isn't reachable from the root container (it's per-request), so
21
+ * only the handler — which can build a request-scoped child — can supply it. So authenticate has to be
22
+ * a callback the handler builds, regardless of DI.
23
+ */
24
+ export declare function createWebsocketsAuthenticator(rootContainer: Container, extensions: RegisterApiRequestStackConfig["extensions"]): WebsocketsConnectionAuthenticator;
@@ -0,0 +1,22 @@
1
+ import { RequestContainer } from "@webiny/event-handler-core";
2
+ import { registerApiRequestStack } from "@webiny/api-event-handler-core";
3
+ import { AuthenticationContext } from "@webiny/api-core/features/security/authentication/AuthenticationContext/index.js";
4
+ function createWebsocketsAuthenticator(rootContainer, extensions) {
5
+ return async (token)=>{
6
+ const child = rootContainer.createChildContainer();
7
+ child.registerInstance(RequestContainer, child);
8
+ await registerApiRequestStack(child, {
9
+ extensions
10
+ });
11
+ const identity = await child.resolve(AuthenticationContext).authenticate(token);
12
+ if (!identity?.id) return null;
13
+ return {
14
+ id: identity.id,
15
+ displayName: identity.displayName,
16
+ type: identity.type
17
+ };
18
+ };
19
+ }
20
+ export { createWebsocketsAuthenticator };
21
+
22
+ //# sourceMappingURL=createWebsocketsAuthenticator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websockets/createWebsocketsAuthenticator.js","sources":["../../src/websockets/createWebsocketsAuthenticator.ts"],"sourcesContent":["import type { Container } from \"@webiny/di\";\nimport { RequestContainer } from \"@webiny/event-handler-core\";\nimport {\n registerApiRequestStack,\n type RegisterApiRequestStackConfig\n} from \"@webiny/api-event-handler-core\";\nimport { AuthenticationContext } from \"@webiny/api-core/features/security/authentication/AuthenticationContext/index.js\";\nimport type { WebsocketsConnectionAuthenticator } from \"@webiny/api-websockets-standalone\";\n\n/**\n * Builds the `authenticate` callback the server WebSocket upgrade acceptor uses to turn a\n * connection's `?token` JWT into an identity.\n *\n * Each new WebSocket connection (the HTTP upgrade handshake) is authenticated from its `?token` JWT\n * so the connection is registered under the real identity — targeted server→client sends need this,\n * since SendToIdentity matches connections by identity id. The catch is that AuthenticationContext\n * lives in the per-request stack (registered by ApiCoreFeature, not registerRootStorage), so we can't\n * resolve it from the root container. Instead we spin up a short-lived request-scoped child container,\n * the same way createHandler does for each HTTP request, and resolve it there. We only need the core\n * stack, not the transports, because this is just the token→identity step the HTTP stack already runs\n * via RequestIdentityLoader — and that step doesn't depend on the tenant (the HTTP stack resolves\n * identity before tenant), so there's no request or tenant state to set up. Connections are rare (one\n * per admin session), so a fresh child per connection is cheap enough.\n *\n * This is also why the WebSocket server takes `authenticate` as a plain callback instead of resolving\n * auth itself: AuthenticationContext isn't reachable from the root container (it's per-request), so\n * only the handler — which can build a request-scoped child — can supply it. So authenticate has to be\n * a callback the handler builds, regardless of DI.\n */\nexport function createWebsocketsAuthenticator(\n rootContainer: Container,\n extensions: RegisterApiRequestStackConfig[\"extensions\"]\n): WebsocketsConnectionAuthenticator {\n return async token => {\n const child = rootContainer.createChildContainer();\n child.registerInstance(RequestContainer, child);\n await registerApiRequestStack(child, { extensions });\n const identity = await child.resolve(AuthenticationContext).authenticate(token);\n if (!identity?.id) {\n return null;\n }\n return {\n id: identity.id,\n displayName: identity.displayName,\n type: identity.type\n };\n };\n}\n"],"names":["createWebsocketsAuthenticator","rootContainer","extensions","token","child","RequestContainer","registerApiRequestStack","identity","AuthenticationContext"],"mappings":";;;AA6BO,SAASA,8BACZC,aAAwB,EACxBC,UAAuD;IAEvD,OAAO,OAAMC;QACT,MAAMC,QAAQH,cAAc,oBAAoB;QAChDG,MAAM,gBAAgB,CAACC,kBAAkBD;QACzC,MAAME,wBAAwBF,OAAO;YAAEF;QAAW;QAClD,MAAMK,WAAW,MAAMH,MAAM,OAAO,CAACI,uBAAuB,YAAY,CAACL;QACzE,IAAI,CAACI,UAAU,IACX,OAAO;QAEX,OAAO;YACH,IAAIA,SAAS,EAAE;YACf,aAAaA,SAAS,WAAW;YACjC,MAAMA,SAAS,IAAI;QACvB;IACJ;AACJ"}