@webiny/api-event-handler-standalone 0.0.0-unstable.33e4fd9647

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 (56) 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 +22 -0
  5. package/bulkActions/bulkActionsServer.js.map +1 -0
  6. package/config/abstractions.d.ts +26 -0
  7. package/config/abstractions.js +5 -0
  8. package/config/abstractions.js.map +1 -0
  9. package/config/readStandaloneConfig.d.ts +12 -0
  10. package/config/readStandaloneConfig.js +38 -0
  11. package/config/readStandaloneConfig.js.map +1 -0
  12. package/createWebinyApiHandler.d.ts +15 -0
  13. package/createWebinyApiHandler.js +120 -0
  14. package/createWebinyApiHandler.js.map +1 -0
  15. package/handlers/NodeHttpAssumePermissionsDecorator.d.ts +19 -0
  16. package/handlers/NodeHttpAssumePermissionsDecorator.js +23 -0
  17. package/handlers/NodeHttpAssumePermissionsDecorator.js.map +1 -0
  18. package/handlers/NodeHttpIdentityLoaderDecorator.d.ts +32 -0
  19. package/handlers/NodeHttpIdentityLoaderDecorator.js +45 -0
  20. package/handlers/NodeHttpIdentityLoaderDecorator.js.map +1 -0
  21. package/handlers/NodeHttpTenantLoaderDecorator.d.ts +24 -0
  22. package/handlers/NodeHttpTenantLoaderDecorator.js +29 -0
  23. package/handlers/NodeHttpTenantLoaderDecorator.js.map +1 -0
  24. package/index.d.ts +5 -0
  25. package/index.js +4 -0
  26. package/logger/consoleLogger.d.ts +2 -0
  27. package/logger/consoleLogger.js +12 -0
  28. package/logger/consoleLogger.js.map +1 -0
  29. package/package.json +52 -0
  30. package/publicUrls/ConfigPublicUrls.d.ts +13 -0
  31. package/publicUrls/ConfigPublicUrls.js +22 -0
  32. package/publicUrls/ConfigPublicUrls.js.map +1 -0
  33. package/publicUrls/feature.d.ts +4 -0
  34. package/publicUrls/feature.js +11 -0
  35. package/publicUrls/feature.js.map +1 -0
  36. package/scheduler/RecoverScheduledActionsHandler.d.ts +31 -0
  37. package/scheduler/RecoverScheduledActionsHandler.js +81 -0
  38. package/scheduler/RecoverScheduledActionsHandler.js.map +1 -0
  39. package/scheduler/RunScheduledActionOnceDecorator.d.ts +37 -0
  40. package/scheduler/RunScheduledActionOnceDecorator.js +91 -0
  41. package/scheduler/RunScheduledActionOnceDecorator.js.map +1 -0
  42. package/scheduler/ScheduledActionRecoverEventHandler.d.ts +14 -0
  43. package/scheduler/ScheduledActionRecoverEventHandler.js +5 -0
  44. package/scheduler/ScheduledActionRecoverEventHandler.js.map +1 -0
  45. package/scheduler/ScheduledActionRecoverEventType.d.ts +17 -0
  46. package/scheduler/ScheduledActionRecoverEventType.js +18 -0
  47. package/scheduler/ScheduledActionRecoverEventType.js.map +1 -0
  48. package/scheduler/abstractions/SchedulerSingleton.d.ts +10 -0
  49. package/scheduler/abstractions/SchedulerSingleton.js +5 -0
  50. package/scheduler/abstractions/SchedulerSingleton.js.map +1 -0
  51. package/scheduler/schedulerServer.d.ts +25 -0
  52. package/scheduler/schedulerServer.js +87 -0
  53. package/scheduler/schedulerServer.js.map +1 -0
  54. package/websockets/createWebsocketsAuthenticator.d.ts +24 -0
  55. package/websockets/createWebsocketsAuthenticator.js +22 -0
  56. package/websockets/createWebsocketsAuthenticator.js.map +1 -0
@@ -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 { consoleLogger } from \"~/logger/consoleLogger.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\n const service = new BreeSchedulerService({\n logger: consoleLogger,\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 consoleLogger.error(\n { error: err, id, tenant },\n \"[scheduler] scheduled action failed\"\n );\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 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 consoleLogger.info(\n { recovered: result.recovered },\n \"[scheduler] boot recovery re-armed pending actions\"\n );\n }\n if (result.failedTenants.length > 0) {\n consoleLogger.error(\n { tenants: result.failedTenants },\n \"[scheduler] recovery failed for some tenants\"\n );\n }\n } catch (err) {\n consoleLogger.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 let recovering = false;\n const recover = async (atBoot: boolean) => {\n if (recovering) {\n return;\n }\n recovering = true;\n try {\n await recoverPendingSchedules(dispatcher, 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","service","BreeSchedulerService","consoleLogger","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;IAEzC,MAAMC,UAAU,IAAIC,qBAAqB;QACrC,QAAQC;QACR,WAAW,OAAOC,IAAIC,WAAWC;YAK7B,MAAMC,QAA+B;gBACjC,CAACC,kCAAkC,EAAE;oBACjCJ;oBACAC;oBACAC;oBACA,aAAa,IAAIG,OAAO,WAAW;gBACvC;YACJ;YACA,IAAI;gBACA,MAAMV,WAAW,QAAQ,CAACQ;YAC9B,EAAE,OAAOG,KAAK;gBACVP,cAAc,KAAK,CACf;oBAAE,OAAOO;oBAAKN;oBAAIE;gBAAO,GACzB;YAER;QACJ;IACJ;IAgBAR,cAAc,gBAAgB,CAACa,oBAAoBV;IACnDH,cAAc,gBAAgB,CAACc,kBAAkBX;IAEjDH,cAAc,QAAQ,CAACe;IACvBf,cAAc,QAAQ,CAACgB;IAEvBhB,cAAc,iBAAiB,CAACiB;IAEhCjB,cAAc,QAAQ,CAACkB;IACvBlB,cAAc,QAAQ,CAACmB;AAC3B;AAGA,MAAMC,mBAAmB;AAEzB,eAAeC,wBACXpB,UAAqC,EACrCqB,MAAe;IAEf,MAAMb,QAAsC;QACxC,CAACc,0CAA0C,EAAE;IACjD;IAEA,IAAI;QACA,MAAMC,SAAS,MAAMvB,WAAW,QAAQ,CAA4CQ;QACpF,IAAIa,QACAjB,cAAc,IAAI,CACd;YAAE,WAAWmB,OAAO,SAAS;QAAC,GAC9B;QAGR,IAAIA,OAAO,aAAa,CAAC,MAAM,GAAG,GAC9BnB,cAAc,KAAK,CACf;YAAE,SAASmB,OAAO,aAAa;QAAC,GAChC;IAGZ,EAAE,OAAOZ,KAAK;QACVP,cAAc,KAAK,CAAC;YAAE,OAAOO;QAAI,GAAG;IACxC;AACJ;AAeO,eAAea,qBAClBzB,aAAwB;IAExB,MAAM0B,YAAY1B,cAAc,OAAO,CAACa;IACxC,MAAMa,UAAU,KAAK;IAErB,MAAMzB,aAAaD,cAAc,OAAO,CAACE;IACzC,IAAIyB,aAAa;IACjB,MAAMC,UAAU,OAAON;QACnB,IAAIK,YACA;QAEJA,aAAa;QACb,IAAI;YACA,MAAMN,wBAAwBpB,YAAYqB;QAC9C,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"}