@astrale-os/kernel-server 0.4.17 → 0.5.0-beta.10

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 (235) hide show
  1. package/dist/capture.d.ts +2 -0
  2. package/dist/capture.js +9 -0
  3. package/dist/cors/cors.d.ts +21 -0
  4. package/dist/cors/cors.js +135 -0
  5. package/dist/cors/index.d.ts +2 -0
  6. package/dist/cors/index.js +1 -0
  7. package/dist/cors/types.d.ts +11 -0
  8. package/dist/cors/types.js +1 -0
  9. package/dist/errors/availability.d.ts +10 -0
  10. package/dist/errors/availability.js +14 -0
  11. package/dist/errors/classify.d.ts +3 -0
  12. package/dist/errors/classify.js +41 -0
  13. package/dist/errors/index.d.ts +4 -0
  14. package/dist/errors/index.js +4 -0
  15. package/dist/errors/request-body.d.ts +7 -0
  16. package/dist/errors/request-body.js +14 -0
  17. package/dist/errors/response-body.d.ts +4 -0
  18. package/dist/errors/response-body.js +8 -0
  19. package/dist/index.d.ts +5 -12
  20. package/dist/index.js +4 -19
  21. package/dist/invocation/context.d.ts +6 -0
  22. package/dist/invocation/context.js +60 -0
  23. package/dist/invocation/identity.d.ts +9 -0
  24. package/dist/invocation/identity.js +11 -0
  25. package/dist/invocation/index.d.ts +4 -3
  26. package/dist/invocation/index.js +3 -3
  27. package/dist/invocation/publication-route.d.ts +13 -0
  28. package/dist/invocation/publication-route.js +44 -0
  29. package/dist/invocation/types.d.ts +62 -0
  30. package/dist/invocation/types.js +1 -0
  31. package/dist/issuer/exchange.d.ts +12 -0
  32. package/dist/issuer/exchange.js +136 -0
  33. package/dist/issuer/index.d.ts +3 -0
  34. package/dist/issuer/index.js +1 -0
  35. package/dist/issuer/routes.d.ts +13 -0
  36. package/dist/issuer/routes.js +149 -0
  37. package/dist/issuer/types.d.ts +11 -0
  38. package/dist/issuer/types.js +1 -0
  39. package/dist/lifecycle/active.d.ts +39 -0
  40. package/dist/lifecycle/active.js +199 -0
  41. package/dist/lifecycle/index.d.ts +1 -0
  42. package/dist/lifecycle/index.js +1 -0
  43. package/dist/publication/delivery/handler.d.ts +9 -0
  44. package/dist/publication/delivery/handler.js +127 -0
  45. package/dist/publication/delivery/index.d.ts +2 -0
  46. package/dist/publication/delivery/index.js +1 -0
  47. package/dist/publication/delivery/types.d.ts +11 -0
  48. package/dist/publication/delivery/types.js +1 -0
  49. package/dist/publication/index.d.ts +3 -0
  50. package/dist/publication/index.js +2 -0
  51. package/dist/publication/snapshot.d.ts +16 -0
  52. package/dist/publication/snapshot.js +37 -0
  53. package/dist/publication/table.d.ts +4 -0
  54. package/dist/publication/table.js +16 -0
  55. package/dist/publication/types.d.ts +22 -0
  56. package/dist/publication/types.js +1 -0
  57. package/dist/routes/index.d.ts +1 -0
  58. package/dist/routes/index.js +1 -0
  59. package/dist/routes/routes.d.ts +25 -0
  60. package/dist/routes/routes.js +448 -0
  61. package/dist/server.d.ts +4 -0
  62. package/dist/server.js +266 -0
  63. package/dist/transport/body.d.ts +4 -0
  64. package/dist/transport/body.js +50 -0
  65. package/dist/transport/codecs.d.ts +4 -0
  66. package/dist/transport/codecs.js +5 -0
  67. package/dist/transport/credential.d.ts +10 -0
  68. package/dist/transport/credential.js +28 -0
  69. package/dist/transport/http/handler.d.ts +17 -0
  70. package/dist/transport/http/handler.js +180 -0
  71. package/dist/transport/http/index.d.ts +2 -0
  72. package/dist/transport/http/index.js +2 -0
  73. package/dist/transport/http/render.d.ts +31 -0
  74. package/dist/transport/http/render.js +288 -0
  75. package/dist/transport/index.d.ts +9 -0
  76. package/dist/transport/index.js +8 -0
  77. package/dist/transport/router.d.ts +4 -0
  78. package/dist/transport/router.js +4 -0
  79. package/dist/transport/stream-error.d.ts +3 -0
  80. package/dist/transport/stream-error.js +13 -0
  81. package/dist/transport/types.d.ts +28 -0
  82. package/dist/transport/types.js +1 -0
  83. package/dist/transport/websocket/index.d.ts +2 -0
  84. package/dist/transport/websocket/index.js +2 -0
  85. package/dist/transport/websocket/session.d.ts +20 -0
  86. package/dist/transport/websocket/session.js +335 -0
  87. package/dist/transport/websocket/upgrade.d.ts +20 -0
  88. package/dist/transport/websocket/upgrade.js +46 -0
  89. package/dist/types.d.ts +65 -0
  90. package/dist/types.js +1 -0
  91. package/package.json +24 -14
  92. package/dist/app/create.d.ts +0 -48
  93. package/dist/app/create.d.ts.map +0 -1
  94. package/dist/app/create.js +0 -108
  95. package/dist/app/create.js.map +0 -1
  96. package/dist/app/index.d.ts +0 -4
  97. package/dist/app/index.d.ts.map +0 -1
  98. package/dist/app/index.js +0 -4
  99. package/dist/app/index.js.map +0 -1
  100. package/dist/app/multiplexed.d.ts +0 -53
  101. package/dist/app/multiplexed.d.ts.map +0 -1
  102. package/dist/app/multiplexed.js +0 -64
  103. package/dist/app/multiplexed.js.map +0 -1
  104. package/dist/app/ws-upgrade.d.ts +0 -34
  105. package/dist/app/ws-upgrade.d.ts.map +0 -1
  106. package/dist/app/ws-upgrade.js +0 -94
  107. package/dist/app/ws-upgrade.js.map +0 -1
  108. package/dist/capabilities/contract.d.ts +0 -8
  109. package/dist/capabilities/contract.d.ts.map +0 -1
  110. package/dist/capabilities/contract.js +0 -2
  111. package/dist/capabilities/contract.js.map +0 -1
  112. package/dist/capabilities/defer.d.ts +0 -4
  113. package/dist/capabilities/defer.d.ts.map +0 -1
  114. package/dist/capabilities/defer.js +0 -2
  115. package/dist/capabilities/defer.js.map +0 -1
  116. package/dist/capabilities/hono.d.ts +0 -8
  117. package/dist/capabilities/hono.d.ts.map +0 -1
  118. package/dist/capabilities/hono.js +0 -13
  119. package/dist/capabilities/hono.js.map +0 -1
  120. package/dist/capabilities/index.d.ts +0 -6
  121. package/dist/capabilities/index.d.ts.map +0 -1
  122. package/dist/capabilities/index.js +0 -3
  123. package/dist/capabilities/index.js.map +0 -1
  124. package/dist/capabilities/lifecycle.d.ts +0 -17
  125. package/dist/capabilities/lifecycle.d.ts.map +0 -1
  126. package/dist/capabilities/lifecycle.js +0 -37
  127. package/dist/capabilities/lifecycle.js.map +0 -1
  128. package/dist/capabilities/sleep.d.ts +0 -4
  129. package/dist/capabilities/sleep.d.ts.map +0 -1
  130. package/dist/capabilities/sleep.js +0 -8
  131. package/dist/capabilities/sleep.js.map +0 -1
  132. package/dist/cors.d.ts +0 -21
  133. package/dist/cors.d.ts.map +0 -1
  134. package/dist/cors.js +0 -17
  135. package/dist/cors.js.map +0 -1
  136. package/dist/envelope/batch.d.ts +0 -13
  137. package/dist/envelope/batch.d.ts.map +0 -1
  138. package/dist/envelope/batch.js +0 -96
  139. package/dist/envelope/batch.js.map +0 -1
  140. package/dist/envelope/codec.d.ts +0 -26
  141. package/dist/envelope/codec.d.ts.map +0 -1
  142. package/dist/envelope/codec.js +0 -52
  143. package/dist/envelope/codec.js.map +0 -1
  144. package/dist/envelope/credential.d.ts +0 -20
  145. package/dist/envelope/credential.d.ts.map +0 -1
  146. package/dist/envelope/credential.js +0 -32
  147. package/dist/envelope/credential.js.map +0 -1
  148. package/dist/envelope/http.d.ts +0 -31
  149. package/dist/envelope/http.d.ts.map +0 -1
  150. package/dist/envelope/http.js +0 -132
  151. package/dist/envelope/http.js.map +0 -1
  152. package/dist/envelope/sse.d.ts +0 -11
  153. package/dist/envelope/sse.d.ts.map +0 -1
  154. package/dist/envelope/sse.js +0 -73
  155. package/dist/envelope/sse.js.map +0 -1
  156. package/dist/envelope/ws.d.ts +0 -35
  157. package/dist/envelope/ws.d.ts.map +0 -1
  158. package/dist/envelope/ws.js +0 -142
  159. package/dist/envelope/ws.js.map +0 -1
  160. package/dist/index.d.ts.map +0 -1
  161. package/dist/index.js.map +0 -1
  162. package/dist/invocation/dispatch.d.ts +0 -32
  163. package/dist/invocation/dispatch.d.ts.map +0 -1
  164. package/dist/invocation/dispatch.js +0 -68
  165. package/dist/invocation/dispatch.js.map +0 -1
  166. package/dist/invocation/dispatcher.d.ts +0 -19
  167. package/dist/invocation/dispatcher.d.ts.map +0 -1
  168. package/dist/invocation/dispatcher.js +0 -9
  169. package/dist/invocation/dispatcher.js.map +0 -1
  170. package/dist/invocation/index.d.ts.map +0 -1
  171. package/dist/invocation/index.js.map +0 -1
  172. package/dist/jwks/discovery.d.ts +0 -40
  173. package/dist/jwks/discovery.d.ts.map +0 -1
  174. package/dist/jwks/discovery.js +0 -113
  175. package/dist/jwks/discovery.js.map +0 -1
  176. package/dist/jwks/index.d.ts +0 -3
  177. package/dist/jwks/index.d.ts.map +0 -1
  178. package/dist/jwks/index.js +0 -3
  179. package/dist/jwks/index.js.map +0 -1
  180. package/dist/jwks/issuers.d.ts +0 -47
  181. package/dist/jwks/issuers.d.ts.map +0 -1
  182. package/dist/jwks/issuers.js +0 -76
  183. package/dist/jwks/issuers.js.map +0 -1
  184. package/dist/jwks/keys.d.ts +0 -44
  185. package/dist/jwks/keys.d.ts.map +0 -1
  186. package/dist/jwks/keys.js +0 -56
  187. package/dist/jwks/keys.js.map +0 -1
  188. package/dist/plan/host-params.d.ts +0 -13
  189. package/dist/plan/host-params.d.ts.map +0 -1
  190. package/dist/plan/host-params.js +0 -23
  191. package/dist/plan/host-params.js.map +0 -1
  192. package/dist/plan/host.d.ts +0 -24
  193. package/dist/plan/host.d.ts.map +0 -1
  194. package/dist/plan/host.js +0 -24
  195. package/dist/plan/host.js.map +0 -1
  196. package/dist/plan/index.d.ts +0 -4
  197. package/dist/plan/index.d.ts.map +0 -1
  198. package/dist/plan/index.js +0 -4
  199. package/dist/plan/index.js.map +0 -1
  200. package/dist/plan/walk.d.ts +0 -51
  201. package/dist/plan/walk.d.ts.map +0 -1
  202. package/dist/plan/walk.js +0 -110
  203. package/dist/plan/walk.js.map +0 -1
  204. package/dist/render/binary.d.ts +0 -19
  205. package/dist/render/binary.d.ts.map +0 -1
  206. package/dist/render/binary.js +0 -33
  207. package/dist/render/binary.js.map +0 -1
  208. package/dist/render/body.d.ts +0 -10
  209. package/dist/render/body.d.ts.map +0 -1
  210. package/dist/render/body.js +0 -17
  211. package/dist/render/body.js.map +0 -1
  212. package/dist/render/index.d.ts +0 -3
  213. package/dist/render/index.d.ts.map +0 -1
  214. package/dist/render/index.js +0 -3
  215. package/dist/render/index.js.map +0 -1
  216. package/dist/routed/credential.d.ts +0 -10
  217. package/dist/routed/credential.d.ts.map +0 -1
  218. package/dist/routed/credential.js +0 -55
  219. package/dist/routed/credential.js.map +0 -1
  220. package/dist/routed/handler.d.ts +0 -38
  221. package/dist/routed/handler.d.ts.map +0 -1
  222. package/dist/routed/handler.js +0 -123
  223. package/dist/routed/handler.js.map +0 -1
  224. package/dist/routed/index.d.ts +0 -5
  225. package/dist/routed/index.d.ts.map +0 -1
  226. package/dist/routed/index.js +0 -5
  227. package/dist/routed/index.js.map +0 -1
  228. package/dist/routed/params.d.ts +0 -40
  229. package/dist/routed/params.d.ts.map +0 -1
  230. package/dist/routed/params.js +0 -146
  231. package/dist/routed/params.js.map +0 -1
  232. package/dist/routed/stream.d.ts +0 -21
  233. package/dist/routed/stream.d.ts.map +0 -1
  234. package/dist/routed/stream.js +0 -69
  235. package/dist/routed/stream.js.map +0 -1
