raffel 1.0.9 → 1.0.10-next.22f4d4e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/adapters/index.d.ts +1 -1
  2. package/dist/adapters/index.d.ts.map +1 -1
  3. package/dist/adapters/websocket.d.ts +100 -1
  4. package/dist/adapters/websocket.d.ts.map +1 -1
  5. package/dist/adapters/websocket.js +428 -14
  6. package/dist/adapters/websocket.js.map +1 -1
  7. package/dist/channels/channel-manager.d.ts.map +1 -1
  8. package/dist/channels/channel-manager.js +265 -4
  9. package/dist/channels/channel-manager.js.map +1 -1
  10. package/dist/channels/history.d.ts +54 -0
  11. package/dist/channels/history.d.ts.map +1 -0
  12. package/dist/channels/history.js +78 -0
  13. package/dist/channels/history.js.map +1 -0
  14. package/dist/channels/index.d.ts +9 -1
  15. package/dist/channels/index.d.ts.map +1 -1
  16. package/dist/channels/index.js +8 -1
  17. package/dist/channels/index.js.map +1 -1
  18. package/dist/channels/recovery.d.ts +57 -0
  19. package/dist/channels/recovery.d.ts.map +1 -0
  20. package/dist/channels/recovery.js +58 -0
  21. package/dist/channels/recovery.js.map +1 -0
  22. package/dist/channels/rest-api.d.ts +32 -0
  23. package/dist/channels/rest-api.d.ts.map +1 -0
  24. package/dist/channels/rest-api.js +197 -0
  25. package/dist/channels/rest-api.js.map +1 -0
  26. package/dist/channels/ticket-store.d.ts +25 -0
  27. package/dist/channels/ticket-store.d.ts.map +1 -0
  28. package/dist/channels/ticket-store.js +70 -0
  29. package/dist/channels/ticket-store.js.map +1 -0
  30. package/dist/channels/types.d.ts +267 -5
  31. package/dist/channels/types.d.ts.map +1 -1
  32. package/dist/channels/types.js +15 -1
  33. package/dist/channels/types.js.map +1 -1
  34. package/dist/client/index.d.ts +39 -0
  35. package/dist/client/index.d.ts.map +1 -0
  36. package/dist/client/index.js +531 -0
  37. package/dist/client/index.js.map +1 -0
  38. package/dist/client/reconnect.d.ts +33 -0
  39. package/dist/client/reconnect.d.ts.map +1 -0
  40. package/dist/client/reconnect.js +60 -0
  41. package/dist/client/reconnect.js.map +1 -0
  42. package/dist/client/types.d.ts +107 -0
  43. package/dist/client/types.d.ts.map +1 -0
  44. package/dist/client/types.js +5 -0
  45. package/dist/client/types.js.map +1 -0
  46. package/dist/index.d.ts +9 -4
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +10 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/middleware/interceptors/field-filter.d.ts +51 -0
  51. package/dist/middleware/interceptors/field-filter.d.ts.map +1 -0
  52. package/dist/middleware/interceptors/field-filter.js +81 -0
  53. package/dist/middleware/interceptors/field-filter.js.map +1 -0
  54. package/dist/middleware/interceptors/guard.d.ts +51 -0
  55. package/dist/middleware/interceptors/guard.d.ts.map +1 -0
  56. package/dist/middleware/interceptors/guard.js +45 -0
  57. package/dist/middleware/interceptors/guard.js.map +1 -0
  58. package/dist/middleware/interceptors/index.d.ts +4 -0
  59. package/dist/middleware/interceptors/index.d.ts.map +1 -1
  60. package/dist/middleware/interceptors/index.js +4 -0
  61. package/dist/middleware/interceptors/index.js.map +1 -1
  62. package/dist/resource-module/index.d.ts +44 -0
  63. package/dist/resource-module/index.d.ts.map +1 -0
  64. package/dist/resource-module/index.js +146 -0
  65. package/dist/resource-module/index.js.map +1 -0
  66. package/dist/resource-module/types.d.ts +109 -0
  67. package/dist/resource-module/types.d.ts.map +1 -0
  68. package/dist/resource-module/types.js +7 -0
  69. package/dist/resource-module/types.js.map +1 -0
  70. package/dist/resource-module/watch-helpers.d.ts +16 -0
  71. package/dist/resource-module/watch-helpers.d.ts.map +1 -0
  72. package/dist/resource-module/watch-helpers.js +65 -0
  73. package/dist/resource-module/watch-helpers.js.map +1 -0
  74. package/dist/server/types.d.ts +27 -0
  75. package/dist/server/types.d.ts.map +1 -1
  76. package/package.json +9 -1
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Channel History
3
+ *
4
+ * Pluggable message history for channel catchup on reconnect.
5
+ * Stores recent messages per channel in a circular buffer.
6
+ */
7
+ /**
8
+ * Create an in-memory history store with circular buffer per channel
9
+ */
10
+ export function createMemoryHistoryStore(options = {}) {
11
+ const { maxSize = 100, ttl = 300_000, gcInterval = 30_000, } = options;
12
+ /** Per-channel circular buffer */
13
+ const buffers = new Map();
14
+ // Periodic GC for expired entries
15
+ const timer = setInterval(() => {
16
+ const now = Date.now();
17
+ for (const [channel, entries] of buffers) {
18
+ const filtered = entries.filter((e) => now - e.timestamp < ttl);
19
+ if (filtered.length === 0) {
20
+ buffers.delete(channel);
21
+ }
22
+ else if (filtered.length < entries.length) {
23
+ buffers.set(channel, filtered);
24
+ }
25
+ }
26
+ }, gcInterval);
27
+ if (timer.unref)
28
+ timer.unref();
29
+ return {
30
+ append(entry) {
31
+ let buffer = buffers.get(entry.channel);
32
+ if (!buffer) {
33
+ buffer = [];
34
+ buffers.set(entry.channel, buffer);
35
+ }
36
+ buffer.push(entry);
37
+ // Circular: trim to maxSize
38
+ if (buffer.length > maxSize) {
39
+ buffers.set(entry.channel, buffer.slice(buffer.length - maxSize));
40
+ }
41
+ },
42
+ getAfterSeq(channel, sinceSeq, epoch, limit) {
43
+ const buffer = buffers.get(channel);
44
+ if (!buffer || buffer.length === 0)
45
+ return [];
46
+ const now = Date.now();
47
+ const results = [];
48
+ for (const entry of buffer) {
49
+ // Must match epoch
50
+ if (entry.epoch !== epoch)
51
+ continue;
52
+ // Must be after sinceSeq
53
+ if (entry.seq <= sinceSeq)
54
+ continue;
55
+ // Must not be expired
56
+ if (now - entry.timestamp >= ttl)
57
+ continue;
58
+ results.push(entry);
59
+ if (limit && results.length >= limit)
60
+ break;
61
+ }
62
+ return results;
63
+ },
64
+ trim(channel, trimSize) {
65
+ const buffer = buffers.get(channel);
66
+ if (!buffer)
67
+ return;
68
+ if (buffer.length > trimSize) {
69
+ buffers.set(channel, buffer.slice(buffer.length - trimSize));
70
+ }
71
+ },
72
+ dispose() {
73
+ clearInterval(timer);
74
+ buffers.clear();
75
+ },
76
+ };
77
+ }
78
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/channels/history.ts"],"names":[],"mappings":"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"}
@@ -43,5 +43,13 @@
43
43
  */
