@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.
Files changed (129) hide show
  1. package/README.md +39 -26
  2. package/README.zh-CN.md +182 -0
  3. package/dist/cjs/Entrypoint.d.ts +13 -7
  4. package/dist/cjs/Entrypoint.js +12 -6
  5. package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.d.ts +3 -2
  6. package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.js +3 -2
  7. package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +2 -1
  8. package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.js +2 -1
  9. package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.d.ts +3 -2
  10. package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.js +8 -2
  11. package/dist/cjs/adapter/express/Responders.d.ts +2 -1
  12. package/dist/cjs/adapter/express/Responders.js +4 -2
  13. package/dist/cjs/adapter/express/StaticUI.d.ts +2 -1
  14. package/dist/cjs/adapter/express/StaticUI.js +2 -1
  15. package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.d.ts +2 -1
  16. package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.js +4 -1
  17. package/dist/cjs/adapter/fastify/Responders.d.ts +2 -1
  18. package/dist/cjs/adapter/fastify/Responders.js +4 -2
  19. package/dist/cjs/adapter/http/HTTPServiceEntrypoint.d.ts +4 -3
  20. package/dist/cjs/adapter/http/HTTPServiceEntrypoint.js +10 -22
  21. package/dist/cjs/adapter/http/HTTPServiceProxy.d.ts +190 -0
  22. package/dist/cjs/adapter/http/HTTPServiceProxy.js +371 -0
  23. package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +11 -7
  24. package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +15 -26
  25. package/dist/cjs/adapter/schedule/ScheduleEntrypoint.d.ts +2 -1
  26. package/dist/cjs/adapter/schedule/ScheduleEntrypoint.js +12 -5
  27. package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +2 -1
  28. package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.js +2 -1
  29. package/dist/cjs/channel/BuiltinChannels.js +4 -2
  30. package/dist/cjs/channel/EntrypointAdapter.d.ts +2 -2
  31. package/dist/cjs/decorators/HTTPAction.d.ts +5 -5
  32. package/dist/cjs/decorators/Schedule.d.ts +4 -2
  33. package/dist/cjs/decorators/Schedule.js +3 -2
  34. package/dist/cjs/decorators/ServiceAction.d.ts +12 -8
  35. package/dist/cjs/decorators/http/DELETE.d.ts +5 -5
  36. package/dist/cjs/decorators/http/GET.d.ts +7 -7
  37. package/dist/cjs/decorators/http/HEAD.d.ts +9 -9
  38. package/dist/cjs/decorators/http/OPTIONS.d.ts +8 -7
  39. package/dist/cjs/decorators/http/PATCH.d.ts +5 -5
  40. package/dist/cjs/decorators/http/POST.d.ts +5 -5
  41. package/dist/cjs/decorators/http/PUT.d.ts +7 -7
  42. package/dist/cjs/exceptions/AccessDeniedException.d.ts +7 -1
  43. package/dist/cjs/exceptions/AccessDeniedException.js +8 -3
  44. package/dist/cjs/exceptions/EntrypointStoppedException.d.ts +2 -1
  45. package/dist/cjs/exceptions/EntrypointStoppedException.js +2 -1
  46. package/dist/cjs/exceptions/InvalidActionGroupException.d.ts +4 -0
  47. package/dist/cjs/exceptions/InvalidActionGroupException.js +4 -0
  48. package/dist/cjs/exceptions/ServiceUnavailableException.d.ts +15 -0
  49. package/dist/cjs/exceptions/ServiceUnavailableException.js +18 -0
  50. package/dist/cjs/exports/Entrypoint.d.ts +3 -0
  51. package/dist/cjs/exports/Entrypoint.js +6 -1
  52. package/dist/cjs/lib/AccessControl.d.ts +2 -1
  53. package/dist/cjs/lib/AccessControl.js +2 -1
  54. package/dist/cjs/lib/AccessControlRule.d.ts +2 -1
  55. package/dist/cjs/lib/AccessControlRule.js +2 -1
  56. package/dist/cjs/lib/ExplicitStatus.d.ts +11 -0
  57. package/dist/cjs/lib/ExplicitStatus.js +22 -0
  58. package/dist/cjs/lib/Middleware.d.ts +2 -1
  59. package/dist/cjs/lib/Middleware.js +2 -1
  60. package/dist/cjs/lib/Preflight.d.ts +8 -0
  61. package/dist/cjs/lib/Preflight.js +13 -0
  62. package/dist/cjs/lib/ReadBody.d.ts +9 -0
  63. package/dist/cjs/lib/ReadBody.js +25 -0
  64. package/dist/cjs/lib/pattern/FlattenPattern.d.ts +14 -0
  65. package/dist/cjs/lib/pattern/FlattenPattern.js +59 -0
  66. package/dist/esm/Entrypoint.js +12 -6
  67. package/dist/esm/adapter/commander/CommanderCLIEntrypoint.js +3 -2
  68. package/dist/esm/adapter/electron/ElectronIPCServiceEntrypoint.js +2 -1
  69. package/dist/esm/adapter/express/ExpressHTTPEntrypoint.js +8 -2
  70. package/dist/esm/adapter/express/Responders.js +4 -2
  71. package/dist/esm/adapter/express/StaticUI.js +2 -1
  72. package/dist/esm/adapter/fastify/FastifyHTTPEntrypoint.js +4 -1
  73. package/dist/esm/adapter/fastify/Responders.js +4 -2
  74. package/dist/esm/adapter/http/HTTPServiceEntrypoint.js +9 -21
  75. package/dist/esm/adapter/http/HTTPServiceProxy.js +366 -0
  76. package/dist/esm/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +14 -25
  77. package/dist/esm/adapter/schedule/ScheduleEntrypoint.js +12 -5
  78. package/dist/esm/adapter/socket.io/SocketIOServiceEntrypoint.js +2 -1
  79. package/dist/esm/channel/BuiltinChannels.js +4 -2
  80. package/dist/esm/decorators/Schedule.js +3 -2
  81. package/dist/esm/exceptions/AccessDeniedException.js +8 -3
  82. package/dist/esm/exceptions/EntrypointStoppedException.js +2 -1
  83. package/dist/esm/exceptions/InvalidActionGroupException.js +4 -0
  84. package/dist/esm/exceptions/ServiceUnavailableException.js +14 -0
  85. package/dist/esm/exports/Entrypoint.js +3 -0
  86. package/dist/esm/lib/AccessControl.js +2 -1
  87. package/dist/esm/lib/AccessControlRule.js +2 -1
  88. package/dist/esm/lib/ExplicitStatus.js +18 -0
  89. package/dist/esm/lib/Middleware.js +2 -1
  90. package/dist/esm/lib/Preflight.js +10 -0
  91. package/dist/esm/lib/ReadBody.js +22 -0
  92. package/dist/esm/lib/pattern/FlattenPattern.js +55 -0
  93. package/dist/types/Entrypoint.d.ts +13 -7
  94. package/dist/types/adapter/commander/CommanderCLIEntrypoint.d.ts +3 -2
  95. package/dist/types/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +2 -1
  96. package/dist/types/adapter/express/ExpressHTTPEntrypoint.d.ts +3 -2
  97. package/dist/types/adapter/express/Responders.d.ts +2 -1
  98. package/dist/types/adapter/express/StaticUI.d.ts +2 -1
  99. package/dist/types/adapter/fastify/FastifyHTTPEntrypoint.d.ts +2 -1
  100. package/dist/types/adapter/fastify/Responders.d.ts +2 -1
  101. package/dist/types/adapter/http/HTTPServiceEntrypoint.d.ts +4 -3
  102. package/dist/types/adapter/http/HTTPServiceProxy.d.ts +190 -0
  103. package/dist/types/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +11 -7
  104. package/dist/types/adapter/schedule/ScheduleEntrypoint.d.ts +2 -1
  105. package/dist/types/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +2 -1
  106. package/dist/types/channel/EntrypointAdapter.d.ts +2 -2
  107. package/dist/types/decorators/HTTPAction.d.ts +5 -5
  108. package/dist/types/decorators/Schedule.d.ts +4 -2
  109. package/dist/types/decorators/ServiceAction.d.ts +12 -8
  110. package/dist/types/decorators/http/DELETE.d.ts +5 -5
  111. package/dist/types/decorators/http/GET.d.ts +7 -7
  112. package/dist/types/decorators/http/HEAD.d.ts +9 -9
  113. package/dist/types/decorators/http/OPTIONS.d.ts +8 -7
  114. package/dist/types/decorators/http/PATCH.d.ts +5 -5
  115. package/dist/types/decorators/http/POST.d.ts +5 -5
  116. package/dist/types/decorators/http/PUT.d.ts +7 -7
  117. package/dist/types/exceptions/AccessDeniedException.d.ts +7 -1
  118. package/dist/types/exceptions/EntrypointStoppedException.d.ts +2 -1
  119. package/dist/types/exceptions/InvalidActionGroupException.d.ts +4 -0
  120. package/dist/types/exceptions/ServiceUnavailableException.d.ts +15 -0
  121. package/dist/types/exports/Entrypoint.d.ts +3 -0
  122. package/dist/types/lib/AccessControl.d.ts +2 -1
  123. package/dist/types/lib/AccessControlRule.d.ts +2 -1
  124. package/dist/types/lib/ExplicitStatus.d.ts +11 -0
  125. package/dist/types/lib/Middleware.d.ts +2 -1
  126. package/dist/types/lib/Preflight.d.ts +8 -0
  127. package/dist/types/lib/ReadBody.d.ts +9 -0
  128. package/dist/types/lib/pattern/FlattenPattern.d.ts +14 -0
  129. package/package.json +35 -4
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flattenPattern = flattenPattern;
4
+ exports.flattenSubject = flattenSubject;
5
+ /**
6
+ * The nesting levels of a subject flattened: deeper than the patterns may be (2 levels)
7
+ */
8
+ const MAX_DEPTH = 3;
9
+ /**
10
+ * Whether a value is a plain object, whose properties a pattern compares one by one
11
+ * @param value
12
+ */
13
+ function isPlainObject(value) {
14
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
15
+ return false;
16
+ const prototype = Object.getPrototypeOf(value);
17
+ return prototype === Object.prototype || prototype === null;
18
+ }
19
+ /**
20
+ * A pattern with the properties of its nested objects as keys joined by dots (`{res: {name: 'user'}}` gives
21
+ * `{'res.name': 'user'}`): their values are compared one by one, not as the text of their object. An empty nested
22
+ * object stays a value.
23
+ * @param pattern
24
+ * @param prefix
25
+ */
26
+ function flattenPattern(pattern, prefix = '') {
27
+ const flat = {};
28
+ for (const [key, value] of Object.entries(pattern)) {
29
+ if (isPlainObject(value) && Object.keys(value).length) {
30
+ Object.assign(flat, flattenPattern(value, `${prefix}${key}.`));
31
+ }
32
+ else {
33
+ flat[`${prefix}${key}`] = value;
34
+ }
35
+ }
36
+ return flat;
37
+ }
38
+ /**
39
+ * A subject matched by the flattened patterns: its own properties, with the ones of its nested objects as keys joined
40
+ * by dots
41
+ * @param subject
42
+ */
43
+ function flattenSubject(subject) {
44
+ if (!isPlainObject(subject))
45
+ return subject;
46
+ const flat = { ...subject };
47
+ const add = (object, prefix, depth) => {
48
+ for (const [key, value] of Object.entries(object)) {
49
+ if (!isPlainObject(value))
50
+ continue;
51
+ for (const [nestedKey, nestedValue] of Object.entries(value))
52
+ flat[`${prefix}${key}.${nestedKey}`] = nestedValue;
53
+ if (depth < MAX_DEPTH)
54
+ add(value, `${prefix}${key}.`, depth + 1);
55
+ }
56
+ };
57
+ add(subject, '', 1);
58
+ return flat;
59
+ }
@@ -58,7 +58,8 @@ export { ScheduleContext } from './context/ScheduleContext.js';
58
58
  * if (cliMap.has(command)) console.log(await handler(new CLIContext({command: command, data: {}})))