@@ -0,0 +1,335 @@
1
+ import * as errors from '@astrale-os/kernel-protocol/errors';
2
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
3
+ import { classifyError } from '../../errors/index.js';
4
+ import { ResponseBodyTooLargeError } from '../../errors/index.js';
5
+ import { invocationContext, publicationRouteDecision, } from '../../invocation/index.js';
6
+ import { byteLength } from '../body.js';
7
+ import { resolveCredential } from '../credential.js';
8
+ import { renderBinary, renderRedirect } from '../http/render.js';
9
+ import { interruptedStreamError } from '../stream-error.js';
10
+ const WS_OPEN = 1;
11
+ /** Build one socket-pinned transport session. */
12
+ export function createWebSocketSession(config) {
13
+ const codec = config.codec;
14
+ const scopes = new Set();
15
+ const operations = new Map();
16
+ let opened;
17
+ let output = Promise.resolve();
18
+ const sendOutput = (socket, data) => {
19
+ if (byteLength(data) > config.maximumResponseBytes) {
20
+ socket.close(1009, 'Message too big');
21
+ return Promise.reject(new ResponseBodyTooLargeError(config.maximumResponseBytes));
22
+ }
23
+ const pending = output.then(async () => {
24
+ if (socket.readyState !== WS_OPEN)
25
+ return;
26
+ try {
27
+ await socket.send(data);
28
+ }
29
+ catch (cause) {
30
+ socket.close(1011, 'Internal error');
31
+ throw new WebSocketOutputError(cause);
32
+ }
33
+ });
34
+ output = pending.catch(() => undefined);
35
+ return pending;
36
+ };
37
+ const handlers = {
38
+ async open(socket) {
39
+ if (!config.active.attachSocket(socket)) {
40
+ socket.close(1012, 'Server draining');
41
+ return;
42
+ }
43
+ opened = socket;
44
+ },
45
+ async message(socket, data) {
46
+ const admittedCodec = codec;
47
+ if (socket.readyState !== WS_OPEN)
48
+ return;
49
+ if ((admittedCodec.binary && typeof data === 'string') ||
50
+ (!admittedCodec.binary && typeof data !== 'string')) {
51
+ socket.close(1003, 'Unsupported message type');
52
+ return;
53
+ }
54
+ if (byteLength(data) > config.maximumRequestBytes) {
55
+ socket.close(1009, 'Message too big');
56
+ return;
57
+ }
58
+ let decoded;
59
+ try {
60
+ decoded = admittedCodec.decode(data);
61
+ }
62
+ catch {
63
+ socket.close(1002, 'Invalid Invocation message');
64
+ return;
65
+ }
66
+ if (isCancelRequest(decoded)) {
67
+ let cancellation;
68
+ try {
69
+ cancellation = protocol.decodeCancelRequest(decoded);
70
+ }
71
+ catch {
72
+ socket.close(1002, 'Invalid cancellation message');
73
+ return;
74
+ }
75
+ const operation = operations.get(requestKey(cancellation.requestId));
76
+ if (operation === undefined) {
77
+ await sendError(socket, admittedCodec, cancellation.requestId, config.invocation(), {
78
+ code: errors.CODES.INVALID_REQUEST,
79
+ message: 'Request ID is not active on this connection.',
80
+ }, sendOutput);
81
+ return;
82
+ }
83
+ operation.cancel('client cancelled invocation');
84
+ return;
85
+ }
86
+ let envelope;
87
+ try {
88
+ envelope = protocol.decodeRequest(decoded);
89
+ }
90
+ catch {
91
+ socket.close(1002, 'Invalid Invocation request');
92
+ return;
93
+ }
94
+ const assigned = config.invocation();
95
+ const key = requestKey(envelope.requestId);
96
+ if (operations.has(key)) {
97
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, {
98
+ code: errors.CODES.INVALID_REQUEST,
99
+ message: 'Request ID is already active on this connection.',
100
+ }, sendOutput);
101
+ return;
102
+ }
103
+ const publication = config.currentPublication?.();
104
+ if (config.currentPublication !== undefined && publication === undefined) {
105
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, {
106
+ code: errors.CODES.BACKEND_UNAVAILABLE,
107
+ message: 'Publication is unavailable.',
108
+ }, sendOutput);
109
+ return;
110
+ }
111
+ if (envelope.routing?.kind === 'via') {
112
+ if (publication === undefined) {
113
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, {
114
+ code: errors.CODES.INVALID_REQUEST,
115
+ message: 'Publication routing requires a Publication-backed destination.',
116
+ }, sendOutput);
117
+ return;
118
+ }
119
+ const decision = publicationRouteDecision(publication.publication, envelope.routing.publication);
120
+ if (decision.kind === 'rejected') {
121
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, decision.error, sendOutput);
122
+ return;
123
+ }
124
+ }
125
+ const credential = resolveCredential(envelope.credential, config.authorization);
126
+ if (!credential.ok) {
127
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, credential.error, sendOutput);
128
+ return;
129
+ }
130
+ const operation = config.active.begin(config.invocationTimeoutMs);
131
+ if (operation === undefined) {
132
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, {
133
+ code: errors.CODES.BACKEND_UNAVAILABLE,
134
+ message: 'server is unavailable.',
135
+ }, sendOutput);
136
+ return;
137
+ }
138
+ operations.set(key, operation);
139
+ scopes.add(operation);
140
+ try {
141
+ const result = await config.handler.dispatch(Object.freeze({
142
+ call: envelope.call,
143
+ ...(envelope.trace === undefined ? {} : { trace: envelope.trace }),
144
+ ...(credential.credential === undefined ? {} : { credential: credential.credential }),
145
+ ...(envelope.delegate === undefined ? {} : { delegate: envelope.delegate }),
146
+ ...(envelope.routing?.kind === 'via' ? { via: envelope.routing } : {}),
147
+ ...(envelope.schema === undefined ? {} : { schema: envelope.schema }),
148
+ ...(publication === undefined ? {} : { publication }),
149
+ context: invocationContext(assigned, operation, config.active, config.host, undefined, envelope.idempotencyKey),
150
+ }));
151
+ await sendResult(socket, admittedCodec, envelope.requestId, assigned, result, operation, sendOutput);
152
+ }
153
+ catch (input) {
154
+ if (input instanceof ResponseBodyTooLargeError || input instanceof WebSocketOutputError) {
155
+ return;
156
+ }
157
+ if (socket.readyState === WS_OPEN) {
158
+ const error = operation.timedOut
159
+ ? {
160
+ code: errors.CODES.DEADLINE_EXCEEDED,
161
+ message: 'Invocation deadline exceeded.',
162
+ }
163
+ : operation.signal.aborted
164
+ ? { code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }
165
+ : classifyError(input);
166
+ if (error.code === errors.CODES.INTERNAL_ERROR) {
167
+ socket.close(1011, 'Internal error');
168
+ return;
169
+ }
170
+ await sendError(socket, admittedCodec, envelope.requestId, assigned, error, sendOutput);
171
+ }
172
+ }
173
+ finally {
174
+ operations.delete(key);
175
+ scopes.delete(operation);
176
+ operation.settle();
177
+ }
178
+ },
179
+ close(socket) {
180
+ config.active.detachSocket(socket);
181
+ for (const scope of scopes)
182
+ scope.cancel('websocket closed');
183
+ if (opened === socket)
184
+ opened = undefined;
185
+ },
186
+ error(socket) {
187
+ config.active.detachSocket(socket);
188
+ for (const scope of scopes)
189
+ scope.cancel('websocket failed');
190
+ if (socket.readyState === WS_OPEN)
191
+ socket.close(1011, 'Internal error');
192
+ if (opened === socket)
193
+ opened = undefined;
194
+ },
195
+ };
196
+ return handlers;
197
+ }
198
+ async function sendResult(socket, codec, requestId, assigned, result, operation, sendOutput) {
199
+ switch (result.kind) {
200
+ case 'value':
201
+ await send(socket, codec, {
202
+ result: result.value,
203
+ requestId,
204
+ invocation: assigned,
205
+ }, sendOutput);
206
+ return;
207
+ case 'redirect':
208
+ await send(socket, codec, renderRedirect(requestId, assigned, result.redirect), sendOutput);
209
+ return;
210
+ case 'miss':
211
+ await send(socket, codec, protocol.miss(requestId, assigned), sendOutput);
212
+ return;
213
+ case 'binary': {
214
+ const binary = renderBinary(requestId, assigned, result.value);
215
+ if (socket.readyState === WS_OPEN) {
216
+ await sendOutput(socket, protocol.websocket.encodeBinary(binary));
217
+ }
218
+ return;
219
+ }
220
+ case 'stream': {
221
+ const iterator = result.stream[Symbol.asyncIterator]();
222
+ let terminal = false;
223
+ let cancellation;
224
+ let sequence = Promise.resolve();
225
+ const emit = (frame) => {
226
+ const pending = sequence.then(() => send(socket, codec, frame, sendOutput));
227
+ sequence = pending.catch(() => undefined);
228
+ return pending;
229
+ };
230
+ const cancel = (reason) => {
231
+ cancellation ??= cancelStream(reason);
232
+ return cancellation;
233
+ };
234
+ const cancelStream = async (reason) => {
235
+ if (terminal)
236
+ return;
237
+ terminal = true;
238
+ const terminalOutput = emit({
239
+ kind: 'stream.error',
240
+ requestId,
241
+ error: interruptedStreamError(operation.timedOut),
242
+ });
243
+ await Promise.allSettled([
244
+ terminalOutput,
245
+ Promise.resolve().then(() => result.stream.cancel(reason)),
246
+ Promise.resolve().then(() => iterator.return?.()),
247
+ ]);
248
+ };
249
+ const started = emit({
250
+ kind: 'stream.start',
251
+ requestId,
252
+ invocation: assigned,
253
+ });
254
+ operation.onCancel(cancel);
255
+ let count = 0;
256
+ try {
257
+ await started;
258
+ while (!terminal) {
259
+ const next = await iterator.next();
260
+ if (terminal)
261
+ break;
262
+ if (next.done) {
263
+ terminal = true;
264
+ await emit({ kind: 'stream.end', requestId, count });
265
+ break;
266
+ }
267
+ await emit(protocol.decodeStreamFrame({
268
+ kind: 'stream.item',
269
+ requestId,
270
+ index: count,
271
+ value: next.value,
272
+ }));
273
+ count += 1;
274
+ }
275
+ if (cancellation !== undefined)
276
+ await cancellation;
277
+ }
278
+ catch (input) {
279
+ if (input instanceof ResponseBodyTooLargeError || input instanceof WebSocketOutputError) {
280
+ terminal = true;
281
+ await Promise.allSettled([
282
+ Promise.resolve().then(() => result.stream.cancel('transport output failed')),
283
+ Promise.resolve().then(() => iterator.return?.()),
284
+ ]);
285
+ throw input;
286
+ }
287
+ if (terminal) {
288
+ if (cancellation !== undefined)
289
+ await cancellation;
290
+ return;
291
+ }
292
+ terminal = true;
293
+ const error = classifyError(input);
294
+ if (error.code === errors.CODES.INTERNAL_ERROR) {
295
+ socket.close(1011, 'Internal error');
296
+ await Promise.allSettled([
297
+ Promise.resolve().then(() => result.stream.cancel('stream failed')),
298
+ Promise.resolve().then(() => iterator.return?.()),
299
+ ]);
300
+ return;
301
+ }
302
+ await Promise.allSettled([
303
+ emit({
304
+ kind: 'stream.error',
305
+ requestId,
306
+ error,
307
+ }),
308
+ Promise.resolve().then(() => result.stream.cancel('stream failed')),
309
+ Promise.resolve().then(() => iterator.return?.()),
310
+ ]);
311
+ }
312
+ }
313
+ }
314
+ }
315
+ function sendError(socket, codec, requestId, invocation, error, output) {
316
+ return send(socket, codec, { error, requestId, invocation }, output);
317
+ }
318
+ function send(socket, codec, value, output) {
319
+ return output(socket, codec.encode(value));
320
+ }
321
+ class WebSocketOutputError extends Error {
322
+ constructor(cause) {
323
+ super('WebSocket adapter rejected output.', { cause });
324
+ this.name = 'WebSocketOutputError';
325
+ }
326
+ }
327
+ function requestKey(requestId) {
328
+ return requestId === null ? 'null:' : `${typeof requestId}:${String(requestId)}`;
329
+ }
330
+ function isCancelRequest(input) {
331
+ return (typeof input === 'object' &&
332
+ input !== null &&
333
+ !Array.isArray(input) &&
334
+ input.kind === 'cancel');
335
+ }
@@ -0,0 +1,20 @@
1
+ import type { CorsPolicy } from '../../cors/index.js';
2
+ import type { ApplicationHost, Handler } from '../../invocation/index.js';
3
+ import type { NextInvocationId } from '../../invocation/index.js';
4
+ import type { ActiveOperations } from '../../lifecycle/index.js';
5
+ import type { PublicationSnapshot } from '../../publication/index.js';
6
+ import type { WebSocketAdapter } from '../types.js';
7
+ export interface WebSocketHandlerConfig {
8
+ readonly request: Request;
9
+ readonly adapter: WebSocketAdapter;
10
+ readonly handler: Handler;
11
+ readonly active: ActiveOperations;
12
+ readonly invocation: NextInvocationId;
13
+ readonly maximumRequestBytes: number;
14
+ readonly maximumResponseBytes: number;
15
+ readonly invocationTimeoutMs: number;
16
+ readonly cors: CorsPolicy;
17
+ readonly currentPublication?: () => PublicationSnapshot | undefined;
18
+ readonly host?: ApplicationHost;
19
+ }
20
+ export declare function handleWebSocketUpgrade(config: WebSocketHandlerConfig): Promise<Response>;
@@ -0,0 +1,46 @@
1
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
+ import { loadCodecs } from '../codecs.js';
3
+ import { createWebSocketSession } from './session.js';
4
+ export async function handleWebSocketUpgrade(config) {
5
+ if (!config.cors.allowsBrowserOrigin(config.request))
6
+ return failure(403);
7
+ const operation = config.active.begin(config.invocationTimeoutMs, config.request.signal);
8
+ if (operation === undefined)
9
+ return failure(503, config.cors.response(config.request));
10
+ try {
11
+ const supported = await loadCodecs();
12
+ const selection = protocol.websocket.selectSubprotocol(config.request.headers.get('sec-websocket-protocol'), supported);
13
+ if (selection === undefined)
14
+ return failure(400, config.cors.response(config.request));
15
+ return await config.adapter.upgrade(config.request, {
16
+ subprotocol: selection.subprotocol,
17
+ handlers: createWebSocketSession({
18
+ codec: selection.codec,
19
+ authorization: config.request.headers.get('authorization'),
20
+ handler: config.handler,
21
+ active: config.active,
22
+ invocation: config.invocation,
23
+ maximumRequestBytes: config.maximumRequestBytes,
24
+ maximumResponseBytes: config.maximumResponseBytes,
25
+ invocationTimeoutMs: config.invocationTimeoutMs,
26
+ ...(config.currentPublication === undefined
27
+ ? {}
28
+ : { currentPublication: config.currentPublication }),
29
+ ...(config.host === undefined ? {} : { host: config.host }),
30
+ }),
31
+ signal: operation.signal,
32
+ });
33
+ }
34
+ catch {
35
+ return failure(500, config.cors.response(config.request));
36
+ }
37
+ finally {
38
+ operation.settle();
39
+ }
40
+ }
41
+ function failure(status, input = {}) {
42
+ return new Response(null, {
43
+ status,
44
+ headers: { ...input, 'cache-control': 'no-store' },
45
+ });
46
+ }
@@ -0,0 +1,65 @@
1
+ import type { CorsConfig } from './cors/index.js';
2
+ import type { Handler, InvocationSource } from './invocation/index.js';
3
+ import type { exchange, Issuer } from './issuer/index.js';
4
+ import type { Provider as DeliveryProvider } from './publication/delivery/index.js';
5
+ import type { PublicationSource } from './publication/index.js';
6
+ import type { Application, WebSocketAdapter } from './transport/index.js';
7
+ export interface ServerLimits {
8
+ readonly maximumRequestBytes: number;
9
+ /** Maximum bytes in one encoded envelope, finite-binary body, or stream frame. */
10
+ readonly maximumResponseBytes?: number;
11
+ /** Maximum Server-owned active operations across all mounted protocol surfaces. */
12
+ readonly maximumConcurrentOperations?: number;
13
+ readonly maximumPublicationBytes?: number;
14
+ readonly maximumBundleBytes?: number;
15
+ readonly maximumDeliveryBytes?: number;
16
+ /** Maximum lifetime of one invocation, including a returned stream. */
17
+ readonly invocationTimeoutMs: number;
18
+ }
19
+ export interface ServerCloseOptions {
20
+ /** Absolute Unix time in milliseconds after which active work is cancelled. */
21
+ readonly deadline: number;
22
+ }
23
+ export interface HealthProbe {
24
+ /** Non-blocking snapshot; health never performs provider I/O. */
25
+ check(): boolean;
26
+ }
27
+ /** Static transport configuration admitted before Runtime Boot performs I/O. */
28
+ export interface ServerOptions {
29
+ readonly limits: ServerLimits;
30
+ /** Required source-owned identity allocation for every Invocation response attempt. */
31
+ readonly invocation: InvocationSource;
32
+ readonly path?: string;
33
+ /** `false` delegates CORS and every preflight to an outer Application. */
34
+ readonly cors?: CorsConfig | false;
35
+ readonly health?: {
36
+ readonly path?: string;
37
+ readonly probe: HealthProbe;
38
+ } | false;
39
+ readonly issuer?: Issuer;
40
+ /** Present only for the configured self issuer; owns verification and signing. */
41
+ readonly tokenExchange?: exchange.Provider;
42
+ readonly publication?: PublicationSource;
43
+ /** Required exactly when the current Publication advertises delivery. */
44
+ readonly delivery?: DeliveryProvider;
45
+ readonly websocket?: WebSocketAdapter;
46
+ }
47
+ /** Static options plus the one ready semantic capability consumed by Server. */
48
+ export interface ServerConfig extends ServerOptions {
49
+ readonly handler: Handler;
50
+ }
51
+ /** Admitted Server construction with the ready handler deliberately left unbound. */
52
+ export interface PreparedServer {
53
+ create(handler: Handler): Server;
54
+ }
55
+ export interface Server {
56
+ readonly app: Application;
57
+ drain(): void;
58
+ close(options: ServerCloseOptions): Promise<void>;
59
+ }
60
+ export type { CorsConfig } from './cors/index.js';
61
+ export type { InvocationSource } from './invocation/index.js';
62
+ export type { ApplicationHost, ExecutionControl, Handler, HandlerRequest, HandlerResult, HandlerStream, InvocationContext, RequestBackground, RequestEvidence, RequestSleep, } from './invocation/index.js';
63
+ export type { exchange, Issuer } from './issuer/index.js';
64
+ export type { PublicationSnapshot, PublicationSnapshotInput, PublicationSource, } from './publication/index.js';
65
+ export type { Application, WebSocketAdapter, WebSocketHandlers, WebSocketLike, WebSocketUpgradeOptions, } from './transport/index.js';
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-server",
3
- "version": "0.4.17",
4
- "description": "Astrale Kernel Server - kernel envelope HTTP/WS server with Hono",
3
+ "version": "0.5.0-beta.10",
4
+ "description": "Portable HTTP and WebSocket boundary for Astrale invocation, publication, installation, and issuer protocols",
5
5
  "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/astrale-os/kernel.git",
