arkos 1.7.0-canary.21 → 1.7.0-canary.23
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/dist/cjs/components/arkos-gateway/arkos-gateway.js +70 -39
- package/dist/cjs/components/arkos-gateway/arkos-gateway.js.map +1 -1
- package/dist/cjs/components/arkos-gateway/types.js.map +1 -1
- package/dist/cjs/components/arkos-gateway/utils/emit-builders.js +180 -0
- package/dist/cjs/components/arkos-gateway/utils/emit-builders.js.map +1 -0
- package/dist/cjs/components/arkos-gateway/utils/memory-gateway-store.js +8 -0
- package/dist/cjs/components/arkos-gateway/utils/memory-gateway-store.js.map +1 -1
- package/dist/cjs/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.js +14 -0
- package/dist/cjs/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.js.map +1 -1
- package/dist/cjs/modules/swagger/utils/get-open-api-login-html.js +3 -1
- package/dist/cjs/modules/swagger/utils/get-open-api-login-html.js.map +1 -1
- package/dist/cjs/utils/cli/dev.js +1 -1
- package/dist/cjs/utils/cli/dev.js.map +1 -1
- package/dist/cjs/utils/cli/index.js.map +1 -1
- package/dist/cjs/utils/cli/utils/cli.helpers.js +1 -1
- package/dist/cjs/utils/cli/utils/template-generator/templates/generate-multiple-components.js +3 -3
- package/dist/cjs/utils/cli/utils/template-generator/templates/generate-multiple-components.js.map +1 -1
- package/dist/cjs/utils/cli/utils/template-generator/templates/{middlewares-template.js → interceptors-template.js} +1 -1
- package/dist/cjs/utils/cli/utils/template-generator/templates/interceptors-template.js.map +1 -0
- package/dist/cjs/utils/cli/utils/template-generators.js +2 -2
- package/dist/cjs/utils/cli/utils/template-generators.js.map +1 -1
- package/dist/cjs/utils/dynamic-loader.js +3 -14
- package/dist/cjs/utils/dynamic-loader.js.map +1 -1
- package/dist/cjs/utils/helpers/exit-error.js +1 -1
- package/dist/cjs/utils/helpers/exit-error.js.map +1 -1
- package/dist/cjs/utils/remove-dir.js +1 -1
- package/dist/cjs/utils/remove-dir.js.map +1 -1
- package/dist/esm/components/arkos-gateway/arkos-gateway.js +64 -33
- package/dist/esm/components/arkos-gateway/arkos-gateway.js.map +1 -1
- package/dist/esm/components/arkos-gateway/types.js.map +1 -1
- package/dist/esm/components/arkos-gateway/utils/emit-builders.js +172 -0
- package/dist/esm/components/arkos-gateway/utils/emit-builders.js.map +1 -0
- package/dist/esm/components/arkos-gateway/utils/memory-gateway-store.js +8 -0
- package/dist/esm/components/arkos-gateway/utils/memory-gateway-store.js.map +1 -1
- package/dist/esm/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.js +14 -0
- package/dist/esm/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.js.map +1 -1
- package/dist/esm/modules/swagger/utils/get-open-api-login-html.js +3 -1
- package/dist/esm/modules/swagger/utils/get-open-api-login-html.js.map +1 -1
- package/dist/esm/utils/cli/dev.js +1 -1
- package/dist/esm/utils/cli/dev.js.map +1 -1
- package/dist/esm/utils/cli/index.js.map +1 -1
- package/dist/esm/utils/cli/utils/cli.helpers.js +1 -1
- package/dist/esm/utils/cli/utils/template-generator/templates/generate-multiple-components.js +3 -3
- package/dist/esm/utils/cli/utils/template-generator/templates/generate-multiple-components.js.map +1 -1
- package/dist/esm/utils/cli/utils/template-generator/templates/{middlewares-template.js → interceptors-template.js} +1 -1
- package/dist/esm/utils/cli/utils/template-generator/templates/interceptors-template.js.map +1 -0
- package/dist/esm/utils/cli/utils/template-generators.js +1 -1
- package/dist/esm/utils/cli/utils/template-generators.js.map +1 -1
- package/dist/esm/utils/dynamic-loader.js +3 -14
- package/dist/esm/utils/dynamic-loader.js.map +1 -1
- package/dist/esm/utils/helpers/exit-error.js +1 -1
- package/dist/esm/utils/helpers/exit-error.js.map +1 -1
- package/dist/esm/utils/remove-dir.js +1 -1
- package/dist/esm/utils/remove-dir.js.map +1 -1
- package/dist/types/components/arkos-gateway/arkos-gateway.d.ts +3 -4
- package/dist/types/components/arkos-gateway/types.d.ts +122 -7
- package/dist/types/components/arkos-gateway/utils/emit-builders.d.ts +175 -0
- package/dist/types/components/arkos-gateway/utils/memory-gateway-store.d.ts +1 -0
- package/dist/types/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.d.ts +1 -0
- package/package.json +1 -1
- package/dist/cjs/components/arkos-gateway/utils/gateway-builders.js +0 -121
- package/dist/cjs/components/arkos-gateway/utils/gateway-builders.js.map +0 -1
- package/dist/cjs/utils/cli/utils/template-generator/templates/middlewares-template.js.map +0 -1
- package/dist/esm/components/arkos-gateway/utils/gateway-builders.js +0 -113
- package/dist/esm/components/arkos-gateway/utils/gateway-builders.js.map +0 -1
- package/dist/esm/utils/cli/utils/template-generator/templates/middlewares-template.js.map +0 -1
- package/dist/types/components/arkos-gateway/utils/gateway-builders.d.ts +0 -110
- /package/dist/types/utils/cli/utils/template-generator/templates/{middlewares-template.d.ts → interceptors-template.d.ts} +0 -0
|
@@ -13,7 +13,7 @@ const validation_manager_1 = __importDefault(require("../../types/validation/val
|
|
|
13
13
|
const error_handler_1 = require("../../exports/error-handler/index.js");
|
|
14
14
|
const auth_error_objects_1 = require("../../modules/auth/utils/auth-error-objects.js");
|
|
15
15
|
const error_prettifier_1 = __importDefault(require("../../modules/base/utils/error-prettifier.js"));
|
|
16
|
-
const
|
|
16
|
+
const emit_builders_1 = require("./utils/emit-builders.js");
|
|
17
17
|
const deepmerge_helper_1 = __importDefault(require("../../utils/helpers/deepmerge.helper.js"));
|
|
18
18
|
const memory_gateway_store_1 = require("./utils/memory-gateway-store.js");
|
|
19
19
|
class IArkosGateway {
|
|
@@ -92,10 +92,10 @@ class IArkosGateway {
|
|
|
92
92
|
return this;
|
|
93
93
|
}
|
|
94
94
|
register(io, options = {}) {
|
|
95
|
-
if (
|
|
96
|
-
throw new Error(`gateway.register() can only be called once
|
|
97
|
-
|
|
98
|
-
this._register(io, undefined, [], [], options);
|
|
95
|
+
if (io._arkosGatewayRegistered)
|
|
96
|
+
throw new Error(`The method gateway.register() can only be called once per io server instance. Use gateway.use() to compose gateways, see https://www.arkosjs.com/docs/components/advanced-guides/web-sockets/setup.`);
|
|
97
|
+
io._arkosGatewayRegistered = true;
|
|
98
|
+
this._register(io, undefined, this.hooks || [], this.pipes || [], options);
|
|
99
99
|
}
|
|
100
100
|
_register(io, parentConfig, inheritedHooks = [], inheritedPipes = [], options = {}) {
|
|
101
101
|
options.store = options.store ?? memory_gateway_store_1.defaultGatewayStore;
|
|
@@ -146,6 +146,18 @@ class IArkosGateway {
|
|
|
146
146
|
const connectionStartTime = new Date().getTime();
|
|
147
147
|
if (socket.user?.id)
|
|
148
148
|
socket.join(`arkos::user:${socket.user.id}`);
|
|
149
|
+
try {
|
|
150
|
+
for (const handler of connectHandlers)
|
|
151
|
+
await handler(socket, io);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
(0, helpers_1.handleArkosGatewayErrors)(err, socket, io, [], {
|
|
155
|
+
startTime: connectionStartTime,
|
|
156
|
+
namespace: this.config.name,
|
|
157
|
+
event: "connection",
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
149
161
|
socket.on("disconnect", async () => {
|
|
150
162
|
(0, helpers_1.handleGatewayLifecycleLog)(this.config.name, "disconnected", socket.id);
|
|
151
163
|
await (0, rate_limiter_1.clearRateLimitForSocket)(socket.id, store);
|
|
@@ -161,18 +173,6 @@ class IArkosGateway {
|
|
|
161
173
|
});
|
|
162
174
|
}
|
|
163
175
|
});
|
|
164
|
-
try {
|
|
165
|
-
for (const handler of connectHandlers)
|
|
166
|
-
await handler(socket, io);
|
|
167
|
-
}
|
|
168
|
-
catch (err) {
|
|
169
|
-
(0, helpers_1.handleArkosGatewayErrors)(err, socket, io, [], {
|
|
170
|
-
startTime: connectionStartTime,
|
|
171
|
-
namespace: this.config.name,
|
|
172
|
-
event: "connection",
|
|
173
|
-
});
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
176
|
for (const entry of this.events) {
|
|
177
177
|
if (entry.config._pipeOnly || !entry.handler)
|
|
178
178
|
continue;
|
|
@@ -180,13 +180,20 @@ class IArkosGateway {
|
|
|
180
180
|
socket.on(eventConfig.event, async (...args) => {
|
|
181
181
|
const startTime = new Date().getTime();
|
|
182
182
|
let data = args[0];
|
|
183
|
-
const ack =
|
|
183
|
+
const ack = typeof args[args.length - 1] === "function"
|
|
184
184
|
? args[args.length - 1]
|
|
185
185
|
: undefined;
|
|
186
|
+
let ackCalled = false;
|
|
187
|
+
const wrappedAck = ack
|
|
188
|
+
? (...response) => {
|
|
189
|
+
ackCalled = true;
|
|
190
|
+
ack(...response);
|
|
191
|
+
}
|
|
192
|
+
: undefined;
|
|
186
193
|
function resolveDedup() {
|
|
187
|
-
if (
|
|
194
|
+
if (eventConfig.dedup === false ||
|
|
188
195
|
localConfig.dedup === false ||
|
|
189
|
-
|
|
196
|
+
parentConfig?.dedup === false)
|
|
190
197
|
return null;
|
|
191
198
|
return {
|
|
192
199
|
enabled: true,
|
|
@@ -197,18 +204,40 @@ class IArkosGateway {
|
|
|
197
204
|
};
|
|
198
205
|
}
|
|
199
206
|
const dedupOpt = resolveDedup();
|
|
200
|
-
if (dedupOpt?.enabled !== false) {
|
|
201
|
-
if (!data._mid)
|
|
202
|
-
throw new error_handler_1.BadRequestError("Missing _mid in your payload for deduplication", "MissingDedupMessageId");
|
|
203
|
-
const key = `arkos::dedup:${eventConfig.event}:${data._mid}`;
|
|
204
|
-
const ttl = dedupOpt?.ttl ?? 3600;
|
|
205
|
-
if (await store.has(key))
|
|
206
|
-
return;
|
|
207
|
-
await store.set(key, ttl);
|
|
208
|
-
const { _mid, ...payload } = data;
|
|
209
|
-
data = payload;
|
|
210
|
-
}
|
|
211
207
|
try {
|
|
208
|
+
const meta = data?._meta;
|
|
209
|
+
if (eventConfig?.maxAge && !meta.timestamp)
|
|
210
|
+
throw new error_handler_1.BadRequestError("Missing _meta.timestamp for maxAge deduplication");
|
|
211
|
+
if (meta.timestamp !== undefined) {
|
|
212
|
+
const timestamp = new Date(meta.timestamp);
|
|
213
|
+
if (isNaN(timestamp.getTime())) {
|
|
214
|
+
throw new error_handler_1.BadRequestError("Invalid data._meta.timestamp", "InvalidTimestamp");
|
|
215
|
+
}
|
|
216
|
+
const age = Date.now() - timestamp.getTime();
|
|
217
|
+
if (age < 0) {
|
|
218
|
+
throw new error_handler_1.BadRequestError("Timestamp is in the future", "FutureTimestamp");
|
|
219
|
+
}
|
|
220
|
+
if (eventConfig?.maxAge && age > eventConfig?.maxAge) {
|
|
221
|
+
throw new error_handler_1.BadRequestError("Message is too old", "StaleMessage");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (dedupOpt && dedupOpt?.enabled !== false) {
|
|
225
|
+
if (!meta?.mid)
|
|
226
|
+
throw new error_handler_1.BadRequestError("Missing data._meta.mid in your payload for deduplication", "MissingDedupMessageId", { data });
|
|
227
|
+
if (typeof meta.mid !== "string" || meta.mid.trim() === "")
|
|
228
|
+
throw new error_handler_1.BadRequestError("Invalid data._meta.mid, it must be a non-empty string", "InvalidMessageId");
|
|
229
|
+
const key = `arkos::dedup:${eventConfig.event}:${meta.mid}`;
|
|
230
|
+
const ttl = dedupOpt?.ttl ?? 3600;
|
|
231
|
+
const acquired = await store.setIfNotExists(key, ttl);
|
|
232
|
+
if (acquired === false)
|
|
233
|
+
return wrappedAck?.({
|
|
234
|
+
success: true,
|
|
235
|
+
duplicate: true,
|
|
236
|
+
});
|
|
237
|
+
const { _meta, ...payload } = data;
|
|
238
|
+
data = payload;
|
|
239
|
+
socket.meta = meta;
|
|
240
|
+
}
|
|
212
241
|
const rateLimitOptions = eventConfig.rateLimit ?? resolvedRateLimit;
|
|
213
242
|
if (rateLimitOptions !== false) {
|
|
214
243
|
const { allowed, retryAfter } = await (0, rate_limiter_1.checkRateLimit)(socket.id, eventConfig.event, rateLimitOptions || {}, options.store);
|
|
@@ -249,8 +278,11 @@ class IArkosGateway {
|
|
|
249
278
|
}
|
|
250
279
|
socket.data = data;
|
|
251
280
|
await (0, helpers_1.runArkosGatewayPipes)([...inheritedPipes, ...eventPipes], socket, data, io);
|
|
252
|
-
await handler(socket, data, io,
|
|
281
|
+
await handler(socket, data, io, wrappedAck);
|
|
253
282
|
(0, helpers_1.handleGatewayEventLog)(this.config.name, eventConfig.event, 200, startTime);
|
|
283
|
+
if (eventConfig.ack && ack && !ackCalled) {
|
|
284
|
+
ack({ success: true });
|
|
285
|
+
}
|
|
254
286
|
}
|
|
255
287
|
catch (err) {
|
|
256
288
|
(0, helpers_1.handleArkosGatewayErrors)(err, socket, io, errorHandlers, {
|
|
@@ -271,26 +303,25 @@ class IArkosGateway {
|
|
|
271
303
|
}
|
|
272
304
|
}
|
|
273
305
|
toUser(userId) {
|
|
274
|
-
return new
|
|
306
|
+
return new emit_builders_1.GatewayUserEmitBuilder(userId, this.io.of(this.config.name), this.config, this.registryOptions?.store);
|
|
275
307
|
}
|
|
276
308
|
toRoom(room) {
|
|
277
|
-
return new
|
|
309
|
+
return new emit_builders_1.GatewayEmitBuilder(this.io.of(this.config.name).to(room), this.config, this.registryOptions?.store);
|
|
278
310
|
}
|
|
279
311
|
toAll() {
|
|
280
|
-
return new
|
|
312
|
+
return new emit_builders_1.GatewayEmitBuilder(this.io.of(this.config.name), this.config, this.registryOptions?.store);
|
|
281
313
|
}
|
|
282
314
|
user(userId) {
|
|
283
|
-
return new
|
|
315
|
+
return new emit_builders_1.GatewayUserBuilder(userId, this.io.of(this.config.name));
|
|
284
316
|
}
|
|
285
317
|
room(roomId) {
|
|
286
|
-
return new
|
|
318
|
+
return new emit_builders_1.GatewayRoomBuilder(roomId, this.io.of(this.config.name));
|
|
287
319
|
}
|
|
288
320
|
toSocket(socket) {
|
|
289
|
-
return new
|
|
321
|
+
return new emit_builders_1.GatewaySocketEmitBuilder(socket, this.config, this.registryOptions?.store);
|
|
290
322
|
}
|
|
291
323
|
}
|
|
292
324
|
exports.IArkosGateway = IArkosGateway;
|
|
293
|
-
IArkosGateway.registered = false;
|
|
294
325
|
function ArkosGateway(config) {
|
|
295
326
|
return new IArkosGateway(config);
|
|
296
327
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arkos-gateway.js","sourceRoot":"","sources":["../../../../src/components/arkos-gateway/arkos-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ArkosGatewayConfig,\n ArkosGatewayErrorHandler,\n ArkosGatewayEventConfig,\n ArkosGatewayEventEntry,\n ArkosGatewayHandler,\n ArkosGatewayHookHandler,\n ArkosGatewayHookType,\n ArkosGatewayPipe,\n ArkosGatewayConnectionHandler,\n ArkosSocket,\n ArkosGatewayRegisterOptions,\n} from \"./types\";\nimport { Validator } from \"../../types/validation/validator\";\nimport { Server } from \"socket.io\";\nimport { authActionService, authService } from \"../../exports/services\";\nimport { checkRateLimit, clearRateLimitForSocket } from \"./utils/rate-limiter\";\nimport {\n handleArkosGatewayErrors,\n runArkosGatewayPipes,\n handleGatewayEventLog,\n handleGatewayLifecycleLog,\n} from \"./utils/helpers\";\nimport authHookManager from \"../../modules/auth/utils/auth-hooks-manager\";\nimport { getArkosConfig } from \"../../server\";\nimport validationManager from \"../../types/validation/validation-manager\";\nimport {\n BadRequestError,\n TooManyRequestsError,\n} from \"../../exports/error-handler\";\nimport { loginRequiredError } from \"../../modules/auth/utils/auth-error-objects\";\nimport errorPrettifier from \"../../modules/base/utils/error-prettifier\";\nimport {\n GatewayEmitBuilder,\n GatewayRoomBuilder,\n GatewaySocketEmitBuilder,\n GatewayUserBuilder,\n GatewayUserEmitBuilder,\n} from \"./utils/gateway-builders\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport { defaultGatewayStore } from \"./utils/memory-gateway-store\";\n\nexport class IArkosGateway {\n private config: ArkosGatewayConfig;\n private events: ArkosGatewayEventEntry[] = [];\n private pipes: ArkosGatewayPipe[] = [];\n private gateways: IArkosGateway[] = [];\n private hooks: {\n type: ArkosGatewayHookType;\n handler: ArkosGatewayHookHandler;\n }[] = [];\n private io?: Server;\n private registryOptions?: ArkosGatewayRegisterOptions;\n static registered = false;\n\n constructor(config: ArkosGatewayConfig) {\n this.config = config;\n this.config.name = config.name ?? \"web-socket\";\n }\n\n /**\n * Register a Socket.io connection-level middleware — runs before a socket\n * is accepted into this gateway's namespace. Also accepts a child\n * `ArkosGateway` which will inherit this gateway's name as prefix along\n * with its auth, rateLimit, and pipes.\n *\n * @example\n * chatGateway.use((socket, data, io) => {\n * console.log(\"incoming connection\", socket.id)\n * })\n *\n * // nested gateway\n * chatGateway.use(notificationsGateway)\n */\n use(...middlewareOrGateway: Array<ArkosGatewayPipe | IArkosGateway>): this {\n for (const item of middlewareOrGateway) {\n if (item instanceof IArkosGateway) {\n this.gateways.push(item);\n } else if (typeof item === \"function\") {\n // stored but applied as ns.use() during register()\n this.pipes.push(item as ArkosGatewayPipe);\n } else {\n throw new Error(\n `Invalid value for gateway.use() — expected an ArkosGateway instance or a middleware function but received \"${typeof item}\".`\n );\n }\n }\n return this;\n }\n\n /**\n * Register a pipe — middleware that runs before event handlers.\n *\n * When called with a function only, the pipe runs before every event\n * handler in this gateway and drills down to child gateways, identical\n * to how Express `router.use(fn)` works.\n *\n * When called with an event config object, the pipe runs only for that\n * specific event.\n *\n * @example\n * // runs before every event in this gateway\n * chatGateway.pipe((socket, data, io) => {\n * })\n *\n * // runs only before \"send_message\"\n * chatGateway.pipe({ event: \"send_message\" }, (socket, data, io) => {\n * })\n */\n pipe(fn: ArkosGatewayPipe): this;\n pipe<TSchema extends Validator = any>(\n eventConfig: { event: string },\n fn: ArkosGatewayPipe\n ): this;\n pipe<TSchema extends Validator = any>(\n fnOrConfig: ArkosGatewayPipe | ArkosGatewayEventConfig<TSchema>,\n fn?: ArkosGatewayPipe\n ): this {\n if (typeof fnOrConfig === \"function\") {\n this.pipes.push(fnOrConfig);\n } else if (fnOrConfig && typeof fnOrConfig === \"object\" && fn) {\n const entry = this.events.find(\n (e) => e.config.event === fnOrConfig.event\n );\n if (entry) {\n entry.pipes = entry.pipes ?? [];\n entry.pipes.push(fn);\n } else {\n // deferred — pipe registered before on()\n this.events.push({\n config: { event: fnOrConfig.event, _pipeOnly: true } as any,\n handler: null as any,\n pipes: [fn],\n });\n }\n } else {\n throw new Error(\n `Invalid arguments for gateway.pipe() — pass a middleware function, or an event config object followed by a middleware function.`\n );\n }\n return this;\n }\n\n /**\n * Register an event handler.\n *\n * @example\n * chatGateway.on(\n * { event: \"send_message\", validation: MessageSchema, ack: true },\n * (socket, data, io, ack) => {\n * socket.to(data.room).emit(\"receive_message\", data)\n * ack?.({ status: \"ok\" })\n * }\n * )\n */\n on<TSchema extends Validator = any>(\n eventConfig: ArkosGatewayEventConfig<TSchema>,\n handler: ArkosGatewayHandler\n ): this {\n if (eventConfig.disabled) return this;\n\n if (eventConfig.authorization && this.config.authentication === false) {\n throw new Error(\n `Event \"${eventConfig.event}\" on \"${this.config.name}\" gateway defines authorization rules ` +\n `but the gateway has authentication: false. Enable authentication on the gateway to use per-event authorization.`\n );\n }\n\n const deferred = this.events.find(\n (e) => (e.config as any)._pipeOnly && e.config.event === eventConfig.event\n );\n\n const entry: ArkosGatewayEventEntry = {\n config: eventConfig,\n handler,\n // snapshot global pipes at registration time + any deferred scoped pipes\n pipes: [...this.pipes, ...(deferred?.pipes ?? [])],\n };\n\n if (deferred) {\n const idx = this.events.indexOf(deferred);\n this.events.splice(idx, 1, entry);\n } else {\n this.events.push(entry);\n }\n\n authActionService.add(eventConfig.event, this.config.name!, {\n [eventConfig.event]: eventConfig.authorization,\n });\n\n return this;\n }\n\n /**\n * Register a lifecycle hook.\n *\n * - `\"connection\"` — called after a socket successfully connects and passes authentication.\n * - `\"disconnect\"` — called when a socket disconnects.\n * - `\"error\"` — called when an error is thrown inside any event handler.\n * If not registered, Arkos emits a default `\"error\"` event to the socket.\n *\n * @example\n * chatGateway.hook(\"connection\", (socket, io) => {\n * console.log(\"connected\", socket.user.id)\n * })\n *\n * chatGateway.hook(\"disconnect\", (socket, io) => {\n * console.log(\"disconnected\", socket.id)\n * })\n *\n * chatGateway.hook(\"error\", (err, socket, io) => {\n * socket.emit(\"error\", { message: err.message })\n * })\n */\n hook(type: \"connection\", handler: ArkosGatewayConnectionHandler): this;\n hook(type: \"disconnect\", handler: ArkosGatewayConnectionHandler): this;\n hook(type: \"error\", handler: ArkosGatewayErrorHandler): this;\n hook(\n type: ArkosGatewayHookType,\n handler: ArkosGatewayConnectionHandler | ArkosGatewayErrorHandler\n ): this {\n this.hooks.push({ type, handler });\n return this;\n }\n\n /**\n * Wire this gateway into a Socket.io `Server` instance.\n * Registers the namespace, auth middleware, rate limiting, pipes,\n * and all event handlers — then recurses into child gateways.\n *\n * @example\n * const io = new Server(server)\n * chatGateway.register(io)\n */\n register(io: Server, options: ArkosGatewayRegisterOptions = {}): void {\n if (IArkosGateway.registered)\n throw new Error(\n `gateway.register() can only be called once by a global top level gateway. Use gateway.use() to compose gateways, see https://www.arkosjs.com/docs/components/advanced-guides/web-sockets/setup.`\n );\n IArkosGateway.registered = true;\n this._register(io, undefined, [], [], options);\n }\n\n private _register(\n io: Server,\n parentConfig?: ArkosGatewayConfig,\n inheritedHooks: {\n type: ArkosGatewayHookType;\n handler: ArkosGatewayHookHandler;\n }[] = [],\n inheritedPipes: ArkosGatewayPipe[] = [],\n options: ArkosGatewayRegisterOptions = {}\n ): void {\n options.store = options.store ?? defaultGatewayStore;\n const { store } = options;\n this.registryOptions = options;\n\n const parentName = parentConfig?.name;\n const ownName = this.config.name;\n this.io = io;\n this.config = deepmerge(parentConfig || {}, this.config, {\n arrayMerge: (_, sourceArray) => sourceArray,\n });\n this.config.name = ownName;\n const localConfig = this.config;\n\n const namespaceName = parentName\n ? `${parentName}${ownName ?? \"\"}`\n : (ownName ?? \"\");\n\n const ns = io.of(namespaceName);\n\n const resolvedAuth =\n this.config.authentication !== false &&\n parentConfig?.authentication !== false;\n\n const resolvedRateLimit = this.config.rateLimit ?? parentConfig?.rateLimit;\n const resolvedHooks = [...inheritedHooks, ...this.hooks];\n\n const connectHandlers = resolvedHooks\n .filter((h) => h.type === \"connection\")\n .map((h) => h.handler as ArkosGatewayConnectionHandler);\n\n const disconnectHandlers = resolvedHooks\n .filter((h) => h.type === \"disconnect\")\n .map((h) => h.handler as ArkosGatewayConnectionHandler);\n\n const errorHandlers = resolvedHooks\n .filter((h) => h.type === \"error\")\n .map((h) => h.handler as ArkosGatewayErrorHandler);\n\n if (resolvedAuth) {\n ns.use(async (socket: ArkosSocket, next) => {\n try {\n await authHookManager.runAuthenticate(\n { context: socket, done: next },\n async (socket) => {\n const user = await authService.getAuthenticatedUser(socket);\n if (!user) throw loginRequiredError;\n return user;\n }\n );\n } catch (err: any) {\n next(err);\n }\n });\n }\n\n ns.on(\"connection\", async (socket: ArkosSocket) => {\n handleGatewayLifecycleLog(this.config.name, \"connected\", socket.id);\n const connectionStartTime = new Date().getTime();\n\n if (socket.user?.id) socket.join(`arkos::user:${socket.user.id}`);\n\n socket.on(\"disconnect\", async () => {\n handleGatewayLifecycleLog(this.config.name, \"disconnected\", socket.id);\n\n await clearRateLimitForSocket(socket.id, store);\n try {\n for (const handler of disconnectHandlers) await handler(socket, io);\n } catch (err) {\n handleArkosGatewayErrors(err, socket, io, [], {\n startTime: connectionStartTime,\n namespace: this.config.name,\n event: \"disconnection\",\n });\n }\n });\n\n try {\n for (const handler of connectHandlers) await handler(socket, io);\n } catch (err: any) {\n handleArkosGatewayErrors(err, socket, io, [], {\n startTime: connectionStartTime,\n namespace: this.config.name,\n event: \"connection\",\n });\n return;\n }\n\n for (const entry of this.events) {\n if ((entry.config as any)._pipeOnly || !entry.handler) continue;\n\n const { config: eventConfig, handler, pipes: eventPipes = [] } = entry;\n\n socket.on(eventConfig.event, async (...args: any[]) => {\n const startTime = new Date().getTime();\n let data = args[0];\n\n const ack =\n eventConfig.ack && typeof args[args.length - 1] === \"function\"\n ? args[args.length - 1]\n : undefined;\n\n function resolveDedup() {\n if (\n parentConfig?.dedup === false ||\n localConfig.dedup === false ||\n eventConfig.dedup === false\n )\n return null;\n\n return {\n enabled: true,\n ttl: 3600,\n ...parentConfig?.dedup,\n ...localConfig?.dedup,\n ...eventConfig?.dedup,\n };\n }\n\n const dedupOpt = resolveDedup();\n\n if (dedupOpt?.enabled !== false) {\n if (!data._mid)\n throw new BadRequestError(\n \"Missing _mid in your payload for deduplication\",\n \"MissingDedupMessageId\"\n );\n\n const key = `arkos::dedup:${eventConfig.event}:${data._mid}`;\n const ttl = dedupOpt?.ttl ?? 3600;\n\n if (await store.has(key)) return;\n await store.set(key, ttl);\n\n const { _mid, ...payload } = data;\n data = payload;\n }\n\n try {\n const rateLimitOptions = eventConfig.rateLimit ?? resolvedRateLimit;\n if (rateLimitOptions !== false) {\n const { allowed, retryAfter } = await checkRateLimit(\n socket.id,\n eventConfig.event,\n rateLimitOptions || {},\n options.store!\n );\n if (!allowed) {\n throw new TooManyRequestsError(undefined, undefined, {\n retryAfter,\n });\n }\n }\n\n if (eventConfig.authorization && resolvedAuth) {\n await authHookManager.runAuthorize(\n { context: socket, done: () => {} },\n eventConfig.event,\n this.config.name!,\n eventConfig.authorization\n );\n }\n\n if (eventConfig.validation) {\n const arkosConfig = getArkosConfig();\n const {\n validationFn,\n isValidValidator,\n validatorName,\n validatorNameType,\n } = validationManager;\n\n if (!isValidValidator(eventConfig.validation))\n throw new Error(\n `Your validation resolver is set to ${arkosConfig.validation!.resolver}, ` +\n `please provide a valid ${validatorName} in order to use { validation: ${validatorNameType} } ` +\n `under event handler \"${eventConfig.event}\" in \"${this.config.name}\" gateway.`\n );\n\n const shouldValidate = validationManager.shouldValidate(\n eventConfig.validation,\n data\n );\n\n if (shouldValidate === \"prohibit\")\n throw new BadRequestError(\n \"Event data is not allowed for this event.\",\n \"EventDataNotAllowed\",\n { data }\n );\n else if (shouldValidate === \"passthrough\") data = data;\n else {\n try {\n data = await (validationFn as any)(\n eventConfig.validation,\n data\n );\n } catch (err: any) {\n const { validationConfig } = validationManager;\n\n const resolver = validationConfig?.resolver;\n const isZod = validationConfig?.resolver === \"zod\";\n\n const prettifiedError = errorPrettifier.prettify(\n resolver as any,\n err\n );\n const error = prettifiedError[0];\n\n throw new BadRequestError(\n error.message,\n `InvalidData`,\n isZod ? err.format() : err\n );\n }\n }\n }\n\n socket.data = data;\n\n await runArkosGatewayPipes(\n [...inheritedPipes, ...eventPipes],\n socket,\n data,\n io\n );\n\n await handler(socket, data, io, ack);\n\n handleGatewayEventLog(\n this.config.name,\n eventConfig.event,\n 200,\n startTime\n );\n } catch (err: any) {\n handleArkosGatewayErrors(\n err,\n socket,\n io,\n errorHandlers,\n {\n startTime,\n namespace: this.config.name,\n event: eventConfig.event,\n },\n ack\n );\n }\n });\n }\n });\n\n for (const child of this.gateways) {\n child._register(\n io,\n {\n name: namespaceName,\n authentication: resolvedAuth,\n rateLimit: resolvedRateLimit,\n },\n resolvedHooks,\n inheritedPipes,\n options\n );\n }\n }\n\n /**\n * Returns a builder for sending events to a specific user across all\n * their active socket connections.\n *\n * @example\n * await gateway.toUser(userId).emit(\"notification\", data)\n * await gateway.toUser(userId).emit(\"order_update\", data, {\n * timeout: 5000,\n * retries: 3,\n * })\n *\n * @since 1.7.0-canary.19\n */\n toUser(userId: string) {\n return new GatewayUserEmitBuilder(\n userId,\n this.io!.of(this.config.name),\n this.config,\n this.registryOptions?.store!\n );\n }\n /**\n * Returns a builder for sending events to all sockets in a room.\n *\n * @example\n * await gateway.toRoom(\"room-123\").emit(\"update\", data)\n *\n * @since 1.7.0-canary.19\n */\n toRoom(room: string) {\n return new GatewayEmitBuilder(\n this.io!.of(this.config.name).to(room),\n this.config,\n this.registryOptions?.store!\n );\n }\n\n /**\n * Returns a builder for broadcasting events to all sockets\n * connected to this gateway's namespace.\n *\n * @example\n * await gateway.toAll().emit(\"announcement\", data)\n *\n * @since 1.7.0-canary.19\n */\n toAll() {\n return new GatewayEmitBuilder(\n this.io!.of(this.config.name),\n this.config,\n this.registryOptions?.store!\n );\n }\n\n /**\n * Returns a handle for querying state and managing a specific user.\n *\n * @example\n * gateway.user(userId).isOnline()\n * gateway.user(userId).socketCount()\n *\n * @since 1.7.0-canary.19\n */\n user(userId: string) {\n return new GatewayUserBuilder(userId, this.io!.of(this.config.name));\n }\n\n /**\n * Returns a handle for querying state of a specific room.\n *\n * @example\n * gateway.room(\"room-123\").sockets()\n * gateway.room(\"room-123\").size()\n *\n * @since 1.7.0-canary.19\n */\n room(roomId: string) {\n return new GatewayRoomBuilder(roomId, this.io!.of(this.config.name));\n }\n\n /**\n * Returns a builder for sending events to a specific socket connection.\n * Use this when you have a direct socket reference and want Arkos sugar\n * (timeout, retries) on top of native socket.emit().\n *\n * @example\n * await gateway.toSocket(socket).emit(\"notification\", data)\n * await gateway.toSocket(socket).emit(\"order_update\", data, {\n * timeout: 5000,\n * retries: 3,\n * })\n *\n * @since 1.7.0-canary.19\n */\n toSocket(socket: ArkosSocket) {\n return new GatewaySocketEmitBuilder(\n socket,\n this.config,\n this.registryOptions?.store!\n );\n }\n}\n\n/**\n * Creates an Arkos WebSocket Gateway backed by Socket.io.\n *\n * Handles authentication, validation, rate limiting, middleware,\n * error handling, and nested gateways — all declaratively.\n *\n * @example\n * ```ts\n * // chat.gateway.ts\n * const chatGateway = ArkosGateway({\n * name: \"chat\",\n * authentication: true,\n * rateLimit: { windowMs: 60_000, max: 200 },\n * })\n *\n * chatGateway.use((socket, data, next) => {\n * console.log(`[${socket.user.id}] event received`)\n * next()\n * })\n *\n * chatGateway.on(\n * { event: \"send_message\", validation: MessageSchema, ack: true },\n * (socket, data, io, ack) => {\n * socket.to(data.room).emit(\"receive_message\", data)\n * ack?.({ status: \"ok\" })\n * }\n * )\n *\n * // app.ts\n * const app = arkos()\n * await app.build()\n *\n * const server = http.createServer(app)\n * const io = new Server(server)\n *\n * chatGateway.register(io)\n *\n * app.listen(server)\n * ```\n * @since 1.7.0-canary.18\n * @see {@link https://www.arkosjs.com/docs/core-concepts/components/gateways} for full documentation\n */\nfunction ArkosGateway(config: ArkosGatewayConfig) {\n return new IArkosGateway(config);\n}\n\nexport default ArkosGateway;\n"]}
|
|
1
|
+
{"version":3,"file":"arkos-gateway.js","sourceRoot":"","sources":["../../../../src/components/arkos-gateway/arkos-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ArkosGatewayConfig,\n ArkosGatewayErrorHandler,\n ArkosGatewayEventConfig,\n ArkosGatewayEventEntry,\n ArkosGatewayHandler,\n ArkosGatewayHookHandler,\n ArkosGatewayHookType,\n ArkosGatewayPipe,\n ArkosGatewayConnectionHandler,\n ArkosSocket,\n ArkosGatewayRegisterOptions,\n} from \"./types\";\nimport { Validator } from \"../../types/validation/validator\";\nimport { Server } from \"socket.io\";\nimport { authActionService, authService } from \"../../exports/services\";\nimport { checkRateLimit, clearRateLimitForSocket } from \"./utils/rate-limiter\";\nimport {\n handleArkosGatewayErrors,\n runArkosGatewayPipes,\n handleGatewayEventLog,\n handleGatewayLifecycleLog,\n} from \"./utils/helpers\";\nimport authHookManager from \"../../modules/auth/utils/auth-hooks-manager\";\nimport { getArkosConfig } from \"../../server\";\nimport validationManager from \"../../types/validation/validation-manager\";\nimport {\n BadRequestError,\n TooManyRequestsError,\n} from \"../../exports/error-handler\";\nimport { loginRequiredError } from \"../../modules/auth/utils/auth-error-objects\";\nimport errorPrettifier from \"../../modules/base/utils/error-prettifier\";\nimport {\n GatewayEmitBuilder,\n GatewayRoomBuilder,\n GatewaySocketEmitBuilder,\n GatewayUserBuilder,\n GatewayUserEmitBuilder,\n} from \"./utils/emit-builders\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport { defaultGatewayStore } from \"./utils/memory-gateway-store\";\n\nexport class IArkosGateway {\n private config: ArkosGatewayConfig;\n private events: ArkosGatewayEventEntry[] = [];\n private pipes: ArkosGatewayPipe[] = [];\n private gateways: IArkosGateway[] = [];\n private hooks: {\n type: ArkosGatewayHookType;\n handler: ArkosGatewayHookHandler;\n }[] = [];\n private io?: Server;\n private registryOptions?: ArkosGatewayRegisterOptions;\n\n constructor(config: ArkosGatewayConfig) {\n this.config = config;\n this.config.name = config.name ?? \"web-socket\";\n }\n\n /**\n * Register a Socket.io connection-level middleware — runs before a socket\n * is accepted into this gateway's namespace. Also accepts a child\n * `ArkosGateway` which will inherit this gateway's name as prefix along\n * with its auth, rateLimit, and pipes.\n *\n * @example\n * chatGateway.use((socket, data, io) => {\n * console.log(\"incoming connection\", socket.id)\n * })\n *\n * // nested gateway\n * chatGateway.use(notificationsGateway)\n */\n use(...middlewareOrGateway: Array<ArkosGatewayPipe | IArkosGateway>): this {\n for (const item of middlewareOrGateway) {\n if (item instanceof IArkosGateway) {\n this.gateways.push(item);\n } else if (typeof item === \"function\") {\n // stored but applied as ns.use() during register()\n this.pipes.push(item as ArkosGatewayPipe);\n } else {\n throw new Error(\n `Invalid value for gateway.use() — expected an ArkosGateway instance or a middleware function but received \"${typeof item}\".`\n );\n }\n }\n return this;\n }\n\n /**\n * Register a pipe — middleware that runs before event handlers.\n *\n * When called with a function only, the pipe runs before every event\n * handler in this gateway and drills down to child gateways, identical\n * to how Express `router.use(fn)` works.\n *\n * When called with an event config object, the pipe runs only for that\n * specific event.\n *\n * @example\n * // runs before every event in this gateway\n * chatGateway.pipe((socket, data, io) => {\n * })\n *\n * // runs only before \"send_message\"\n * chatGateway.pipe({ event: \"send_message\" }, (socket, data, io) => {\n * })\n */\n pipe(fn: ArkosGatewayPipe): this;\n pipe<TSchema extends Validator = any>(\n eventConfig: { event: string },\n fn: ArkosGatewayPipe\n ): this;\n pipe<TSchema extends Validator = any>(\n fnOrConfig: ArkosGatewayPipe | ArkosGatewayEventConfig<TSchema>,\n fn?: ArkosGatewayPipe\n ): this {\n if (typeof fnOrConfig === \"function\") {\n this.pipes.push(fnOrConfig);\n } else if (fnOrConfig && typeof fnOrConfig === \"object\" && fn) {\n const entry = this.events.find(\n (e) => e.config.event === fnOrConfig.event\n );\n if (entry) {\n entry.pipes = entry.pipes ?? [];\n entry.pipes.push(fn);\n } else {\n // deferred — pipe registered before on()\n this.events.push({\n config: { event: fnOrConfig.event, _pipeOnly: true } as any,\n handler: null as any,\n pipes: [fn],\n });\n }\n } else {\n throw new Error(\n `Invalid arguments for gateway.pipe() — pass a middleware function, or an event config object followed by a middleware function.`\n );\n }\n return this;\n }\n\n /**\n * Register an event handler.\n *\n * @example\n * chatGateway.on(\n * { event: \"send_message\", validation: MessageSchema, ack: true },\n * (socket, data, io, ack) => {\n * socket.to(data.room).emit(\"receive_message\", data)\n * ack?.({ status: \"ok\" })\n * }\n * )\n */\n on<TSchema extends Validator = any>(\n eventConfig: ArkosGatewayEventConfig<TSchema>,\n handler: ArkosGatewayHandler\n ): this {\n if (eventConfig.disabled) return this;\n\n if (eventConfig.authorization && this.config.authentication === false) {\n throw new Error(\n `Event \"${eventConfig.event}\" on \"${this.config.name}\" gateway defines authorization rules ` +\n `but the gateway has authentication: false. Enable authentication on the gateway to use per-event authorization.`\n );\n }\n\n const deferred = this.events.find(\n (e) => (e.config as any)._pipeOnly && e.config.event === eventConfig.event\n );\n\n const entry: ArkosGatewayEventEntry = {\n config: eventConfig,\n handler,\n // snapshot global pipes at registration time + any deferred scoped pipes\n pipes: [...this.pipes, ...(deferred?.pipes ?? [])],\n };\n\n if (deferred) {\n const idx = this.events.indexOf(deferred);\n this.events.splice(idx, 1, entry);\n } else {\n this.events.push(entry);\n }\n\n authActionService.add(eventConfig.event, this.config.name!, {\n [eventConfig.event]: eventConfig.authorization,\n });\n\n return this;\n }\n\n /**\n * Register a lifecycle hook.\n *\n * - `\"connection\"` — called after a socket successfully connects and passes authentication.\n * - `\"disconnect\"` — called when a socket disconnects.\n * - `\"error\"` — called when an error is thrown inside any event handler.\n * If not registered, Arkos emits a default `\"error\"` event to the socket.\n *\n * @example\n * chatGateway.hook(\"connection\", (socket, io) => {\n * console.log(\"connected\", socket.user.id)\n * })\n *\n * chatGateway.hook(\"disconnect\", (socket, io) => {\n * console.log(\"disconnected\", socket.id)\n * })\n *\n * chatGateway.hook(\"error\", (err, socket, io) => {\n * socket.emit(\"error\", { message: err.message })\n * })\n */\n hook(type: \"connection\", handler: ArkosGatewayConnectionHandler): this;\n hook(type: \"disconnect\", handler: ArkosGatewayConnectionHandler): this;\n hook(type: \"error\", handler: ArkosGatewayErrorHandler): this;\n hook(\n type: ArkosGatewayHookType,\n handler: ArkosGatewayConnectionHandler | ArkosGatewayErrorHandler\n ): this {\n this.hooks.push({ type, handler });\n return this;\n }\n\n /**\n * Wire this gateway into a Socket.io `Server` instance.\n * Registers the namespace, auth middleware, rate limiting, pipes,\n * and all event handlers — then recurses into child gateways.\n *\n * @example\n * const io = new Server(server)\n * chatGateway.register(io)\n */\n register(io: Server, options: ArkosGatewayRegisterOptions = {}): void {\n if ((io as any)._arkosGatewayRegistered)\n throw new Error(\n `The method gateway.register() can only be called once per io server instance. Use gateway.use() to compose gateways, see https://www.arkosjs.com/docs/components/advanced-guides/web-sockets/setup.`\n );\n (io as any)._arkosGatewayRegistered = true;\n this._register(io, undefined, this.hooks || [], this.pipes || [], options);\n }\n\n private _register(\n io: Server,\n parentConfig?: ArkosGatewayConfig,\n inheritedHooks: {\n type: ArkosGatewayHookType;\n handler: ArkosGatewayHookHandler;\n }[] = [],\n inheritedPipes: ArkosGatewayPipe[] = [],\n options: ArkosGatewayRegisterOptions = {}\n ): void {\n options.store = options.store ?? defaultGatewayStore;\n const { store } = options;\n this.registryOptions = options;\n\n const parentName = parentConfig?.name;\n const ownName = this.config.name;\n this.io = io;\n this.config = deepmerge(parentConfig || {}, this.config, {\n arrayMerge: (_, sourceArray) => sourceArray,\n });\n this.config.name = ownName;\n const localConfig = this.config;\n\n const namespaceName = parentName\n ? `${parentName}${ownName ?? \"\"}`\n : (ownName ?? \"\");\n\n const ns = io.of(namespaceName);\n\n const resolvedAuth =\n this.config.authentication !== false &&\n parentConfig?.authentication !== false;\n\n const resolvedRateLimit = this.config.rateLimit ?? parentConfig?.rateLimit;\n const resolvedHooks = [...inheritedHooks, ...this.hooks];\n\n const connectHandlers = resolvedHooks\n .filter((h) => h.type === \"connection\")\n .map((h) => h.handler as ArkosGatewayConnectionHandler);\n\n const disconnectHandlers = resolvedHooks\n .filter((h) => h.type === \"disconnect\")\n .map((h) => h.handler as ArkosGatewayConnectionHandler);\n\n const errorHandlers = resolvedHooks\n .filter((h) => h.type === \"error\")\n .map((h) => h.handler as ArkosGatewayErrorHandler);\n\n if (resolvedAuth) {\n ns.use(async (socket: ArkosSocket, next) => {\n try {\n await authHookManager.runAuthenticate(\n { context: socket, done: next },\n async (socket) => {\n const user = await authService.getAuthenticatedUser(socket);\n if (!user) throw loginRequiredError;\n return user;\n }\n );\n } catch (err: any) {\n next(err);\n }\n });\n }\n\n ns.on(\"connection\", async (socket: ArkosSocket) => {\n handleGatewayLifecycleLog(this.config.name, \"connected\", socket.id);\n const connectionStartTime = new Date().getTime();\n\n if (socket.user?.id) socket.join(`arkos::user:${socket.user.id}`);\n\n try {\n for (const handler of connectHandlers) await handler(socket, io);\n } catch (err: any) {\n handleArkosGatewayErrors(err, socket, io, [], {\n startTime: connectionStartTime,\n namespace: this.config.name,\n event: \"connection\",\n });\n return;\n }\n\n socket.on(\"disconnect\", async () => {\n handleGatewayLifecycleLog(this.config.name, \"disconnected\", socket.id);\n\n await clearRateLimitForSocket(socket.id, store);\n try {\n for (const handler of disconnectHandlers) await handler(socket, io);\n } catch (err) {\n handleArkosGatewayErrors(err, socket, io, [], {\n startTime: connectionStartTime,\n namespace: this.config.name,\n event: \"disconnection\",\n });\n }\n });\n\n for (const entry of this.events) {\n if ((entry.config as any)._pipeOnly || !entry.handler) continue;\n\n const { config: eventConfig, handler, pipes: eventPipes = [] } = entry;\n\n socket.on(eventConfig.event, async (...args: any[]) => {\n const startTime = new Date().getTime();\n let data = args[0];\n\n const ack =\n typeof args[args.length - 1] === \"function\"\n ? args[args.length - 1]\n : undefined;\n\n let ackCalled = false;\n const wrappedAck = ack\n ? (...response: any) => {\n ackCalled = true;\n ack(...response);\n }\n : undefined;\n\n function resolveDedup() {\n if (\n eventConfig.dedup === false ||\n localConfig.dedup === false ||\n parentConfig?.dedup === false\n )\n return null;\n\n return {\n enabled: true,\n ttl: 3600,\n ...parentConfig?.dedup,\n ...localConfig?.dedup,\n ...eventConfig?.dedup,\n };\n }\n\n const dedupOpt = resolveDedup();\n\n try {\n const meta = data?._meta;\n\n if (eventConfig?.maxAge && !meta.timestamp)\n throw new BadRequestError(\n \"Missing _meta.timestamp for maxAge deduplication\"\n );\n\n if (meta.timestamp !== undefined) {\n const timestamp = new Date(meta.timestamp);\n\n if (isNaN(timestamp.getTime())) {\n throw new BadRequestError(\n \"Invalid data._meta.timestamp\",\n \"InvalidTimestamp\"\n );\n }\n\n const age = Date.now() - timestamp.getTime();\n\n if (age < 0) {\n throw new BadRequestError(\n \"Timestamp is in the future\",\n \"FutureTimestamp\"\n );\n }\n\n if (eventConfig?.maxAge && age > eventConfig?.maxAge) {\n throw new BadRequestError(\"Message is too old\", \"StaleMessage\");\n }\n }\n\n if (dedupOpt && dedupOpt?.enabled !== false) {\n if (!meta?.mid)\n throw new BadRequestError(\n \"Missing data._meta.mid in your payload for deduplication\",\n \"MissingDedupMessageId\",\n { data }\n );\n\n if (typeof meta.mid !== \"string\" || meta.mid.trim() === \"\")\n throw new BadRequestError(\n \"Invalid data._meta.mid, it must be a non-empty string\",\n \"InvalidMessageId\"\n );\n\n const key = `arkos::dedup:${eventConfig.event}:${meta.mid}`;\n const ttl = dedupOpt?.ttl ?? 3600;\n\n const acquired = await store.setIfNotExists(key, ttl);\n\n if (acquired === false)\n return wrappedAck?.({\n success: true,\n duplicate: true,\n });\n\n const { _meta, ...payload } = data;\n data = payload;\n\n socket.meta = meta;\n }\n\n const rateLimitOptions = eventConfig.rateLimit ?? resolvedRateLimit;\n if (rateLimitOptions !== false) {\n const { allowed, retryAfter } = await checkRateLimit(\n socket.id,\n eventConfig.event,\n rateLimitOptions || {},\n options.store!\n );\n if (!allowed) {\n throw new TooManyRequestsError(undefined, undefined, {\n retryAfter,\n });\n }\n }\n\n if (eventConfig.authorization && resolvedAuth) {\n await authHookManager.runAuthorize(\n { context: socket, done: () => {} },\n eventConfig.event,\n this.config.name!,\n eventConfig.authorization\n );\n }\n\n if (eventConfig.validation) {\n const arkosConfig = getArkosConfig();\n const {\n validationFn,\n isValidValidator,\n validatorName,\n validatorNameType,\n } = validationManager;\n\n if (!isValidValidator(eventConfig.validation))\n throw new Error(\n `Your validation resolver is set to ${arkosConfig.validation!.resolver}, ` +\n `please provide a valid ${validatorName} in order to use { validation: ${validatorNameType} } ` +\n `under event handler \"${eventConfig.event}\" in \"${this.config.name}\" gateway.`\n );\n\n const shouldValidate = validationManager.shouldValidate(\n eventConfig.validation,\n data\n );\n\n if (shouldValidate === \"prohibit\")\n throw new BadRequestError(\n \"Event data is not allowed for this event.\",\n \"EventDataNotAllowed\",\n { data }\n );\n else if (shouldValidate === \"passthrough\") data = data;\n else {\n try {\n data = await (validationFn as any)(\n eventConfig.validation,\n data\n );\n } catch (err: any) {\n const { validationConfig } = validationManager;\n\n const resolver = validationConfig?.resolver;\n const isZod = validationConfig?.resolver === \"zod\";\n\n const prettifiedError = errorPrettifier.prettify(\n resolver as any,\n err\n );\n const error = prettifiedError[0];\n\n throw new BadRequestError(\n error.message,\n `InvalidData`,\n isZod ? err.format() : err\n );\n }\n }\n }\n\n socket.data = data;\n\n await runArkosGatewayPipes(\n [...inheritedPipes, ...eventPipes],\n socket,\n data,\n io\n );\n\n await handler(socket, data, io, wrappedAck);\n\n handleGatewayEventLog(\n this.config.name,\n eventConfig.event,\n 200,\n startTime\n );\n\n if (eventConfig.ack && ack && !ackCalled) {\n ack({ success: true });\n }\n } catch (err: any) {\n handleArkosGatewayErrors(\n err,\n socket,\n io,\n errorHandlers,\n {\n startTime,\n namespace: this.config.name,\n event: eventConfig.event,\n },\n ack\n );\n }\n });\n }\n });\n\n for (const child of this.gateways) {\n child._register(\n io,\n {\n name: namespaceName,\n authentication: resolvedAuth,\n rateLimit: resolvedRateLimit,\n },\n resolvedHooks,\n inheritedPipes,\n options\n );\n }\n }\n\n /**\n * Returns a builder for sending events to a specific user across all\n * their active socket connections.\n *\n * @example\n * await gateway.toUser(userId).emit(\"notification\", data)\n * await gateway.toUser(userId).emit(\"order_update\", data, {\n * timeout: 5000,\n * retries: 3,\n * })\n *\n * @since 1.7.0-canary.19\n */\n toUser(userId: string) {\n return new GatewayUserEmitBuilder(\n userId,\n this.io!.of(this.config.name),\n this.config,\n this.registryOptions?.store!\n );\n }\n /**\n * Returns a builder for sending events to all sockets in a room.\n *\n * @example\n * await gateway.toRoom(\"room-123\").emit(\"update\", data)\n *\n * @since 1.7.0-canary.19\n */\n toRoom(room: string) {\n return new GatewayEmitBuilder(\n this.io!.of(this.config.name).to(room),\n this.config,\n this.registryOptions?.store!\n );\n }\n\n /**\n * Returns a builder for broadcasting events to all sockets\n * connected to this gateway's namespace.\n *\n * @example\n * await gateway.toAll().emit(\"announcement\", data)\n *\n * @since 1.7.0-canary.19\n */\n toAll() {\n return new GatewayEmitBuilder(\n this.io!.of(this.config.name),\n this.config,\n this.registryOptions?.store!\n );\n }\n\n /**\n * Returns a handle for querying state and managing a specific user.\n *\n * @example\n * gateway.user(userId).isOnline()\n * gateway.user(userId).socketCount()\n *\n * @since 1.7.0-canary.19\n */\n user(userId: string) {\n return new GatewayUserBuilder(userId, this.io!.of(this.config.name));\n }\n\n /**\n * Returns a handle for querying state of a specific room.\n *\n * @example\n * gateway.room(\"room-123\").sockets()\n * gateway.room(\"room-123\").size()\n *\n * @since 1.7.0-canary.19\n */\n room(roomId: string) {\n return new GatewayRoomBuilder(roomId, this.io!.of(this.config.name));\n }\n\n /**\n * Returns a builder for sending events to a specific socket connection.\n * Use this when you have a direct socket reference and want Arkos sugar\n * (timeout, retries) on top of native socket.emit().\n *\n * @example\n * await gateway.toSocket(socket).emit(\"notification\", data)\n * await gateway.toSocket(socket).emit(\"order_update\", data, {\n * timeout: 5000,\n * retries: 3,\n * })\n *\n * @since 1.7.0-canary.19\n */\n toSocket(socket: ArkosSocket) {\n return new GatewaySocketEmitBuilder(\n socket,\n this.config,\n this.registryOptions?.store!\n );\n }\n}\n\n/**\n * Creates an Arkos WebSocket Gateway backed by Socket.io.\n *\n * Handles authentication, validation, rate limiting, middleware,\n * error handling, and nested gateways — all declaratively.\n *\n * @example\n * ```ts\n * // chat.gateway.ts\n * const chatGateway = ArkosGateway({\n * name: \"chat\",\n * authentication: true,\n * rateLimit: { windowMs: 60_000, max: 200 },\n * })\n *\n * chatGateway.use((socket, data, next) => {\n * console.log(`[${socket.user.id}] event received`)\n * next()\n * })\n *\n * chatGateway.on(\n * { event: \"send_message\", validation: MessageSchema, ack: true },\n * (socket, data, io, ack) => {\n * socket.to(data.room).emit(\"receive_message\", data)\n * ack?.({ status: \"ok\" })\n * }\n * )\n *\n * // app.ts\n * const app = arkos()\n * await app.build()\n *\n * const server = http.createServer(app)\n * const io = new Server(server)\n *\n * chatGateway.register(io)\n *\n * app.listen(server)\n * ```\n * @since 1.7.0-canary.18\n * @see {@link https://www.arkosjs.com/docs/core-concepts/components/gateways} for full documentation\n */\nfunction ArkosGateway(config: ArkosGatewayConfig) {\n return new IArkosGateway(config);\n}\n\nexport default ArkosGateway;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/components/arkos-gateway/types.ts"],"names":[],"mappings":";;;AAiYA,MAAa,sBAAsB;CAAG;AAAtC,wDAAsC","sourcesContent":["import { Server, Socket } from \"socket.io\";\nimport { User } from \"../../types\";\nimport { DefaultEventsMap } from \"socket.io\";\nimport { Validator } from \"../../types/validation/validator\";\nimport { Options as RateLimitOptions } from \"express-rate-limit\";\nimport { ArkosPolicyRule } from \"../arkos-policy/types\";\n\n/**\n * An events map is an interface that maps event names to their value, which\n * represents the type of the `on` listener.\n */\nexport interface EventsMap {\n [event: string]: any;\n}\n\nexport interface ArkosSocket<\n ListenEvents extends EventsMap = DefaultEventsMap,\n EmitEvents extends EventsMap = ListenEvents,\n ServerSideEvents extends EventsMap = DefaultEventsMap,\n SocketData extends Validator = any,\n> extends Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData> {\n /**\n * Populated by Arkos after successful authentication on connection.\n * Available in all event handlers when authentication: true on the gateway.\n */\n user?: User;\n\n /**\n * Populated by Arkos after successful validation.\n * Typed to the event's validation schema when using TypeScript.\n */\n data: SocketData;\n\n /**\n * User access token\n */\n accessToken?: string;\n}\n\nexport type ArkosGatewayPipe = (\n socket: ArkosSocket,\n data: any,\n io: Server\n) => void | Promise<void>;\n\nexport type ArkosGatewayEventConfig<TSchema extends Validator = any> = {\n /**\n * The Socket.io event name to listen for.\n *\n * @example\n * event: \"send_message\"\n */\n event: string;\n /**\n * Zod schema or class-validator DTO to validate the incoming event payload.\n * The validated data is passed as the second argument to the handler\n * and also available on socket.validatedData.\n *\n * @example\n * validation: z.object({ room: z.string(), content: z.string() })\n */\n validation?: TSchema;\n\n /**\n * Per-event rate limiting. Overrides gateway-level rateLimit for this event.\n * Useful for stricter limits on expensive events like send_message vs typing.\n *\n * @example\n * rateLimit: { windowMs: 10_000, max: 5 }\n */\n rateLimit?: Partial<RateLimitOptions> | false;\n\n /**\n * Role-based authorization for this specific event.\n *\n * Gateway must have authentication: true for this to work.\n * Arkos will throw at registration time if gateway has authentication: false.\n *\n * @example\n * authorization: { roles: [\"Admin\", \"Moderator\"] }\n */\n authorization?: ArkosPolicyRule;\n\n /**\n * Whether this event handler expects an acknowledgement callback.\n * When true, the handler receives ack as the fourth argument.\n *\n * @example\n * ack: true\n * // handler: (socket, data, io, ack) => { ack({ status: \"ok\" }) }\n */\n ack?: boolean;\n /**\n * Disables this event handler without removing it.\n * Useful for feature flags or temporary disabling.\n */\n disabled?: boolean;\n /**\n * Per-event deduplication configuration for incoming gateway handlers.\n *\n * Controls whether an incoming event should be processed only once based on its\n * unique message identifier (`_mid`).\n *\n * Deduplication is applied BEFORE the handler executes, ensuring idempotency\n * at the processing boundary (not at transport/emit level).\n *\n * This is typically used to prevent:\n * - duplicate client submissions\n * - retry-based re-delivery\n * - reconnect replay of the same logical event\n */\n dedup?:\n | {\n /**\n * Enables or disables deduplication for this event handler.\n *\n * When enabled, the gateway will check whether the incoming message ID\n * has already been processed and skip execution if so.\n *\n * Overrides the gateway-level `dedup.enabled` configuration.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Time-to-live for the deduplication key in seconds.\n *\n * Defines how long a processed message ID is remembered to prevent\n * duplicate execution within that window.\n *\n * Overrides the gateway-level `dedup.ttl` configuration.\n *\n * @default 3600\n */\n ttl?: number;\n }\n | false;\n};\n\nexport type ArkosGatewayAckFn = (response: any) => void;\n\nexport type ArkosGatewayHandler<TData = any> = (\n socket: ArkosSocket,\n data: TData,\n io: Server,\n ack?: ArkosGatewayAckFn\n) => void | Promise<void>;\n\nexport type ArkosGatewayEventEntry = {\n config: ArkosGatewayEventConfig;\n handler: ArkosGatewayHandler;\n pipes: ArkosGatewayPipe[];\n};\n\nexport type ArkosGatewayConnectionHandler = (\n socket: ArkosSocket,\n io: Server\n) => void | Promise<void>;\n\nexport type ArkosGatewayHookHandler =\n | ArkosGatewayConnectionHandler\n | ArkosGatewayErrorHandler;\n\nexport type ArkosGatewayErrorHandler = (\n error: any,\n socket: ArkosSocket,\n io: Server\n) => void | Promise<void>;\n\nexport type ArkosGatewayHookType = \"connection\" | \"disconnect\" | \"error\";\n\nexport type ArkosGatewayConfig = {\n /**\n * Socket.io namespace for this gateway.\n *\n * @example\n * name: \"/chat\"\n * // connects at: http://localhost:3000/chat\n */\n name: string;\n\n /**\n * Whether this gateway requires authentication on connection.\n * When true, Arkos runs the auth middleware on socket connection,\n * populating socket.user. Unauthenticated sockets are rejected.\n *\n * @default false\n */\n authentication?: boolean;\n\n /**\n * Gateway-level rate limiting applied per socket connection.\n * Can be overridden per event using rateLimit in chatGateway.on().\n *\n * @example\n * rateLimit: { windowMs: 60_000, max: 200 }\n */\n rateLimit?: Partial<RateLimitOptions>;\n /**\n * Configuration for the deduplication system on this gateway.\n * Deduplication prevents the same event from being processed\n * multiple times within a given time window.\n *\n * All settings drill down to child gateways and can be overridden per child\n * or per listener (`gateway.on`)\n *\n * @example\n * ArkosGateway({\n * name: \"/chat\",\n * dedup: {\n * enabled: true,\n * ttl: 3600,\n * }\n * })\n */\n dedup?:\n | {\n /**\n * Whether deduplication is enabled for this gateway.\n * Defaults to `true` — opt out explicitly if you need duplicate events.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Time-to-live in seconds for deduplication keys.\n * After this period the same message ID can be processed again.\n *\n * @default 3600\n */\n ttl?: number;\n }\n | false;\n};\n\n/**\n * Interface for a custom deduplication store.\n * Implement this to plug in Redis, bento-cache, or any other\n * distributed store for deduplication across multiple server instances.\n *\n * @example\n * class RedisDeduplicationStore implements ArkosGatewayDedupStore {\n * constructor(private redis: RedisClientType) {}\n *\n * async has(key: string): Promise<boolean> {\n * return (await this.redis.exists(key)) === 1\n * }\n *\n * async set(key: string, ttl: number): Promise<void> {\n * await this.redis.setEx(key, ttl, \"1\")\n * }\n * }\n */\nexport interface ArkosGatewayDedupStore {\n /**\n * Returns whether the key exists in the store.\n * Used to detect duplicate messages.\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Stores a key with a TTL in seconds.\n * Called after a message is processed to mark it as seen.\n */\n set(key: string, ttl: number): Promise<void>;\n}\n\n/**\n * Options available on every Arkos-owned emit method.\n * All fields are optional — defaults are applied at the gateway level.\n *\n * @example\n * gateway.toUser(userId).emit(\"notification\", data, {\n * timeout: 5000,\n * retries: 3,\n * })\n */\nexport interface ArkosEmitOptions {\n /**\n * Timeout in milliseconds to wait for an acknowledgement from the client.\n * Only applies to `toUser()` and `toSocket()` — broadcasts have no ack.\n *\n * @default 5000\n */\n timeout?: number;\n\n /**\n * Number of retry attempts if the emit times out or fails.\n * Uses exponential backoff between attempts, capped at 5 seconds.\n * Only applies to `toUser()` and `toSocket()`.\n *\n * @default 0\n */\n retries?: number;\n}\n\n/**\n * Options for `gateway.register()`.\n * Passed once at the root gateway registration — applies to all child gateways.\n *\n * @example\n * gateway.register(io, {\n * store: new MultiTierStore([\n * new MemoryStore(),\n * new RedisStore(redis)\n * ])\n * })\n */\nexport type ArkosGatewayRegisterOptions = {\n /**\n * Unified store for rate limiting and deduplication across all gateways.\n * Defaults to an in-memory store — zero config required for single-instance deployments.\n *\n * For distributed deployments (multiple Node processes / instances), plug in a\n * Redis store or a multi-tier store to share state across instances.\n *\n * @example\n * store: new RedisArkosStore(redis)\n * store: new MultiTierStore([new MemoryStore(), new RedisStore(redis)])\n */\n store?: ArkosGatewayStore;\n};\n\n/**\n * Unified store interface for rate limiting and deduplication.\n * Implement this to plug in Redis, Valkey, or any distributed store.\n *\n * @example\n * class RedisArkosStore implements ArkosGatewayStore {\n * constructor(private redis: RedisClientType) {}\n *\n * async increment(key: string, windowMs: number) {\n * const count = await this.redis.incr(key)\n * if (count === 1) await this.redis.pExpire(key, windowMs)\n * const ttl = await this.redis.pTtl(key)\n * return { count, resetAt: Date.now() + ttl }\n * }\n *\n * async clear(prefix: string) {\n * const keys = await this.redis.keys(`${prefix}*`)\n * if (keys.length) await this.redis.del(keys)\n * }\n *\n * async has(key: string) {\n * return (await this.redis.exists(key)) === 1\n * }\n *\n * async set(key: string, ttl: number) {\n * await this.redis.setEx(key, ttl, \"1\")\n * }\n * }\n */\nexport interface ArkosGatewayStore {\n /**\n * Increment a rate limit counter for a given key and window.\n * Called on every event to track request counts.\n * Key format: `arkos::rl:{socketId}:{event}`\n */\n increment(\n key: string,\n windowMs: number\n ): Promise<{ count: number; resetAt: number }>;\n\n /**\n * Clear all rate limit entries matching a prefix.\n * Called on socket disconnect.\n * Prefix format: `arkos::rl:{socketId}`\n */\n clear(prefix: string): Promise<void>;\n\n /**\n * Check if a dedup key exists.\n * Called before emitting to detect duplicate messages.\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Store a dedup key with a TTL in seconds.\n * Called after emitting to mark a message as seen.\n */\n set(key: string, ttl: number): Promise<void>;\n}\n\nexport class ArkosGatewayController {}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/components/arkos-gateway/types.ts"],"names":[],"mappings":";;;AAqfA,MAAa,sBAAsB;CAAG;AAAtC,wDAAsC","sourcesContent":["import { Server, Socket } from \"socket.io\";\nimport { User } from \"../../types\";\nimport { DefaultEventsMap } from \"socket.io\";\nimport { Validator } from \"../../types/validation/validator\";\nimport { Options as RateLimitOptions } from \"express-rate-limit\";\nimport { ArkosPolicyRule } from \"../arkos-policy/types\";\n\n/**\n * An events map is an interface that maps event names to their value, which\n * represents the type of the `on` listener.\n */\nexport interface EventsMap {\n [event: string]: any;\n}\n\nexport interface ArkosSocket<\n ListenEvents extends EventsMap = DefaultEventsMap,\n EmitEvents extends EventsMap = ListenEvents,\n ServerSideEvents extends EventsMap = DefaultEventsMap,\n SocketData extends Validator = any,\n SocketLocals extends Record<string, any> = Record<string, any>,\n> extends Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData> {\n /**\n * Populated by Arkos after successful authentication on connection.\n * Available in all event handlers when authentication: true on the gateway.\n */\n user?: User;\n\n /**\n * Populated by Arkos after successful validation.\n * Typed to the event's validation schema when using TypeScript.\n */\n data: SocketData;\n\n /**\n * User access token\n */\n accessToken?: string;\n /**\n * Metadata associated with the incoming message/event.\n *\n * - `_mid`: Unique message identifier used for deduplication. When deduplication is enabled,\n * Arkos checks this ID to prevent processing the same message multiple times.\n * - `timestamp`: Timestamp when the message was sent from the client.\n *\n * @example\n * socket.on(\"send_message\", (data) => {\n * console.log(socket.meta?._mid); // \"abc123\"\n * console.log(socket.meta?.timestamp); // 2024-01-01T00:00:00.000Z\n * });\n */\n meta?: {\n _mid?: string;\n /**\n * Message timestamp used by deduplication and freshness checks.\n *\n * Required when using `dedup.maxAge`.\n */\n timestamp?: string | number | Date;\n };\n\n /**\n * Custom local data storage for the socket lifecycle.\n *\n * Use this to store any custom data that needs to persist throughout the\n * socket's connection lifetime. Unlike `socket.data` which is overwritten\n * by validation on each event, `socket.locals` persists across events\n * and is not automatically modified by Arkos.\n *\n * @example\n * // Store custom data in a middleware\n * chatGateway.pipe((socket, data, io) => {\n * socket.locals.requestCount = (socket.locals.requestCount || 0) + 1;\n * socket.locals.lastActivity = new Date();\n * });\n *\n * @example\n * // Type-safe locals with TypeScript\n * interface MySocketLocals {\n * requestCount: number;\n * lastActivity: Date;\n * userPreferences?: object;\n * }\n *\n * const socket: ArkosSocket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any, MySocketLocals>;\n * socket.locals.requestCount; // typed as number\n */\n locals?: SocketLocals;\n}\n\nexport type ArkosGatewayPipe = (\n socket: ArkosSocket,\n data: any,\n io: Server\n) => void | Promise<void>;\n\nexport type ArkosGatewayEventConfig<TSchema extends Validator = any> = {\n /**\n * The Socket.io event name to listen for.\n *\n * @example\n * event: \"send_message\"\n */\n event: string;\n /**\n * Zod schema or class-validator DTO to validate the incoming event payload.\n * The validated data is passed as the second argument to the handler\n * and also available on socket.validatedData.\n *\n * @example\n * validation: z.object({ room: z.string(), content: z.string() })\n */\n validation?: TSchema;\n\n /**\n * Per-event rate limiting. Overrides gateway-level rateLimit for this event.\n * Useful for stricter limits on expensive events like send_message vs typing.\n *\n * @example\n * rateLimit: { windowMs: 10_000, max: 5 }\n */\n rateLimit?: Partial<RateLimitOptions> | false;\n\n /**\n * Role-based authorization for this specific event.\n *\n * Gateway must have authentication: true for this to work.\n * Arkos will throw at registration time if gateway has authentication: false.\n *\n * @example\n * authorization: { roles: [\"Admin\", \"Moderator\"] }\n */\n authorization?: ArkosPolicyRule;\n\n /**\n * If `true`, the gateway will automatically call `ack({ success: true })`\n * after the handler finishes, unless the handler already called ack manually.\n * The ack callback is **always** passed to the handler as the 4th argument.\n */\n ack?: boolean;\n\n /**\n * Disables this event handler without removing it.\n * Useful for feature flags or temporary disabling.\n */\n disabled?: boolean;\n /**\n * Maximum age in milliseconds for incoming messages.\n * Events older than this threshold are rejected.\n *\n * Useful for short-lived real-time events such as typing,\n * presence, and cursor updates.\n *\n * Requires `data._meta.timestamp`.\n */\n maxAge?: number;\n /**\n * Per-event deduplication configuration for incoming gateway handlers.\n *\n * Controls whether an incoming event should be processed only once based on its\n * unique message identifier (`_mid`).\n *\n * Deduplication is applied BEFORE the handler executes, ensuring idempotency\n * at the processing boundary (not at transport/emit level).\n *\n * This is typically used to prevent:\n * - duplicate client submissions\n * - retry-based re-delivery\n * - reconnect replay of the same logical event\n */\n\n dedup?:\n | {\n /**\n * Enables or disables deduplication for this event handler.\n *\n * When enabled, the gateway will check whether the incoming message ID\n * has already been processed and skip execution if so.\n *\n * Overrides the gateway-level `dedup.enabled` configuration.\n *\n * @default true\n */\n enabled?: boolean;\n /**\n * Time-to-live for the deduplication key in seconds.\n *\n * Defines how long a processed message ID is remembered to prevent\n * duplicate execution within that window.\n *\n * Overrides the gateway-level `dedup.ttl` configuration.\n *\n * @default 3600\n */\n ttl?: number;\n }\n | false;\n};\n\nexport type ArkosGatewayAckFn = (response: any) => void;\n\nexport type ArkosGatewayHandler<TData = any> = (\n socket: ArkosSocket,\n data: TData,\n io: Server,\n ack?: ArkosGatewayAckFn\n) => void | Promise<void>;\n\nexport type ArkosGatewayEventEntry = {\n config: ArkosGatewayEventConfig;\n handler: ArkosGatewayHandler;\n pipes: ArkosGatewayPipe[];\n};\n\nexport type ArkosGatewayConnectionHandler = (\n socket: ArkosSocket,\n io: Server\n) => void | Promise<void>;\n\nexport type ArkosGatewayHookHandler =\n | ArkosGatewayConnectionHandler\n | ArkosGatewayErrorHandler;\n\nexport type ArkosGatewayErrorHandler = (\n error: any,\n socket: ArkosSocket,\n io: Server\n) => void | Promise<void>;\n\nexport type ArkosGatewayHookType = \"connection\" | \"disconnect\" | \"error\";\n\nexport type ArkosGatewayConfig = {\n /**\n * Socket.io namespace for this gateway.\n *\n * @example\n * name: \"/chat\"\n * // connects at: http://localhost:3000/chat\n */\n name: string;\n\n /**\n * Whether this gateway requires authentication on connection.\n * When true, Arkos runs the auth middleware on socket connection,\n * populating socket.user. Unauthenticated sockets are rejected.\n *\n * @default false\n */\n authentication?: boolean;\n\n /**\n * Gateway-level rate limiting applied per socket connection.\n * Can be overridden per event using rateLimit in chatGateway.on().\n *\n * @example\n * rateLimit: { windowMs: 60_000, max: 200 }\n */\n rateLimit?: Partial<RateLimitOptions>;\n /**\n * Configuration for the deduplication system on this gateway.\n * Deduplication prevents the same event from being processed\n * multiple times within a given time window.\n *\n * All settings drill down to child gateways and can be overridden per child\n * or per listener (`gateway.on`)\n *\n * @example\n * ArkosGateway({\n * name: \"/chat\",\n * dedup: {\n * enabled: true,\n * ttl: 3600,\n * }\n * })\n */\n dedup?:\n | {\n /**\n * Whether deduplication is enabled for this gateway.\n * Defaults to `true` — opt out explicitly if you need duplicate events.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Time-to-live in seconds for deduplication keys.\n * After this period the same message ID can be processed again.\n *\n * @default 3600\n */\n ttl?: number;\n }\n | false;\n};\n\n/**\n * Interface for a custom deduplication store.\n * Implement this to plug in Redis, bento-cache, or any other\n * distributed store for deduplication across multiple server instances.\n *\n * @example\n * class RedisDeduplicationStore implements ArkosGatewayDedupStore {\n * constructor(private redis: RedisClientType) {}\n *\n * async has(key: string): Promise<boolean> {\n * return (await this.redis.exists(key)) === 1\n * }\n *\n * async set(key: string, ttl: number): Promise<void> {\n * await this.redis.setEx(key, ttl, \"1\")\n * }\n * }\n */\nexport interface ArkosGatewayDedupStore {\n /**\n * Returns whether the key exists in the store.\n * Used to detect duplicate messages.\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Stores a key with a TTL in seconds.\n * Called after a message is processed to mark it as seen.\n */\n set(key: string, ttl: number): Promise<void>;\n}\n\n/**\n * Options available on every Arkos-owned emit method.\n * All fields are optional — defaults are applied at the gateway level.\n *\n * @example\n * gateway.toUser(userId).emit(\"notification\", data, {\n * timeout: 5000,\n * retries: 3,\n * })\n */\nexport interface ArkosEmitOptions {\n /**\n * Timeout in milliseconds to wait for an acknowledgement from the client.\n * Only applies to `toUser()` and `toSocket()` — broadcasts have no ack.\n *\n * @default 5000\n */\n timeout?: number;\n\n /**\n * Number of retry attempts if the emit times out or fails.\n * Uses exponential backoff between attempts, capped at 5 seconds.\n * Only applies to `toUser()` and `toSocket()`.\n *\n * @default 0\n */\n retries?: number;\n /**\n * Whether to expect an acknowledgement from the client.\n *\n * When `true`, the emit uses `emitWithAck` under the hood — the server\n * waits for the client to call its ack callback, and the resolved value\n * is returned in `result.data`.\n *\n * When `false` (default), the emit is fire-and-forget (or fire-and-confirm\n * if `timeout` is set, using a server-side timeout callback).\n *\n * @default false\n *\n * // wait for client ack + get response back\n * const result = await gateway.toUser(userId).emit(\"order:confirm\", data, { ack: true })\n * if (result.success) console.log(result.data) // client's ack payload\n */\n ack?: boolean;\n /**\n * Whether to send the event without waiting for room membership or\n * buffering. Volatile events are discarded if the client is not\n * immediately reachable (e.g. disconnected or busy).\n *\n * Useful for high-frequency, non-critical events like cursor positions\n * or typing indicators where losing a packet is acceptable.\n *\n * @default false\n *\n * @example\n * await gateway.toUser(userId).emit(\"cursor\", data, { volatile: true })\n */\n volatile?: boolean;\n\n /**\n * Whether to enable per-message deflate compression on the payload.\n * Disabling compression can improve performance for small, frequent\n * messages where compression overhead outweighs the size savings.\n *\n * @default true\n *\n * @example\n * await gateway.toUser(userId).emit(\"ping\", data, { compress: false })\n */\n compress?: boolean;\n}\n\n/**\n * Options for `gateway.register()`.\n * Passed once at the root gateway registration — applies to all child gateways.\n *\n * @example\n * gateway.register(io, {\n * store: new MultiTierStore([\n * new MemoryStore(),\n * new RedisStore(redis)\n * ])\n * })\n */\nexport type ArkosGatewayRegisterOptions = {\n /**\n * Unified store for rate limiting and deduplication across all gateways.\n * Defaults to an in-memory store — zero config required for single-instance deployments.\n *\n * For distributed deployments (multiple Node processes / instances), plug in a\n * Redis store or a multi-tier store to share state across instances.\n *\n * @example\n * store: new RedisArkosStore(redis)\n * store: new MultiTierStore([new MemoryStore(), new RedisStore(redis)])\n */\n store?: ArkosGatewayStore;\n};\n\n/**\n * Unified store interface for rate limiting and deduplication.\n * Implement this to plug in Redis, Valkey, or any distributed store.\n *\n * @example\n * class RedisArkosStore implements ArkosGatewayStore {\n * constructor(private redis: RedisClientType) {}\n *\n * async increment(key: string, windowMs: number) {\n * const count = await this.redis.incr(key)\n * if (count === 1) await this.redis.pExpire(key, windowMs)\n * const ttl = await this.redis.pTtl(key)\n * return { count, resetAt: Date.now() + ttl }\n * }\n *\n * async clear(prefix: string) {\n * const keys = await this.redis.keys(`${prefix}*`)\n * if (keys.length) await this.redis.del(keys)\n * }\n *\n * async has(key: string) {\n * return (await this.redis.exists(key)) === 1\n * }\n *\n * async set(key: string, ttl: number) {\n * await this.redis.setEx(key, ttl, \"1\")\n * }\n * }\n */\nexport interface ArkosGatewayStore {\n /**\n * Increment a rate limit counter for a given key and window.\n * Called on every event to track request counts.\n * Key format: `arkos::rl:{socketId}:{event}`\n */\n increment(\n key: string,\n windowMs: number\n ): Promise<{ count: number; resetAt: number }>;\n /**\n * Clear all rate limit entries matching a prefix.\n * Called on socket disconnect.\n * Prefix format: `arkos::rl:{socketId}`\n */\n clear(prefix: string): Promise<void>;\n /**\n * Check if a dedup key exists.\n * Called before emitting to detect duplicate messages.\n */\n has(key: string): Promise<boolean>;\n /**\n * Store a dedup key with a TTL in seconds.\n * Called after emitting to mark a message as seen.\n */\n set(key: string, ttl: number): Promise<void>;\n /**\n * Atomically stores a deduplication key only if it does not already exist.\n *\n * Returns `true` if the key was successfully created and the caller\n * should continue processing the message.\n *\n * Returns `false` if the key already exists, indicating the message\n * has already been seen and should be treated as a duplicate.\n *\n * Implementations should guarantee atomic behavior whenever possible\n * (for example Redis `SET NX EX`) to prevent race conditions under\n * concurrent processing.\n *\n * @param key Unique deduplication key.\n * @param ttl Time-to-live in seconds before the key expires.\n */\n setIfNotExists(key: string, ttl: number): Promise<boolean>;\n}\n\nexport class ArkosGatewayController {}\n"]}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GatewaySocketEmitBuilder = exports.GatewayUserEmitBuilder = exports.GatewayEmitBuilder = exports.GatewayRoomBuilder = exports.GatewayUserBuilder = void 0;
|
|
4
|
+
const uuidv7_1 = require("uuidv7");
|
|
5
|
+
class GatewayUserBuilder {
|
|
6
|
+
constructor(userId, ns) {
|
|
7
|
+
this.userId = userId;
|
|
8
|
+
this.ns = ns;
|
|
9
|
+
this.cachedSockets = null;
|
|
10
|
+
}
|
|
11
|
+
async fetchSockets() {
|
|
12
|
+
if (!this.cachedSockets) {
|
|
13
|
+
this.cachedSockets = await this.ns
|
|
14
|
+
.in(`arkos::user:${this.userId}`)
|
|
15
|
+
.fetchSockets();
|
|
16
|
+
}
|
|
17
|
+
return this.cachedSockets;
|
|
18
|
+
}
|
|
19
|
+
async isOnline() {
|
|
20
|
+
return (await this.fetchSockets()).length > 0;
|
|
21
|
+
}
|
|
22
|
+
async socketCount() {
|
|
23
|
+
return (await this.fetchSockets()).length;
|
|
24
|
+
}
|
|
25
|
+
async socketIds() {
|
|
26
|
+
return (await this.fetchSockets()).map((s) => s.id);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.GatewayUserBuilder = GatewayUserBuilder;
|
|
30
|
+
class GatewayRoomBuilder {
|
|
31
|
+
constructor(roomId, ns) {
|
|
32
|
+
this.roomId = roomId;
|
|
33
|
+
this.ns = ns;
|
|
34
|
+
}
|
|
35
|
+
async size() {
|
|
36
|
+
const sockets = await this.ns.in(this.roomId).fetchSockets();
|
|
37
|
+
return sockets.length;
|
|
38
|
+
}
|
|
39
|
+
async sockets() {
|
|
40
|
+
const sockets = await this.ns.in(this.roomId).fetchSockets();
|
|
41
|
+
return sockets.map((s) => s.id);
|
|
42
|
+
}
|
|
43
|
+
async isEmpty() {
|
|
44
|
+
return (await this.size()) === 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.GatewayRoomBuilder = GatewayRoomBuilder;
|
|
48
|
+
class GatewayEmitBuilder {
|
|
49
|
+
constructor(target, gatewayConfig, store, room) {
|
|
50
|
+
this.target = target;
|
|
51
|
+
this.gatewayConfig = gatewayConfig;
|
|
52
|
+
this.store = store;
|
|
53
|
+
this.room = room;
|
|
54
|
+
}
|
|
55
|
+
resolve() {
|
|
56
|
+
return this.target;
|
|
57
|
+
}
|
|
58
|
+
async emit(event, data, options = {}) {
|
|
59
|
+
let target = this.target;
|
|
60
|
+
if (options.volatile)
|
|
61
|
+
target = target.volatile;
|
|
62
|
+
target.compress(options.compress ?? true).emit(event, { ...data });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.GatewayEmitBuilder = GatewayEmitBuilder;
|
|
66
|
+
class GatewayUserEmitBuilder extends GatewayEmitBuilder {
|
|
67
|
+
constructor(userId, ns, gatewayConfig, store) {
|
|
68
|
+
super(ns.in(`arkos::user:${userId}`), gatewayConfig, store);
|
|
69
|
+
this.userId = userId;
|
|
70
|
+
this.ns = ns;
|
|
71
|
+
}
|
|
72
|
+
resolve() {
|
|
73
|
+
return this.ns.in(`arkos::user:${this.userId}`);
|
|
74
|
+
}
|
|
75
|
+
async emit(event, data, options = {}) {
|
|
76
|
+
const sockets = [...this.ns.sockets.values()].filter((s) => s.rooms.has(`arkos::user:${this.userId}`));
|
|
77
|
+
if (!sockets.length)
|
|
78
|
+
return { success: false, reason: "offline" };
|
|
79
|
+
if ("_meta" in (data || {}))
|
|
80
|
+
throw new Error(`Cannot emit event "${event}" with a pre-existing _meta field. ` +
|
|
81
|
+
`ArkosGateway manages _meta automatically for deduplication and freshness. ` +
|
|
82
|
+
`Remove the _meta property from your payload.`);
|
|
83
|
+
const timeout = options.timeout ?? 5000;
|
|
84
|
+
const maxRetries = options.retries ?? 0;
|
|
85
|
+
const dataWithMeta = {
|
|
86
|
+
...data,
|
|
87
|
+
_meta: { mid: (0, uuidv7_1.uuidv7)(), timestamp: Date.now() },
|
|
88
|
+
};
|
|
89
|
+
const attemptEmit = async () => {
|
|
90
|
+
if (options.ack) {
|
|
91
|
+
const results = await Promise.allSettled(sockets.map((socket) => {
|
|
92
|
+
const s = options.volatile
|
|
93
|
+
? socket.volatile.timeout(timeout)
|
|
94
|
+
: socket.timeout(timeout);
|
|
95
|
+
return s
|
|
96
|
+
.compress(options.compress ?? true)
|
|
97
|
+
.emitWithAck(event, dataWithMeta);
|
|
98
|
+
}));
|
|
99
|
+
const first = results.find((r) => r.status === "fulfilled");
|
|
100
|
+
if (first && first.status === "fulfilled")
|
|
101
|
+
return { success: true, data: first.value };
|
|
102
|
+
return { success: false };
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const results = await Promise.allSettled(sockets.map((socket) => new Promise((resolve) => {
|
|
106
|
+
const s = options.volatile
|
|
107
|
+
? socket.volatile.timeout(timeout)
|
|
108
|
+
: socket.timeout(timeout);
|
|
109
|
+
s.compress(options.compress ?? true).emit(event, dataWithMeta, (err) => resolve(!err));
|
|
110
|
+
})));
|
|
111
|
+
const ok = results.some((r) => r.status === "fulfilled" && r.value === true);
|
|
112
|
+
return { success: ok };
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
116
|
+
const result = await attemptEmit();
|
|
117
|
+
if (result.success)
|
|
118
|
+
return result;
|
|
119
|
+
if (attempt < maxRetries) {
|
|
120
|
+
const delay = Math.min(Math.pow(2, attempt) * 1000, 5000);
|
|
121
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { success: false, reason: "timeout" };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.GatewayUserEmitBuilder = GatewayUserEmitBuilder;
|
|
128
|
+
class GatewaySocketEmitBuilder extends GatewayEmitBuilder {
|
|
129
|
+
constructor(socket, gatewayConfig, store) {
|
|
130
|
+
super({ emit: () => { } }, gatewayConfig, store);
|
|
131
|
+
this.socket = socket;
|
|
132
|
+
}
|
|
133
|
+
resolve() {
|
|
134
|
+
return this.socket;
|
|
135
|
+
}
|
|
136
|
+
async emit(event, data, options = {}) {
|
|
137
|
+
const timeout = options.timeout ?? 5000;
|
|
138
|
+
const maxRetries = options.retries ?? 0;
|
|
139
|
+
if ("_meta" in (data || {}))
|
|
140
|
+
throw new Error(`Cannot emit event "${event}" with a pre-existing _meta field. ` +
|
|
141
|
+
`ArkosGateway manages _meta automatically for deduplication and freshness. ` +
|
|
142
|
+
`Remove the _meta property from your payload.`);
|
|
143
|
+
const dataWithMeta = {
|
|
144
|
+
...data,
|
|
145
|
+
_meta: { mid: (0, uuidv7_1.uuidv7)(), timestamp: Date.now() },
|
|
146
|
+
};
|
|
147
|
+
const attemptEmit = async () => {
|
|
148
|
+
const s = options.volatile
|
|
149
|
+
? this.socket.volatile.timeout(timeout)
|
|
150
|
+
: this.socket.timeout(timeout);
|
|
151
|
+
const sc = s.compress(options.compress ?? true);
|
|
152
|
+
if (options.ack) {
|
|
153
|
+
try {
|
|
154
|
+
const response = (await sc.emitWithAck(event, dataWithMeta));
|
|
155
|
+
return { success: true, data: response };
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return { success: false };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
return new Promise((resolve) => {
|
|
163
|
+
sc.emit(event, dataWithMeta, (err) => resolve({ success: !err }));
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
168
|
+
const result = await attemptEmit();
|
|
169
|
+
if (result.success)
|
|
170
|
+
return result;
|
|
171
|
+
if (attempt < maxRetries) {
|
|
172
|
+
const delay = Math.min(Math.pow(2, attempt) * 1000, 5000);
|
|
173
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { success: false, reason: "timeout" };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.GatewaySocketEmitBuilder = GatewaySocketEmitBuilder;
|
|
180
|
+
//# sourceMappingURL=emit-builders.js.map
|