59
59
  * })
60
60
  * })
61
- * }
61
+ * },
62
+ * bootstrap: ['entrypoint']
62
63
  * }))
63
64
  * ```
64
65
  */
@@ -90,8 +91,9 @@ export const buildCLIEntrypoint = (entrypoint) => entrypoint;
90
91
  * controllers: [HealthController],
91
92
  * http: buildHTTPEntrypoint((module, routeMap, handler, registerDestroy): void => {
92
93
  * const server: Server = createServer(async (request, response): Promise<void> => {
94
+ * //The route map holds the routes and the methods as declared: '/health', 'get'
93
95
  * const route: string = (request.url ?? '/').split('?')[0]
94
- * const method: string = request.method ?? 'GET'
96
+ * const method: string = (request.method ?? 'GET').toLowerCase()
95
97
  * if (!routeMap.get(route)?.has(method)) {
96
98
  * response.writeHead(404).end()
97
99
  * return
@@ -108,7 +110,8 @@ export const buildCLIEntrypoint = (entrypoint) => entrypoint;
108
110
  * registerDestroy((): Promise<void> => new Promise((resolve): void => void server.close((): void => resolve())))
109
111
  * })
110
112
  * })
111
- * }
113
+ * },
114
+ * bootstrap: ['entrypoint']
112
115
  * }))
113
116
  * ```