9
+ "directory": "server"
10
+ },
6
11
  "files": [
7
12
  "dist"
8
13
  ],
@@ -14,9 +19,13 @@
14
19
  "types": "./dist/index.d.ts",
15
20
  "import": "./dist/index.js"
16
21
  },
17
- "./app": {
18
- "types": "./dist/app/index.d.ts",
19
- "import": "./dist/app/index.js"
22
+ "./publication/snapshot": {
23
+ "types": "./dist/publication/snapshot.d.ts",
24
+ "import": "./dist/publication/snapshot.js"
25
+ },
26
+ "./server": {
27
+ "types": "./dist/server.d.ts",
28
+ "import": "./dist/server.js"
20
29
  },
21
30
  "./package.json": "./package.json"
22
31
  },
@@ -24,23 +33,24 @@
24
33
  "access": "public"
25
34
  },
26
35
  "dependencies": {
36
+ "@astrale-os/kernel-core": ">=0.9.0-beta.9 <1.0.0",
37
+ "@astrale-os/kernel-dsl": ">=0.2.0-beta.7 <1.0.0",
38
+ "@astrale-os/kernel-protocol": ">=0.5.0-beta.9 <1.0.0",
27
39
  "hono": "^4.6.20",
28
- "zod": "^4.3.6",
29
- "@astrale-os/kernel-api": "^0.4.18",
30
- "@astrale-os/kernel-core": "^0.8.1"
40
+ "msgpackr": "^2.0.5"
31
41
  },
