@astrale-os/sdk 0.4.9 → 0.4.11

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 (54) hide show
  1. package/dist/auth/identity.d.ts +2 -0
  2. package/dist/auth/identity.d.ts.map +1 -1
  3. package/dist/auth/verify.js +1 -1
  4. package/dist/auth/verify.js.map +1 -1
  5. package/dist/cli/spec.d.ts.map +1 -1
  6. package/dist/cli/spec.js +1 -0
  7. package/dist/cli/spec.js.map +1 -1
  8. package/dist/config/define-domain.d.ts +5 -1
  9. package/dist/config/define-domain.d.ts.map +1 -1
  10. package/dist/config/define-domain.js +1 -0
  11. package/dist/config/define-domain.js.map +1 -1
  12. package/dist/defer/index.d.ts +24 -0
  13. package/dist/defer/index.d.ts.map +1 -0
  14. package/dist/defer/index.js +42 -0
  15. package/dist/defer/index.js.map +1 -0
  16. package/dist/define/remote-function.d.ts +3 -0
  17. package/dist/define/remote-function.d.ts.map +1 -1
  18. package/dist/define/remote-function.js.map +1 -1
  19. package/dist/dispatch/dispatcher.d.ts +6 -3
  20. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  21. package/dist/dispatch/dispatcher.js +45 -23
  22. package/dist/dispatch/dispatcher.js.map +1 -1
  23. package/dist/dispatch/execute.d.ts +2 -0
  24. package/dist/dispatch/execute.d.ts.map +1 -1
  25. package/dist/dispatch/execute.js +1 -0
  26. package/dist/dispatch/execute.js.map +1 -1
  27. package/dist/domain/build-spec.js +4 -0
  28. package/dist/domain/build-spec.js.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/method/context.d.ts +3 -0
  33. package/dist/method/context.d.ts.map +1 -1
  34. package/dist/server/auxiliary-routes.d.ts.map +1 -1
  35. package/dist/server/auxiliary-routes.js +47 -11
  36. package/dist/server/auxiliary-routes.js.map +1 -1
  37. package/dist/server/worker-entry.d.ts +3 -2
  38. package/dist/server/worker-entry.d.ts.map +1 -1
  39. package/dist/server/worker-entry.js +2 -2
  40. package/dist/server/worker-entry.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/auth/identity.ts +2 -0
  43. package/src/auth/verify.ts +1 -1
  44. package/src/cli/spec.ts +1 -0
  45. package/src/config/define-domain.ts +6 -1
  46. package/src/defer/index.ts +64 -0
  47. package/src/define/remote-function.ts +3 -0
  48. package/src/dispatch/dispatcher.ts +50 -22
  49. package/src/dispatch/execute.ts +3 -0
  50. package/src/domain/build-spec.ts +4 -0
  51. package/src/index.ts +3 -0
  52. package/src/method/context.ts +3 -0
  53. package/src/server/auxiliary-routes.ts +59 -10
  54. package/src/server/worker-entry.ts +12 -4
@@ -18,10 +18,15 @@ import type { AuthContext } from '@astrale-os/kernel-core'
18
18
  import type { Context, Hono } from 'hono'
19
19
 
20
20
  import {
21
+ decodeKernelRequest,
21
22
  isKernelErrorClassifiable,
23
+ KERNEL_CONTENT_TYPE_JSON,
24
+ KERNEL_CONTENT_TYPE_MSGPACK,
22
25
  KERNEL_ERROR_CODES,
23
26
  kernelErrorHttpStatus,
27
+ resolveCodec,
24
28
  type KernelErrorPayload,
29
+ type RequestId,
25
30
  } from '@astrale-os/kernel-api'
26
31
  import {
27
32
  isSubdomainOf,
@@ -41,6 +46,7 @@ import type { AuxIdentityMap } from '../dispatch/identity'
41
46
  import { makeFunctionContext } from '../auth/function-context'
42
47
  import { makeDomainAuthority } from '../auth/issuer-mint'
43
48
  import { resolveInboundAuth } from '../auth/resolve'
49
+ import { createHandlerLifecycle, executionLifetime } from '../defer'
44
50
  import { runAuthorize } from '../dispatch/authorize'
45
51
  import { SdkResultValidationError, SdkValidationError } from '../dispatch/errors'
46
52
  import { validateParams, validateResult } from '../dispatch/validate'
@@ -137,11 +143,8 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
137
143
  identity,
138
144
  corsHeaders,
139
145
  run: async ({ c, auth, kernel }) => {
140
- const rawBody: unknown = await c.req.raw
141
- .clone()
142
- .json()
143
- .catch(() => ({}))
144
- const validation = validateParams(def.inputSchema, rawBody)
146
+ const input = await readRemoteFunctionInput(c)
147
+ const validation = validateParams(def.inputSchema, input.params)
145
148
  if (!validation.ok) {
146
149
  throw new SdkValidationError(validation.issues as SdkValidationError['issues'])
147
150
  }
@@ -169,10 +172,20 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
169
172
  }),
170
173
  }
171
174
  if (def.authorize) await runAuthorize(def.authorize, ctx)