114
117
  */
@@ -149,7 +152,8 @@ export const buildHTTPEntrypoint = (entrypoint) => entrypoint;
149
152
  * registerDestroy((): void => void bus.off('request', listener))
150
153
  * })
151
154
  * })
152
- * }
155
+ * },
156
+ * bootstrap: ['entrypoint']
153
157
  * }))
154
158
  * ```
155
159
  */
@@ -199,7 +203,8 @@ export const buildServiceEntrypoint = (entrypoint) => entrypoint;
199
203
  * http: expressHTTPEntrypoint({port: 3000}),
200
204
  * schedule: scheduleEntrypoint()
201
205
  * })
202
- * }
206
+ * },
207
+ * bootstrap: ['entrypoint']
203
208
  * }))
204
209
  * ```
205
210
  */
@@ -269,7 +274,8 @@ const LEGACY_CHANNELS = new Set(['cli', 'http', 'service']);
269
274
  * http: expressHTTPEntrypoint({port: 3000}),
270
275
  * service: socketIOServiceEntrypoint({port: 3001})
271
276
  * })
272
- * }
277
+ * },
278
+ * bootstrap: ['entrypoint']
273
279
  * }))
274
280
  * .onLaunched(async (app: Application): Promise<void> => {
275
281
  * const entrypoint: Entrypoint = await app.getObject<Entrypoint>('entrypoint')
@@ -190,7 +190,7 @@ function errorMessage(error) {
190
190
  * @CLIAction('greet <name>', GreetDTO)
191
191
  * public async greet(input: GreetDTO): Promise<string> {
192
192
  * const text: string = `Hello, ${input.name}`
193
- * return (input.loud ? text.toUpperCase() : text).repeat(input.times ?? 1)
193
+ * return Array(input.times ?? 1).fill(input.loud ? text.toUpperCase() : text).join('\n')
194
194
  * }
195
195
  * }
196
196
  *
@@ -202,7 +202,8 @@ function errorMessage(error) {
202
202
  * controllers: [GreetController],
203
203
  * cli: commanderCLIEntrypoint({bin: 'tool', description: 'A greeting tool', version: '1.0.0'})
204
204
  * })
205
- * }
205
+ * },
206
+ * bootstrap: ['entrypoint']
206
207
  * })
