@appstrata/protocol 0.1.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 +106 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +5 -0
- package/dist/client/proxy.d.ts +72 -0
- package/dist/client/proxy.d.ts.map +1 -0
- package/dist/client/proxy.js +446 -0
- package/dist/client/rpc.d.ts +62 -0
- package/dist/client/rpc.d.ts.map +1 -0
- package/dist/client/rpc.js +138 -0
- package/dist/host/index.d.ts +5 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +4 -0
- package/dist/host/message-bridge.d.ts +146 -0
- package/dist/host/message-bridge.d.ts.map +1 -0
- package/dist/host/message-bridge.js +360 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/messages.d.ts +197 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +141 -0
- package/dist/transport/http/client-polling.d.ts +32 -0
- package/dist/transport/http/client-polling.d.ts.map +1 -0
- package/dist/transport/http/client-polling.js +181 -0
- package/dist/transport/http/client-sse.d.ts +30 -0
- package/dist/transport/http/client-sse.d.ts.map +1 -0
- package/dist/transport/http/client-sse.js +155 -0
- package/dist/transport/http/host-manager.d.ts +63 -0
- package/dist/transport/http/host-manager.d.ts.map +1 -0
- package/dist/transport/http/host-manager.js +74 -0
- package/dist/transport/http/host-polling.d.ts +65 -0
- package/dist/transport/http/host-polling.d.ts.map +1 -0
- package/dist/transport/http/host-polling.js +143 -0
- package/dist/transport/http/host-sse.d.ts +56 -0
- package/dist/transport/http/host-sse.d.ts.map +1 -0
- package/dist/transport/http/host-sse.js +149 -0
- package/dist/transport/http/index.d.ts +13 -0
- package/dist/transport/http/index.d.ts.map +1 -0
- package/dist/transport/http/index.js +12 -0
- package/dist/transport/http/types.d.ts +73 -0
- package/dist/transport/http/types.d.ts.map +1 -0
- package/dist/transport/http/types.js +8 -0
- package/dist/transport/index.d.ts +33 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +37 -0
- package/dist/transport/postMessage.d.ts +70 -0
- package/dist/transport/postMessage.d.ts.map +1 -0
- package/dist/transport/postMessage.js +94 -0
- package/dist/transport/relay.d.ts +118 -0
- package/dist/transport/relay.d.ts.map +1 -0
- package/dist/transport/relay.js +216 -0
- package/dist/transport/types.d.ts +30 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +6 -0
- package/package.json +60 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-polling host-side HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the AppStrata Transport interface to an HTTP server using
|
|
5
|
+
* long polling for the server→client channel.
|
|
6
|
+
*
|
|
7
|
+
* ## Usage
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const transport = createHttpPollingHostTransport({ sessionId: "abc" });
|
|
11
|
+
*
|
|
12
|
+
* // When a POST arrives with a message from the client:
|
|
13
|
+
* transport.handleIncomingMessage(req.body);
|
|
14
|
+
*
|
|
15
|
+
* // When a poll request arrives (GET /poll):
|
|
16
|
+
* const messages = await transport.handlePollRequest(30_000);
|
|
17
|
+
* if (messages === null) {
|
|
18
|
+
* res.status(409).json({ error: "Poll already active" });
|
|
19
|
+
* } else {
|
|
20
|
+
* res.json({ messages });
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Pass the transport to an AppHost or MessageBridge — done.
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ## Lifecycles
|
|
27
|
+
*
|
|
28
|
+
* - **Transport**: one per session, long-lived.
|
|
29
|
+
* - **Poll requests**: each one blocks until messages are available or
|
|
30
|
+
* the timeout elapses, then returns all pending messages.
|
|
31
|
+
* - **POST requests**: stateless. Each one delivers a single inbound message.
|
|
32
|
+
*/
|
|
33
|
+
import type { ProtocolMessage } from "../../messages.js";
|
|
34
|
+
import type { HttpHostTransport, HttpHostTransportOptions } from "./types.js";
|
|
35
|
+
/**
|
|
36
|
+
* Long-polling-specific host transport interface.
|
|
37
|
+
*
|
|
38
|
+
* Extends the base `HttpHostTransport` with `handlePollRequest`, which
|
|
39
|
+
* the HTTP server calls when a client polls for new messages.
|
|
40
|
+
*/
|
|
41
|
+
export interface HttpPollingHostTransport extends HttpHostTransport {
|
|
42
|
+
/**
|
|
43
|
+
* Handle a poll request from the client.
|
|
44
|
+
*
|
|
45
|
+
* Returns all pending messages immediately if any are queued.
|
|
46
|
+
* Otherwise blocks until at least one message arrives or `timeoutMs`
|
|
47
|
+
* elapses, whichever comes first.
|
|
48
|
+
*
|
|
49
|
+
* Returns `null` if a poll is already in progress for this transport.
|
|
50
|
+
* The HTTP layer should map this to a 409 Conflict response — two
|
|
51
|
+
* concurrent polls for the same session is a client bug.
|
|
52
|
+
*
|
|
53
|
+
* @param timeoutMs - Maximum time to wait for messages (default 30 000 ms)
|
|
54
|
+
* @returns Array of pending protocol messages (may be empty on timeout),
|
|
55
|
+
* or `null` if another poll is already active
|
|
56
|
+
*/
|
|
57
|
+
handlePollRequest(timeoutMs?: number): Promise<ProtocolMessage[] | null>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a long-polling-based host-side HTTP transport for a single session.
|
|
61
|
+
*
|
|
62
|
+
* See module-level doc for lifecycle and wiring instructions.
|
|
63
|
+
*/
|
|
64
|
+
export declare function createHttpPollingHostTransport(options: HttpHostTransportOptions): HttpPollingHostTransport;
|
|
65
|
+
//# sourceMappingURL=host-polling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-polling.d.ts","sourceRoot":"","sources":["../../../src/transport/http/host-polling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,GAAG,IAAI,CAAC,CAAC;CAC1E;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CA+H1B"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-polling host-side HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the AppStrata Transport interface to an HTTP server using
|
|
5
|
+
* long polling for the server→client channel.
|
|
6
|
+
*
|
|
7
|
+
* ## Usage
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const transport = createHttpPollingHostTransport({ sessionId: "abc" });
|
|
11
|
+
*
|
|
12
|
+
* // When a POST arrives with a message from the client:
|
|
13
|
+
* transport.handleIncomingMessage(req.body);
|
|
14
|
+
*
|
|
15
|
+
* // When a poll request arrives (GET /poll):
|
|
16
|
+
* const messages = await transport.handlePollRequest(30_000);
|
|
17
|
+
* if (messages === null) {
|
|
18
|
+
* res.status(409).json({ error: "Poll already active" });
|
|
19
|
+
* } else {
|
|
20
|
+
* res.json({ messages });
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Pass the transport to an AppHost or MessageBridge — done.
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ## Lifecycles
|
|
27
|
+
*
|
|
28
|
+
* - **Transport**: one per session, long-lived.
|
|
29
|
+
* - **Poll requests**: each one blocks until messages are available or
|
|
30
|
+
* the timeout elapses, then returns all pending messages.
|
|
31
|
+
* - **POST requests**: stateless. Each one delivers a single inbound message.
|
|
32
|
+
*/
|
|
33
|
+
import { isProtocolMessage } from "../../messages.js";
|
|
34
|
+
/**
|
|
35
|
+
* Create a long-polling-based host-side HTTP transport for a single session.
|
|
36
|
+
*
|
|
37
|
+
* See module-level doc for lifecycle and wiring instructions.
|
|
38
|
+
*/
|
|
39
|
+
export function createHttpPollingHostTransport(options) {
|
|
40
|
+
const { sessionId } = options;
|
|
41
|
+
const handlers = new Set();
|
|
42
|
+
let closed = false;
|
|
43
|
+
// Queue outbound messages for the next poll request
|
|
44
|
+
const pendingMessages = [];
|
|
45
|
+
const MAX_PENDING = 100;
|
|
46
|
+
// Resolver for the single currently-waiting poll request (if any).
|
|
47
|
+
// Only one poll should be active per transport at a time — the client
|
|
48
|
+
// sends a poll, waits for the response, then immediately re-polls.
|
|
49
|
+
let activePollResolver = null;
|
|
50
|
+
/**
|
|
51
|
+
* Dispatch message to all handlers.
|
|
52
|
+
*/
|
|
53
|
+
function dispatchMessage(message) {
|
|
54
|
+
for (const handler of [...handlers]) {
|
|
55
|
+
try {
|
|
56
|
+
handler(message);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(`[AppStrata Host] Error in message handler (session ${sessionId}):`, error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Queue a message and wake the waiting poll request if one exists.
|
|
65
|
+
*/
|
|
66
|
+
function queueMessage(message) {
|
|
67
|
+
pendingMessages.push(message);
|
|
68
|
+
if (pendingMessages.length > MAX_PENDING) {
|
|
69
|
+
pendingMessages.shift(); // Remove oldest
|
|
70
|
+
}
|
|
71
|
+
// If a poll request is waiting, resolve it immediately
|
|
72
|
+
if (activePollResolver) {
|
|
73
|
+
const messages = pendingMessages.splice(0);
|
|
74
|
+
const resolve = activePollResolver;
|
|
75
|
+
activePollResolver = null;
|
|
76
|
+
resolve(messages);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
// Standard Transport interface
|
|
81
|
+
send(message) {
|
|
82
|
+
if (closed) {
|
|
83
|
+
console.warn(`[AppStrata Host] Cannot send: transport closed (session ${sessionId})`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
queueMessage(message);
|
|
87
|
+
},
|
|
88
|
+
onMessage(handler) {
|
|
89
|
+
handlers.add(handler);
|
|
90
|
+
return () => handlers.delete(handler);
|
|
91
|
+
},
|
|
92
|
+
close() {
|
|
93
|
+
closed = true;
|
|
94
|
+
handlers.clear();
|
|
95
|
+
pendingMessages.length = 0;
|
|
96
|
+
// Resolve the waiting poll request (if any) with an empty array
|
|
97
|
+
if (activePollResolver) {
|
|
98
|
+
const resolve = activePollResolver;
|
|
99
|
+
activePollResolver = null;
|
|
100
|
+
resolve([]);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
// HTTP-specific extensions
|
|
104
|
+
handleIncomingMessage(message) {
|
|
105
|
+
if (closed)
|
|
106
|
+
return;
|
|
107
|
+
if (!isProtocolMessage(message)) {
|
|
108
|
+
console.warn(`[AppStrata Host] Invalid message received (session ${sessionId})`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
dispatchMessage(message);
|
|
112
|
+
},
|
|
113
|
+
handlePollRequest(timeoutMs = 30000) {
|
|
114
|
+
if (closed) {
|
|
115
|
+
return Promise.resolve([]);
|
|
116
|
+
}
|
|
117
|
+
// Reject concurrent polls — only one should be active at a time.
|
|
118
|
+
// Two concurrent polls means either a client bug or a session
|
|
119
|
+
// collision; the HTTP layer should return 409 Conflict.
|
|
120
|
+
if (activePollResolver) {
|
|
121
|
+
return Promise.resolve(null);
|
|
122
|
+
}
|
|
123
|
+
// If there are already pending messages, return them immediately
|
|
124
|
+
if (pendingMessages.length > 0) {
|
|
125
|
+
const messages = pendingMessages.splice(0);
|
|
126
|
+
return Promise.resolve(messages);
|
|
127
|
+
}
|
|
128
|
+
// Wait for messages or timeout
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
activePollResolver = (messages) => {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
resolve(messages);
|
|
133
|
+
};
|
|
134
|
+
const timer = setTimeout(() => {
|
|
135
|
+
activePollResolver = null;
|
|
136
|
+
// Drain whatever has accumulated (likely empty)
|
|
137
|
+
const messages = pendingMessages.splice(0);
|
|
138
|
+
resolve(messages);
|
|
139
|
+
}, timeoutMs);
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE host-side HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the AppStrata Transport interface to an HTTP server using
|
|
5
|
+
* Server-Sent Events for the server→client channel.
|
|
6
|
+
*
|
|
7
|
+
* ## Usage
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const transport = createHttpSseHostTransport({ sessionId: "abc" });
|
|
11
|
+
*
|
|
12
|
+
* // When a POST arrives with a message from the client:
|
|
13
|
+
* transport.handleIncomingMessage(req.body);
|
|
14
|
+
*
|
|
15
|
+
* // When an SSE connection opens:
|
|
16
|
+
* const cleanup = transport.setSseWriter((data) => res.write(data));
|
|
17
|
+
* req.on("close", cleanup);
|
|
18
|
+
*
|
|
19
|
+
* // Pass the transport to an AppHost or MessageBridge — done.
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ## Lifecycles
|
|
23
|
+
*
|
|
24
|
+
* - **Transport**: one per session, long-lived. Survives SSE disconnects.
|
|
25
|
+
* - **SSE connection**: comes and goes. Messages queue while disconnected,
|
|
26
|
+
* flush automatically when `setSseWriter()` is called again.
|
|
27
|
+
* - **POST requests**: stateless. Each one delivers a single inbound message.
|
|
28
|
+
*/
|
|
29
|
+
import type { HttpHostTransport, HttpHostTransportOptions } from "./types.js";
|
|
30
|
+
/**
|
|
31
|
+
* SSE-specific host transport interface.
|
|
32
|
+
*
|
|
33
|
+
* Extends the base `HttpHostTransport` with methods to wire up an SSE
|
|
34
|
+
* response stream for pushing messages to the client.
|
|
35
|
+
*/
|
|
36
|
+
export interface HttpSseHostTransport extends HttpHostTransport {
|
|
37
|
+
/**
|
|
38
|
+
* Set the SSE writer function for sending messages to the client.
|
|
39
|
+
* Call this when an SSE connection is established.
|
|
40
|
+
*
|
|
41
|
+
* @param writer - Function that writes data to the SSE response stream
|
|
42
|
+
* @returns Cleanup function to call when SSE connection closes
|
|
43
|
+
*/
|
|
44
|
+
setSseWriter(writer: (data: string) => void): () => void;
|
|
45
|
+
/**
|
|
46
|
+
* Check if an SSE connection is active.
|
|
47
|
+
*/
|
|
48
|
+
hasSseConnection(): boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create an SSE-based host-side HTTP transport for a single session.
|
|
52
|
+
*
|
|
53
|
+
* See module-level doc for lifecycle and wiring instructions.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createHttpSseHostTransport(options: HttpHostTransportOptions): HttpSseHostTransport;
|
|
56
|
+
//# sourceMappingURL=host-sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-sse.d.ts","sourceRoot":"","sources":["../../../src/transport/http/host-sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAEzD;;OAEG;IACH,gBAAgB,IAAI,OAAO,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,wBAAwB,GAAG,oBAAoB,CA8HlG"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE host-side HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the AppStrata Transport interface to an HTTP server using
|
|
5
|
+
* Server-Sent Events for the server→client channel.
|
|
6
|
+
*
|
|
7
|
+
* ## Usage
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const transport = createHttpSseHostTransport({ sessionId: "abc" });
|
|
11
|
+
*
|
|
12
|
+
* // When a POST arrives with a message from the client:
|
|
13
|
+
* transport.handleIncomingMessage(req.body);
|
|
14
|
+
*
|
|
15
|
+
* // When an SSE connection opens:
|
|
16
|
+
* const cleanup = transport.setSseWriter((data) => res.write(data));
|
|
17
|
+
* req.on("close", cleanup);
|
|
18
|
+
*
|
|
19
|
+
* // Pass the transport to an AppHost or MessageBridge — done.
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ## Lifecycles
|
|
23
|
+
*
|
|
24
|
+
* - **Transport**: one per session, long-lived. Survives SSE disconnects.
|
|
25
|
+
* - **SSE connection**: comes and goes. Messages queue while disconnected,
|
|
26
|
+
* flush automatically when `setSseWriter()` is called again.
|
|
27
|
+
* - **POST requests**: stateless. Each one delivers a single inbound message.
|
|
28
|
+
*/
|
|
29
|
+
import { isProtocolMessage } from "../../messages.js";
|
|
30
|
+
/**
|
|
31
|
+
* Create an SSE-based host-side HTTP transport for a single session.
|
|
32
|
+
*
|
|
33
|
+
* See module-level doc for lifecycle and wiring instructions.
|
|
34
|
+
*/
|
|
35
|
+
export function createHttpSseHostTransport(options) {
|
|
36
|
+
const { sessionId } = options;
|
|
37
|
+
const handlers = new Set();
|
|
38
|
+
let sseWriter = null;
|
|
39
|
+
let closed = false;
|
|
40
|
+
// Queue messages when no SSE connection is available
|
|
41
|
+
const pendingMessages = [];
|
|
42
|
+
const MAX_PENDING = 100;
|
|
43
|
+
/**
|
|
44
|
+
* Dispatch message to all handlers.
|
|
45
|
+
*/
|
|
46
|
+
function dispatchMessage(message) {
|
|
47
|
+
for (const handler of [...handlers]) {
|
|
48
|
+
try {
|
|
49
|
+
handler(message);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error(`[AppStrata Host] Error in message handler (session ${sessionId}):`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Send message via SSE or queue it.
|
|
58
|
+
*/
|
|
59
|
+
function sendToClient(message) {
|
|
60
|
+
if (sseWriter) {
|
|
61
|
+
const data = `data: ${JSON.stringify(message)}\n\n`;
|
|
62
|
+
try {
|
|
63
|
+
sseWriter(data);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`[AppStrata Host] Error writing to SSE (session ${sessionId}):`, error);
|
|
67
|
+
queueMessage(message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
queueMessage(message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Queue a message for later delivery.
|
|
76
|
+
*/
|
|
77
|
+
function queueMessage(message) {
|
|
78
|
+
pendingMessages.push(message);
|
|
79
|
+
if (pendingMessages.length > MAX_PENDING) {
|
|
80
|
+
pendingMessages.shift(); // Remove oldest
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Flush pending messages to SSE.
|
|
85
|
+
*/
|
|
86
|
+
function flushPending() {
|
|
87
|
+
if (!sseWriter)
|
|
88
|
+
return;
|
|
89
|
+
while (pendingMessages.length > 0) {
|
|
90
|
+
const message = pendingMessages.shift();
|
|
91
|
+
const data = `data: ${JSON.stringify(message)}\n\n`;
|
|
92
|
+
try {
|
|
93
|
+
sseWriter(data);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// Put it back and stop flushing
|
|
97
|
+
pendingMessages.unshift(message);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
// Standard Transport interface
|
|
104
|
+
send(message) {
|
|
105
|
+
if (closed) {
|
|
106
|
+
console.warn(`[AppStrata Host] Cannot send: transport closed (session ${sessionId})`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
sendToClient(message);
|
|
110
|
+
},
|
|
111
|
+
onMessage(handler) {
|
|
112
|
+
handlers.add(handler);
|
|
113
|
+
return () => handlers.delete(handler);
|
|
114
|
+
},
|
|
115
|
+
close() {
|
|
116
|
+
closed = true;
|
|
117
|
+
sseWriter = null;
|
|
118
|
+
handlers.clear();
|
|
119
|
+
pendingMessages.length = 0;
|
|
120
|
+
},
|
|
121
|
+
// HTTP-specific extensions
|
|
122
|
+
handleIncomingMessage(message) {
|
|
123
|
+
if (closed)
|
|
124
|
+
return;
|
|
125
|
+
if (!isProtocolMessage(message)) {
|
|
126
|
+
console.warn(`[AppStrata Host] Invalid message received (session ${sessionId})`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
dispatchMessage(message);
|
|
130
|
+
},
|
|
131
|
+
setSseWriter(writer) {
|
|
132
|
+
if (closed) {
|
|
133
|
+
return () => { }; // No-op cleanup
|
|
134
|
+
}
|
|
135
|
+
sseWriter = writer;
|
|
136
|
+
// Flush any pending messages
|
|
137
|
+
flushPending();
|
|
138
|
+
// Return cleanup function
|
|
139
|
+
return () => {
|
|
140
|
+
if (sseWriter === writer) {
|
|
141
|
+
sseWriter = null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
hasSseConnection() {
|
|
146
|
+
return sseWriter !== null;
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP transport implementations for AppStrata.
|
|
3
|
+
*
|
|
4
|
+
* Provides both SSE and long-polling variants for client and host sides,
|
|
5
|
+
* plus a generic session manager.
|
|
6
|
+
*/
|
|
7
|
+
export * from "./types.js";
|
|
8
|
+
export * from "./host-sse.js";
|
|
9
|
+
export * from "./host-polling.js";
|
|
10
|
+
export * from "./host-manager.js";
|
|
11
|
+
export * from "./client-sse.js";
|
|
12
|
+
export * from "./client-polling.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/transport/http/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP transport implementations for AppStrata.
|
|
3
|
+
*
|
|
4
|
+
* Provides both SSE and long-polling variants for client and host sides,
|
|
5
|
+
* plus a generic session manager.
|
|
6
|
+
*/
|
|
7
|
+
export * from "./types.js";
|
|
8
|
+
export * from "./host-sse.js";
|
|
9
|
+
export * from "./host-polling.js";
|
|
10
|
+
export * from "./host-manager.js";
|
|
11
|
+
export * from "./client-sse.js";
|
|
12
|
+
export * from "./client-polling.js";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for HTTP transport implementations.
|
|
3
|
+
*
|
|
4
|
+
* Defines the base `HttpHostTransport` interface that both SSE and
|
|
5
|
+
* long-polling host transports extend, plus common option types used
|
|
6
|
+
* by client transports.
|
|
7
|
+
*/
|
|
8
|
+
import type { Transport } from "../types.js";
|
|
9
|
+
import type { ProtocolMessage } from "../../messages.js";
|
|
10
|
+
/**
|
|
11
|
+
* Base interface for HTTP host transports.
|
|
12
|
+
*
|
|
13
|
+
* Extends the generic `Transport` with `handleIncomingMessage`, which the
|
|
14
|
+
* HTTP server's POST handler calls to feed client messages into the
|
|
15
|
+
* transport layer. Concrete variants (SSE, long-polling) extend this
|
|
16
|
+
* with their own server→client delivery mechanism.
|
|
17
|
+
*/
|
|
18
|
+
export interface HttpHostTransport extends Transport {
|
|
19
|
+
/**
|
|
20
|
+
* Handle an incoming message from the client (called by HTTP POST handler).
|
|
21
|
+
* This triggers the `onMessage` handlers.
|
|
22
|
+
*
|
|
23
|
+
* @param message - The protocol message received via HTTP POST
|
|
24
|
+
*/
|
|
25
|
+
handleIncomingMessage(message: ProtocolMessage): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Options shared by all host transport factories.
|
|
29
|
+
*/
|
|
30
|
+
export interface HttpHostTransportOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Session ID for this transport instance.
|
|
33
|
+
*/
|
|
34
|
+
sessionId: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Common options shared by all HTTP client transports.
|
|
38
|
+
*/
|
|
39
|
+
export interface HttpClientTransportOptions {
|
|
40
|
+
/**
|
|
41
|
+
* URL for sending messages (HTTP POST).
|
|
42
|
+
* The session ID will be sent as `X-Session-Id` header.
|
|
43
|
+
*/
|
|
44
|
+
sendUrl: string;
|
|
45
|
+
/**
|
|
46
|
+
* URL for receiving messages from the server.
|
|
47
|
+
* - SSE transport: EventSource endpoint
|
|
48
|
+
* - Polling transport: long-poll endpoint
|
|
49
|
+
*
|
|
50
|
+
* The session ID will be appended as `?sessionId=...` query parameter.
|
|
51
|
+
*/
|
|
52
|
+
receiveUrl: string;
|
|
53
|
+
/**
|
|
54
|
+
* Session ID to include in requests.
|
|
55
|
+
* Used by the host to correlate messages with the correct app instance.
|
|
56
|
+
*/
|
|
57
|
+
sessionId: string;
|
|
58
|
+
/**
|
|
59
|
+
* Optional authentication token.
|
|
60
|
+
* If provided, will be sent as `Authorization: Bearer ${token}`.
|
|
61
|
+
*/
|
|
62
|
+
authToken?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Request timeout in milliseconds.
|
|
65
|
+
* @default 30000
|
|
66
|
+
*/
|
|
67
|
+
timeout?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Custom headers to include in all requests.
|
|
70
|
+
*/
|
|
71
|
+
headers?: Record<string, string>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/transport/http/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMzD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IAClD;;;;;OAKG;IACH,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;CACvD;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAElC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport layer for AppStrata Digital Signage SDK.
|
|
3
|
+
*
|
|
4
|
+
* Provides transport implementations for both App and Host sides.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export * from "./postMessage.js";
|
|
8
|
+
export * from "./http/index.js";
|
|
9
|
+
export * from "./relay.js";
|
|
10
|
+
import type { Transport } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Create the prescribed App-side transport per spec section 5.2.
|
|
13
|
+
*
|
|
14
|
+
* Always uses `window.parent.postMessage()` as the send primitive.
|
|
15
|
+
*
|
|
16
|
+
* @param hostOrigin - The host origin from URL params.
|
|
17
|
+
* If provided, used as targetOrigin for postMessage and for validating
|
|
18
|
+
* incoming message origins. If omitted, defaults to '*' (same-party
|
|
19
|
+
* origin profile per spec).
|
|
20
|
+
* @returns Transport instance
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* // Same-party origin (no hostOrigin param)
|
|
25
|
+
* const transport = createAppTransport();
|
|
26
|
+
*
|
|
27
|
+
* // Third-party origin (hostOrigin from URL)
|
|
28
|
+
* const params = new URLSearchParams(window.location.search);
|
|
29
|
+
* const transport = createAppTransport(params.get("hostOrigin") ?? undefined);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function createAppTransport(hostWindow?: Window, hostOrigin?: string): Transport;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAE3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAKtF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport layer for AppStrata Digital Signage SDK.
|
|
3
|
+
*
|
|
4
|
+
* Provides transport implementations for both App and Host sides.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export * from "./postMessage.js";
|
|
8
|
+
export * from "./http/index.js";
|
|
9
|
+
export * from "./relay.js";
|
|
10
|
+
import { createPostMessageTransport } from "./postMessage.js";
|
|
11
|
+
/**
|
|
12
|
+
* Create the prescribed App-side transport per spec section 5.2.
|
|
13
|
+
*
|
|
14
|
+
* Always uses `window.parent.postMessage()` as the send primitive.
|
|
15
|
+
*
|
|
16
|
+
* @param hostOrigin - The host origin from URL params.
|
|
17
|
+
* If provided, used as targetOrigin for postMessage and for validating
|
|
18
|
+
* incoming message origins. If omitted, defaults to '*' (same-party
|
|
19
|
+
* origin profile per spec).
|
|
20
|
+
* @returns Transport instance
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* // Same-party origin (no hostOrigin param)
|
|
25
|
+
* const transport = createAppTransport();
|
|
26
|
+
*
|
|
27
|
+
* // Third-party origin (hostOrigin from URL)
|
|
28
|
+
* const params = new URLSearchParams(window.location.search);
|
|
29
|
+
* const transport = createAppTransport(params.get("hostOrigin") ?? undefined);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function createAppTransport(hostWindow, hostOrigin) {
|
|
33
|
+
return createPostMessageTransport({
|
|
34
|
+
targetWindow: hostWindow ?? window.parent,
|
|
35
|
+
targetOrigin: hostOrigin ?? "*",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostMessage transport implementation.
|
|
3
|
+
*
|
|
4
|
+
* Unified transport for both host and app sides.
|
|
5
|
+
* Works for iframe and same-window scenarios.
|
|
6
|
+
*/
|
|
7
|
+
import type { Transport } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Options for creating a postMessage transport.
|
|
10
|
+
*/
|
|
11
|
+
export interface PostMessageTransportOptions {
|
|
12
|
+
/**
|
|
13
|
+
* The target window to communicate with.
|
|
14
|
+
*
|
|
15
|
+
* Host-side:
|
|
16
|
+
* - For iframe apps: iframe.contentWindow
|
|
17
|
+
* - For same-window apps: window
|
|
18
|
+
*
|
|
19
|
+
* App-side:
|
|
20
|
+
* - For iframe apps: window.parent
|
|
21
|
+
* - For same-window apps: window
|
|
22
|
+
*/
|
|
23
|
+
targetWindow: Window;
|
|
24
|
+
/**
|
|
25
|
+
* Origin to use for postMessage security.
|
|
26
|
+
*
|
|
27
|
+
* @default
|
|
28
|
+
* - For same-window (targetWindow === window): window.location.origin
|
|
29
|
+
* - For cross-window (targetWindow !== window): "*"
|
|
30
|
+
*/
|
|
31
|
+
targetOrigin?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a postMessage transport.
|
|
35
|
+
*
|
|
36
|
+
* Sends messages to the target window, receives messages from it.
|
|
37
|
+
* Automatically determines sensible origin defaults based on whether
|
|
38
|
+
* the target is the same window or a different window.
|
|
39
|
+
*
|
|
40
|
+
* @param options - Transport configuration
|
|
41
|
+
* @returns Transport instance
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // Host-side: iframe app
|
|
46
|
+
* const iframe = document.getElementById("app-frame") as HTMLIFrameElement;
|
|
47
|
+
* const transport = createPostMessageTransport({
|
|
48
|
+
* targetWindow: iframe.contentWindow!,
|
|
49
|
+
* targetOrigin: "https://app.example.com"
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Host-side: same-window app
|
|
53
|
+
* const transport = createPostMessageTransport({
|
|
54
|
+
* targetWindow: window,
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // App-side: in iframe
|
|
58
|
+
* const transport = createPostMessageTransport({
|
|
59
|
+
* targetWindow: window.parent,
|
|
60
|
+
* targetOrigin: "https://player.example.com"
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* // App-side: same-window
|
|
64
|
+
* const transport = createPostMessageTransport({
|
|
65
|
+
* targetWindow: window,
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function createPostMessageTransport(options: PostMessageTransportOptions): Transport;
|
|
70
|
+
//# sourceMappingURL=postMessage.d.ts.map
|