@enbox/dwn-server 0.0.2 → 0.0.4
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/LICENSE +3 -2
- package/README.md +115 -215
- package/dist/esm/src/admin/activity-log.d.ts +44 -0
- package/dist/esm/src/admin/activity-log.d.ts.map +1 -0
- package/dist/esm/src/admin/activity-log.js +85 -0
- package/dist/esm/src/admin/activity-log.js.map +1 -0
- package/dist/esm/src/admin/admin-api.d.ts +61 -0
- package/dist/esm/src/admin/admin-api.d.ts.map +1 -0
- package/dist/esm/src/admin/admin-api.js +1047 -0
- package/dist/esm/src/admin/admin-api.js.map +1 -0
- package/dist/esm/src/admin/admin-auth.d.ts +9 -0
- package/dist/esm/src/admin/admin-auth.d.ts.map +1 -0
- package/dist/esm/src/admin/admin-auth.js +45 -0
- package/dist/esm/src/admin/admin-auth.js.map +1 -0
- package/dist/esm/src/admin/admin-store.d.ts +111 -0
- package/dist/esm/src/admin/admin-store.d.ts.map +1 -0
- package/dist/esm/src/admin/admin-store.js +376 -0
- package/dist/esm/src/admin/admin-store.js.map +1 -0
- package/dist/esm/src/admin/audit-log.d.ts +94 -0
- package/dist/esm/src/admin/audit-log.d.ts.map +1 -0
- package/dist/esm/src/admin/audit-log.js +220 -0
- package/dist/esm/src/admin/audit-log.js.map +1 -0
- package/dist/esm/src/admin/index.d.ts +10 -0
- package/dist/esm/src/admin/index.d.ts.map +1 -0
- package/dist/esm/src/admin/index.js +7 -0
- package/dist/esm/src/admin/index.js.map +1 -0
- package/dist/esm/src/admin/types.d.ts +306 -0
- package/dist/esm/src/admin/types.d.ts.map +1 -0
- package/dist/esm/src/admin/types.js +2 -0
- package/dist/esm/src/admin/types.js.map +1 -0
- package/dist/esm/src/admin/webhook-manager.d.ts +55 -0
- package/dist/esm/src/admin/webhook-manager.d.ts.map +1 -0
- package/dist/esm/src/admin/webhook-manager.js +184 -0
- package/dist/esm/src/admin/webhook-manager.js.map +1 -0
- package/dist/esm/src/config.d.ts +124 -9
- package/dist/esm/src/config.d.ts.map +1 -1
- package/dist/esm/src/config.js +155 -13
- package/dist/esm/src/config.js.map +1 -1
- package/dist/esm/src/connection/connection-manager.d.ts +32 -9
- package/dist/esm/src/connection/connection-manager.d.ts.map +1 -1
- package/dist/esm/src/connection/connection-manager.js +38 -5
- package/dist/esm/src/connection/connection-manager.js.map +1 -1
- package/dist/esm/src/connection/flow-controller.d.ts +53 -0
- package/dist/esm/src/connection/flow-controller.d.ts.map +1 -0
- package/dist/esm/src/connection/flow-controller.js +101 -0
- package/dist/esm/src/connection/flow-controller.js.map +1 -0
- package/dist/esm/src/connection/socket-connection.d.ts +54 -18
- package/dist/esm/src/connection/socket-connection.d.ts.map +1 -1
- package/dist/esm/src/connection/socket-connection.js +102 -40
- package/dist/esm/src/connection/socket-connection.js.map +1 -1
- package/dist/esm/src/delivery-service.d.ts +43 -0
- package/dist/esm/src/delivery-service.d.ts.map +1 -0
- package/dist/esm/src/delivery-service.js +574 -0
- package/dist/esm/src/delivery-service.js.map +1 -0
- package/dist/esm/src/dwn-error.d.ts +10 -1
- package/dist/esm/src/dwn-error.d.ts.map +1 -1
- package/dist/esm/src/dwn-error.js +9 -0
- package/dist/esm/src/dwn-error.js.map +1 -1
- package/dist/esm/src/dwn-server.d.ts +13 -6
- package/dist/esm/src/dwn-server.d.ts.map +1 -1
- package/dist/esm/src/dwn-server.js +199 -24
- package/dist/esm/src/dwn-server.js.map +1 -1
- package/dist/esm/src/http-api.d.ts +28 -13
- package/dist/esm/src/http-api.d.ts.map +1 -1
- package/dist/esm/src/http-api.js +649 -374
- package/dist/esm/src/http-api.js.map +1 -1
- package/dist/esm/src/index.d.ts +6 -2
- package/dist/esm/src/index.d.ts.map +1 -1
- package/dist/esm/src/index.js +4 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/json-rpc-api.js +2 -1
- package/dist/esm/src/json-rpc-api.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js +109 -7
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts +20 -0
- package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/ack.js +41 -0
- package/dist/esm/src/json-rpc-handlers/subscription/ack.js.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/close.js +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/close.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/index.js +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.js.map +1 -1
- package/dist/esm/src/lib/json-rpc-router.d.ts +25 -8
- package/dist/esm/src/lib/json-rpc-router.d.ts.map +1 -1
- package/dist/esm/src/lib/json-rpc-router.js.map +1 -1
- package/dist/esm/src/lib/sql-utils.d.ts +6 -0
- package/dist/esm/src/lib/sql-utils.d.ts.map +1 -0
- package/dist/esm/src/lib/sql-utils.js +8 -0
- package/dist/esm/src/lib/sql-utils.js.map +1 -0
- package/dist/esm/src/main.js +0 -6
- package/dist/esm/src/main.js.map +1 -1
- package/dist/esm/src/message-processed-hook.d.ts +35 -0
- package/dist/esm/src/message-processed-hook.d.ts.map +1 -0
- package/dist/esm/src/message-processed-hook.js +2 -0
- package/dist/esm/src/message-processed-hook.js.map +1 -0
- package/dist/esm/src/metrics.d.ts +14 -2
- package/dist/esm/src/metrics.d.ts.map +1 -1
- package/dist/esm/src/metrics.js +41 -1
- package/dist/esm/src/metrics.js.map +1 -1
- package/dist/esm/src/plugins/event-log-nats.d.ts +25 -0
- package/dist/esm/src/plugins/event-log-nats.d.ts.map +1 -0
- package/dist/esm/src/plugins/event-log-nats.js +379 -0
- package/dist/esm/src/plugins/event-log-nats.js.map +1 -0
- package/dist/esm/src/rate-limiter.d.ts +60 -0
- package/dist/esm/src/rate-limiter.d.ts.map +1 -0
- package/dist/esm/src/rate-limiter.js +116 -0
- package/dist/esm/src/rate-limiter.js.map +1 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts +53 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts.map +1 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.js +90 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.js.map +1 -0
- package/dist/esm/src/registration/open-auth-handler.d.ts +37 -0
- package/dist/esm/src/registration/open-auth-handler.d.ts.map +1 -0
- package/dist/esm/src/registration/open-auth-handler.js +214 -0
- package/dist/esm/src/registration/open-auth-handler.js.map +1 -0
- package/dist/esm/src/registration/proof-of-work-manager.d.ts +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.d.ts.map +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.js +3 -3
- package/dist/esm/src/registration/proof-of-work-manager.js.map +1 -1
- package/dist/esm/src/registration/provider-auth-plugin.d.ts +46 -0
- package/dist/esm/src/registration/provider-auth-plugin.d.ts.map +1 -0
- package/dist/esm/src/registration/provider-auth-plugin.js +29 -0
- package/dist/esm/src/registration/provider-auth-plugin.js.map +1 -0
- package/dist/esm/src/registration/registration-manager.d.ts +28 -5
- package/dist/esm/src/registration/registration-manager.d.ts.map +1 -1
- package/dist/esm/src/registration/registration-manager.js +83 -12
- package/dist/esm/src/registration/registration-manager.js.map +1 -1
- package/dist/esm/src/registration/registration-store.d.ts +83 -3
- package/dist/esm/src/registration/registration-store.d.ts.map +1 -1
- package/dist/esm/src/registration/registration-store.js +248 -11
- package/dist/esm/src/registration/registration-store.js.map +1 -1
- package/dist/esm/src/storage.d.ts +5 -5
- package/dist/esm/src/storage.d.ts.map +1 -1
- package/dist/esm/src/storage.js +105 -24
- package/dist/esm/src/storage.js.map +1 -1
- package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -1
- package/dist/esm/src/web5-connect/sql-ttl-cache.js +11 -3
- package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -1
- package/dist/esm/src/web5-connect/web5-connect-server.d.ts.map +1 -1
- package/dist/esm/src/web5-connect/web5-connect-server.js +2 -2
- package/dist/esm/src/web5-connect/web5-connect-server.js.map +1 -1
- package/dist/esm/src/ws-api.d.ts +18 -4
- package/dist/esm/src/ws-api.d.ts.map +1 -1
- package/dist/esm/src/ws-api.js +12 -16
- package/dist/esm/src/ws-api.js.map +1 -1
- package/package.json +34 -53
- package/src/admin/activity-log.ts +100 -0
- package/src/admin/admin-api.ts +1308 -0
- package/src/admin/admin-auth.ts +56 -0
- package/src/admin/admin-store.ts +515 -0
- package/src/admin/audit-log.ts +327 -0
- package/src/admin/index.ts +34 -0
- package/src/admin/types.ts +352 -0
- package/src/admin/webhook-manager.ts +245 -0
- package/src/config.ts +190 -22
- package/src/connection/connection-manager.ts +67 -17
- package/src/connection/flow-controller.ts +117 -0
- package/src/connection/socket-connection.ts +144 -67
- package/src/delivery-service.ts +740 -0
- package/src/dwn-error.ts +11 -2
- package/src/dwn-server.ts +254 -39
- package/src/http-api.ts +736 -392
- package/src/index.ts +13 -2
- package/src/json-rpc-api.ts +2 -1
- package/src/json-rpc-handlers/dwn/process-message.ts +149 -15
- package/src/json-rpc-handlers/subscription/ack.ts +63 -0
- package/src/json-rpc-handlers/subscription/close.ts +5 -9
- package/src/json-rpc-handlers/subscription/index.ts +1 -0
- package/src/lib/json-rpc-router.ts +26 -11
- package/src/lib/sql-utils.ts +7 -0
- package/src/main.ts +0 -8
- package/src/message-processed-hook.ts +33 -0
- package/src/metrics.ts +57 -8
- package/src/plugins/event-log-nats.ts +466 -0
- package/src/process-handlers.ts +5 -5
- package/src/rate-limiter.ts +143 -0
- package/src/registration/jwt-provider-auth-plugin.ts +119 -0
- package/src/registration/open-auth-handler.ts +263 -0
- package/src/registration/proof-of-work-manager.ts +11 -10
- package/src/registration/provider-auth-plugin.ts +84 -0
- package/src/registration/registration-manager.ts +129 -31
- package/src/registration/registration-store.ts +332 -22
- package/src/storage.ts +136 -40
- package/src/web5-connect/sql-ttl-cache.ts +12 -5
- package/src/web5-connect/web5-connect-server.ts +9 -8
- package/src/ws-api.ts +39 -26
- package/dist/cjs/index.js +0 -6811
- package/dist/cjs/package.json +0 -1
- package/dist/esm/src/json-rpc-socket.d.ts +0 -39
- package/dist/esm/src/json-rpc-socket.d.ts.map +0 -1
- package/dist/esm/src/json-rpc-socket.js +0 -125
- package/dist/esm/src/json-rpc-socket.js.map +0 -1
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts +0 -10
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts.map +0 -1
- package/dist/esm/src/lib/http-server-shutdown-handler.js +0 -65
- package/dist/esm/src/lib/http-server-shutdown-handler.js.map +0 -1
- package/dist/esm/src/lib/json-rpc.d.ts +0 -54
- package/dist/esm/src/lib/json-rpc.d.ts.map +0 -1
- package/dist/esm/src/lib/json-rpc.js +0 -60
- package/dist/esm/src/lib/json-rpc.js.map +0 -1
- package/dist/esm/src/registration/proof-of-work-types.d.ts +0 -8
- package/dist/esm/src/registration/proof-of-work-types.d.ts.map +0 -1
- package/dist/esm/src/registration/proof-of-work-types.js +0 -2
- package/dist/esm/src/registration/proof-of-work-types.js.map +0 -1
- package/dist/esm/src/registration/registration-types.d.ts +0 -18
- package/dist/esm/src/registration/registration-types.d.ts.map +0 -1
- package/dist/esm/src/registration/registration-types.js +0 -2
- package/dist/esm/src/registration/registration-types.js.map +0 -1
- package/dist/esm/tests/common-scenario-validator.d.ts +0 -11
- package/dist/esm/tests/common-scenario-validator.d.ts.map +0 -1
- package/dist/esm/tests/common-scenario-validator.js +0 -114
- package/dist/esm/tests/common-scenario-validator.js.map +0 -1
- package/dist/esm/tests/connection/connection-manager.spec.d.ts +0 -2
- package/dist/esm/tests/connection/connection-manager.spec.d.ts.map +0 -1
- package/dist/esm/tests/connection/connection-manager.spec.js +0 -47
- package/dist/esm/tests/connection/connection-manager.spec.js.map +0 -1
- package/dist/esm/tests/connection/socket-connection.spec.d.ts +0 -2
- package/dist/esm/tests/connection/socket-connection.spec.d.ts.map +0 -1
- package/dist/esm/tests/connection/socket-connection.spec.js +0 -125
- package/dist/esm/tests/connection/socket-connection.spec.js.map +0 -1
- package/dist/esm/tests/cors/http-api.browser.d.ts +0 -2
- package/dist/esm/tests/cors/http-api.browser.d.ts.map +0 -1
- package/dist/esm/tests/cors/http-api.browser.js +0 -60
- package/dist/esm/tests/cors/http-api.browser.js.map +0 -1
- package/dist/esm/tests/cors/ping.browser.d.ts +0 -2
- package/dist/esm/tests/cors/ping.browser.d.ts.map +0 -1
- package/dist/esm/tests/cors/ping.browser.js +0 -7
- package/dist/esm/tests/cors/ping.browser.js.map +0 -1
- package/dist/esm/tests/dwn-process-message.spec.d.ts +0 -2
- package/dist/esm/tests/dwn-process-message.spec.d.ts.map +0 -1
- package/dist/esm/tests/dwn-process-message.spec.js +0 -172
- package/dist/esm/tests/dwn-process-message.spec.js.map +0 -1
- package/dist/esm/tests/dwn-server.spec.d.ts +0 -2
- package/dist/esm/tests/dwn-server.spec.d.ts.map +0 -1
- package/dist/esm/tests/dwn-server.spec.js +0 -49
- package/dist/esm/tests/dwn-server.spec.js.map +0 -1
- package/dist/esm/tests/http-api.spec.d.ts +0 -2
- package/dist/esm/tests/http-api.spec.d.ts.map +0 -1
- package/dist/esm/tests/http-api.spec.js +0 -775
- package/dist/esm/tests/http-api.spec.js.map +0 -1
- package/dist/esm/tests/json-rpc-socket.spec.d.ts +0 -2
- package/dist/esm/tests/json-rpc-socket.spec.d.ts.map +0 -1
- package/dist/esm/tests/json-rpc-socket.spec.js +0 -225
- package/dist/esm/tests/json-rpc-socket.spec.js.map +0 -1
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/data-store-sqlite.js +0 -23
- package/dist/esm/tests/plugins/data-store-sqlite.js.map +0 -1
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/event-log-sqlite.js +0 -23
- package/dist/esm/tests/plugins/event-log-sqlite.js.map +0 -1
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +0 -17
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +0 -1
- package/dist/esm/tests/plugins/event-stream-in-memory.js +0 -21
- package/dist/esm/tests/plugins/event-stream-in-memory.js.map +0 -1
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/message-store-sqlite.js +0 -23
- package/dist/esm/tests/plugins/message-store-sqlite.js.map +0 -1
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +0 -23
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +0 -1
- package/dist/esm/tests/process-handler.spec.d.ts +0 -2
- package/dist/esm/tests/process-handler.spec.d.ts.map +0 -1
- package/dist/esm/tests/process-handler.spec.js +0 -60
- package/dist/esm/tests/process-handler.spec.js.map +0 -1
- package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts +0 -2
- package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts.map +0 -1
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js +0 -157
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +0 -1
- package/dist/esm/tests/rpc-subscribe-close.spec.d.ts +0 -2
- package/dist/esm/tests/rpc-subscribe-close.spec.d.ts.map +0 -1
- package/dist/esm/tests/rpc-subscribe-close.spec.js +0 -81
- package/dist/esm/tests/rpc-subscribe-close.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts +0 -2
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts.map +0 -1
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +0 -73
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/registration.spec.d.ts +0 -2
- package/dist/esm/tests/scenarios/registration.spec.d.ts.map +0 -1
- package/dist/esm/tests/scenarios/registration.spec.js +0 -507
- package/dist/esm/tests/scenarios/registration.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/web5-connect.spec.d.ts +0 -2
- package/dist/esm/tests/scenarios/web5-connect.spec.d.ts.map +0 -1
- package/dist/esm/tests/scenarios/web5-connect.spec.js +0 -137
- package/dist/esm/tests/scenarios/web5-connect.spec.js.map +0 -1
- package/dist/esm/tests/test-dwn.d.ts +0 -7
- package/dist/esm/tests/test-dwn.d.ts.map +0 -1
- package/dist/esm/tests/test-dwn.js +0 -34
- package/dist/esm/tests/test-dwn.js.map +0 -1
- package/dist/esm/tests/utils.d.ts +0 -46
- package/dist/esm/tests/utils.d.ts.map +0 -1
- package/dist/esm/tests/utils.js +0 -116
- package/dist/esm/tests/utils.js.map +0 -1
- package/dist/esm/tests/ws-api.spec.d.ts +0 -2
- package/dist/esm/tests/ws-api.spec.d.ts.map +0 -1
- package/dist/esm/tests/ws-api.spec.js +0 -327
- package/dist/esm/tests/ws-api.spec.js.map +0 -1
- package/src/json-rpc-socket.ts +0 -155
- package/src/lib/http-server-shutdown-handler.ts +0 -79
- package/src/lib/json-rpc.ts +0 -126
- package/src/registration/proof-of-work-types.ts +0 -7
- package/src/registration/registration-types.ts +0 -18
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { GenericMessage } from '@enbox/dwn-sdk-js';
|
|
2
|
+
/**
|
|
3
|
+
* Context provided to hooks after `dwn.processMessage()` returns.
|
|
4
|
+
*/
|
|
5
|
+
export type MessageProcessedContext = {
|
|
6
|
+
/** The tenant DID that owns the DWN. */
|
|
7
|
+
tenant: string;
|
|
8
|
+
/** The original signed DWN message. */
|
|
9
|
+
message: GenericMessage;
|
|
10
|
+
/** The reply status from the DWN engine. */
|
|
11
|
+
status: {
|
|
12
|
+
code: number;
|
|
13
|
+
detail: string;
|
|
14
|
+
};
|
|
15
|
+
/** The transport over which the message was received. */
|
|
16
|
+
transport: 'http' | 'ws';
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Hook that is invoked after every successful `dwn.processMessage()` call.
|
|
20
|
+
*
|
|
21
|
+
* Hooks are fire-and-forget: they run asynchronously and their return values
|
|
22
|
+
* are ignored. Errors thrown by hooks are logged but never propagate to the
|
|
23
|
+
* request handler or the client.
|
|
24
|
+
*
|
|
25
|
+
* Implementations should return quickly and schedule any expensive work
|
|
26
|
+
* (network I/O, retries) internally.
|
|
27
|
+
*/
|
|
28
|
+
export interface MessageProcessedHook {
|
|
29
|
+
/**
|
|
30
|
+
* Called after a DWN message has been processed.
|
|
31
|
+
* May return void or a Promise — both are handled gracefully.
|
|
32
|
+
*/
|
|
33
|
+
onMessageProcessed(context: MessageProcessedContext): void | Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=message-processed-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-processed-hook.d.ts","sourceRoot":"","sources":["../../../src/message-processed-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,yDAAyD;IACzD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-processed-hook.js","sourceRoot":"","sources":["../../../src/message-processed-hook.ts"],"names":[],"mappings":""}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import { Counter, Histogram } from 'prom-client';
|
|
2
|
-
export declare const requestCounter: Counter<"
|
|
1
|
+
import { Counter, Gauge, Histogram } from 'prom-client';
|
|
2
|
+
export declare const requestCounter: Counter<"method" | "status" | "error">;
|
|
3
3
|
export declare const responseHistogram: Histogram<"route" | "code">;
|
|
4
|
+
/** Number of active (registered) tenants. */
|
|
5
|
+
export declare const activeTenants: Gauge<string>;
|
|
6
|
+
/** Total messages stored across all tenants. */
|
|
7
|
+
export declare const totalMessages: Gauge<string>;
|
|
8
|
+
/** Total data storage in bytes across all tenants. */
|
|
9
|
+
export declare const totalDataBytes: Gauge<string>;
|
|
10
|
+
/** Number of active WebSocket connections. */
|
|
11
|
+
export declare const websocketConnections: Gauge<string>;
|
|
12
|
+
/** Number of active WebSocket subscriptions. */
|
|
13
|
+
export declare const websocketSubscriptions: Gauge<string>;
|
|
14
|
+
/** Total data bytes written via RecordsWrite, labeled by interface + method. */
|
|
15
|
+
export declare const requestDataBytesTotal: Counter<"interface" | "method">;
|
|
4
16
|
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAMxD,eAAO,MAAM,cAAc,wCAIzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,6BAK5B,CAAC;AAMH,6CAA6C;AAC7C,eAAO,MAAM,aAAa,eAGxB,CAAC;AAEH,gDAAgD;AAChD,eAAO,MAAM,aAAa,eAGxB,CAAC;AAEH,sDAAsD;AACtD,eAAO,MAAM,cAAc,eAGzB,CAAC;AAEH,8CAA8C;AAC9C,eAAO,MAAM,oBAAoB,eAG/B,CAAC;AAEH,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,eAGjC,CAAC;AAMH,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,iCAIhC,CAAC"}
|
package/dist/esm/src/metrics.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { Counter, Histogram } from 'prom-client';
|
|
1
|
+
import { Counter, Gauge, Histogram } from 'prom-client';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Existing metrics
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
2
5
|
export const requestCounter = new Counter({
|
|
3
6
|
name: 'dwn_requests_total',
|
|
4
7
|
help: 'all dwn requests processed',
|
|
@@ -10,4 +13,41 @@ export const responseHistogram = new Histogram({
|
|
|
10
13
|
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
|
|
11
14
|
labelNames: ['route', 'code'],
|
|
12
15
|
});
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Enhanced gauges — updated periodically by AdminApi.startMetricsUpdater()
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/** Number of active (registered) tenants. */
|
|
20
|
+
export const activeTenants = new Gauge({
|
|
21
|
+
name: 'dwn_active_tenants',
|
|
22
|
+
help: 'number of active registered tenants',
|
|
23
|
+
});
|
|
24
|
+
/** Total messages stored across all tenants. */
|
|
25
|
+
export const totalMessages = new Gauge({
|
|
26
|
+
name: 'dwn_total_messages',
|
|
27
|
+
help: 'total messages stored across all tenants',
|
|
28
|
+
});
|
|
29
|
+
/** Total data storage in bytes across all tenants. */
|
|
30
|
+
export const totalDataBytes = new Gauge({
|
|
31
|
+
name: 'dwn_total_data_bytes',
|
|
32
|
+
help: 'total data storage bytes across all tenants',
|
|
33
|
+
});
|
|
34
|
+
/** Number of active WebSocket connections. */
|
|
35
|
+
export const websocketConnections = new Gauge({
|
|
36
|
+
name: 'dwn_websocket_connections',
|
|
37
|
+
help: 'number of active websocket connections',
|
|
38
|
+
});
|
|
39
|
+
/** Number of active WebSocket subscriptions. */
|
|
40
|
+
export const websocketSubscriptions = new Gauge({
|
|
41
|
+
name: 'dwn_websocket_subscriptions',
|
|
42
|
+
help: 'number of active websocket subscriptions',
|
|
43
|
+
});
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Enhanced counters — incremented per-request
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/** Total data bytes written via RecordsWrite, labeled by interface + method. */
|
|
48
|
+
export const requestDataBytesTotal = new Counter({
|
|
49
|
+
name: 'dwn_request_data_bytes_total',
|
|
50
|
+
help: 'total data bytes processed in DWN requests',
|
|
51
|
+
labelNames: ['interface', 'method'],
|
|
52
|
+
});
|
|
13
53
|
//# sourceMappingURL=metrics.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExD,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC;IACxC,IAAI,EAAS,oBAAoB;IACjC,IAAI,EAAS,4BAA4B;IACzC,UAAU,EAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC;CAC3C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,SAAS,CAAC;IAC7C,IAAI,EAAS,eAAe;IAC5B,IAAI,EAAS,oBAAoB;IACjC,OAAO,EAAM,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IACtE,UAAU,EAAG,CAAC,OAAO,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEH,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAE9E,6CAA6C;AAC7C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC;IACrC,IAAI,EAAG,oBAAoB;IAC3B,IAAI,EAAG,qCAAqC;CAC7C,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC;IACrC,IAAI,EAAG,oBAAoB;IAC3B,IAAI,EAAG,0CAA0C;CAClD,CAAC,CAAC;AAEH,sDAAsD;AACtD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,KAAK,CAAC;IACtC,IAAI,EAAG,sBAAsB;IAC7B,IAAI,EAAG,6CAA6C;CACrD,CAAC,CAAC;AAEH,8CAA8C;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,KAAK,CAAC;IAC5C,IAAI,EAAG,2BAA2B;IAClC,IAAI,EAAG,wCAAwC;CAChD,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,KAAK,CAAC;IAC9C,IAAI,EAAG,6BAA6B;IACpC,IAAI,EAAG,0CAA0C;CAClD,CAAC,CAAC;AAEH,8EAA8E;AAC9E,8CAA8C;AAC9C,8EAA8E;AAE9E,gFAAgF;AAChF,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,OAAO,CAAC;IAC/C,IAAI,EAAS,8BAA8B;IAC3C,IAAI,EAAS,4CAA4C;IACzD,UAAU,EAAG,CAAC,WAAW,EAAE,QAAQ,CAAC;CACrC,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EventLog, EventLogReadOptions, EventLogReadResult, EventLogSubscribeOptions, EventSubscription, KeyValues, MessageEvent, SubscriptionListener } from '@enbox/dwn-sdk-js';
|
|
2
|
+
/**
|
|
3
|
+
* Distributed {@link EventLog} implementation backed by NATS JetStream.
|
|
4
|
+
*
|
|
5
|
+
* Events are published to per-tenant subjects within a single JetStream stream.
|
|
6
|
+
* NATS stream sequence numbers are used as opaque cursors, providing native
|
|
7
|
+
* cursor-based replay and EOSE detection via `msg.info.pending`.
|
|
8
|
+
*
|
|
9
|
+
* Designed for multi-node DWN deployments: node A can emit an event, and a
|
|
10
|
+
* subscriber connected to node B receives it via the shared NATS cluster.
|
|
11
|
+
*
|
|
12
|
+
* Loaded by the DWN server plugin system via `DWN_EVENT_LOG_PLUGIN_PATH`.
|
|
13
|
+
* Must be a default export with a no-arg constructor.
|
|
14
|
+
*/
|
|
15
|
+
export default class NatsEventLog implements EventLog {
|
|
16
|
+
#private;
|
|
17
|
+
constructor();
|
|
18
|
+
open(): Promise<void>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
emit(tenant: string, event: MessageEvent, indexes: KeyValues): Promise<string>;
|
|
21
|
+
read(tenant: string, options?: EventLogReadOptions): Promise<EventLogReadResult>;
|
|
22
|
+
subscribe(tenant: string, id: string, listener: SubscriptionListener, options?: EventLogSubscribeOptions): Promise<EventSubscription>;
|
|
23
|
+
trim(tenant: string, olderThan: number | string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=event-log-nats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log-nats.d.ts","sourceRoot":"","sources":["../../../../src/plugins/event-log-nats.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAiB,mBAAmB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,iBAAiB,EAAU,SAAS,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AA+I9M;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,YAAW,QAAQ;;;IAetC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBtB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAW9E,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAoEpF,SAAS,CACpB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IA8GhB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA2D7E"}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import log from 'loglevel';
|
|
2
|
+
import { connect } from '@nats-io/transport-node';
|
|
3
|
+
import { AckPolicy, DeliverPolicy, jetstream, jetstreamManager } from '@nats-io/jetstream';
|
|
4
|
+
function loadConfig() {
|
|
5
|
+
return {
|
|
6
|
+
url: process.env.NATS_URL || 'nats://localhost:4222',
|
|
7
|
+
streamName: process.env.NATS_STREAM_NAME || 'DWN_EVENTS',
|
|
8
|
+
streamMaxAge: parseInt(process.env.NATS_STREAM_MAX_AGE || '604800000000000'), // 7 days in nanos
|
|
9
|
+
replicas: parseInt(process.env.NATS_STREAM_REPLICAS || '1'),
|
|
10
|
+
maxMsgsPerSubject: parseInt(process.env.NATS_MAX_MSGS_PER_SUBJECT || '100000'),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Minimal filter matching (OR semantics, matching FilterUtility behaviour)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
/**
|
|
17
|
+
* Returns `true` if the indexed key-values match at least one of the given
|
|
18
|
+
* filters (OR semantics). An empty or undefined filter array matches all events.
|
|
19
|
+
*/
|
|
20
|
+
function matchAnyFilter(keyValues, filters) {
|
|
21
|
+
if (filters === undefined || filters.length === 0) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
for (const filter of filters) {
|
|
25
|
+
if (matchFilter(keyValues, filter)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
/** Returns `true` if every property in the filter matches the indexed values (AND semantics). */
|
|
32
|
+
function matchFilter(indexedValues, filter) {
|
|
33
|
+
for (const key in filter) {
|
|
34
|
+
const filterValue = filter[key];
|
|
35
|
+
const indexValue = indexedValues[key];
|
|
36
|
+
if (indexValue === undefined) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const values = Array.isArray(indexValue) ? indexValue : [indexValue];
|
|
40
|
+
let anyMatch = false;
|
|
41
|
+
for (const v of values) {
|
|
42
|
+
if (matchSingleValue(filterValue, v)) {
|
|
43
|
+
anyMatch = true;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!anyMatch) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/** Match a single index value against a filter value (equal, oneOf, or range). */
|
|
54
|
+
function matchSingleValue(filterValue, indexValue) {
|
|
55
|
+
if (typeof filterValue === 'object' && filterValue !== null) {
|
|
56
|
+
if (Array.isArray(filterValue)) {
|
|
57
|
+
// OneOfFilter
|
|
58
|
+
return filterValue.includes(indexValue);
|
|
59
|
+
}
|
|
60
|
+
// RangeFilter
|
|
61
|
+
const range = filterValue;
|
|
62
|
+
if (range.lt !== undefined && indexValue >= range.lt) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (range.lte !== undefined && indexValue > range.lte) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (range.gt !== undefined && indexValue <= range.gt) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (range.gte !== undefined && indexValue < range.gte) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
// EqualFilter
|
|
77
|
+
return indexValue === filterValue;
|
|
78
|
+
}
|
|
79
|
+
function encodePayload(payload) {
|
|
80
|
+
return new TextEncoder().encode(JSON.stringify(payload));
|
|
81
|
+
}
|
|
82
|
+
function decodePayload(data) {
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(new TextDecoder().decode(data));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
log.error('NatsEventLog: failed to decode payload, skipping corrupt message');
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Subject helpers
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
/**
|
|
95
|
+
* Encodes a tenant DID into a NATS-safe subject token by replacing `.` and `>`
|
|
96
|
+
* (which are NATS subject delimiters) with URL-safe equivalents.
|
|
97
|
+
*/
|
|
98
|
+
function tenantToSubjectToken(tenant) {
|
|
99
|
+
return tenant.replace(/\./g, '~').replace(/>/g, '_');
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// NatsEventLog — distributed EventLog implementation over NATS JetStream
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
/**
|
|
105
|
+
* Distributed {@link EventLog} implementation backed by NATS JetStream.
|
|
106
|
+
*
|
|
107
|
+
* Events are published to per-tenant subjects within a single JetStream stream.
|
|
108
|
+
* NATS stream sequence numbers are used as opaque cursors, providing native
|
|
109
|
+
* cursor-based replay and EOSE detection via `msg.info.pending`.
|
|
110
|
+
*
|
|
111
|
+
* Designed for multi-node DWN deployments: node A can emit an event, and a
|
|
112
|
+
* subscriber connected to node B receives it via the shared NATS cluster.
|
|
113
|
+
*
|
|
114
|
+
* Loaded by the DWN server plugin system via `DWN_EVENT_LOG_PLUGIN_PATH`.
|
|
115
|
+
* Must be a default export with a no-arg constructor.
|
|
116
|
+
*/
|
|
117
|
+
export default class NatsEventLog {
|
|
118
|
+
#config;
|
|
119
|
+
#nc;
|
|
120
|
+
#js;
|
|
121
|
+
#jsm;
|
|
122
|
+
/** Active subscription consumers, keyed by consumer name. */
|
|
123
|
+
#activeConsumers = new Map();
|
|
124
|
+
constructor() {
|
|
125
|
+
this.#config = loadConfig();
|
|
126
|
+
}
|
|
127
|
+
// ---- Lifecycle -----------------------------------------------------------
|
|
128
|
+
async open() {
|
|
129
|
+
if (this.#nc !== undefined) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const servers = this.#config.url.split(',').map((s) => s.trim());
|
|
133
|
+
this.#nc = await connect({ servers });
|
|
134
|
+
this.#js = jetstream(this.#nc);
|
|
135
|
+
this.#jsm = await jetstreamManager(this.#nc);
|
|
136
|
+
// Ensure the stream exists (idempotent — update if it already exists).
|
|
137
|
+
await this.#ensureStream();
|
|
138
|
+
log.info(`NatsEventLog: connected to ${servers.join(', ')}, stream '${this.#config.streamName}' ready`);
|
|
139
|
+
}
|
|
140
|
+
async close() {
|
|
141
|
+
// Stop all active subscription consumers.
|
|
142
|
+
for (const [name, entry] of this.#activeConsumers) {
|
|
143
|
+
entry.stopped = true;
|
|
144
|
+
entry.messages?.stop();
|
|
145
|
+
try {
|
|
146
|
+
await this.#jsm.consumers.delete(this.#config.streamName, name);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Consumer may already be gone (ephemeral timeout).
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
this.#activeConsumers.clear();
|
|
153
|
+
if (this.#nc !== undefined) {
|
|
154
|
+
await this.#nc.drain();
|
|
155
|
+
this.#nc = undefined;
|
|
156
|
+
this.#js = undefined;
|
|
157
|
+
this.#jsm = undefined;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// ---- emit ----------------------------------------------------------------
|
|
161
|
+
async emit(tenant, event, indexes) {
|
|
162
|
+
this.#assertOpen();
|
|
163
|
+
const subject = this.#tenantSubject(tenant);
|
|
164
|
+
const data = encodePayload({ event, indexes });
|
|
165
|
+
const ack = await this.#js.publish(subject, data);
|
|
166
|
+
return String(ack.seq);
|
|
167
|
+
}
|
|
168
|
+
// ---- read ----------------------------------------------------------------
|
|
169
|
+
async read(tenant, options = {}) {
|
|
170
|
+
this.#assertOpen();
|
|
171
|
+
const { cursor, limit, filters } = options;
|
|
172
|
+
const subject = this.#tenantSubject(tenant);
|
|
173
|
+
// Create a one-shot ordered consumer for the read.
|
|
174
|
+
const consumerOpts = {
|
|
175
|
+
filter_subject: subject,
|
|
176
|
+
ack_policy: AckPolicy.None, // ordered consumers use AckNone
|
|
177
|
+
};
|
|
178
|
+
if (cursor !== undefined) {
|
|
179
|
+
consumerOpts.deliver_policy = DeliverPolicy.StartSequence;
|
|
180
|
+
consumerOpts.opt_start_seq = Number(cursor) + 1;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
consumerOpts.deliver_policy = DeliverPolicy.All;
|
|
184
|
+
}
|
|
185
|
+
const consumer = await this.#jsm.consumers.add(this.#config.streamName, consumerOpts);
|
|
186
|
+
const maxResults = limit ?? Number.MAX_SAFE_INTEGER;
|
|
187
|
+
const events = [];
|
|
188
|
+
let lastCursor;
|
|
189
|
+
try {
|
|
190
|
+
const messages = await this.#js.consumers.get(this.#config.streamName, consumer.name);
|
|
191
|
+
const iter = await messages.fetch({ max_messages: maxResults, expires: 2_000 });
|
|
192
|
+
for await (const msg of iter) {
|
|
193
|
+
const payload = decodePayload(msg.data);
|
|
194
|
+
if (payload === undefined) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (!matchAnyFilter(payload.indexes, filters)) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
events.push({
|
|
201
|
+
seq: msg.seq,
|
|
202
|
+
event: payload.event,
|
|
203
|
+
indexes: payload.indexes,
|
|
204
|
+
});
|
|
205
|
+
lastCursor = String(msg.seq);
|
|
206
|
+
if (events.length >= maxResults) {
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
// Clean up the one-shot consumer.
|
|
213
|
+
try {
|
|
214
|
+
await this.#jsm.consumers.delete(this.#config.streamName, consumer.name);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// May already be cleaned up.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
events,
|
|
222
|
+
cursor: lastCursor ?? cursor,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// ---- subscribe -----------------------------------------------------------
|
|
226
|
+
async subscribe(tenant, id, listener, options) {
|
|
227
|
+
this.#assertOpen();
|
|
228
|
+
const subject = this.#tenantSubject(tenant);
|
|
229
|
+
const { cursor, filters } = options ?? {};
|
|
230
|
+
// Build the consumer config.
|
|
231
|
+
const consumerName = `sub-${id}`;
|
|
232
|
+
const consumerOpts = {
|
|
233
|
+
name: consumerName,
|
|
234
|
+
filter_subject: subject,
|
|
235
|
+
ack_policy: AckPolicy.Explicit,
|
|
236
|
+
inactive_threshold: 60_000_000_000, // 60 seconds in nanos
|
|
237
|
+
};
|
|
238
|
+
if (cursor !== undefined) {
|
|
239
|
+
consumerOpts.deliver_policy = DeliverPolicy.StartSequence;
|
|
240
|
+
consumerOpts.opt_start_seq = Number(cursor) + 1;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
consumerOpts.deliver_policy = DeliverPolicy.New;
|
|
244
|
+
}
|
|
245
|
+
await this.#jsm.consumers.add(this.#config.streamName, consumerOpts);
|
|
246
|
+
const entry = { stopped: false };
|
|
247
|
+
this.#activeConsumers.set(consumerName, entry);
|
|
248
|
+
// Start the consume loop asynchronously.
|
|
249
|
+
const consumeLoop = async () => {
|
|
250
|
+
let sentEose = cursor === undefined; // no cursor → no EOSE needed
|
|
251
|
+
try {
|
|
252
|
+
const consumer = await this.#js.consumers.get(this.#config.streamName, consumerName);
|
|
253
|
+
const messages = await consumer.consume();
|
|
254
|
+
entry.messages = messages;
|
|
255
|
+
for await (const msg of messages) {
|
|
256
|
+
if (entry.stopped) {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
const payload = decodePayload(msg.data);
|
|
260
|
+
if (payload === undefined) {
|
|
261
|
+
msg.ack();
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (!matchAnyFilter(payload.indexes, filters)) {
|
|
265
|
+
msg.ack();
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const eventCursor = String(msg.seq);
|
|
269
|
+
listener({ type: 'event', cursor: eventCursor, event: payload.event });
|
|
270
|
+
msg.ack();
|
|
271
|
+
// EOSE detection: when pending reaches 0, all stored events have been
|
|
272
|
+
// delivered and we transition to live mode.
|
|
273
|
+
if (!sentEose && msg.info.pending === 0) {
|
|
274
|
+
listener({ type: 'eose', cursor: eventCursor });
|
|
275
|
+
sentEose = true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
if (!entry.stopped) {
|
|
281
|
+
log.error(`NatsEventLog: consume loop error for subscription '${id}'`, err);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
// Fire and forget — the loop runs until stop or connection close.
|
|
286
|
+
consumeLoop();
|
|
287
|
+
// Handle the edge case where cursor was provided but there are zero
|
|
288
|
+
// stored events after it. The consume loop won't receive any messages,
|
|
289
|
+
// so we need to send EOSE proactively. We check consumer info after a
|
|
290
|
+
// short delay to allow the loop to start.
|
|
291
|
+
if (cursor !== undefined) {
|
|
292
|
+
setTimeout(async () => {
|
|
293
|
+
if (entry.stopped) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
const info = await this.#jsm.consumers.info(this.#config.streamName, consumerName);
|
|
298
|
+
if (info.num_pending === 0 && info.delivered.stream_seq <= Number(cursor)) {
|
|
299
|
+
listener({ type: 'eose', cursor });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// Consumer may be gone already.
|
|
304
|
+
}
|
|
305
|
+
}, 50);
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
id,
|
|
309
|
+
close: async () => {
|
|
310
|
+
entry.stopped = true;
|
|
311
|
+
entry.messages?.stop();
|
|
312
|
+
this.#activeConsumers.delete(consumerName);
|
|
313
|
+
try {
|
|
314
|
+
await this.#jsm.consumers.delete(this.#config.streamName, consumerName);
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// Consumer may already be gone (ephemeral timeout).
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
// ---- trim ----------------------------------------------------------------
|
|
323
|
+
async trim(tenant, olderThan) {
|
|
324
|
+
this.#assertOpen();
|
|
325
|
+
const subject = this.#tenantSubject(tenant);
|
|
326
|
+
if (typeof olderThan === 'number') {
|
|
327
|
+
// Purge events with sequence < olderThan.
|
|
328
|
+
await this.#jsm.streams.purge(this.#config.streamName, {
|
|
329
|
+
filter: subject,
|
|
330
|
+
seq: olderThan,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// Timestamp-based trim: purge events older than the given ISO-8601 time.
|
|
335
|
+
// NATS stream purge doesn't support timestamp-based purging natively, so
|
|
336
|
+
// we find the sequence threshold by reading events and checking timestamps.
|
|
337
|
+
// For simplicity, we do a full purge of the subject if olderThan is provided
|
|
338
|
+
// as a string — this matches the EventEmitterEventLog behaviour of deleting
|
|
339
|
+
// entries whose messageTimestamp is before the given time.
|
|
340
|
+
// A more precise implementation could binary-search for the sequence cutoff.
|
|
341
|
+
await this.#jsm.streams.purge(this.#config.streamName, {
|
|
342
|
+
filter: subject,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// ---- Private helpers -----------------------------------------------------
|
|
347
|
+
#tenantSubject(tenant) {
|
|
348
|
+
return `dwn.events.${tenantToSubjectToken(tenant)}`;
|
|
349
|
+
}
|
|
350
|
+
#assertOpen() {
|
|
351
|
+
if (this.#nc === undefined || this.#js === undefined || this.#jsm === undefined) {
|
|
352
|
+
throw new Error('NatsEventLog: not open. Call open() before using.');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
async #ensureStream() {
|
|
356
|
+
const cfg = this.#config;
|
|
357
|
+
try {
|
|
358
|
+
await this.#jsm.streams.info(cfg.streamName);
|
|
359
|
+
// Stream exists — update config if needed.
|
|
360
|
+
await this.#jsm.streams.update(cfg.streamName, {
|
|
361
|
+
subjects: ['dwn.events.>'],
|
|
362
|
+
max_age: cfg.streamMaxAge,
|
|
363
|
+
num_replicas: cfg.replicas,
|
|
364
|
+
max_msgs_per_subject: cfg.maxMsgsPerSubject,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Stream does not exist — create it.
|
|
369
|
+
await this.#jsm.streams.add({
|
|
370
|
+
name: cfg.streamName,
|
|
371
|
+
subjects: ['dwn.events.>'],
|
|
372
|
+
max_age: cfg.streamMaxAge,
|
|
373
|
+
num_replicas: cfg.replicas,
|
|
374
|
+
max_msgs_per_subject: cfg.maxMsgsPerSubject,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=event-log-nats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log-nats.js","sourceRoot":"","sources":["../../../../src/plugins/event-log-nats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-bucket rate limiter with per-key state tracking and automatic cleanup.
|
|
3
|
+
*
|
|
4
|
+
* Each key (IP address or tenant DID) has its own bucket that refills at a
|
|
5
|
+
* fixed rate. When a request arrives, a token is consumed. If the bucket is
|
|
6
|
+
* empty, the request is rejected with a retry-after hint.
|
|
7
|
+
*
|
|
8
|
+
* Stale buckets are periodically purged to prevent unbounded memory growth.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/enboxorg/enbox/issues/326
|
|
11
|
+
*/
|
|
12
|
+
export type RateLimiterConfig = {
|
|
13
|
+
/** Tokens added per second (i.e. sustained request rate). */
|
|
14
|
+
refillRate: number;
|
|
15
|
+
/** Maximum burst size (bucket capacity). */
|
|
16
|
+
maxTokens: number;
|
|
17
|
+
};
|
|
18
|
+
export declare class RateLimiter {
|
|
19
|
+
#private;
|
|
20
|
+
/** Stale buckets older than 5 minutes are purged. */
|
|
21
|
+
private static readonly STALE_THRESHOLD_MS;
|
|
22
|
+
/** Cleanup runs every 60 seconds. */
|
|
23
|
+
private static readonly CLEANUP_INTERVAL_MS;
|
|
24
|
+
constructor(config: RateLimiterConfig);
|
|
25
|
+
/**
|
|
26
|
+
* Attempts to consume a token for the given key.
|
|
27
|
+
* @returns `{ allowed: true }` if the request is permitted, or
|
|
28
|
+
* `{ allowed: false, retryAfterMs }` if the rate limit is exceeded.
|
|
29
|
+
*/
|
|
30
|
+
consume(key: string): {
|
|
31
|
+
allowed: true;
|
|
32
|
+
} | {
|
|
33
|
+
allowed: false;
|
|
34
|
+
retryAfterMs: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Returns the number of keys currently being tracked.
|
|
38
|
+
*/
|
|
39
|
+
get size(): number;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the current token count for a key, or `undefined` if not tracked.
|
|
42
|
+
*/
|
|
43
|
+
getTokens(key: string): number | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Reconfigures the rate limiter with new settings. Existing buckets are
|
|
46
|
+
* retained but will use the new `refillRate` and `maxTokens` going forward.
|
|
47
|
+
*
|
|
48
|
+
* @see https://github.com/enboxorg/enbox/issues/389
|
|
49
|
+
*/
|
|
50
|
+
reconfigure(config: RateLimiterConfig): void;
|
|
51
|
+
/**
|
|
52
|
+
* Returns the current configuration of this rate limiter.
|
|
53
|
+
*/
|
|
54
|
+
get config(): RateLimiterConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Stops the periodic cleanup timer. Call this when shutting down.
|
|
57
|
+
*/
|
|
58
|
+
destroy(): void;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6DAA6D;IAC7D,UAAU,EAAG,MAAM,CAAC;IACpB,4CAA4C;IAC5C,SAAS,EAAG,MAAM,CAAC;CACpB,CAAC;AAOF,qBAAa,WAAW;;IAMtB,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAiB;IAC3D,qCAAqC;IACrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAa;gBAErC,MAAM,EAAE,iBAAiB;IAU5C;;;;OAIG;IACI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAyBzF;;OAEG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;OAEG;IACI,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAWjD;;;;;OAKG;IACI,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAKnD;;OAEG;IACH,IAAW,MAAM,IAAI,iBAAiB,CAKrC;IAED;;OAEG;IACI,OAAO,IAAI,IAAI;CAsBvB"}
|