207
208
  * ```
208
209
  */
@@ -90,7 +90,8 @@ async function loadIPCMain() {
90
90
  * controllers: [EchoController],
91
91
  * service: electronIPCServiceEntrypoint()
92
92
  * })
93
- * }
93
+ * },
94
+ * bootstrap: ['entrypoint']
94
95
  * })
95
96
  *
96
97
  * //In the preload script of a window
@@ -6,6 +6,7 @@ import { HTTPChannel } from '../../channel/BuiltinChannels.js';
6
6
  import { EventsResult, FileResult, StatusResult } from '../../lib/Result.js';
7
7
  import { RouteNotFoundException } from '../../exceptions/RouteNotFoundException.js';
8
8
  import { trackConnections } from '../../lib/Connections.js';
9
+ import { markExplicitStatus } from '../../lib/ExplicitStatus.js';
9
10
  import { JSONResponder } from './Responders.js';
10
11
  /**
11
12
  * The first segments of the routes
@@ -94,7 +95,8 @@ function eventFrame(data, event, id) {
94
95
  * fallback: staticUI('./ui/dist')
95
96
  * })
96
97
  * })
97
- * }
98
+ * },
99
+ * bootstrap: ['entrypoint']
98
100
  * })
99
101
  * ```
100
102
  */
