@portel/photon-core 2.1.2 → 2.3.0
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/README.md +61 -0
- package/dist/base.d.ts +42 -2
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +75 -7
- package/dist/base.js.map +1 -1
- package/dist/channels/daemon-broker.d.ts +35 -0
- package/dist/channels/daemon-broker.d.ts.map +1 -0
- package/dist/channels/daemon-broker.js +229 -0
- package/dist/channels/daemon-broker.js.map +1 -0
- package/dist/channels/http-broker.d.ts +45 -0
- package/dist/channels/http-broker.d.ts.map +1 -0
- package/dist/channels/http-broker.js +182 -0
- package/dist/channels/http-broker.js.map +1 -0
- package/dist/channels/index.d.ts +53 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +67 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/noop-broker.d.ts +21 -0
- package/dist/channels/noop-broker.d.ts.map +1 -0
- package/dist/channels/noop-broker.js +38 -0
- package/dist/channels/noop-broker.js.map +1 -0
- package/dist/channels/redis-broker.d.ts +45 -0
- package/dist/channels/redis-broker.d.ts.map +1 -0
- package/dist/channels/redis-broker.js +214 -0
- package/dist/channels/redis-broker.js.map +1 -0
- package/dist/channels/registry.d.ts +49 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +150 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/types.d.ts +85 -0
- package/dist/channels/types.d.ts.map +1 -0
- package/dist/channels/types.js +8 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/config.d.ts +63 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -0
- package/dist/decorators.d.ts +48 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +64 -0
- package/dist/decorators.js.map +1 -0
- package/dist/design-system/tokens.d.ts +66 -0
- package/dist/design-system/tokens.d.ts.map +1 -1
- package/dist/design-system/tokens.js +324 -44
- package/dist/design-system/tokens.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-apps.d.ts +130 -0
- package/dist/mcp-apps.d.ts.map +1 -0
- package/dist/mcp-apps.js +87 -0
- package/dist/mcp-apps.js.map +1 -0
- package/dist/schema-extractor.d.ts +41 -3
- package/dist/schema-extractor.d.ts.map +1 -1
- package/dist/schema-extractor.js +166 -14
- package/dist/schema-extractor.js.map +1 -1
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +15 -3
- package/src/base.ts +82 -6
- package/src/channels/daemon-broker.ts +271 -0
- package/src/channels/http-broker.ts +221 -0
- package/src/channels/index.ts +96 -0
- package/src/channels/noop-broker.ts +47 -0
- package/src/channels/redis-broker.ts +252 -0
- package/src/channels/registry.ts +170 -0
- package/src/channels/types.ts +95 -0
- package/src/config.ts +134 -0
- package/src/decorators.ts +87 -0
- package/src/design-system/tokens.ts +381 -57
- package/src/index.ts +39 -0
- package/src/mcp-apps.ts +204 -0
- package/src/schema-extractor.ts +191 -15
- package/src/types.ts +103 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Channel Broker
|
|
3
|
+
*
|
|
4
|
+
* Uses HTTP webhooks for publishing and Server-Sent Events (SSE) for subscribing.
|
|
5
|
+
* Works with any HTTP-based pub/sub system.
|
|
6
|
+
*
|
|
7
|
+
* Best for:
|
|
8
|
+
* - Serverless environments (AWS Lambda, Vercel, etc.)
|
|
9
|
+
* - Cloud functions that can't maintain long-lived connections
|
|
10
|
+
* - Integration with existing webhook infrastructure
|
|
11
|
+
*
|
|
12
|
+
* Publishing: POST to publishUrl with JSON body
|
|
13
|
+
* Subscribing: SSE connection to subscribeUrl
|
|
14
|
+
*/
|
|
15
|
+
import { registerBroker } from './registry.js';
|
|
16
|
+
export class HttpBroker {
|
|
17
|
+
type = 'http';
|
|
18
|
+
publishUrl;
|
|
19
|
+
subscribeUrl;
|
|
20
|
+
authToken;
|
|
21
|
+
headers;
|
|
22
|
+
timeout;
|
|
23
|
+
subscriptions = new Map();
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.publishUrl = options.publishUrl || process.env.PHOTON_CHANNEL_HTTP_URL;
|
|
26
|
+
this.subscribeUrl = options.subscribeUrl || process.env.PHOTON_CHANNEL_SSE_URL || this.publishUrl;
|
|
27
|
+
this.authToken = options.authToken || process.env.PHOTON_CHANNEL_AUTH_TOKEN;
|
|
28
|
+
this.headers = options.headers || {};
|
|
29
|
+
this.timeout = options.timeout || 30000;
|
|
30
|
+
}
|
|
31
|
+
getHeaders() {
|
|
32
|
+
const headers = {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
...this.headers,
|
|
35
|
+
};
|
|
36
|
+
if (this.authToken) {
|
|
37
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
38
|
+
}
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
async publish(message) {
|
|
42
|
+
if (!this.publishUrl) {
|
|
43
|
+
throw new Error('HTTP broker: publishUrl not configured');
|
|
44
|
+
}
|
|
45
|
+
const payload = {
|
|
46
|
+
...message,
|
|
47
|
+
timestamp: message.timestamp || Date.now(),
|
|
48
|
+
};
|
|
49
|
+
const controller = new AbortController();
|
|
50
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
51
|
+
try {
|
|
52
|
+
const response = await fetch(this.publishUrl, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: this.getHeaders(),
|
|
55
|
+
body: JSON.stringify(payload),
|
|
56
|
+
signal: controller.signal,
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const text = await response.text().catch(() => '');
|
|
60
|
+
throw new Error(`HTTP publish failed: ${response.status} ${response.statusText} - ${text}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
clearTimeout(timeoutId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async subscribe(channel, handler) {
|
|
68
|
+
if (!this.subscribeUrl) {
|
|
69
|
+
throw new Error('HTTP broker: subscribeUrl not configured');
|
|
70
|
+
}
|
|
71
|
+
// Build SSE URL with channel
|
|
72
|
+
const url = new URL(this.subscribeUrl);
|
|
73
|
+
url.searchParams.set('channel', channel);
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
// Start SSE connection
|
|
76
|
+
const connectSSE = async () => {
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(url.toString(), {
|
|
79
|
+
headers: {
|
|
80
|
+
...this.getHeaders(),
|
|
81
|
+
'Accept': 'text/event-stream',
|
|
82
|
+
'Cache-Control': 'no-cache',
|
|
83
|
+
},
|
|
84
|
+
signal: controller.signal,
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`SSE connection failed: ${response.status}`);
|
|
88
|
+
}
|
|
89
|
+
if (!response.body) {
|
|
90
|
+
throw new Error('SSE response has no body');
|
|
91
|
+
}
|
|
92
|
+
const reader = response.body.getReader();
|
|
93
|
+
const decoder = new TextDecoder();
|
|
94
|
+
let buffer = '';
|
|
95
|
+
while (true) {
|
|
96
|
+
const { done, value } = await reader.read();
|
|
97
|
+
if (done)
|
|
98
|
+
break;
|
|
99
|
+
buffer += decoder.decode(value, { stream: true });
|
|
100
|
+
// Parse SSE events
|
|
101
|
+
const lines = buffer.split('\n');
|
|
102
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
103
|
+
let eventType = 'message';
|
|
104
|
+
let eventData = '';
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
if (line.startsWith('event:')) {
|
|
107
|
+
eventType = line.slice(6).trim();
|
|
108
|
+
}
|
|
109
|
+
else if (line.startsWith('data:')) {
|
|
110
|
+
eventData += line.slice(5).trim();
|
|
111
|
+
}
|
|
112
|
+
else if (line === '' && eventData) {
|
|
113
|
+
// End of event, process it
|
|
114
|
+
if (eventType === 'message' || eventType === 'channel') {
|
|
115
|
+
try {
|
|
116
|
+
const message = JSON.parse(eventData);
|
|
117
|
+
if (message.channel === channel || channel === '*') {
|
|
118
|
+
handler(message);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
console.error('Error parsing SSE message:', err);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
eventData = '';
|
|
126
|
+
eventType = 'message';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
if (err.name !== 'AbortError') {
|
|
133
|
+
console.error('SSE connection error:', err);
|
|
134
|
+
// Reconnect after delay (unless aborted)
|
|
135
|
+
if (!controller.signal.aborted) {
|
|
136
|
+
setTimeout(connectSSE, 5000);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
// Track subscription
|
|
142
|
+
if (!this.subscriptions.has(channel)) {
|
|
143
|
+
this.subscriptions.set(channel, { controller, handlers: new Set() });
|
|
144
|
+
// Start SSE connection
|
|
145
|
+
connectSSE();
|
|
146
|
+
}
|
|
147
|
+
this.subscriptions.get(channel).handlers.add(handler);
|
|
148
|
+
const subscription = {
|
|
149
|
+
channel,
|
|
150
|
+
active: true,
|
|
151
|
+
unsubscribe: () => {
|
|
152
|
+
subscription.active = false;
|
|
153
|
+
const sub = this.subscriptions.get(channel);
|
|
154
|
+
if (sub) {
|
|
155
|
+
sub.handlers.delete(handler);
|
|
156
|
+
if (sub.handlers.size === 0) {
|
|
157
|
+
sub.controller.abort();
|
|
158
|
+
this.subscriptions.delete(channel);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
return subscription;
|
|
164
|
+
}
|
|
165
|
+
isConnected() {
|
|
166
|
+
return this.subscriptions.size > 0;
|
|
167
|
+
}
|
|
168
|
+
async connect() {
|
|
169
|
+
// Connection happens lazily on first subscribe
|
|
170
|
+
}
|
|
171
|
+
async disconnect() {
|
|
172
|
+
// Abort all SSE connections
|
|
173
|
+
for (const [, sub] of this.subscriptions) {
|
|
174
|
+
sub.controller.abort();
|
|
175
|
+
}
|
|
176
|
+
this.subscriptions.clear();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Register the broker
|
|
180
|
+
registerBroker('http', (options) => new HttpBroker(options));
|
|
181
|
+
export default HttpBroker;
|
|
182
|
+
//# sourceMappingURL=http-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-broker.js","sourceRoot":"","sources":["../../src/channels/http-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-Based Pub/Sub Module
|
|
3
|
+
*
|
|
4
|
+
* Provides a pluggable architecture for cross-process messaging.
|
|
5
|
+
* Supports multiple backends: local daemon, Redis, HTTP, and more.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { getBroker, ChannelMessage } from '@portel/photon-core/channels';
|
|
10
|
+
*
|
|
11
|
+
* // Publishing
|
|
12
|
+
* const broker = getBroker();
|
|
13
|
+
* await broker.publish({
|
|
14
|
+
* channel: 'board:my-board',
|
|
15
|
+
* event: 'update',
|
|
16
|
+
* data: { taskId: '123', status: 'done' }
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Subscribing
|
|
20
|
+
* const sub = await broker.subscribe('board:my-board', (msg) => {
|
|
21
|
+
* console.log('Received:', msg);
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Later: unsubscribe
|
|
25
|
+
* sub.unsubscribe();
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Configuration (via environment variables):
|
|
29
|
+
* - PHOTON_CHANNEL_BROKER: Explicit broker type (daemon, redis, http, noop)
|
|
30
|
+
* - PHOTON_REDIS_URL: Redis connection URL (enables redis broker)
|
|
31
|
+
* - PHOTON_CHANNEL_HTTP_URL: HTTP webhook URL (enables http broker)
|
|
32
|
+
*/
|
|
33
|
+
export type { ChannelBroker, ChannelMessage, ChannelHandler, Subscription, BrokerConfig, BrokerFactory, } from './types.js';
|
|
34
|
+
export { registerBroker, getRegisteredBrokers, createBroker, detectBroker, getBroker, setBroker, clearBroker, } from './registry.js';
|
|
35
|
+
import './noop-broker.js';
|
|
36
|
+
import './daemon-broker.js';
|
|
37
|
+
import './redis-broker.js';
|
|
38
|
+
import './http-broker.js';
|
|
39
|
+
export { NoOpBroker } from './noop-broker.js';
|
|
40
|
+
export { DaemonBroker, type DaemonBrokerOptions } from './daemon-broker.js';
|
|
41
|
+
export { RedisBroker, type RedisBrokerOptions } from './redis-broker.js';
|
|
42
|
+
export { HttpBroker, type HttpBrokerOptions } from './http-broker.js';
|
|
43
|
+
import type { ChannelMessage } from './types.js';
|
|
44
|
+
/**
|
|
45
|
+
* Publish a message to a channel
|
|
46
|
+
* Convenience function that uses the auto-detected broker
|
|
47
|
+
*/
|
|
48
|
+
export declare function publish(message: ChannelMessage): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Publish a message with simplified parameters
|
|
51
|
+
*/
|
|
52
|
+
export declare function publishEvent(channel: string, event: string, data?: unknown, source?: string): Promise<void>;
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,YAAY,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,eAAe,CAAC;AAIvB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,kBAAkB,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAItE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;GAGG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-Based Pub/Sub Module
|
|
3
|
+
*
|
|
4
|
+
* Provides a pluggable architecture for cross-process messaging.
|
|
5
|
+
* Supports multiple backends: local daemon, Redis, HTTP, and more.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { getBroker, ChannelMessage } from '@portel/photon-core/channels';
|
|
10
|
+
*
|
|
11
|
+
* // Publishing
|
|
12
|
+
* const broker = getBroker();
|
|
13
|
+
* await broker.publish({
|
|
14
|
+
* channel: 'board:my-board',
|
|
15
|
+
* event: 'update',
|
|
16
|
+
* data: { taskId: '123', status: 'done' }
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Subscribing
|
|
20
|
+
* const sub = await broker.subscribe('board:my-board', (msg) => {
|
|
21
|
+
* console.log('Received:', msg);
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Later: unsubscribe
|
|
25
|
+
* sub.unsubscribe();
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Configuration (via environment variables):
|
|
29
|
+
* - PHOTON_CHANNEL_BROKER: Explicit broker type (daemon, redis, http, noop)
|
|
30
|
+
* - PHOTON_REDIS_URL: Redis connection URL (enables redis broker)
|
|
31
|
+
* - PHOTON_CHANNEL_HTTP_URL: HTTP webhook URL (enables http broker)
|
|
32
|
+
*/
|
|
33
|
+
// Export registry functions
|
|
34
|
+
export { registerBroker, getRegisteredBrokers, createBroker, detectBroker, getBroker, setBroker, clearBroker, } from './registry.js';
|
|
35
|
+
// Import broker implementations to register them
|
|
36
|
+
// This has side effects (registers each broker type)
|
|
37
|
+
import './noop-broker.js';
|
|
38
|
+
import './daemon-broker.js';
|
|
39
|
+
import './redis-broker.js';
|
|
40
|
+
import './http-broker.js';
|
|
41
|
+
// Export broker classes for direct use/extension
|
|
42
|
+
export { NoOpBroker } from './noop-broker.js';
|
|
43
|
+
export { DaemonBroker } from './daemon-broker.js';
|
|
44
|
+
export { RedisBroker } from './redis-broker.js';
|
|
45
|
+
export { HttpBroker } from './http-broker.js';
|
|
46
|
+
// Convenience function for simple publishing
|
|
47
|
+
import { getBroker } from './registry.js';
|
|
48
|
+
/**
|
|
49
|
+
* Publish a message to a channel
|
|
50
|
+
* Convenience function that uses the auto-detected broker
|
|
51
|
+
*/
|
|
52
|
+
export async function publish(message) {
|
|
53
|
+
return getBroker().publish(message);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Publish a message with simplified parameters
|
|
57
|
+
*/
|
|
58
|
+
export async function publishEvent(channel, event, data, source) {
|
|
59
|
+
return publish({
|
|
60
|
+
channel,
|
|
61
|
+
event,
|
|
62
|
+
data,
|
|
63
|
+
source,
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAYH,4BAA4B;AAC5B,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,iDAAiD;AACjD,qDAAqD;AACrD,OAAO,kBAAkB,CAAC;AAC1B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,kBAAkB,CAAC;AAE1B,iDAAiD;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,UAAU,EAA0B,MAAM,kBAAkB,CAAC;AAEtE,6CAA6C;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAG1C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAuB;IACnD,OAAO,SAAS,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,KAAa,EACb,IAAc,EACd,MAAe;IAEf,OAAO,OAAO,CAAC;QACb,OAAO;QACP,KAAK;QACL,IAAI;QACJ,MAAM;QACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NoOp Channel Broker
|
|
3
|
+
*
|
|
4
|
+
* Silent fallback broker that does nothing.
|
|
5
|
+
* Used when no external channel broker is configured.
|
|
6
|
+
* Useful for:
|
|
7
|
+
* - Development without channel infrastructure
|
|
8
|
+
* - Testing in isolation
|
|
9
|
+
* - Graceful degradation when broker is unavailable
|
|
10
|
+
*/
|
|
11
|
+
import type { ChannelBroker, ChannelMessage, ChannelHandler, Subscription } from './types.js';
|
|
12
|
+
export declare class NoOpBroker implements ChannelBroker {
|
|
13
|
+
readonly type = "noop";
|
|
14
|
+
publish(_message: ChannelMessage): Promise<void>;
|
|
15
|
+
subscribe(channel: string, _handler: ChannelHandler): Promise<Subscription>;
|
|
16
|
+
isConnected(): boolean;
|
|
17
|
+
connect(): Promise<void>;
|
|
18
|
+
disconnect(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export default NoOpBroker;
|
|
21
|
+
//# sourceMappingURL=noop-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop-broker.d.ts","sourceRoot":"","sources":["../../src/channels/noop-broker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG9F,qBAAa,UAAW,YAAW,aAAa;IAC9C,QAAQ,CAAC,IAAI,UAAU;IAEjB,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IASjF,WAAW,IAAI,OAAO;IAIhB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAGlC;AAKD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NoOp Channel Broker
|
|
3
|
+
*
|
|
4
|
+
* Silent fallback broker that does nothing.
|
|
5
|
+
* Used when no external channel broker is configured.
|
|
6
|
+
* Useful for:
|
|
7
|
+
* - Development without channel infrastructure
|
|
8
|
+
* - Testing in isolation
|
|
9
|
+
* - Graceful degradation when broker is unavailable
|
|
10
|
+
*/
|
|
11
|
+
import { registerBroker } from './registry.js';
|
|
12
|
+
export class NoOpBroker {
|
|
13
|
+
type = 'noop';
|
|
14
|
+
async publish(_message) {
|
|
15
|
+
// Silent no-op - message is discarded
|
|
16
|
+
}
|
|
17
|
+
async subscribe(channel, _handler) {
|
|
18
|
+
// Return a dummy subscription that does nothing
|
|
19
|
+
return {
|
|
20
|
+
channel,
|
|
21
|
+
active: false, // Indicate this is not a real subscription
|
|
22
|
+
unsubscribe: () => { },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
isConnected() {
|
|
26
|
+
return true; // Always "connected" since there's nothing to connect to
|
|
27
|
+
}
|
|
28
|
+
async connect() {
|
|
29
|
+
// No-op
|
|
30
|
+
}
|
|
31
|
+
async disconnect() {
|
|
32
|
+
// No-op
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Register the broker
|
|
36
|
+
registerBroker('noop', () => new NoOpBroker());
|
|
37
|
+
export default NoOpBroker;
|
|
38
|
+
//# sourceMappingURL=noop-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop-broker.js","sourceRoot":"","sources":["../../src/channels/noop-broker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,OAAO,UAAU;IACZ,IAAI,GAAG,MAAM,CAAC;IAEvB,KAAK,CAAC,OAAO,CAAC,QAAwB;QACpC,sCAAsC;IACxC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAAe,EAAE,QAAwB;QACvD,gDAAgD;QAChD,OAAO;YACL,OAAO;YACP,MAAM,EAAE,KAAK,EAAE,2CAA2C;YAC1D,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC;SACtB,CAAC;IACJ,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,CAAC,yDAAyD;IACxE,CAAC;IAED,KAAK,CAAC,OAAO;QACX,QAAQ;IACV,CAAC;IAED,KAAK,CAAC,UAAU;QACd,QAAQ;IACV,CAAC;CACF;AAED,sBAAsB;AACtB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC;AAE/C,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis Channel Broker
|
|
3
|
+
*
|
|
4
|
+
* Uses Redis pub/sub for cross-process and cross-server messaging.
|
|
5
|
+
*
|
|
6
|
+
* Best for:
|
|
7
|
+
* - Multi-server deployments
|
|
8
|
+
* - Cloud environments (AWS, GCP, etc.)
|
|
9
|
+
* - High-throughput scenarios
|
|
10
|
+
*
|
|
11
|
+
* Requires: ioredis package (optional dependency)
|
|
12
|
+
*/
|
|
13
|
+
import type { ChannelBroker, ChannelMessage, ChannelHandler, Subscription } from './types.js';
|
|
14
|
+
export interface RedisBrokerOptions {
|
|
15
|
+
/** Redis connection URL (redis://host:port) */
|
|
16
|
+
url?: string;
|
|
17
|
+
/** Channel prefix for namespacing */
|
|
18
|
+
prefix?: string;
|
|
19
|
+
/** Redis client options (passed to ioredis) */
|
|
20
|
+
clientOptions?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export declare class RedisBroker implements ChannelBroker {
|
|
23
|
+
readonly type = "redis";
|
|
24
|
+
private url;
|
|
25
|
+
private prefix;
|
|
26
|
+
private clientOptions;
|
|
27
|
+
private pubClient;
|
|
28
|
+
private subClient;
|
|
29
|
+
private connected;
|
|
30
|
+
private subscriptions;
|
|
31
|
+
constructor(options?: RedisBrokerOptions);
|
|
32
|
+
private getChannelKey;
|
|
33
|
+
connect(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Simple wildcard pattern matching
|
|
36
|
+
* Supports * for single-segment wildcard
|
|
37
|
+
*/
|
|
38
|
+
private matchPattern;
|
|
39
|
+
disconnect(): Promise<void>;
|
|
40
|
+
publish(message: ChannelMessage): Promise<void>;
|
|
41
|
+
subscribe(channel: string, handler: ChannelHandler): Promise<Subscription>;
|
|
42
|
+
isConnected(): boolean;
|
|
43
|
+
}
|
|
44
|
+
export default RedisBroker;
|
|
45
|
+
//# sourceMappingURL=redis-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-broker.d.ts","sourceRoot":"","sources":["../../src/channels/redis-broker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG9F,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAqBD,qBAAa,WAAY,YAAW,aAAa;IAC/C,QAAQ,CAAC,IAAI,WAAW;IAExB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAA0C;gBAEnD,OAAO,GAAE,kBAAuB;IAM5C,OAAO,CAAC,aAAa;IAIf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2E9B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAed,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB3B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IA6ChF,WAAW,IAAI,OAAO;CAGvB;AAKD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis Channel Broker
|
|
3
|
+
*
|
|
4
|
+
* Uses Redis pub/sub for cross-process and cross-server messaging.
|
|
5
|
+
*
|
|
6
|
+
* Best for:
|
|
7
|
+
* - Multi-server deployments
|
|
8
|
+
* - Cloud environments (AWS, GCP, etc.)
|
|
9
|
+
* - High-throughput scenarios
|
|
10
|
+
*
|
|
11
|
+
* Requires: ioredis package (optional dependency)
|
|
12
|
+
*/
|
|
13
|
+
import { registerBroker } from './registry.js';
|
|
14
|
+
// Lazy-load Redis to make it optional
|
|
15
|
+
let Redis = null;
|
|
16
|
+
async function getRedis() {
|
|
17
|
+
if (!Redis) {
|
|
18
|
+
try {
|
|
19
|
+
// Dynamic import to avoid bundling redis if not used
|
|
20
|
+
// @ts-ignore - ioredis is an optional peer dependency
|
|
21
|
+
const module = await import('ioredis');
|
|
22
|
+
Redis = module.default || module;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error('Redis broker requires ioredis package. Install it with: npm install ioredis');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return Redis;
|
|
29
|
+
}
|
|
30
|
+
export class RedisBroker {
|
|
31
|
+
type = 'redis';
|
|
32
|
+
url;
|
|
33
|
+
prefix;
|
|
34
|
+
clientOptions;
|
|
35
|
+
pubClient = null;
|
|
36
|
+
subClient = null;
|
|
37
|
+
connected = false;
|
|
38
|
+
subscriptions = new Map();
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.url = options.url || process.env.PHOTON_REDIS_URL || process.env.REDIS_URL || 'redis://localhost:6379';
|
|
41
|
+
this.prefix = options.prefix || process.env.PHOTON_REDIS_PREFIX || 'photon:channel:';
|
|
42
|
+
this.clientOptions = options.clientOptions || {};
|
|
43
|
+
}
|
|
44
|
+
getChannelKey(channel) {
|
|
45
|
+
return `${this.prefix}${channel}`;
|
|
46
|
+
}
|
|
47
|
+
async connect() {
|
|
48
|
+
if (this.connected)
|
|
49
|
+
return;
|
|
50
|
+
const RedisClient = await getRedis();
|
|
51
|
+
// Create publish client
|
|
52
|
+
this.pubClient = new RedisClient(this.url, {
|
|
53
|
+
...this.clientOptions,
|
|
54
|
+
lazyConnect: true,
|
|
55
|
+
});
|
|
56
|
+
// Create subscribe client (Redis requires separate connections for pub/sub)
|
|
57
|
+
this.subClient = new RedisClient(this.url, {
|
|
58
|
+
...this.clientOptions,
|
|
59
|
+
lazyConnect: true,
|
|
60
|
+
});
|
|
61
|
+
// Connect both clients
|
|
62
|
+
await Promise.all([
|
|
63
|
+
this.pubClient.connect(),
|
|
64
|
+
this.subClient.connect(),
|
|
65
|
+
]);
|
|
66
|
+
// Handle incoming messages
|
|
67
|
+
this.subClient.on('message', (redisChannel, messageStr) => {
|
|
68
|
+
// Strip prefix to get original channel
|
|
69
|
+
const channel = redisChannel.startsWith(this.prefix)
|
|
70
|
+
? redisChannel.slice(this.prefix.length)
|
|
71
|
+
: redisChannel;
|
|
72
|
+
const handlers = this.subscriptions.get(channel);
|
|
73
|
+
if (!handlers || handlers.size === 0)
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
const message = JSON.parse(messageStr);
|
|
77
|
+
handlers.forEach((handler) => {
|
|
78
|
+
try {
|
|
79
|
+
handler(message);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.error('Error in channel handler:', err);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error('Error parsing channel message:', err);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// Handle pattern messages (for wildcard subscriptions)
|
|
91
|
+
this.subClient.on('pmessage', (pattern, redisChannel, messageStr) => {
|
|
92
|
+
const channel = redisChannel.startsWith(this.prefix)
|
|
93
|
+
? redisChannel.slice(this.prefix.length)
|
|
94
|
+
: redisChannel;
|
|
95
|
+
// Find matching pattern handlers
|
|
96
|
+
for (const [subPattern, handlers] of this.subscriptions) {
|
|
97
|
+
if (this.matchPattern(subPattern, channel)) {
|
|
98
|
+
try {
|
|
99
|
+
const message = JSON.parse(messageStr);
|
|
100
|
+
handlers.forEach((handler) => {
|
|
101
|
+
try {
|
|
102
|
+
handler(message);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
console.error('Error in channel handler:', err);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.error('Error parsing channel message:', err);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
this.connected = true;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Simple wildcard pattern matching
|
|
119
|
+
* Supports * for single-segment wildcard
|
|
120
|
+
*/
|
|
121
|
+
matchPattern(pattern, channel) {
|
|
122
|
+
if (!pattern.includes('*')) {
|
|
123
|
+
return pattern === channel;
|
|
124
|
+
}
|
|
125
|
+
const patternParts = pattern.split(':');
|
|
126
|
+
const channelParts = channel.split(':');
|
|
127
|
+
if (patternParts.length !== channelParts.length) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return patternParts.every((part, i) => part === '*' || part === channelParts[i]);
|
|
131
|
+
}
|
|
132
|
+
async disconnect() {
|
|
133
|
+
if (!this.connected)
|
|
134
|
+
return;
|
|
135
|
+
// Unsubscribe from all channels
|
|
136
|
+
for (const channel of this.subscriptions.keys()) {
|
|
137
|
+
const redisChannel = this.getChannelKey(channel);
|
|
138
|
+
if (channel.includes('*')) {
|
|
139
|
+
await this.subClient.punsubscribe(redisChannel);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
await this.subClient.unsubscribe(redisChannel);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
this.subscriptions.clear();
|
|
146
|
+
// Disconnect clients
|
|
147
|
+
await Promise.all([
|
|
148
|
+
this.pubClient?.quit(),
|
|
149
|
+
this.subClient?.quit(),
|
|
150
|
+
]);
|
|
151
|
+
this.pubClient = null;
|
|
152
|
+
this.subClient = null;
|
|
153
|
+
this.connected = false;
|
|
154
|
+
}
|
|
155
|
+
async publish(message) {
|
|
156
|
+
if (!this.connected) {
|
|
157
|
+
await this.connect();
|
|
158
|
+
}
|
|
159
|
+
const redisChannel = this.getChannelKey(message.channel);
|
|
160
|
+
const payload = {
|
|
161
|
+
...message,
|
|
162
|
+
timestamp: message.timestamp || Date.now(),
|
|
163
|
+
};
|
|
164
|
+
await this.pubClient.publish(redisChannel, JSON.stringify(payload));
|
|
165
|
+
}
|
|
166
|
+
async subscribe(channel, handler) {
|
|
167
|
+
if (!this.connected) {
|
|
168
|
+
await this.connect();
|
|
169
|
+
}
|
|
170
|
+
const redisChannel = this.getChannelKey(channel);
|
|
171
|
+
const isPattern = channel.includes('*');
|
|
172
|
+
// Track handler
|
|
173
|
+
if (!this.subscriptions.has(channel)) {
|
|
174
|
+
this.subscriptions.set(channel, new Set());
|
|
175
|
+
// Subscribe to Redis channel
|
|
176
|
+
if (isPattern) {
|
|
177
|
+
await this.subClient.psubscribe(redisChannel);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
await this.subClient.subscribe(redisChannel);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
this.subscriptions.get(channel).add(handler);
|
|
184
|
+
const subscription = {
|
|
185
|
+
channel,
|
|
186
|
+
active: true,
|
|
187
|
+
unsubscribe: async () => {
|
|
188
|
+
subscription.active = false;
|
|
189
|
+
const handlers = this.subscriptions.get(channel);
|
|
190
|
+
if (handlers) {
|
|
191
|
+
handlers.delete(handler);
|
|
192
|
+
if (handlers.size === 0) {
|
|
193
|
+
this.subscriptions.delete(channel);
|
|
194
|
+
// Unsubscribe from Redis
|
|
195
|
+
if (isPattern) {
|
|
196
|
+
await this.subClient.punsubscribe(redisChannel);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
await this.subClient.unsubscribe(redisChannel);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
return subscription;
|
|
206
|
+
}
|
|
207
|
+
isConnected() {
|
|
208
|
+
return this.connected;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Register the broker
|
|
212
|
+
registerBroker('redis', (options) => new RedisBroker(options));
|
|
213
|
+
export default RedisBroker;
|
|
214
|
+
//# sourceMappingURL=redis-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-broker.js","sourceRoot":"","sources":["../../src/channels/redis-broker.ts"],"names":[],"mappings":"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"}
|