@lakutata/entrypoint 3.0.0 → 3.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 +39 -26
- package/README.zh-CN.md +182 -0
- package/dist/cjs/Entrypoint.d.ts +13 -7
- package/dist/cjs/Entrypoint.js +12 -6
- package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.d.ts +3 -2
- package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.js +3 -2
- package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +2 -1
- package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.js +2 -1
- package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.d.ts +3 -2
- package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.js +8 -2
- package/dist/cjs/adapter/express/Responders.d.ts +2 -1
- package/dist/cjs/adapter/express/Responders.js +4 -2
- package/dist/cjs/adapter/express/StaticUI.d.ts +2 -1
- package/dist/cjs/adapter/express/StaticUI.js +2 -1
- package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.d.ts +2 -1
- package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.js +4 -1
- package/dist/cjs/adapter/fastify/Responders.d.ts +2 -1
- package/dist/cjs/adapter/fastify/Responders.js +4 -2
- package/dist/cjs/adapter/http/HTTPServiceEntrypoint.d.ts +4 -3
- package/dist/cjs/adapter/http/HTTPServiceEntrypoint.js +10 -22
- package/dist/cjs/adapter/http/HTTPServiceProxy.d.ts +190 -0
- package/dist/cjs/adapter/http/HTTPServiceProxy.js +371 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +11 -7
- package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +15 -26
- package/dist/cjs/adapter/schedule/ScheduleEntrypoint.d.ts +2 -1
- package/dist/cjs/adapter/schedule/ScheduleEntrypoint.js +12 -5
- package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +2 -1
- package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.js +2 -1
- package/dist/cjs/channel/BuiltinChannels.js +4 -2
- package/dist/cjs/channel/EntrypointAdapter.d.ts +2 -2
- package/dist/cjs/decorators/HTTPAction.d.ts +5 -5
- package/dist/cjs/decorators/Schedule.d.ts +4 -2
- package/dist/cjs/decorators/Schedule.js +3 -2
- package/dist/cjs/decorators/ServiceAction.d.ts +12 -8
- package/dist/cjs/decorators/http/DELETE.d.ts +5 -5
- package/dist/cjs/decorators/http/GET.d.ts +7 -7
- package/dist/cjs/decorators/http/HEAD.d.ts +9 -9
- package/dist/cjs/decorators/http/OPTIONS.d.ts +8 -7
- package/dist/cjs/decorators/http/PATCH.d.ts +5 -5
- package/dist/cjs/decorators/http/POST.d.ts +5 -5
- package/dist/cjs/decorators/http/PUT.d.ts +7 -7
- package/dist/cjs/exceptions/AccessDeniedException.d.ts +7 -1
- package/dist/cjs/exceptions/AccessDeniedException.js +8 -3
- package/dist/cjs/exceptions/EntrypointStoppedException.d.ts +2 -1
- package/dist/cjs/exceptions/EntrypointStoppedException.js +2 -1
- package/dist/cjs/exceptions/InvalidActionGroupException.d.ts +4 -0
- package/dist/cjs/exceptions/InvalidActionGroupException.js +4 -0
- package/dist/cjs/exceptions/ServiceUnavailableException.d.ts +15 -0
- package/dist/cjs/exceptions/ServiceUnavailableException.js +18 -0
- package/dist/cjs/exports/Entrypoint.d.ts +3 -0
- package/dist/cjs/exports/Entrypoint.js +6 -1
- package/dist/cjs/lib/AccessControl.d.ts +2 -1
- package/dist/cjs/lib/AccessControl.js +2 -1
- package/dist/cjs/lib/AccessControlRule.d.ts +2 -1
- package/dist/cjs/lib/AccessControlRule.js +2 -1
- package/dist/cjs/lib/ExplicitStatus.d.ts +11 -0
- package/dist/cjs/lib/ExplicitStatus.js +22 -0
- package/dist/cjs/lib/Middleware.d.ts +2 -1
- package/dist/cjs/lib/Middleware.js +2 -1
- package/dist/cjs/lib/Preflight.d.ts +8 -0
- package/dist/cjs/lib/Preflight.js +13 -0
- package/dist/cjs/lib/ReadBody.d.ts +9 -0
- package/dist/cjs/lib/ReadBody.js +25 -0
- package/dist/cjs/lib/pattern/FlattenPattern.d.ts +14 -0
- package/dist/cjs/lib/pattern/FlattenPattern.js +59 -0
- package/dist/esm/Entrypoint.js +12 -6
- package/dist/esm/adapter/commander/CommanderCLIEntrypoint.js +3 -2
- package/dist/esm/adapter/electron/ElectronIPCServiceEntrypoint.js +2 -1
- package/dist/esm/adapter/express/ExpressHTTPEntrypoint.js +8 -2
- package/dist/esm/adapter/express/Responders.js +4 -2
- package/dist/esm/adapter/express/StaticUI.js +2 -1
- package/dist/esm/adapter/fastify/FastifyHTTPEntrypoint.js +4 -1
- package/dist/esm/adapter/fastify/Responders.js +4 -2
- package/dist/esm/adapter/http/HTTPServiceEntrypoint.js +9 -21
- package/dist/esm/adapter/http/HTTPServiceProxy.js +366 -0
- package/dist/esm/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +14 -25
- package/dist/esm/adapter/schedule/ScheduleEntrypoint.js +12 -5
- package/dist/esm/adapter/socket.io/SocketIOServiceEntrypoint.js +2 -1
- package/dist/esm/channel/BuiltinChannels.js +4 -2
- package/dist/esm/decorators/Schedule.js +3 -2
- package/dist/esm/exceptions/AccessDeniedException.js +8 -3
- package/dist/esm/exceptions/EntrypointStoppedException.js +2 -1
- package/dist/esm/exceptions/InvalidActionGroupException.js +4 -0
- package/dist/esm/exceptions/ServiceUnavailableException.js +14 -0
- package/dist/esm/exports/Entrypoint.js +3 -0
- package/dist/esm/lib/AccessControl.js +2 -1
- package/dist/esm/lib/AccessControlRule.js +2 -1
- package/dist/esm/lib/ExplicitStatus.js +18 -0
- package/dist/esm/lib/Middleware.js +2 -1
- package/dist/esm/lib/Preflight.js +10 -0
- package/dist/esm/lib/ReadBody.js +22 -0
- package/dist/esm/lib/pattern/FlattenPattern.js +55 -0
- package/dist/types/Entrypoint.d.ts +13 -7
- package/dist/types/adapter/commander/CommanderCLIEntrypoint.d.ts +3 -2
- package/dist/types/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +2 -1
- package/dist/types/adapter/express/ExpressHTTPEntrypoint.d.ts +3 -2
- package/dist/types/adapter/express/Responders.d.ts +2 -1
- package/dist/types/adapter/express/StaticUI.d.ts +2 -1
- package/dist/types/adapter/fastify/FastifyHTTPEntrypoint.d.ts +2 -1
- package/dist/types/adapter/fastify/Responders.d.ts +2 -1
- package/dist/types/adapter/http/HTTPServiceEntrypoint.d.ts +4 -3
- package/dist/types/adapter/http/HTTPServiceProxy.d.ts +190 -0
- package/dist/types/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +11 -7
- package/dist/types/adapter/schedule/ScheduleEntrypoint.d.ts +2 -1
- package/dist/types/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +2 -1
- package/dist/types/channel/EntrypointAdapter.d.ts +2 -2
- package/dist/types/decorators/HTTPAction.d.ts +5 -5
- package/dist/types/decorators/Schedule.d.ts +4 -2
- package/dist/types/decorators/ServiceAction.d.ts +12 -8
- package/dist/types/decorators/http/DELETE.d.ts +5 -5
- package/dist/types/decorators/http/GET.d.ts +7 -7
- package/dist/types/decorators/http/HEAD.d.ts +9 -9
- package/dist/types/decorators/http/OPTIONS.d.ts +8 -7
- package/dist/types/decorators/http/PATCH.d.ts +5 -5
- package/dist/types/decorators/http/POST.d.ts +5 -5
- package/dist/types/decorators/http/PUT.d.ts +7 -7
- package/dist/types/exceptions/AccessDeniedException.d.ts +7 -1
- package/dist/types/exceptions/EntrypointStoppedException.d.ts +2 -1
- package/dist/types/exceptions/InvalidActionGroupException.d.ts +4 -0
- package/dist/types/exceptions/ServiceUnavailableException.d.ts +15 -0
- package/dist/types/exports/Entrypoint.d.ts +3 -0
- package/dist/types/lib/AccessControl.d.ts +2 -1
- package/dist/types/lib/AccessControlRule.d.ts +2 -1
- package/dist/types/lib/ExplicitStatus.d.ts +11 -0
- package/dist/types/lib/Middleware.d.ts +2 -1
- package/dist/types/lib/Preflight.d.ts +8 -0
- package/dist/types/lib/ReadBody.d.ts +9 -0
- package/dist/types/lib/pattern/FlattenPattern.d.ts +14 -0
- package/package.json +35 -4
|
@@ -12,6 +12,7 @@ const BuiltinChannels_js_1 = require("../../channel/BuiltinChannels.js");
|
|
|
12
12
|
const Result_js_1 = require("../../lib/Result.js");
|
|
13
13
|
const RouteNotFoundException_js_1 = require("../../exceptions/RouteNotFoundException.js");
|
|
14
14
|
const Connections_js_1 = require("../../lib/Connections.js");
|
|
15
|
+
const ExplicitStatus_js_1 = require("../../lib/ExplicitStatus.js");
|
|
15
16
|
const Responders_js_1 = require("./Responders.js");
|
|
16
17
|
/**
|
|
17
18
|
* The content types of the common file extensions
|
|
@@ -127,7 +128,8 @@ function isFastifyInstance(value) {
|
|
|
127
128
|
* }
|
|
128
129
|
* })
|
|
129
130
|
* })
|
|
130
|
-
* }
|
|
131
|
+
* },
|
|
132
|
+
* bootstrap: ['entrypoint']
|
|
131
133
|
* })
|
|
132
134
|
* ```
|
|
133
135
|
*/
|
|
@@ -222,6 +224,7 @@ function fastifyHTTPEntrypoint(options = {}) {
|
|
|
222
224
|
return await sendFile(result, request, reply);
|
|
223
225
|
if (result instanceof Result_js_1.StatusResult) {
|
|
224
226
|
reply.code(result.status).headers(result.headers);
|
|
227
|
+
(0, ExplicitStatus_js_1.markExplicitStatus)(reply.raw);
|
|
225
228
|
return respond.success(reply, result.data, request);
|
|
226
229
|
}
|
|
227
230
|
respond.success(reply, result, request);
|
|
@@ -4,6 +4,7 @@ exports.FastifyJSONResponder = void 0;
|
|
|
4
4
|
exports.fastifyJSONEnvelope = fastifyJSONEnvelope;
|
|
5
5
|
const HTTPErrors_js_1 = require("../../lib/HTTPErrors.js");
|
|
6
6
|
const Envelope_js_1 = require("../../lib/Envelope.js");
|
|
7
|
+
const ExplicitStatus_js_1 = require("../../lib/ExplicitStatus.js");
|
|
7
8
|
/**
|
|
8
9
|
* Send JSON
|
|
9
10
|
* @param reply
|
|
@@ -20,7 +21,7 @@ function sendJSON(reply, data) {
|
|
|
20
21
|
exports.FastifyJSONResponder = {
|
|
21
22
|
success: (reply, data) => {
|
|
22
23
|
if (data === undefined) {
|
|
23
|
-
reply.code(reply.statusCode === 200 ? 204 : reply.statusCode).send();
|
|
24
|
+
reply.code(reply.statusCode === 200 && !(0, ExplicitStatus_js_1.hasExplicitStatus)(reply.raw) ? 204 : reply.statusCode).send();
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
sendJSON(reply, data);
|
|
@@ -70,7 +71,8 @@ exports.FastifyJSONResponder = {
|
|
|
70
71
|
* })
|
|
71
72
|
* })
|
|
72
73
|
* })
|
|
73
|
-
* }
|
|
74
|
+
* },
|
|
75
|
+
* bootstrap: ['entrypoint']
|
|
74
76
|
* })
|
|
75
77
|
* ```
|
|
76
78
|
*/
|
|
@@ -66,8 +66,8 @@ export declare function httpRequestOfContext(context: ServiceContext): IncomingM
|
|
|
66
66
|
* A request is a `POST` to the path whose JSON body holds the action's pattern and input (`{"cmd": "echo", "text":
|
|
67
67
|
* "hi"}`). The response is always 200 with the body `{success, error, payload}` (see `ServiceResponseCodec`): `payload`
|
|
68
68
|
* is the result, or `error` the whole error object when the action failed (no matching action, validation…). A body that
|
|
69
|
-
* is not JSON
|
|
70
|
-
* headers allow any origin.
|
|
69
|
+
* is not JSON is answered with 400 and an error body, a body over `limit` with 413; any other method or path with 404.
|
|
70
|
+
* The CORS headers allow any origin, and the headers a preflight asks for (`Authorization`…).
|
|
71
71
|
*
|
|
72
72
|
* The events pushed to the clients (`Entrypoint.notify`, `Entrypoint.broadcast`) are streamed as Server-Sent Events by
|
|
73
73
|
* `GET <path>/events`: each event's name is the SSE event, its arguments the JSON array of its data. A client gives its
|
|
@@ -102,7 +102,8 @@ export declare function httpRequestOfContext(context: ServiceContext): IncomingM
|
|
|
102
102
|
* controllers: [EchoController],
|
|
103
103
|
* service: httpServiceEntrypoint({port: 3000, path: '/rpc'})
|
|
104
104
|
* })
|
|
105
|
-
* }
|
|
105
|
+
* },
|
|
106
|
+
* bootstrap: ['entrypoint']
|
|
106
107
|
* })
|
|
107
108
|
*
|
|
108
109
|
* //A client: the response is {"success": true, "error": null, "payload": "hi"}
|
|
@@ -9,6 +9,8 @@ const BuiltinChannels_js_1 = require("../../channel/BuiltinChannels.js");
|
|
|
9
9
|
const ServiceResponseCodec_js_1 = require("../../lib/ServiceResponseCodec.js");
|
|
10
10
|
const EntrypointStoppedException_js_1 = require("../../exceptions/EntrypointStoppedException.js");
|
|
11
11
|
const Connections_js_1 = require("../../lib/Connections.js");
|
|
12
|
+
const ReadBody_js_1 = require("../../lib/ReadBody.js");
|
|
13
|
+
const Preflight_js_1 = require("../../lib/Preflight.js");
|
|
12
14
|
/**
|
|
13
15
|
* Get the HTTP request carrying a service action's request, when {@link httpServiceEntrypoint} serves it (to read its
|
|
14
16
|
* headers, its remote address…).
|
|
@@ -31,7 +33,8 @@ function httpRequestOfContext(context) {
|
|
|
31
33
|
return context.request;
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
|
-
* The headers of the responses: the tools of the browsers may call the adapter (the development of the applications)
|
|
36
|
+
* The headers of the responses: the tools of the browsers may call the adapter (the development of the applications);
|
|
37
|
+
* a preflight is allowed the headers it asks for
|
|
35
38
|
*/
|
|
36
39
|
const CORS_HEADERS = {
|
|
37
40
|
'Access-Control-Allow-Origin': '*',
|
|
@@ -59,22 +62,6 @@ function reply(response, status, body) {
|
|
|
59
62
|
return;
|
|
60
63
|
response.writeHead(status, { ...CORS_HEADERS, 'Content-Type': 'application/json; charset=utf-8' }).end(JSON.stringify(body));
|
|
61
64
|
}
|
|
62
|
-
/**
|
|
63
|
-
* The body of a request, up to a limit
|
|
64
|
-
* @param request
|
|
65
|
-
* @param limit
|
|
66
|
-
*/
|
|
67
|
-
async function readBody(request, limit) {
|
|
68
|
-
const chunks = [];
|
|
69
|
-
let size = 0;
|
|
70
|
-
for await (const chunk of request) {
|
|
71
|
-
size += chunk.length;
|
|
72
|
-
if (size > limit)
|
|
73
|
-
throw Object.assign(new Error(`The body of the request is larger than ${limit} bytes`), { statusCode: 413 });
|
|
74
|
-
chunks.push(chunk);
|
|
75
|
-
}
|
|
76
|
-
return Buffer.concat(chunks).toString('utf8');
|
|
77
|
-
}
|
|
78
65
|
/**
|
|
79
66
|
* Create a service adapter of an entrypoint served over plain HTTP, handy to call the service actions from tools such
|
|
80
67
|
* as curl or Postman during development. It needs no optional dependency.
|
|
@@ -82,8 +69,8 @@ async function readBody(request, limit) {
|
|
|
82
69
|
* A request is a `POST` to the path whose JSON body holds the action's pattern and input (`{"cmd": "echo", "text":
|
|
83
70
|
* "hi"}`). The response is always 200 with the body `{success, error, payload}` (see `ServiceResponseCodec`): `payload`
|
|
84
71
|
* is the result, or `error` the whole error object when the action failed (no matching action, validation…). A body that
|
|
85
|
-
* is not JSON
|
|
86
|
-
* headers allow any origin.
|
|
72
|
+
* is not JSON is answered with 400 and an error body, a body over `limit` with 413; any other method or path with 404.
|
|
73
|
+
* The CORS headers allow any origin, and the headers a preflight asks for (`Authorization`…).
|
|
87
74
|
*
|
|
88
75
|
* The events pushed to the clients (`Entrypoint.notify`, `Entrypoint.broadcast`) are streamed as Server-Sent Events by
|
|
89
76
|
* `GET <path>/events`: each event's name is the SSE event, its arguments the JSON array of its data. A client gives its
|
|
@@ -118,7 +105,8 @@ async function readBody(request, limit) {
|
|
|
118
105
|
* controllers: [EchoController],
|
|
119
106
|
* service: httpServiceEntrypoint({port: 3000, path: '/rpc'})
|
|
120
107
|
* })
|
|
121
|
-
* }
|
|
108
|
+
* },
|
|
109
|
+
* bootstrap: ['entrypoint']
|
|
122
110
|
* })
|
|
123
111
|
*
|
|
124
112
|
* //A client: the response is {"success": true, "error": null, "payload": "hi"}
|
|
@@ -189,7 +177,7 @@ function httpServiceEntrypoint(options = {}) {
|
|
|
189
177
|
});
|
|
190
178
|
let data;
|
|
191
179
|
try {
|
|
192
|
-
const body = await readBody(request, limit);
|
|
180
|
+
const body = await (0, ReadBody_js_1.readBody)(request, limit);
|
|
193
181
|
data = body ? JSON.parse(body) : {};
|
|
194
182
|
}
|
|
195
183
|
catch (error) {
|
|
@@ -215,7 +203,7 @@ function httpServiceEntrypoint(options = {}) {
|
|
|
215
203
|
server = (0, node_http_1.createServer)((request, response) => {
|
|
216
204
|
const url = new URL(request.url ?? '/', 'http://localhost');
|
|
217
205
|
if (request.method === 'OPTIONS')
|
|
218
|
-
return void response.writeHead(204, CORS_HEADERS).end();
|
|
206
|
+
return void response.writeHead(204, (0, Preflight_js_1.preflightHeaders)(request, CORS_HEADERS)).end();
|
|
219
207
|
if (request.method === 'GET' && url.pathname === eventsPath)
|
|
220
208
|
return listen(request, response, url);
|
|
221
209
|
if (request.method === 'POST' && url.pathname === requestPath)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { type ActionPattern, Component, type ComponentOptionsBuilder } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of an {@link HTTPServiceProxy}, given to {@link buildHTTPServiceProxy}.
|
|
4
|
+
*/
|
|
5
|
+
export type BuildHTTPServiceProxyOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* The address of the service: the URL of its `httpServiceEntrypoint`, with the adapter's `path`
|
|
8
|
+
* (`http://inventory:3000/`, `https://orders.internal/rpc`).
|
|
9
|
+
*/
|
|
10
|
+
readonly url: string;
|
|
11
|
+
/**
|
|
12
|
+
* The headers sent with each call and with the event stream, such as `{Authorization: 'Bearer <token>'}`.
|
|
13
|
+
*/
|
|
14
|
+
readonly headers?: Record<string, string>;
|
|
15
|
+
/**
|
|
16
|
+
* The client's id, sent as the `id` header of the calls and the `id` query parameter of the event stream: the
|
|
17
|
+
* service's `ServiceContext.clientId`, which `Entrypoint.notify()` pushes events to. Give a stable one to receive
|
|
18
|
+
* the events across restarts. At most 200 characters.
|
|
19
|
+
* @default a random UUID
|
|
20
|
+
*/
|
|
21
|
+
readonly clientId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The delay, in milliseconds, before the event stream is opened again once it ended or failed (the service
|
|
24
|
+
* restarted, unreachable). A non-negative integer.
|
|
25
|
+
* @default 1000
|
|
26
|
+
*/
|
|
27
|
+
readonly reconnectDelay?: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Build the component options of an {@link HTTPServiceProxy}, to register it in the `components` of an application.
|
|
31
|
+
* @param options The service's address, and the headers, client id and reconnection delay.
|
|
32
|
+
* @returns The component options.
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import {Application} from 'lakutata'
|
|
36
|
+
* import {buildHTTPServiceProxy, type HTTPServiceProxy} from 'lakutata/com/entrypoint'
|
|
37
|
+
*
|
|
38
|
+
* Application
|
|
39
|
+
* .run(() => ({
|
|
40
|
+
* id: 'shop.app',
|
|
41
|
+
* name: 'Shop',
|
|
42
|
+
* components: {
|
|
43
|
+
* inventory: buildHTTPServiceProxy({url: 'http://inventory.internal:3000/'}),
|
|
44
|
+
* //With a token, and a stable client id to receive the events of the service
|
|
45
|
+
* orders: buildHTTPServiceProxy({
|
|
46
|
+
* url: 'https://orders.internal/rpc',
|
|
47
|
+
* headers: {Authorization: `Bearer ${process.env.ORDERS_TOKEN}`},
|
|
48
|
+
* clientId: 'shop-1'
|
|
49
|
+
* })
|
|
50
|
+
* }
|
|
51
|
+
* }))
|
|
52
|
+
* .onLaunched(async (app: Application): Promise<void> => {
|
|
53
|
+
* const inventory: HTTPServiceProxy = await app.getObject<HTTPServiceProxy>('inventory')
|
|
54
|
+
* console.log(await inventory.invoke({cmd: 'stock.get', product: 'lamp'}))
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare const buildHTTPServiceProxy: ComponentOptionsBuilder<BuildHTTPServiceProxyOptions>;
|
|
59
|
+
/**
|
|
60
|
+
* The client of a lakutata service served over HTTP by `httpServiceEntrypoint`: it invokes the service's actions by
|
|
61
|
+
* their patterns and listens to the events the service pushes to it. It is a component (a singleton), registered with
|
|
62
|
+
* {@link buildHTTPServiceProxy}; it needs no package (it uses the `fetch()` of Node.js), and its event stream is opened
|
|
63
|
+
* when the first listener is added.
|
|
64
|
+
*
|
|
65
|
+
* The failures of the remote actions are thrown as `ServiceInvokeException`, with the remote error's message and
|
|
66
|
+
* properties (`errno`, `statusCode`…) and the address as its `service`; a service that gives no response (unreachable,
|
|
67
|
+
* too slow, or an answer that is not a service response) as a `ServiceUnavailableException`.
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* import {Component} from 'lakutata'
|
|
71
|
+
* import {type HTTPServiceProxy, ServiceInvokeException, ServiceUnavailableException} from 'lakutata/com/entrypoint'
|
|
72
|
+
* import {Inject} from 'lakutata/decorator/di'
|
|
73
|
+
*
|
|
74
|
+
* class StockWatcher extends Component {
|
|
75
|
+
* //Registered as inventory: buildHTTPServiceProxy({url: 'http://inventory.internal:3000/'})
|
|
76
|
+
* @Inject('inventory')
|
|
77
|
+
* protected readonly inventory: HTTPServiceProxy
|
|
78
|
+
*
|
|
79
|
+
* protected async init(): Promise<void> {
|
|
80
|
+
* //The events the inventory pushes with Entrypoint.notify(clientId, 'lowStock', product, quantity)
|
|
81
|
+
* this.inventory.on('lowStock', (product: string, quantity: number): void => {
|
|
82
|
+
* console.log(`${product}: ${quantity} left`)
|
|
83
|
+
* })
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* public async quantity(product: string): Promise<number | undefined> {
|
|
87
|
+
* try {
|
|
88
|
+
* //5 seconds at most; the inventory aborts the action's signal when the call gives up
|
|
89
|
+
* return await this.inventory.invoke({cmd: 'stock.get', product: product}, 5000)
|
|
90
|
+
* } catch (error) {
|
|
91
|
+
* //The action failed: its error
|
|
92
|
+
* if (error instanceof ServiceInvokeException && error.errno === 'E_PRODUCT_NOT_FOUND') return undefined
|
|
93
|
+
* //The inventory gave no response
|
|
94
|
+
* if (error instanceof ServiceUnavailableException) return undefined
|
|
95
|
+
* throw error
|
|
96
|
+
* }
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare class HTTPServiceProxy extends Component {
|
|
102
|
+
#private;
|
|
103
|
+
/**
|
|
104
|
+
* The service's address, as configured (see {@link BuildHTTPServiceProxyOptions.url}); required.
|
|
105
|
+
*/
|
|
106
|
+
protected readonly url: string;
|
|
107
|
+
/**
|
|
108
|
+
* The headers sent with each call and with the event stream; none by default.
|
|
109
|
+
*/
|
|
110
|
+
protected readonly headers?: Record<string, string>;
|
|
111
|
+
/**
|
|
112
|
+
* The configured client id; a random UUID is used without it (see {@link HTTPServiceProxy.id}).
|
|
113
|
+
*/
|
|
114
|
+
protected readonly clientId?: string;
|
|
115
|
+
/**
|
|
116
|
+
* The delay, in milliseconds, before the event stream is opened again.
|
|
117
|
+
* @default 1000
|
|
118
|
+
*/
|
|
119
|
+
protected readonly reconnectDelay: number;
|
|
120
|
+
/**
|
|
121
|
+
* Create the client id and the address of the event stream (`<url>/events?id=<id>`); nothing connects before the
|
|
122
|
+
* first call.
|
|
123
|
+
* @protected
|
|
124
|
+
*/
|
|
125
|
+
protected init(): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Close the event stream and remove the listeners (the calls in progress complete).
|
|
128
|
+
* @protected
|
|
129
|
+
*/
|
|
130
|
+
protected destroy(): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* The client's id: the configured `clientId`, or a random UUID. It is sent as the `id` header of the calls, and is
|
|
133
|
+
* the `ServiceContext.clientId` of the service's actions.
|
|
134
|
+
*/
|
|
135
|
+
get id(): string;
|
|
136
|
+
/**
|
|
137
|
+
* Invoke an action of the service, matched by its pattern.
|
|
138
|
+
* @param input The action's pattern and its input in one object (`{cmd: 'order.get', id: 1}`), sent as JSON: the
|
|
139
|
+
* values JSON cannot hold (functions, `undefined`, class instances) are lost or turned into plain objects.
|
|
140
|
+
* @param timeout The longest wait for the response, in milliseconds; 600000 (10 minutes) by default. The request is
|
|
141
|
+
* abandoned after it, and the service aborts the action's signal.
|
|
142
|
+
* @returns The action's result, decoded from JSON: `undefined` when the action returned nothing.
|
|
143
|
+
* @throws {ServiceInvokeException} When the action fails (no action matches the pattern, an invalid input, an error
|
|
144
|
+
* thrown, the service stopping), or the service refuses the request (another path, a body over its limit): the
|
|
145
|
+
* remote error's message and properties (`errno`…), the address as its `service`.
|
|
146
|
+
* @throws {ServiceUnavailableException} When the service gives no response: unreachable, no response within the
|
|
147
|
+
* timeout, or an answer that is not a service response. Its `cause` is the error of the request.
|
|
148
|
+
*/
|
|
149
|
+
invoke<T extends Record<string, any>>(input: ActionPattern<T>, timeout?: number): Promise<any>;
|
|
150
|
+
/**
|
|
151
|
+
* Listen to an event the service pushes to this client (`Entrypoint.notify()`, `broadcast()`); the first listener
|
|
152
|
+
* opens the event stream. The listeners' errors are ignored. The `client` event, received when the stream opens,
|
|
153
|
+
* carries the client's id.
|
|
154
|
+
* @param eventName The event's name.
|
|
155
|
+
* @param listener Called with the event's arguments, decoded from JSON.
|
|
156
|
+
* @returns The proxy, to chain the calls.
|
|
157
|
+
*/
|
|
158
|
+
on(eventName: string, listener: (...args: any[]) => void): this;
|
|
159
|
+
/**
|
|
160
|
+
* Listen to an event the service pushes to this client, once: the listener is removed before it is called.
|
|
161
|
+
* @param eventName The event's name.
|
|
162
|
+
* @param listener Called with the event's arguments, decoded from JSON.
|
|
163
|
+
* @returns The proxy, to chain the calls.
|
|
164
|
+
*/
|
|
165
|
+
once(eventName: string, listener: (...args: any[]) => void): this;
|
|
166
|
+
/**
|
|
167
|
+
* Stop listening to an event; removing the last listener of all the events closes the event stream.
|
|
168
|
+
* @param eventName The event's name.
|
|
169
|
+
* @param listener The listener to remove; without it, all the event's listeners are removed.
|
|
170
|
+
* @returns The proxy, to chain the calls.
|
|
171
|
+
*/
|
|
172
|
+
off(eventName: string, listener?: (...args: any[]) => void): this;
|
|
173
|
+
/**
|
|
174
|
+
* Add a listener, and open the event stream when it is not open.
|
|
175
|
+
* @param eventName The event's name.
|
|
176
|
+
* @param listener The listener.
|
|
177
|
+
* @param once Whether the listener is removed after its first call.
|
|
178
|
+
* @returns The proxy.
|
|
179
|
+
* @protected
|
|
180
|
+
*/
|
|
181
|
+
protected add(eventName: string, listener: (...args: any[]) => void, once: boolean): this;
|
|
182
|
+
/**
|
|
183
|
+
* Read the event stream and call the listeners; the stream is opened again after `reconnectDelay` milliseconds
|
|
184
|
+
* when it ends or fails, while there are listeners. It stops once no listener is left (removed by
|
|
185
|
+
* {@link HTTPServiceProxy.off}, or the last `once()` listener called) or the proxy is destroyed.
|
|
186
|
+
* @returns A promise resolved once the reading stops.
|
|
187
|
+
* @protected
|
|
188
|
+
*/
|
|
189
|
+
protected stream(): Promise<void>;
|
|
190
|
+
}
|