@@ -193,6 +195,7 @@ export function expressHTTPEntrypoint(options = {}) {
193
195
  return await sendFile(result, request, response);
194
196
  if (result instanceof StatusResult) {
195
197
  response.status(result.status).set(result.headers);
198
+ markExplicitStatus(response);
196
199
  return respond.success(response, result.data, request);
197
200
  }
198
201
  respond.success(response, result, request);
@@ -251,7 +254,10 @@ export function expressHTTPEntrypoint(options = {}) {
251
254
  await options.before?.(app, { module: runtime.module, server: server, routes: routes, actionPrefixes: actionPrefixes, isActionPath: isActionPath });
252
255
  if (options.body !== false)
253
256
  app.use(express.json({ limit: options.body?.limit ?? '1mb' }), express.urlencoded({ extended: true, limit: options.body?.limit ?? '1mb' }));
254
- for (const action of runtime.actions) {
257
+ //The HEAD actions first: express answers a HEAD request with the first GET route of its path when no HEAD route
258
+ //comes before it
259
+ const isHead = (action) => String(action.address.method).toLowerCase() === 'head';
260
+ for (const action of [...runtime.actions.filter(isHead), ...runtime.actions.filter((action) => !isHead(action))]) {
255
261
  const method = String(action.address.method).toLowerCase();
256
262
  const register = app[method];
257
263
  if (typeof register !== 'function')
@@ -1,5 +1,6 @@
1
1
  import { describeError, statusOfError } from '../../lib/HTTPErrors.js';
2
2
  import { envelopeOf, errorExtraOf } from '../../lib/Envelope.js';
3
+ import { hasExplicitStatus } from '../../lib/ExplicitStatus.js';
3
4
  export { describeError, statusOfError };
4
5
  /**
5
6
  * The default responder of {@link expressHTTPEntrypoint}: a result is sent as JSON, no result as an empty 204 (or the
@@ -9,7 +10,7 @@ export { describeError, statusOfError };
9
10
  export const JSONResponder = {
10
11
  success: (response, data) => {
11
12
  if (data === undefined) {
12
- response.status(response.statusCode === 200 ? 204 : response.statusCode).end();
13
+ response.status(response.statusCode === 200 && !hasExplicitStatus(response) ? 204 : response.statusCode).end();
13
14
  return;
14
15
  }
15
16
  response.json(data);
@@ -58,7 +59,8 @@ export const JSONResponder = {
58
59
  * })
59
60
  * })
60
61
  * })
61
- * }
62
+ * },
63
+ * bootstrap: ['entrypoint']
62
64
  * })
63
65
  * ```
64
66
  */
@@ -61,7 +61,8 @@ export function assetsMaxAge(value) {
61
61
  * fallback: staticUI('./ui/dist', {assetsMaxAge: 86400})
62
62
  * })
63
63
  * })
64
- * }
64
+ * },
65
+ * bootstrap: ['entrypoint']
65
66
  * })
66
67
  * ```
67
68
  */
@@ -6,6 +6,7 @@ import { HTTPChannel } from '../../channel/BuiltinChannels.js';
6
6
  import { EventsResult, FileResult, StatusResult } from '../../lib/Result.js';
7
7
  import { RouteNotFoundException } from '../../exceptions/RouteNotFoundException.js';
8
8
  import { trackConnections } from '../../lib/Connections.js';
9
+ import { markExplicitStatus } from '../../lib/ExplicitStatus.js';
9
10
  import { FastifyJSONResponder } from './Responders.js';
10
11
  /**
11
12
  * The content types of the common file extensions
@@ -121,7 +122,8 @@ function isFastifyInstance(value) {
121
122
  * }
122
123
  * })
123
124
  * })
124
- * }
125
+ * },
126
+ * bootstrap: ['entrypoint']
125
127
  * })
126
128
  * ```
127
129
  */
