@livequery/core 2.0.104 → 2.0.106
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/LIVEQUERY_SPEC.md +451 -0
- package/README.md +571 -314
- package/build/src/ApiGatewayHandler.d.ts +56 -0
- package/build/src/ApiGatewayHandler.js +210 -0
- package/build/src/ApiGatewayHandler.js.map +1 -0
- package/build/src/ApiGatewayLinker.d.ts +61 -0
- package/build/src/ApiGatewayLinker.js +229 -0
- package/build/src/ApiGatewayLinker.js.map +1 -0
- package/build/src/ApiServiceLinker.d.ts +18 -0
- package/build/src/ApiServiceLinker.js +61 -0
- package/build/src/ApiServiceLinker.js.map +1 -0
- package/build/src/LivequeryContext.d.ts +43 -0
- package/build/src/LivequeryContext.js +2 -0
- package/build/src/LivequeryContext.js.map +1 -0
- package/build/src/LivequeryDatasource.d.ts +8 -0
- package/build/src/LivequeryDatasource.js +2 -0
- package/build/src/LivequeryDatasource.js.map +1 -0
- package/build/src/LivequeryRequestParser.d.ts +13 -0
- package/build/src/LivequeryRequestParser.js +34 -0
- package/build/src/LivequeryRequestParser.js.map +1 -0
- package/build/src/UdpDiscovery.d.ts +26 -0
- package/build/src/UdpDiscovery.js +238 -0
- package/build/src/UdpDiscovery.js.map +1 -0
- package/build/src/WebsocketGateway.d.ts +36 -0
- package/build/src/WebsocketGateway.js +353 -0
- package/build/src/WebsocketGateway.js.map +1 -0
- package/build/src/const.d.ts +8 -0
- package/build/src/const.js +10 -0
- package/build/src/const.js.map +1 -0
- package/build/src/headers.d.ts +9 -0
- package/build/src/headers.js +32 -0
- package/build/src/headers.js.map +1 -0
- package/build/src/helpers/PathHelper.d.ts +11 -0
- package/build/src/helpers/PathHelper.js +41 -0
- package/build/src/helpers/PathHelper.js.map +1 -0
- package/build/src/helpers/hidePrivateFields.d.ts +3 -0
- package/build/src/helpers/hidePrivateFields.js +29 -0
- package/build/src/helpers/hidePrivateFields.js.map +1 -0
- package/build/src/helpers/nodeRequestToWebRequest.d.ts +7 -0
- package/build/src/helpers/nodeRequestToWebRequest.js +17 -0
- package/build/src/helpers/nodeRequestToWebRequest.js.map +1 -0
- package/build/src/helpers/writeWebResponse.d.ts +2 -0
- package/build/src/helpers/writeWebResponse.js +9 -0
- package/build/src/helpers/writeWebResponse.js.map +1 -0
- package/build/src/index.d.ts +9 -0
- package/build/src/index.js +10 -0
- package/build/src/index.js.map +1 -0
- package/build/src/parseLivequeryRequest.d.ts +10 -0
- package/build/src/parseLivequeryRequest.js +17 -0
- package/build/src/parseLivequeryRequest.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +40 -103
- package/dist/LivequeryCollection.d.ts +0 -92
- package/dist/LivequeryCollection.d.ts.map +0 -1
- package/dist/LivequeryCollection.js +0 -231
- package/dist/LivequeryCollection.js.map +0 -1
- package/dist/LivequeryCore.d.ts +0 -67
- package/dist/LivequeryCore.d.ts.map +0 -1
- package/dist/LivequeryCore.js +0 -343
- package/dist/LivequeryCore.js.map +0 -1
- package/dist/LivequeryDocument.d.ts +0 -23
- package/dist/LivequeryDocument.d.ts.map +0 -1
- package/dist/LivequeryDocument.js +0 -22
- package/dist/LivequeryDocument.js.map +0 -1
- package/dist/LivequeryMemoryStorage.d.ts +0 -14
- package/dist/LivequeryMemoryStorage.d.ts.map +0 -1
- package/dist/LivequeryMemoryStorage.js +0 -89
- package/dist/LivequeryMemoryStorage.js.map +0 -1
- package/dist/LivequeryStorge.d.ts +0 -12
- package/dist/LivequeryStorge.d.ts.map +0 -1
- package/dist/LivequeryStorge.js +0 -2
- package/dist/LivequeryStorge.js.map +0 -1
- package/dist/LivequeryTransporter.d.ts +0 -22
- package/dist/LivequeryTransporter.d.ts.map +0 -1
- package/dist/LivequeryTransporter.js +0 -2
- package/dist/LivequeryTransporter.js.map +0 -1
- package/dist/helpers/filterDocs.d.ts +0 -5
- package/dist/helpers/filterDocs.d.ts.map +0 -1
- package/dist/helpers/filterDocs.js +0 -80
- package/dist/helpers/filterDocs.js.map +0 -1
- package/dist/helpers/tryCatch.d.ts +0 -2
- package/dist/helpers/tryCatch.d.ts.map +0 -1
- package/dist/helpers/tryCatch.js +0 -10
- package/dist/helpers/tryCatch.js.map +0 -1
- package/dist/helpers/whenCompleted.d.ts +0 -3
- package/dist/helpers/whenCompleted.d.ts.map +0 -1
- package/dist/helpers/whenCompleted.js +0 -5
- package/dist/helpers/whenCompleted.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/types.d.ts +0 -70
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { IncomingMessage, OutgoingHttpHeaders, ServerResponse } from 'http';
|
|
2
|
+
import { UdpDiscovery, type UdpDiscoveryNode } from './UdpDiscovery.js';
|
|
3
|
+
import { WebsocketGateway } from './WebsocketGateway.js';
|
|
4
|
+
export type RegisterOptions = {
|
|
5
|
+
node_id: string;
|
|
6
|
+
hostname: string;
|
|
7
|
+
port: number;
|
|
8
|
+
paths: Array<{
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
export type ServiceApiMetadata = UdpDiscoveryNode & {
|
|
14
|
+
role: 'service' | 'gateway';
|
|
15
|
+
name: string;
|
|
16
|
+
port: number;
|
|
17
|
+
paths: Array<{
|
|
18
|
+
method: string;
|
|
19
|
+
path: string;
|
|
20
|
+
}>;
|
|
21
|
+
linked: string[];
|
|
22
|
+
target?: string;
|
|
23
|
+
ws?: {
|
|
24
|
+
path: string;
|
|
25
|
+
auth: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type ServiceApiStatus = {
|
|
29
|
+
id: string;
|
|
30
|
+
online: boolean;
|
|
31
|
+
metadata?: ServiceApiMetadata;
|
|
32
|
+
} | {
|
|
33
|
+
id: string;
|
|
34
|
+
online: false;
|
|
35
|
+
};
|
|
36
|
+
export type ApiGatewayOptions = {
|
|
37
|
+
ws?: WebsocketGateway;
|
|
38
|
+
discovery?: UdpDiscovery<ServiceApiMetadata>;
|
|
39
|
+
node_id?: string;
|
|
40
|
+
};
|
|
41
|
+
export declare class ApiGatewayHandler {
|
|
42
|
+
#private;
|
|
43
|
+
private options;
|
|
44
|
+
readonly restGateway: this;
|
|
45
|
+
constructor(options: ApiGatewayOptions);
|
|
46
|
+
register({ node_id, hostname, port, paths }: RegisterOptions): void;
|
|
47
|
+
deregister(node_id: string): void;
|
|
48
|
+
fetch(request: Request): Promise<Response>;
|
|
49
|
+
fetch(req: IncomingMessage & {
|
|
50
|
+
url: string;
|
|
51
|
+
method: string;
|
|
52
|
+
rawBody?: Buffer;
|
|
53
|
+
}, res: ServerResponse, extraHeaders?: OutgoingHttpHeaders): Promise<void>;
|
|
54
|
+
fetchRequest(request: Request): Promise<Response>;
|
|
55
|
+
close(): void;
|
|
56
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { API_GATEWAY_NAMESPACE, LIVEQUERY_API_GATEWAY_DEBUG, LIVEQUERY_MAGIC_KEY, WEBSOCKET_PATH, } from './const.js';
|
|
3
|
+
import { UdpDiscovery } from './UdpDiscovery.js';
|
|
4
|
+
import { nodeRequestToWebRequest } from './helpers/nodeRequestToWebRequest.js';
|
|
5
|
+
import { writeWebResponse } from './helpers/writeWebResponse.js';
|
|
6
|
+
function createNode() {
|
|
7
|
+
return { children: {}, paramPrefixes: [], methods: {} };
|
|
8
|
+
}
|
|
9
|
+
function normalizeSegment(seg) {
|
|
10
|
+
return seg.includes(':') ? seg.split(':')[0] + ':' : seg;
|
|
11
|
+
}
|
|
12
|
+
function isSameServiceDefinition(a, b) {
|
|
13
|
+
return a.port === b.port
|
|
14
|
+
&& JSON.stringify(a.paths) === JSON.stringify(b.paths)
|
|
15
|
+
&& a.ws?.path === b.ws?.path
|
|
16
|
+
&& a.ws?.auth === b.ws?.auth;
|
|
17
|
+
}
|
|
18
|
+
export class ApiGatewayHandler {
|
|
19
|
+
options;
|
|
20
|
+
restGateway = this;
|
|
21
|
+
#root = createNode();
|
|
22
|
+
#nodeId;
|
|
23
|
+
#lws;
|
|
24
|
+
#discovery;
|
|
25
|
+
#discoverySubscription;
|
|
26
|
+
#services = new Map();
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.#nodeId = options.node_id ?? randomUUID();
|
|
30
|
+
this.#lws = options.ws;
|
|
31
|
+
this.#discovery = options.discovery ?? new UdpDiscovery({ key: LIVEQUERY_MAGIC_KEY });
|
|
32
|
+
this.#discoverySubscription = this.#discovery.subscribe(metadata => {
|
|
33
|
+
if (metadata.role !== 'service')
|
|
34
|
+
return;
|
|
35
|
+
if (metadata.namespace !== API_GATEWAY_NAMESPACE)
|
|
36
|
+
return;
|
|
37
|
+
if (metadata.node_id === this.#nodeId)
|
|
38
|
+
return;
|
|
39
|
+
const existing = this.#services.get(metadata.node_id);
|
|
40
|
+
if (existing) {
|
|
41
|
+
if (metadata.version <= existing.metadata.version)
|
|
42
|
+
return;
|
|
43
|
+
if (isSameServiceDefinition(existing.metadata, metadata)) {
|
|
44
|
+
existing.metadata = { ...metadata, host: existing.host };
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.#removeService(metadata.node_id, false);
|
|
48
|
+
}
|
|
49
|
+
this.#join(metadata);
|
|
50
|
+
});
|
|
51
|
+
this.#discovery.broadcast(this.#metadata()).catch(e => console.error(e));
|
|
52
|
+
}
|
|
53
|
+
register({ node_id, hostname, port, paths }) {
|
|
54
|
+
const uri = `${hostname}:${port}`;
|
|
55
|
+
for (const { method, path } of paths) {
|
|
56
|
+
const segments = path.split('/').filter(Boolean).map(normalizeSegment);
|
|
57
|
+
let node = this.#root;
|
|
58
|
+
for (const seg of segments) {
|
|
59
|
+
if (!node.children[seg])
|
|
60
|
+
node.children[seg] = createNode();
|
|
61
|
+
if (seg !== ':' && seg.endsWith(':')) {
|
|
62
|
+
const prefix = seg.slice(0, -1);
|
|
63
|
+
if (!node.paramPrefixes.includes(prefix))
|
|
64
|
+
node.paramPrefixes.push(prefix);
|
|
65
|
+
}
|
|
66
|
+
node = node.children[seg];
|
|
67
|
+
}
|
|
68
|
+
const METHOD = method.toUpperCase();
|
|
69
|
+
if (!node.methods[METHOD])
|
|
70
|
+
node.methods[METHOD] = { hosts: [], rr_index: 0 };
|
|
71
|
+
const exists = node.methods[METHOD].hosts.some(host => host.node_id === node_id && host.uri === uri);
|
|
72
|
+
if (!exists)
|
|
73
|
+
node.methods[METHOD].hosts.push({ node_id, uri });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
deregister(node_id) {
|
|
77
|
+
const queue = [this.#root];
|
|
78
|
+
while (queue.length > 0) {
|
|
79
|
+
const node = queue.shift();
|
|
80
|
+
for (const entry of Object.values(node.methods)) {
|
|
81
|
+
entry.hosts = entry.hosts.filter(host => host.node_id !== node_id);
|
|
82
|
+
}
|
|
83
|
+
queue.push(...Object.values(node.children));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async fetch(requestOrReq, res, extraHeaders) {
|
|
87
|
+
if (requestOrReq instanceof Request) {
|
|
88
|
+
const client_id = requestOrReq.headers.get('x-lcid') ?? requestOrReq.headers.get('socket_id');
|
|
89
|
+
const extraHeaders = client_id && this.options.ws ? {
|
|
90
|
+
'x-lcid': client_id,
|
|
91
|
+
'x-lgid': requestOrReq.headers.get('x-lgid') || this.options.ws?.id || this.#nodeId,
|
|
92
|
+
} : {};
|
|
93
|
+
return this.#fetchRequest(requestOrReq, extraHeaders);
|
|
94
|
+
}
|
|
95
|
+
if (!res)
|
|
96
|
+
throw new Error('ServerResponse is required for Node.js requests');
|
|
97
|
+
const response = await this.#fetchRequest(nodeRequestToWebRequest(requestOrReq, extraHeaders), extraHeaders);
|
|
98
|
+
await writeWebResponse(res, response);
|
|
99
|
+
}
|
|
100
|
+
fetchRequest(request) {
|
|
101
|
+
return this.fetch(request);
|
|
102
|
+
}
|
|
103
|
+
close() {
|
|
104
|
+
this.#discoverySubscription.unsubscribe();
|
|
105
|
+
for (const { subscription } of this.#services.values()) {
|
|
106
|
+
subscription?.unsubscribe();
|
|
107
|
+
}
|
|
108
|
+
this.#services.clear();
|
|
109
|
+
this.#discovery.close();
|
|
110
|
+
}
|
|
111
|
+
async #fetchRequest(request, extraHeaders) {
|
|
112
|
+
const url = new URL(request.url);
|
|
113
|
+
const target = this.#resolve(url.pathname, request.method);
|
|
114
|
+
if (target === undefined) {
|
|
115
|
+
return Response.json({ error: { status: 404, code: 'API_NOT_FOUND' } }, { status: 404 });
|
|
116
|
+
}
|
|
117
|
+
if (target === null) {
|
|
118
|
+
return Response.json({ error: { status: 503, code: 'API_OFFLINE' } }, { status: 503 });
|
|
119
|
+
}
|
|
120
|
+
const headers = new Headers(request.headers);
|
|
121
|
+
headers.delete('content-length');
|
|
122
|
+
headers.delete('host');
|
|
123
|
+
if (extraHeaders) {
|
|
124
|
+
new Headers(extraHeaders).forEach((value, key) => headers.set(key, value));
|
|
125
|
+
}
|
|
126
|
+
const body = request.method === 'GET' || request.method === 'HEAD' ? undefined : request.body;
|
|
127
|
+
try {
|
|
128
|
+
return await globalThis.fetch(`http://${target.uri}${url.pathname}${url.search}`, {
|
|
129
|
+
method: request.method,
|
|
130
|
+
headers,
|
|
131
|
+
body,
|
|
132
|
+
duplex: 'half',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
this.deregister(target.node_id);
|
|
137
|
+
return Response.json({ error: { status: 502, code: 'SERVICE_API_OFFLINE' } }, { status: 502 });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
#matchChild(node, segment) {
|
|
141
|
+
if (node.children[segment])
|
|
142
|
+
return node.children[segment];
|
|
143
|
+
for (const prefix of node.paramPrefixes) {
|
|
144
|
+
if (segment.startsWith(prefix))
|
|
145
|
+
return node.children[prefix + ':'];
|
|
146
|
+
}
|
|
147
|
+
return node.children[':'];
|
|
148
|
+
}
|
|
149
|
+
#resolve(path, method) {
|
|
150
|
+
const segments = path.split('?')[0].split('/').filter(Boolean);
|
|
151
|
+
let node = this.#root;
|
|
152
|
+
for (const segment of segments) {
|
|
153
|
+
node = this.#matchChild(node, segment);
|
|
154
|
+
if (!node)
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const entry = node.methods[method.toUpperCase()];
|
|
158
|
+
if (!entry)
|
|
159
|
+
return undefined;
|
|
160
|
+
if (entry.hosts.length === 0)
|
|
161
|
+
return null;
|
|
162
|
+
const host = entry.hosts[entry.rr_index % entry.hosts.length];
|
|
163
|
+
entry.rr_index++;
|
|
164
|
+
return host;
|
|
165
|
+
}
|
|
166
|
+
#join(metadata) {
|
|
167
|
+
const { port, node_id, name, paths, ws } = metadata;
|
|
168
|
+
const host = metadata.host ?? '';
|
|
169
|
+
const subscription = ws
|
|
170
|
+
? this.#lws?.connect(`ws://${host}:${port}${ws.path}`, ws.auth, () => this.#disconnect(node_id))
|
|
171
|
+
: undefined;
|
|
172
|
+
this.#services.set(node_id, { metadata, subscription, host });
|
|
173
|
+
this.register({ node_id, hostname: host, port, paths: paths ?? [] });
|
|
174
|
+
LIVEQUERY_API_GATEWAY_DEBUG && console.info(`[${new Date().toLocaleString()}] Service API online: ${name} at ${host}:${port}`);
|
|
175
|
+
}
|
|
176
|
+
#disconnect(id) {
|
|
177
|
+
this.#removeService(id, true);
|
|
178
|
+
}
|
|
179
|
+
#removeService(id, logOffline) {
|
|
180
|
+
const service = this.#services.get(id);
|
|
181
|
+
if (!service)
|
|
182
|
+
return;
|
|
183
|
+
const { metadata: { name, port }, subscription, host } = service;
|
|
184
|
+
subscription?.unsubscribe();
|
|
185
|
+
this.#services.delete(id);
|
|
186
|
+
this.deregister(id);
|
|
187
|
+
logOffline && LIVEQUERY_API_GATEWAY_DEBUG && console.error(`[${new Date().toLocaleString()}] Service API OFFLINE: ${name} at ${host}:${port}`);
|
|
188
|
+
}
|
|
189
|
+
#metadata() {
|
|
190
|
+
const metadata = {
|
|
191
|
+
name: 'API gateway',
|
|
192
|
+
paths: [],
|
|
193
|
+
port: 0,
|
|
194
|
+
role: 'gateway',
|
|
195
|
+
linked: [...this.#services.keys()],
|
|
196
|
+
host: '',
|
|
197
|
+
node_id: this.#nodeId,
|
|
198
|
+
namespace: API_GATEWAY_NAMESPACE,
|
|
199
|
+
version: Date.now(),
|
|
200
|
+
};
|
|
201
|
+
if (this.#lws) {
|
|
202
|
+
metadata.ws = {
|
|
203
|
+
auth: this.#lws.auth,
|
|
204
|
+
path: WEBSOCKET_PATH,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return metadata;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=ApiGatewayHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiGatewayHandler.js","sourceRoot":"","sources":["../../src/ApiGatewayHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { IncomingMessage, OutgoingHttpHeaders, ServerResponse } from 'http';
|
|
2
|
+
import type { LivequeryHandler } from './LivequeryContext.js';
|
|
3
|
+
import { UdpDiscovery, type UdpDiscoveryNode } from './UdpDiscovery.js';
|
|
4
|
+
import { WebsocketGateway } from './WebsocketGateway.js';
|
|
5
|
+
export type RegisterOptions = {
|
|
6
|
+
node_id: string;
|
|
7
|
+
hostname: string;
|
|
8
|
+
port: number;
|
|
9
|
+
paths: Array<{
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
export type ServiceApiMetadata = UdpDiscoveryNode & {
|
|
15
|
+
role: 'service' | 'gateway';
|
|
16
|
+
name: string;
|
|
17
|
+
port: number;
|
|
18
|
+
paths: Array<{
|
|
19
|
+
method: string;
|
|
20
|
+
path: string;
|
|
21
|
+
}>;
|
|
22
|
+
linked: string[];
|
|
23
|
+
target?: string;
|
|
24
|
+
ws?: {
|
|
25
|
+
path: string;
|
|
26
|
+
auth: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type ServiceApiStatus = {
|
|
30
|
+
id: string;
|
|
31
|
+
online: boolean;
|
|
32
|
+
metadata?: ServiceApiMetadata;
|
|
33
|
+
} | {
|
|
34
|
+
id: string;
|
|
35
|
+
online: false;
|
|
36
|
+
};
|
|
37
|
+
export type ApiGatewayOptions<Handler extends LivequeryHandler = LivequeryHandler> = {
|
|
38
|
+
websocketGateway?: WebsocketGateway;
|
|
39
|
+
discovery?: UdpDiscovery<ServiceApiMetadata>;
|
|
40
|
+
nodeId?: string;
|
|
41
|
+
restGateway?: Handler;
|
|
42
|
+
};
|
|
43
|
+
export declare class ApiGateway<Handler extends LivequeryHandler = LivequeryHandler> {
|
|
44
|
+
#private;
|
|
45
|
+
readonly restGateway: this;
|
|
46
|
+
constructor(options?: ApiGatewayOptions<Handler>);
|
|
47
|
+
register({ node_id, hostname, port, paths }: RegisterOptions): void;
|
|
48
|
+
deregister(node_id: string): void;
|
|
49
|
+
fetch(request: Request): Promise<Response>;
|
|
50
|
+
fetch(req: IncomingMessage & {
|
|
51
|
+
url: string;
|
|
52
|
+
method: string;
|
|
53
|
+
rawBody?: Buffer;
|
|
54
|
+
}, res: ServerResponse, extraHeaders?: OutgoingHttpHeaders): Promise<void>;
|
|
55
|
+
fetchRequest(request: Request): Promise<Response>;
|
|
56
|
+
close(): void;
|
|
57
|
+
}
|
|
58
|
+
export { ApiGateway as ApiGatewayLinker };
|
|
59
|
+
export { ApiGateway as FetchApiGateway };
|
|
60
|
+
export { ApiGateway as SimpleApiGateway };
|
|
61
|
+
export type ApiGatewayLinkerOptions<Handler extends LivequeryHandler = LivequeryHandler> = ApiGatewayOptions<Handler>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { API_GATEWAY_NAMESPACE, LIVEQUERY_API_GATEWAY_DEBUG, LIVEQUERY_MAGIC_KEY, WEBSOCKET_PATH, } from './const.js';
|
|
3
|
+
import { createRealtimeForwardHeaders } from './headers.js';
|
|
4
|
+
import { UdpDiscovery } from './UdpDiscovery.js';
|
|
5
|
+
function createNode() {
|
|
6
|
+
return { children: {}, paramPrefixes: [], methods: {} };
|
|
7
|
+
}
|
|
8
|
+
function normalizeSegment(seg) {
|
|
9
|
+
return seg.includes(':') ? seg.split(':')[0] + ':' : seg;
|
|
10
|
+
}
|
|
11
|
+
function isSameServiceDefinition(a, b) {
|
|
12
|
+
return a.port === b.port
|
|
13
|
+
&& JSON.stringify(a.paths) === JSON.stringify(b.paths)
|
|
14
|
+
&& a.ws?.path === b.ws?.path
|
|
15
|
+
&& a.ws?.auth === b.ws?.auth;
|
|
16
|
+
}
|
|
17
|
+
export class ApiGateway {
|
|
18
|
+
restGateway = this;
|
|
19
|
+
#root = createNode();
|
|
20
|
+
#nodeId;
|
|
21
|
+
#lws;
|
|
22
|
+
#discovery;
|
|
23
|
+
#discoverySubscription;
|
|
24
|
+
#services = new Map();
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this.#nodeId = options.nodeId ?? randomUUID();
|
|
27
|
+
this.#lws = options.websocketGateway;
|
|
28
|
+
this.#discovery = options.discovery ?? new UdpDiscovery({ key: LIVEQUERY_MAGIC_KEY });
|
|
29
|
+
this.#discoverySubscription = this.#discovery.subscribe(metadata => {
|
|
30
|
+
if (metadata.role !== 'service')
|
|
31
|
+
return;
|
|
32
|
+
if (metadata.namespace !== API_GATEWAY_NAMESPACE)
|
|
33
|
+
return;
|
|
34
|
+
if (metadata.node_id === this.#nodeId)
|
|
35
|
+
return;
|
|
36
|
+
const existing = this.#services.get(metadata.node_id);
|
|
37
|
+
if (existing) {
|
|
38
|
+
if (metadata.version <= existing.metadata.version)
|
|
39
|
+
return;
|
|
40
|
+
if (isSameServiceDefinition(existing.metadata, metadata)) {
|
|
41
|
+
existing.metadata = { ...metadata, host: existing.host };
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.#removeService(metadata.node_id, false);
|
|
45
|
+
}
|
|
46
|
+
this.#join(metadata);
|
|
47
|
+
});
|
|
48
|
+
this.#discovery.broadcast(this.#metadata()).catch(e => console.error(e));
|
|
49
|
+
}
|
|
50
|
+
register({ node_id, hostname, port, paths }) {
|
|
51
|
+
const uri = `${hostname}:${port}`;
|
|
52
|
+
for (const { method, path } of paths) {
|
|
53
|
+
const segments = path.split('/').filter(Boolean).map(normalizeSegment);
|
|
54
|
+
let node = this.#root;
|
|
55
|
+
for (const seg of segments) {
|
|
56
|
+
if (!node.children[seg])
|
|
57
|
+
node.children[seg] = createNode();
|
|
58
|
+
if (seg !== ':' && seg.endsWith(':')) {
|
|
59
|
+
const prefix = seg.slice(0, -1);
|
|
60
|
+
if (!node.paramPrefixes.includes(prefix))
|
|
61
|
+
node.paramPrefixes.push(prefix);
|
|
62
|
+
}
|
|
63
|
+
node = node.children[seg];
|
|
64
|
+
}
|
|
65
|
+
const METHOD = method.toUpperCase();
|
|
66
|
+
if (!node.methods[METHOD])
|
|
67
|
+
node.methods[METHOD] = { hosts: [], rr_index: 0 };
|
|
68
|
+
const exists = node.methods[METHOD].hosts.some(host => host.node_id === node_id && host.uri === uri);
|
|
69
|
+
if (!exists)
|
|
70
|
+
node.methods[METHOD].hosts.push({ node_id, uri });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
deregister(node_id) {
|
|
74
|
+
const queue = [this.#root];
|
|
75
|
+
while (queue.length > 0) {
|
|
76
|
+
const node = queue.shift();
|
|
77
|
+
for (const entry of Object.values(node.methods)) {
|
|
78
|
+
entry.hosts = entry.hosts.filter(host => host.node_id !== node_id);
|
|
79
|
+
}
|
|
80
|
+
queue.push(...Object.values(node.children));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async fetch(requestOrReq, res, extraHeaders) {
|
|
84
|
+
if (requestOrReq instanceof Request) {
|
|
85
|
+
return this.#fetchRequest(requestOrReq, createRealtimeForwardHeaders(requestOrReq, this.#lws?.id));
|
|
86
|
+
}
|
|
87
|
+
if (!res)
|
|
88
|
+
throw new Error('ServerResponse is required for Node.js requests');
|
|
89
|
+
const response = await this.#fetchRequest(nodeRequestToWebRequest(requestOrReq, extraHeaders), extraHeaders);
|
|
90
|
+
await writeWebResponse(res, response);
|
|
91
|
+
}
|
|
92
|
+
fetchRequest(request) {
|
|
93
|
+
return this.fetch(request);
|
|
94
|
+
}
|
|
95
|
+
close() {
|
|
96
|
+
this.#discoverySubscription.unsubscribe();
|
|
97
|
+
for (const { subscription } of this.#services.values()) {
|
|
98
|
+
subscription?.unsubscribe();
|
|
99
|
+
}
|
|
100
|
+
this.#services.clear();
|
|
101
|
+
this.#discovery.close();
|
|
102
|
+
}
|
|
103
|
+
async #fetchRequest(request, extraHeaders) {
|
|
104
|
+
const url = new URL(request.url);
|
|
105
|
+
const target = this.#resolve(url.pathname, request.method);
|
|
106
|
+
if (target === undefined) {
|
|
107
|
+
return Response.json({ error: { status: 404, code: 'API_NOT_FOUND' } }, { status: 404 });
|
|
108
|
+
}
|
|
109
|
+
if (target === null) {
|
|
110
|
+
return Response.json({ error: { status: 503, code: 'API_OFFLINE' } }, { status: 503 });
|
|
111
|
+
}
|
|
112
|
+
const headers = new Headers(request.headers);
|
|
113
|
+
headers.delete('content-length');
|
|
114
|
+
headers.delete('host');
|
|
115
|
+
if (extraHeaders) {
|
|
116
|
+
new Headers(extraHeaders).forEach((value, key) => headers.set(key, value));
|
|
117
|
+
}
|
|
118
|
+
const body = request.method === 'GET' || request.method === 'HEAD' ? undefined : request.body;
|
|
119
|
+
try {
|
|
120
|
+
return await globalThis.fetch(`http://${target.uri}${url.pathname}${url.search}`, {
|
|
121
|
+
method: request.method,
|
|
122
|
+
headers,
|
|
123
|
+
body,
|
|
124
|
+
duplex: 'half',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
this.deregister(target.node_id);
|
|
129
|
+
return Response.json({ error: { status: 502, code: 'SERVICE_API_OFFLINE' } }, { status: 502 });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
#matchChild(node, segment) {
|
|
133
|
+
if (node.children[segment])
|
|
134
|
+
return node.children[segment];
|
|
135
|
+
for (const prefix of node.paramPrefixes) {
|
|
136
|
+
if (segment.startsWith(prefix))
|
|
137
|
+
return node.children[prefix + ':'];
|
|
138
|
+
}
|
|
139
|
+
return node.children[':'];
|
|
140
|
+
}
|
|
141
|
+
#resolve(path, method) {
|
|
142
|
+
const segments = path.split('?')[0].split('/').filter(Boolean);
|
|
143
|
+
let node = this.#root;
|
|
144
|
+
for (const segment of segments) {
|
|
145
|
+
node = this.#matchChild(node, segment);
|
|
146
|
+
if (!node)
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const entry = node.methods[method.toUpperCase()];
|
|
150
|
+
if (!entry)
|
|
151
|
+
return undefined;
|
|
152
|
+
if (entry.hosts.length === 0)
|
|
153
|
+
return null;
|
|
154
|
+
const host = entry.hosts[entry.rr_index % entry.hosts.length];
|
|
155
|
+
entry.rr_index++;
|
|
156
|
+
return host;
|
|
157
|
+
}
|
|
158
|
+
#join(metadata) {
|
|
159
|
+
const { port, node_id, name, paths, ws } = metadata;
|
|
160
|
+
const host = metadata.host ?? '';
|
|
161
|
+
const subscription = ws
|
|
162
|
+
? this.#lws?.connect(`ws://${host}:${port}${ws.path}`, ws.auth, () => this.#disconnect(node_id))
|
|
163
|
+
: undefined;
|
|
164
|
+
this.#services.set(node_id, { metadata, subscription, host });
|
|
165
|
+
this.register({ node_id, hostname: host, port, paths: paths ?? [] });
|
|
166
|
+
LIVEQUERY_API_GATEWAY_DEBUG && console.info(`[${new Date().toLocaleString()}] Service API online: ${name} at ${host}:${port}`);
|
|
167
|
+
}
|
|
168
|
+
#disconnect(id) {
|
|
169
|
+
this.#removeService(id, true);
|
|
170
|
+
}
|
|
171
|
+
#removeService(id, logOffline) {
|
|
172
|
+
const service = this.#services.get(id);
|
|
173
|
+
if (!service)
|
|
174
|
+
return;
|
|
175
|
+
const { metadata: { name, port }, subscription, host } = service;
|
|
176
|
+
subscription?.unsubscribe();
|
|
177
|
+
this.#services.delete(id);
|
|
178
|
+
this.deregister(id);
|
|
179
|
+
logOffline && LIVEQUERY_API_GATEWAY_DEBUG && console.error(`[${new Date().toLocaleString()}] Service API OFFLINE: ${name} at ${host}:${port}`);
|
|
180
|
+
}
|
|
181
|
+
#metadata() {
|
|
182
|
+
const metadata = {
|
|
183
|
+
name: 'API gateway',
|
|
184
|
+
paths: [],
|
|
185
|
+
port: 0,
|
|
186
|
+
role: 'gateway',
|
|
187
|
+
linked: [...this.#services.keys()],
|
|
188
|
+
host: '',
|
|
189
|
+
node_id: this.#nodeId,
|
|
190
|
+
namespace: API_GATEWAY_NAMESPACE,
|
|
191
|
+
version: Date.now(),
|
|
192
|
+
};
|
|
193
|
+
if (this.#lws) {
|
|
194
|
+
metadata.ws = {
|
|
195
|
+
auth: this.#lws.auth,
|
|
196
|
+
path: WEBSOCKET_PATH,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return metadata;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function nodeRequestToWebRequest(req, extraHeaders) {
|
|
203
|
+
const host = req.headers.host ?? '127.0.0.1';
|
|
204
|
+
const headers = new Headers(req.headers);
|
|
205
|
+
if (extraHeaders) {
|
|
206
|
+
new Headers(extraHeaders).forEach((value, key) => headers.set(key, value));
|
|
207
|
+
}
|
|
208
|
+
const body = req.method === 'GET' || req.method === 'HEAD'
|
|
209
|
+
? undefined
|
|
210
|
+
: req.rawBody ? new Uint8Array(req.rawBody) : undefined;
|
|
211
|
+
return new Request(`http://${host}${req.url}`, {
|
|
212
|
+
method: req.method,
|
|
213
|
+
headers,
|
|
214
|
+
body,
|
|
215
|
+
duplex: body ? 'half' : undefined,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function writeWebResponse(res, response) {
|
|
219
|
+
const headers = {};
|
|
220
|
+
response.headers.forEach((value, key) => {
|
|
221
|
+
headers[key] = value;
|
|
222
|
+
});
|
|
223
|
+
res.writeHead(response.status, headers);
|
|
224
|
+
res.end(Buffer.from(await response.arrayBuffer()));
|
|
225
|
+
}
|
|
226
|
+
export { ApiGateway as ApiGatewayLinker };
|
|
227
|
+
export { ApiGateway as FetchApiGateway };
|
|
228
|
+
export { ApiGateway as SimpleApiGateway };
|
|
229
|
+
//# sourceMappingURL=ApiGatewayLinker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiGatewayLinker.js","sourceRoot":"","sources":["../../src/ApiGatewayLinker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UdpDiscovery } from './UdpDiscovery.js';
|
|
2
|
+
import { WebsocketGateway } from './WebsocketGateway.js';
|
|
3
|
+
import { ServiceApiMetadata } from './ApiGatewayHandler.js';
|
|
4
|
+
export type ApiServiceLinkerOptions = {
|
|
5
|
+
paths: Array<{
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
}>;
|
|
9
|
+
ws?: WebsocketGateway;
|
|
10
|
+
discovery?: UdpDiscovery<ServiceApiMetadata>;
|
|
11
|
+
node_id?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare class ApiServiceLinker {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(options: ApiServiceLinkerOptions);
|
|
16
|
+
start(name: string, port: number): void;
|
|
17
|
+
close(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { API_GATEWAY_NAMESPACE, LIVEQUERY_MAGIC_KEY, WEBSOCKET_PATH, } from './const.js';
|
|
3
|
+
import { UdpDiscovery } from './UdpDiscovery.js';
|
|
4
|
+
export class ApiServiceLinker {
|
|
5
|
+
#paths;
|
|
6
|
+
#nodeId;
|
|
7
|
+
#lws;
|
|
8
|
+
#discovery;
|
|
9
|
+
#subscription;
|
|
10
|
+
#metadata;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.#paths = options.paths;
|
|
13
|
+
this.#nodeId = options.node_id ?? randomUUID();
|
|
14
|
+
this.#lws = options.ws;
|
|
15
|
+
this.#discovery = options.discovery ?? new UdpDiscovery({ key: LIVEQUERY_MAGIC_KEY });
|
|
16
|
+
}
|
|
17
|
+
start(name, port) {
|
|
18
|
+
this.#metadata = this.#createMetadata(name, port);
|
|
19
|
+
this.#subscription?.unsubscribe();
|
|
20
|
+
this.#subscription = this.#discovery.subscribe(node => {
|
|
21
|
+
if (node.role !== 'gateway')
|
|
22
|
+
return;
|
|
23
|
+
if (node.namespace !== API_GATEWAY_NAMESPACE)
|
|
24
|
+
return;
|
|
25
|
+
if (node.node_id === this.#nodeId)
|
|
26
|
+
return;
|
|
27
|
+
const metadata = this.#metadata;
|
|
28
|
+
if (metadata)
|
|
29
|
+
this.#discovery.broadcast(this.#bump(metadata)).catch(e => console.error(e));
|
|
30
|
+
});
|
|
31
|
+
this.#discovery.broadcast(this.#metadata).catch(e => console.error(e));
|
|
32
|
+
}
|
|
33
|
+
close() {
|
|
34
|
+
this.#subscription?.unsubscribe();
|
|
35
|
+
this.#discovery.close();
|
|
36
|
+
}
|
|
37
|
+
#createMetadata(name, port) {
|
|
38
|
+
return {
|
|
39
|
+
node_id: this.#nodeId,
|
|
40
|
+
host: '',
|
|
41
|
+
namespace: API_GATEWAY_NAMESPACE,
|
|
42
|
+
version: Date.now(),
|
|
43
|
+
name,
|
|
44
|
+
port,
|
|
45
|
+
role: 'service',
|
|
46
|
+
paths: this.#paths,
|
|
47
|
+
linked: [],
|
|
48
|
+
ws: this.#lws ? {
|
|
49
|
+
auth: this.#lws.auth,
|
|
50
|
+
path: WEBSOCKET_PATH,
|
|
51
|
+
} : undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
#bump(metadata) {
|
|
55
|
+
return {
|
|
56
|
+
...metadata,
|
|
57
|
+
version: Date.now(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=ApiServiceLinker.js.map
|