32
42
  "devDependencies": {
33
43
  "@astrale/typescript-config": "npm:@jsr/astrale__typescript-config@~1.1.0",
34
- "@types/node": "^22.15.0",
35
- "@typescript/native-preview": "latest",
36
- "typescript": "~6.0.1-rc",
37
- "vitest": "^3.0.0"
44
+ "typescript": "7.0.2",
45
+ "vitest": "^4.1.10"
38
46
  },
39
47
  "scripts": {
40
- "build": "tsgo",
48
+ "build": "tsc",
41
49
  "clean": "rm -rf dist",
42
- "typecheck": "tsgo --noEmit",
50
+ "typecheck": "tsc --noEmit",
43
51
  "test": "vitest run",
52
+ "test:boundary:protocol": "vitest run --config vitest.protocol-boundary.config.ts",
53
+ "test:file": "vitest run",
44
54
  "preinstall": "node -e \"try{require('./.check-workspace.cjs')}catch{}\""
45
55
  }
46
56
  }
@@ -1,48 +0,0 @@
1
- /**
2
- * createKernelApp — Hono app builder for the kernel protocol.
3
- *
4
- * Walks a domain of MountableContract and mounts:
5
- * - kernel HTTP envelope (POST) at each base URL
6
- * - native HTTP routes per route binding
7
- * - kernel WS upgrade at the same base URL when transports includes 'ws'
8
- *
9
- * Codecs are hardcoded (json + msgpack). Credentials for kernel HTTP are
10
- * fixed to the Authorization header; route bindings can override via
11
- * `route.credential`.
12
- */
13
- import type { MountableContract } from '@astrale-os/kernel-api/routed';
14
- import type { Hono } from 'hono';
15
- import type { CorsConfig } from '../cors.js';
16
- import type { Dispatcher } from '../invocation/index.js';
17
- import type { JwksKeys } from '../jwks/index.js';
18
- import type { MountPlan, ServerHost } from '../plan/index.js';
19
- import { type WsAdapter } from './ws-upgrade.js';
20
- export type { WsAdapter };
21
- export type KernelAppConfig = {
22
- /** The dispatcher handling hosted invocations. */
23
- dispatcher: Dispatcher;
24
- /** The domain's mountable contracts — produced by defineMethods(schema, methods). */
25
- domain: MountableContract[];
26
- /** Server host URL. Functions anchor against this (ambient functions mount here). */
27
- host: ServerHost;
28
- /** Required — OpenID Connect Discovery + JWKS endpoints. */
29
- jwks: JwksKeys;
30
- /** Allowed transports. 'http' is mandatory. 'ws' is opt-in. */
31
- transports?: readonly ('http' | 'ws')[];
32
- /** CORS configuration. Defaults to { origin: '*' }. */
33
- cors?: CorsConfig;
34
- /** Optional health endpoint path (defaults to '/health'; false disables). */
35
- health?: string | false;
36
- /** Pre-created Hono app (for attaching to an existing app). */
37
- app?: Hono;
38
- /**
39
- * Runtime-specific WS adapter (from `hono/bun`, `@hono/node-ws`, `hono/deno`).
40
- * Required when `transports` includes `'ws'`.
41
- */
42
- ws?: WsAdapter;
43
- };
44
- export declare function createKernelApp(config: KernelAppConfig): {
45
- app: Hono;
46
- plan: MountPlan;
47
- };
48
- //# sourceMappingURL=create.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../app/create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAA;AAIzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAO7D,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE/D,YAAY,EAAE,SAAS,EAAE,CAAA;AAIzB,MAAM,MAAM,eAAe,GAAG;IAC5B,kDAAkD;IAClD,UAAU,EAAE,UAAU,CAAA;IACtB,qFAAqF;IACrF,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,qFAAqF;IACrF,IAAI,EAAE,UAAU,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,QAAQ,CAAA;IACd,+DAA+D;IAC/D,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;IACvC,uDAAuD;IACvD,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IACvB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,IAAI,CAAA;IACV;;;OAGG;IACH,EAAE,CAAC,EAAE,SAAS,CAAA;CACf,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG;IAAE,GAAG,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAyFvF"}