@@ -216,6 +218,7 @@ export function fastifyHTTPEntrypoint(options = {}) {
216
218
  return await sendFile(result, request, reply);
217
219
  if (result instanceof StatusResult) {
218
220
  reply.code(result.status).headers(result.headers);
221
+ markExplicitStatus(reply.raw);
219
222
  return respond.success(reply, result.data, request);
220
223
  }
221
224
  respond.success(reply, result, request);
@@ -1,5 +1,6 @@
1
1
  import { describeError, statusOfError } from '../../lib/HTTPErrors.js';
2
2
  import { envelopeOf, errorExtraOf } from '../../lib/Envelope.js';
3
+ import { hasExplicitStatus } from '../../lib/ExplicitStatus.js';
3
4
  /**
4
5
  * Send JSON
5
6
  * @param reply
@@ -16,7 +17,7 @@ function sendJSON(reply, data) {
16
17
  export const FastifyJSONResponder = {
17
18
  success: (reply, data) => {
18
19
  if (data === undefined) {
19
- reply.code(reply.statusCode === 200 ? 204 : reply.statusCode).send();
20
+ reply.code(reply.statusCode === 200 && !hasExplicitStatus(reply.raw) ? 204 : reply.statusCode).send();
20
21
  return;
21
22
  }
22
23
  sendJSON(reply, data);
@@ -66,7 +67,8 @@ export const FastifyJSONResponder = {
66
67
  * })
67
68
  * })
68
69
  * })
69
- * }
70
+ * },
71
+ * bootstrap: ['entrypoint']
70
72
  * })
71
73
  * ```
72
74
  */
@@ -5,6 +5,8 @@ import { ServiceChannel } from '../../channel/BuiltinChannels.js';
5
5
  import { ServiceResponseCodec } from '../../lib/ServiceResponseCodec.js';
6
6
  import { EntrypointStoppedException } from '../../exceptions/EntrypointStoppedException.js';
7
7
  import { trackConnections } from '../../lib/Connections.js';
8
+ import { readBody } from '../../lib/ReadBody.js';
9
+ import { preflightHeaders } from '../../lib/Preflight.js';
8
10
  /**
9
11
  * Get the HTTP request carrying a service action's request, when {@link httpServiceEntrypoint} serves it (to read its
10
12
  * headers, its remote address…).
@@ -27,7 +29,8 @@ export function httpRequestOfContext(context) {
27
29
  return context.request;
28
30
  }
29
31
  /**
30
- * The headers of the responses: the tools of the browsers may call the adapter (the development of the applications)
32
+ * The headers of the responses: the tools of the browsers may call the adapter (the development of the applications);
33
+ * a preflight is allowed the headers it asks for
31
34
  */
32
35
  const CORS_HEADERS = {
33
36
  'Access-Control-Allow-Origin': '*',
@@ -55,22 +58,6 @@ function reply(response, status, body) {
55
58
  return;
56
59
  response.writeHead(status, { ...CORS_HEADERS, 'Content-Type': 'application/json; charset=utf-8' }).end(JSON.stringify(body));
57
60
  }
58
- /**
59
- * The body of a request, up to a limit
60
- * @param request
61
- * @param limit
62
- */
63
- async function readBody(request, limit) {
64
- const chunks = [];
65
- let size = 0;
66
- for await (const chunk of request) {
67
- size += chunk.length;
68
- if (size > limit)
69
- throw Object.assign(new Error(`The body of the request is larger than ${limit} bytes`), { statusCode: 413 });
70
- chunks.push(chunk);
71
- }
72
- return Buffer.concat(chunks).toString('utf8');
73
- }
74
61
  /**
75
62
  * Create a service adapter of an entrypoint served over plain HTTP, handy to call the service actions from tools such
76
63
  * as curl or Postman during development. It needs no optional dependency.
@@ -78,8 +65,8 @@ async function readBody(request, limit) {
78
65
  * A request is a `POST` to the path whose JSON body holds the action's pattern and input (`{"cmd": "echo", "text":
79
66
  * "hi"}`). The response is always 200 with the body `{success, error, payload}` (see `ServiceResponseCodec`): `payload`
80
67
  * is the result, or `error` the whole error object when the action failed (no matching action, validation…). A body that
81
- * is not JSON or exceeds `limit` is answered with 400 and an error body; any other method or path with 404. The CORS
82
- * headers allow any origin.
68
+ * is not JSON is answered with 400 and an error body, a body over `limit` with 413; any other method or path with 404.
69
+ * The CORS headers allow any origin, and the headers a preflight asks for (`Authorization`…).
83
70
  *
84
71
  * The events pushed to the clients (`Entrypoint.notify`, `Entrypoint.broadcast`) are streamed as Server-Sent Events by
85
72
  * `GET <path>/events`: each event's name is the SSE event, its arguments the JSON array of its data. A client gives its
@@ -114,7 +101,8 @@ async function readBody(request, limit) {
114
101
  * controllers: [EchoController],
115
102
  * service: httpServiceEntrypoint({port: 3000, path: '/rpc'})
116
103
  * })
117
- * }
104
+ * },
105
+ * bootstrap: ['entrypoint']
118
106
  * })
119
107
  *
120
108
  * //A client: the response is {"success": true, "error": null, "payload": "hi"}
@@ -211,7 +199,7 @@ export function httpServiceEntrypoint(options = {}) {
211
199
  server = createServer((request, response) => {
212
200
  const url = new URL(request.url ?? '/', 'http://localhost');
213
201
  if (request.method === 'OPTIONS')
214
- return void response.writeHead(204, CORS_HEADERS).end();
202
+ return void response.writeHead(204, preflightHeaders(request, CORS_HEADERS)).end();
215
203
  if (request.method === 'GET' && url.pathname === eventsPath)
216
204
  return listen(request, response, url);
217
205
  if (request.method === 'POST' && url.pathname === requestPath)