44
44
  export { createChannelManager } from './channel-manager.js';
45
45
  export type { SendToSocketFn } from './channel-manager.js';
46
- export { type ChannelType, type ChannelOptions, type ChannelMember, type ChannelState, type ChannelManager, type SubscribeResult, type ClientInfo, type RoomInfo, type GroupInfo, type ChannelLifecycleHooks, type ClientConnectEvent, type ClientDisconnectEvent, type SubscribeMessage, type SubscribedMessage, type UnsubscribeMessage, type UnsubscribedMessage, type PublishMessage, type ChannelEventMessage, type ChannelErrorMessage, type ChannelMessage, isChannelMessage, getChannelType, requiresAuth, } from './types.js';
46
+ export { createMemoryTicketStore, generateTicket } from './ticket-store.js';
47
+ export type { MemoryTicketStoreOptions } from './ticket-store.js';
48
+ export { createMemoryHistoryStore } from './history.js';
49
+ export type { ChannelHistoryEntry, ChannelHistoryPort, MemoryHistoryStoreOptions, } from './history.js';
50
+ export { createMemoryRecoveryStore, generateRecoveryToken } from './recovery.js';
51
+ export type { RecoverableSession, ConnectionRecoveryPort, MemoryRecoveryStoreOptions, } from './recovery.js';
52
+ export { createChannelRestApi } from './rest-api.js';
53
+ export type { ChannelRestApiOptions } from './rest-api.js';
54
+ export { type ChannelType, type ChannelOptions, type ChannelMember, type ChannelState, type ChannelManager, type SubscribeResult, type ClientInfo, type RoomInfo, type GroupInfo, type ChannelLifecycleHooks, type ClientConnectEvent, type ClientDisconnectEvent, type ConnectionTicket, type TicketStore, type WebSocketAuthConfig, type ChannelRateLimits, type BackpressureConfig, type AuthRefreshMessage, type AuthRefreshedMessage, type RecoverMessage, type SubscribeMessage, type SubscribedMessage, type UnsubscribeMessage, type UnsubscribedMessage, type PublishMessage, type ChannelEventMessage, type ChannelErrorMessage, type ChannelMessage, type BatchSubscribeMessage, type BatchSubscribedMessage, type BatchPublishMessage, type TypingMessage, isChannelMessage, isRecoverMessage, getChannelType, requiresAuth, } from './types.js';
47
55
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAG1B,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAGnB,gBAAgB,EAChB,cAAc,EACd,YAAY,GACb,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAC3E,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AAGjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AACvD,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAChF,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EAGnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAGnB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EAGxB,KAAK,aAAa,EAGlB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,YAAY,GACb,MAAM,YAAY,CAAA"}
@@ -42,7 +42,14 @@
42
42
  * ```
43
43
  */
44
44
  export { createChannelManager } from './channel-manager.js';
45
+ export { createMemoryTicketStore, generateTicket } from './ticket-store.js';
46
+ // History
47
+ export { createMemoryHistoryStore } from './history.js';
48
+ // Recovery
49
+ export { createMemoryRecoveryStore, generateRecoveryToken } from './recovery.js';
50
+ // REST API
51
+ export { createChannelRestApi } from './rest-api.js';
45
52
  export {
46
53
  // Helpers
47
- isChannelMessage, getChannelType, requiresAuth, } from './types.js';
54
+ isChannelMessage, isRecoverMessage, getChannelType, requiresAuth, } from './types.js';
48
55
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAG3D,OAAO;AAyBL,UAAU;AACV,gBAAgB,EAChB,cAAc,EACd,YAAY,GACb,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAE3D,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAG3E,UAAU;AACV,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAOvD,WAAW;AACX,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAOhF,WAAW;AACX,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAGpD,OAAO;AAyCL,UAAU;AACV,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,YAAY,GACb,MAAM,YAAY,CAAA"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Connection State Recovery
3
+ *
4
+ * Saves session state on disconnect and restores on reconnect,
5
+ * allowing clients to recover subscriptions and replay missed messages.
6
+ */
7
+ /**
8
+ * Saved session state for recovery
9
+ */
10
+ export interface RecoverableSession {
11
+ /** Opaque recovery token given to the client */
12
+ recoveryToken: string;
13
+ /** Original socket ID */
14
+ socketId: string;
15
+ /** User ID (if authenticated) */
16
+ userId?: string;
17
+ /** Channels the client was subscribed to, with last seen seq */
18
+ channels: {
19
+ name: string;
20
+ lastSeq: number;
21
+ }[];
22
+ /** Groups the client belonged to */
23
+ groups: string[];
24
+ /** Custom metadata */
25
+ metadata: Record<string, unknown>;
26
+ /** When this session expires */
27
+ expiresAt: number;
28
+ }
29
+ /**
30
+ * Port interface for pluggable recovery storage
31
+ */
32
+ export interface ConnectionRecoveryPort {
33
+ /** Save a recoverable session */
34
+ save(session: RecoverableSession): void;
35
+ /** Get a session by recovery token */
36
+ get(recoveryToken: string): RecoverableSession | null;
37
+ /** Delete a session (consumed or expired) */
38
+ delete(recoveryToken: string): void;
39
+ }
40
+ export interface MemoryRecoveryStoreOptions {
41
+ /** TTL in ms for recovery sessions (default: 120000 = 2 minutes) */
42
+ ttl?: number;
43
+ /** GC interval in ms (default: 30000) */
44
+ gcInterval?: number;
45
+ }
46
+ /**
47
+ * Create an in-memory recovery store with TTL and periodic GC
48
+ */
49
+ export declare function createMemoryRecoveryStore(options?: MemoryRecoveryStoreOptions): ConnectionRecoveryPort & {
50
+ dispose(): void;
51
+ size: number;
52
+ };
53
+ /**
54
+ * Generate a recovery token
55
+ */
56
+ export declare function generateRecoveryToken(): string;
57
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/channels/recovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAA;IACrB,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC7C,oCAAoC;IACpC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,iCAAiC;IACjC,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACvC,sCAAsC;IACtC,GAAG,CAAC,aAAa,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAAA;IACrD,6CAA6C;IAC7C,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAID,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,sBAAsB,GAAG;IAAE,OAAO,IAAI,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAmD5D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Connection State Recovery
3
+ *
4
+ * Saves session state on disconnect and restores on reconnect,
5
+ * allowing clients to recover subscriptions and replay missed messages.
6
+ */
7
+ import { sid } from '../utils/id/index.js';
8
+ /**
9
+ * Create an in-memory recovery store with TTL and periodic GC
10
+ */
11
+ export function createMemoryRecoveryStore(options = {}) {
12
+ const { ttl = 120_000, gcInterval = 30_000, } = options;
13
+ const sessions = new Map();
14
+ // Periodic GC for expired sessions
15
+ const timer = setInterval(() => {
16
+ const now = Date.now();
17
+ for (const [token, session] of sessions) {
18
+ if (session.expiresAt < now) {
19
+ sessions.delete(token);
20
+ }
21
+ }
22
+ }, gcInterval);
23
+ if (timer.unref)
24
+ timer.unref();
25
+ return {
26
+ save(session) {
27
+ sessions.set(session.recoveryToken, session);
28
+ },
29
+ get(recoveryToken) {
30
+ const session = sessions.get(recoveryToken);
31
+ if (!session)
32
+ return null;
33
+ // Check expiration
34
+ if (session.expiresAt < Date.now()) {
35
+ sessions.delete(recoveryToken);
36
+ return null;
37
+ }
38
+ return session;
39
+ },
40
+ delete(recoveryToken) {
41
+ sessions.delete(recoveryToken);
42
+ },
43
+ dispose() {
44
+ clearInterval(timer);
45
+ sessions.clear();
46
+ },
47
+ get size() {
48
+ return sessions.size;
49
+ },
50
+ };
51
+ }
52
+ /**
53
+ * Generate a recovery token
54
+ */
55
+ export function generateRecoveryToken() {
56
+ return `rec_${sid()}`;
57
+ }
58
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/channels/recovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AA6C1C;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAAsC,EAAE;IAExC,MAAM,EACJ,GAAG,GAAG,OAAO,EACb,UAAU,GAAG,MAAM,GACpB,GAAG,OAAO,CAAA;IAEX,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA8B,CAAA;IAEtD,mCAAmC;IACnC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,QAAQ,EAAE,CAAC;YACxC,IAAI,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC;gBAC5B,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;IACH,CAAC,EAAE,UAAU,CAAC,CAAA;IAEd,IAAI,KAAK,CAAC,KAAK;QAAE,KAAK,CAAC,KAAK,EAAE,CAAA;IAE9B,OAAO;QACL,IAAI,CAAC,OAA2B;YAC9B,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;QAC9C,CAAC;QAED,GAAG,CAAC,aAAqB;YACvB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;YAC3C,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAA;YAEzB,mBAAmB;YACnB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBACnC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;gBAC9B,OAAO,IAAI,CAAA;YACb,CAAC;YAED,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,CAAC,aAAqB;YAC1B,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QAChC,CAAC;QAED,OAAO;YACL,aAAa,CAAC,KAAK,CAAC,CAAA;YACpB,QAAQ,CAAC,KAAK,EAAE,CAAA;QAClB,CAAC;QAED,IAAI,IAAI;YACN,OAAO,QAAQ,CAAC,IAAI,CAAA;QACtB,CAAC;KACF,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,OAAO,GAAG,EAAE,EAAE,CAAA;AACvB,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Channel REST API
3
+ *
4
+ * HTTP middleware for server-side channel management.
5
+ * Allows publishing events, listing channels, and sending
6
+ * messages to specific clients via HTTP endpoints.
7
+ */
8
+ import type { IncomingMessage, ServerResponse } from 'node:http';
9
+ import type { ChannelManager } from './types.js';
10
+ export interface ChannelRestApiOptions {
11
+ /** Base path for REST endpoints (default: '/channels') */
12
+ path?: string;
13
+ /** API key for authentication */
14
+ apiKey?: string;
15
+ /** Custom auth function (overrides apiKey) */
16
+ auth?: (req: IncomingMessage) => boolean | Promise<boolean>;
17
+ }
18
+ /**
19
+ * Create HTTP middleware for channel REST API.
20
+ *
21
+ * Returns an `HttpMiddleware` compatible with the Raffel adapter layer:
22
+ * `(req, res) => boolean | Promise<boolean>` — returns true if handled.
23
+ *
24
+ * Endpoints:
25
+ * - POST /<path>/:channel/events — broadcast to channel
26
+ * - GET /<path> — list active channels
27
+ * - GET /<path>/:channel — channel info
28
+ * - POST /<path>/clients/:socketId/events — send to specific client
29
+ * - POST /<path>/broadcast — broadcast to all
30
+ */
31
+ export declare function createChannelRestApi(manager: ChannelManager, options?: ChannelRestApiOptions): (req: IncomingMessage, res: ServerResponse) => boolean | Promise<boolean>;
32
+ //# sourceMappingURL=rest-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../src/channels/rest-api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhD,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,GAClC,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CA4M3E"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Channel REST API
3
+ *
4
+ * HTTP middleware for server-side channel management.
5
+ * Allows publishing events, listing channels, and sending
6
+ * messages to specific clients via HTTP endpoints.
7
+ */
8
+ import { getChannelType } from './types.js';
9
+ /**
10
+ * Create HTTP middleware for channel REST API.
11
+ *
12
+ * Returns an `HttpMiddleware` compatible with the Raffel adapter layer:
13
+ * `(req, res) => boolean | Promise<boolean>` — returns true if handled.
14
+ *
15
+ * Endpoints:
16
+ * - POST /<path>/:channel/events — broadcast to channel
17
+ * - GET /<path> — list active channels
18
+ * - GET /<path>/:channel — channel info
19
+ * - POST /<path>/clients/:socketId/events — send to specific client
20
+ * - POST /<path>/broadcast — broadcast to all
21
+ */
22
+ export function createChannelRestApi(manager, options = {}) {
23
+ const basePath = (options.path ?? '/channels').replace(/\/+$/, '');
24
+ /**
25
+ * Check authorization
26
+ */
27
+ async function checkAuth(req, res) {
28
+ if (options.auth) {
29
+ const allowed = await options.auth(req);
30
+ if (!allowed) {
31
+ sendJson(res, 401, { error: 'Unauthorized' });
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ if (options.apiKey) {
37
+ const auth = req.headers['authorization'];
38
+ if (auth !== `Bearer ${options.apiKey}`) {
39
+ sendJson(res, 401, { error: 'Invalid API key' });
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+ // No auth configured — allow all
45
+ return true;
46
+ }
47
+ function sendJson(res, status, body) {
48
+ res.writeHead(status, { 'Content-Type': 'application/json' });
49
+ res.end(JSON.stringify(body));
50
+ }
51
+ function readBody(req) {
52
+ return new Promise((resolve, reject) => {
53
+ const chunks = [];
54
+ req.on('data', (chunk) => chunks.push(chunk));
55
+ req.on('end', () => {
56
+ try {
57
+ const raw = Buffer.concat(chunks).toString('utf-8');
58
+ resolve(raw ? JSON.parse(raw) : {});
59
+ }
60
+ catch (err) {
61
+ reject(err);
62
+ }
63
+ });
64
+ req.on('error', reject);
65
+ });
66
+ }
67
+ /**
68
+ * Parse URL pathname and extract route match
69
+ */
70
+ function matchRoute(pathname, method) {
71
+ // Normalize
72
+ const path = pathname.replace(/\/+$/, '') || '/';
73
+ // POST /channels/broadcast
74
+ if (method === 'POST' && path === `${basePath}/broadcast`) {
75
+ return { route: 'broadcast' };
76
+ }
77
+ // POST /channels/clients/:socketId/events
78
+ const clientEventsMatch = path.match(new RegExp(`^${escapeRegExp(basePath)}/clients/([^/]+)/events$`));
79
+ if (method === 'POST' && clientEventsMatch) {
80
+ return { route: 'client-events', socketId: clientEventsMatch[1] };
81
+ }
82
+ // POST /channels/:channel/events
83
+ const channelEventsMatch = path.match(new RegExp(`^${escapeRegExp(basePath)}/([^/]+)/events$`));
84
+ if (method === 'POST' && channelEventsMatch) {
85
+ return { route: 'channel-events', channel: decodeURIComponent(channelEventsMatch[1]) };
86
+ }
87
+ // GET /channels/:channel
88
+ const channelInfoMatch = path.match(new RegExp(`^${escapeRegExp(basePath)}/([^/]+)$`));
89
+ if (method === 'GET' && channelInfoMatch) {
90
+ // Exclude reserved sub-paths
91
+ const segment = channelInfoMatch[1];
92
+ if (segment === 'broadcast' || segment === 'clients') {
93
+ return null;
94
+ }
95
+ return { route: 'channel-info', channel: decodeURIComponent(segment) };
96
+ }
97
+ // GET /channels
98
+ if (method === 'GET' && path === basePath) {
99
+ return { route: 'list-channels' };
100
+ }
101
+ return null;
102
+ }
103
+ return async function channelRestApiMiddleware(req, res) {
104
+ const url = new URL(req.url || '/', 'http://localhost');
105
+ const method = req.method?.toUpperCase() ?? 'GET';
106
+ const match = matchRoute(url.pathname, method);
107
+ if (!match)
108
+ return false;
109
+ // Auth check
110
+ const authed = await checkAuth(req, res);
111
+ if (!authed)
112
+ return true; // Already sent 401
113
+ try {
114
+ if (match.route === 'list-channels') {
115
+ const channelNames = manager.getChannels();
116
+ const result = channelNames.map((name) => ({
117
+ name,
118
+ type: getChannelType(name),
119
+ subscriberCount: manager.getSubscriberCount(name),
120
+ }));
121
+ sendJson(res, 200, { channels: result });
122
+ return true;
123
+ }
124
+ if (match.route === 'channel-info') {
125
+ const name = match.channel;
126
+ if (!manager.hasChannel(name)) {
127
+ sendJson(res, 404, { error: 'Channel not found' });
128
+ return true;
129
+ }
130
+ const type = getChannelType(name);
131
+ const info = {
132
+ name,
133
+ type,
134
+ subscriberCount: manager.getSubscriberCount(name),
135
+ subscribers: manager.getSubscribers(name),
136
+ };
137
+ if (type === 'presence') {
138
+ info.members = manager.getMembers(name);
139
+ }
140
+ sendJson(res, 200, info);
141
+ return true;
142
+ }
143
+ if (match.route === 'channel-events') {
144
+ const body = (await readBody(req));
145
+ const event = body.event;
146
+ const data = body.data;
147
+ const except = body.except;
148
+ if (!event) {
149
+ sendJson(res, 400, { error: 'Missing event field' });
150
+ return true;
151
+ }
152
+ manager.broadcast(match.channel, event, data, except);
153
+ sendJson(res, 200, { ok: true });
154
+ return true;
155
+ }
156
+ if (match.route === 'client-events') {
157
+ const body = (await readBody(req));
158
+ const event = body.event;
159
+ const data = body.data;
160
+ if (!event) {
161
+ sendJson(res, 400, { error: 'Missing event field' });
162
+ return true;
163
+ }
164
+ const client = manager.getClient(match.socketId);
165
+ if (!client) {
166
+ sendJson(res, 404, { error: 'Client not found' });
167
+ return true;
168
+ }
169
+ manager.sendToClient(match.socketId, event, data);
170
+ sendJson(res, 200, { ok: true });
171
+ return true;
172
+ }
173
+ if (match.route === 'broadcast') {
174
+ const body = (await readBody(req));
175
+ const event = body.event;
176
+ const data = body.data;
177
+ const except = body.except;
178
+ if (!event) {
179
+ sendJson(res, 400, { error: 'Missing event field' });
180
+ return true;
181
+ }
182
+ manager.broadcastAll(event, data, except);
183
+ sendJson(res, 200, { ok: true });
184
+ return true;
185
+ }
186
+ }
187
+ catch (err) {
188
+ sendJson(res, 500, { error: 'Internal server error' });
189
+ return true;
190
+ }
191
+ return false;
192
+ };
193
+ }
194
+ function escapeRegExp(s) {
195
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
196
+ }
197
+ //# sourceMappingURL=rest-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest-api.js","sourceRoot":"","sources":["../../src/channels/rest-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * In-Memory Ticket Store
3
+ *
4
+ * Default implementation of TicketStore for connection ticket auth.
5
+ * Tickets are single-use, short-lived tokens generated via HTTP
6
+ * and consumed on WebSocket connect.
7
+ */
8
+ import type { ConnectionTicket, TicketStore } from './types.js';
9
+ export interface MemoryTicketStoreOptions {
10
+ /** GC interval in ms (default: 10000) */
11
+ gcInterval?: number;
12
+ }
13
+ export declare function createMemoryTicketStore(options?: MemoryTicketStoreOptions): TicketStore & {
14
+ dispose(): void;
15
+ size: number;
16
+ };
17
+ /**
18
+ * Helper: generate a ticket for a user
19
+ */
20
+ export declare function generateTicket(userId: string, options?: {
21
+ ttl?: number;
22
+ permissions?: string[];
23
+ metadata?: Record<string, unknown>;
24
+ }): ConnectionTicket;
25
+ //# sourceMappingURL=ticket-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-store.d.ts","sourceRoot":"","sources":["../../src/channels/ticket-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE/D,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,WAAW,GAAG;IAAE,OAAO,IAAI,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAuDjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,GACA,gBAAgB,CASlB"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * In-Memory Ticket Store
3
+ *
4
+ * Default implementation of TicketStore for connection ticket auth.
5
+ * Tickets are single-use, short-lived tokens generated via HTTP
6
+ * and consumed on WebSocket connect.
7
+ */
8
+ import { sid } from '../utils/id/index.js';
9
+ export function createMemoryTicketStore(options = {}) {
10
+ const { gcInterval = 10_000 } = options;
11
+ const tickets = new Map();
12
+ // Periodic GC for expired tickets
13
+ const timer = setInterval(() => {
14
+ const now = Date.now();
15
+ for (const [id, ticket] of tickets) {
16
+ if (ticket.expiresAt < now || ticket.usedAt) {
17
+ tickets.delete(id);
18
+ }
19
+ }
20
+ }, gcInterval);
21
+ if (timer.unref)
22
+ timer.unref();
23
+ return {
24
+ async create(ticket) {
25
+ tickets.set(ticket.id, { ...ticket });
26
+ },
27
+ async consume(ticketId) {
28
+ const ticket = tickets.get(ticketId);
29
+ if (!ticket)
30
+ return null;
31
+ // Check expiration
32
+ if (ticket.expiresAt < Date.now()) {
33
+ tickets.delete(ticketId);
34
+ return null;
35
+ }
36
+ // Check single-use
37
+ if (ticket.usedAt) {
38
+ return null;
39
+ }
40
+ // Mark as used and return
41
+ ticket.usedAt = Date.now();
42
+ tickets.delete(ticketId); // Remove after consumption
43
+ return ticket;
44
+ },
45
+ async revoke(ticketId) {
46
+ tickets.delete(ticketId);
47
+ },
48
+ dispose() {
49
+ clearInterval(timer);
50
+ tickets.clear();
51
+ },
52
+ get size() {
53
+ return tickets.size;
54
+ },
55
+ };
56
+ }
57
+ /**
58
+ * Helper: generate a ticket for a user
59
+ */
60
+ export function generateTicket(userId, options) {
61
+ const ttl = options?.ttl ?? 30_000;
62
+ return {
63
+ id: sid(),
64
+ userId,
65
+ permissions: options?.permissions,
66
+ expiresAt: Date.now() + ttl,
67
+ metadata: options?.metadata,
68
+ };
69
+ }
70
+ //# sourceMappingURL=ticket-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-store.js","sourceRoot":"","sources":["../../src/channels/ticket-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAQ1C,MAAM,UAAU,uBAAuB,CACrC,UAAoC,EAAE;IAEtC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,GAAG,OAAO,CAAA;IACvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAA;IAEnD,kCAAkC;IAClC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACnC,IAAI,MAAM,CAAC,SAAS,GAAG,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC5C,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACpB,CAAC;QACH,CAAC;IACH,CAAC,EAAE,UAAU,CAAC,CAAA;IAEd,IAAI,KAAK,CAAC,KAAK;QAAE,KAAK,CAAC,KAAK,EAAE,CAAA;IAE9B,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,MAAwB;YACnC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;QACvC,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,QAAgB;YAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACpC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAA;YAExB,mBAAmB;YACnB,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBAClC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;gBACxB,OAAO,IAAI,CAAA;YACb,CAAC;YAED,mBAAmB;YACnB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,OAAO,IAAI,CAAA;YACb,CAAC;YAED,0BAA0B;YAC1B,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA,CAAC,2BAA2B;YACpD,OAAO,MAAM,CAAA;QACf,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,QAAgB;YAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC1B,CAAC;QAED,OAAO;YACL,aAAa,CAAC,KAAK,CAAC,CAAA;YACpB,OAAO,CAAC,KAAK,EAAE,CAAA;QACjB,CAAC;QAED,IAAI,IAAI;YACN,OAAO,OAAO,CAAC,IAAI,CAAA;QACrB,CAAC;KACF,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAc,EACd,OAIC;IAED,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,MAAM,CAAA;IAClC,OAAO;QACL,EAAE,EAAE,GAAG,EAAE;QACT,MAAM;QACN,WAAW,EAAE,OAAO,EAAE,WAAW;QACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG;QAC3B,QAAQ,EAAE,OAAO,EAAE,QAAQ;KAC5B,CAAA;AACH,CAAC"}