172
- const result = await def.execute({
173
- ...ctx,
174
- step: createInlineStep({ scope: { kind: 'function', ref: `function.${slug}`, slug } }),
175
- })
175
+ const lifecycle = createHandlerLifecycle(executionLifetime(c))
176
+ const result = await (async () => {
177
+ try {
178
+ return await def.execute({
179
+ ...ctx,
180
+ step: createInlineStep({
181
+ scope: { kind: 'function', ref: `function.${slug}`, slug },
182
+ }),
183
+ defer: lifecycle.defer,
184
+ })
185
+ } finally {
186
+ await lifecycle.settle()
187
+ }
188
+ })()
176
189
  const outValidation = validateResult(def.outputSchema, result)
177
190
  if (!outValidation.ok) {
178
191
  throw new SdkResultValidationError(
@@ -180,7 +193,10 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
180
193
  `function.${slug}`,
181
194
  )
182
195
  }
183
- return c.json({ result: outValidation.data })
196
+ return c.json({
197
+ result: outValidation.data,
198
+ ...(input.enveloped ? { id: input.id ?? null } : {}),
199
+ })
184
200
  },
185
201
  })
186
202
  }
@@ -306,6 +322,39 @@ function mountEntry(args: MountEntryArgs): void {
306
322
  })
307
323
  }
308
324
 
325
+ type RemoteFunctionInput = {
326
+ params: unknown
327
+ enveloped: boolean
328
+ id?: RequestId
329
+ }
330
+
331
+ /**
332
+ * A Function binding is reached in two legitimate ways:
333
+ * - kernel-client follows a graph redirect and sends the canonical kernel envelope;
334
+ * - an external webhook caller POSTs its raw JSON contract directly.
335
+ *
336
+ * The vendor content type is the discriminator. Shape-sniffing would break a valid
337
+ * webhook whose own payload happens to contain `method` and `params` fields.
338
+ */
339
+ async function readRemoteFunctionInput(c: Context): Promise<RemoteFunctionInput> {
340
+ const contentType = c.req.header('content-type') ?? ''
341
+ const isJsonEnvelope = contentType.includes(KERNEL_CONTENT_TYPE_JSON)
342
+ const isMsgpackEnvelope = contentType.includes(KERNEL_CONTENT_TYPE_MSGPACK)
343
+
344
+ if (isJsonEnvelope || isMsgpackEnvelope) {
345
+ const bytes = new Uint8Array(await c.req.raw.clone().arrayBuffer())
346
+ const codec = resolveCodec(isMsgpackEnvelope ? 'msgpack' : 'json')
347
+ const request = decodeKernelRequest(codec.decode(bytes))
348
+ return { params: request.params, enveloped: true, id: request.id }
349
+ }
350
+
351
+ const params: unknown = await c.req.raw
352
+ .clone()
353
+ .json()
354
+ .catch(() => ({}))
355
+ return { params, enveloped: false }
356
+ }
357
+
309
358
  function applyCorsToContext(c: Context, headers: Record<string, string>): void {
310
359
  for (const [name, value] of Object.entries(headers)) c.header(name, value)
311
360
  }
@@ -22,6 +22,8 @@
22
22
  * The worker file is then just schema + methods + a `build(url, env)` callback.
23
23
  */
24
24
 
25
+ import type { ExecutionContext } from 'hono'
26
+
25
27
  import type { RemoteServerConfig } from './config'
26
28
 
27
29
  import { createRemoteServer } from './create'
@@ -29,7 +31,13 @@ import { requireEnv } from './require-env'
29
31
  import { canonicalizeServingUrl } from './serving-url'
30
32
 
31
33
  type Fetcher = { fetch(request: Request): Response | Promise<Response> }
32
- type App = { fetch(request: Request): Response | Promise<Response> }
34
+ type App = {
35
+ fetch(
36
+ request: Request,
37
+ env?: unknown,
38
+ executionCtx?: ExecutionContext,
39
+ ): Response | Promise<Response>
40
+ }
33
41
 
34
42
  /** A built app cached by its serving URL: `origin` for same-origin matching,
35
43
  * `app` for in-process self-dispatch. */
@@ -130,7 +138,7 @@ export interface WorkerEntryConfig<TDeps> {
130
138
  }
131
139
 
132
140
  export interface WorkerEntry<TDeps> {
133
- fetch(request: Request, env: TDeps): Response | Promise<Response>
141
+ fetch(request: Request, env: TDeps, executionCtx?: ExecutionContext): Response | Promise<Response>
134
142
  }
135
143
 
136
144
  /**
@@ -243,7 +251,7 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
243
251
  }
244
252
 
245
253
  return {
246
- async fetch(request: Request, env: TDeps): Promise<Response> {
254
+ async fetch(request: Request, env: TDeps, executionCtx?: ExecutionContext): Promise<Response> {
247
255
  if (config.selfBinding) self ??= config.selfBinding(env) ?? null
248
256
  if (config.routeSubrequest) routeEnv = env
249
257
  // Only parse the request URL when a hook actually needs it.
@@ -259,7 +267,7 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
259
267
  : requireEnv(env, 'WORKER_URL', "the worker's public serving URL (its iss identity)")
260
268
  const url = canonicalizeServingUrl(raw)
261
269
  const dispatched = config.rewriteRequest ? config.rewriteRequest(env, request) : request
262
- return getApp(url, env).fetch(dispatched)
270
+ return getApp(url, env).fetch(dispatched, env, executionCtx)
263
271
  },
264
272
  }
265
273
  }