@astrale-os/sdk 0.4.0 → 0.4.2

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 (101) hide show
  1. package/dist/auth/compose.d.ts +2 -2
  2. package/dist/auth/compose.d.ts.map +1 -1
  3. package/dist/auth/compose.js +2 -2
  4. package/dist/auth/compose.js.map +1 -1
  5. package/dist/auth/function-context.d.ts +28 -0
  6. package/dist/auth/function-context.d.ts.map +1 -0
  7. package/dist/auth/function-context.js +47 -0
  8. package/dist/auth/function-context.js.map +1 -0
  9. package/dist/auth/index.d.ts +5 -3
  10. package/dist/auth/index.d.ts.map +1 -1
  11. package/dist/auth/index.js +3 -2
  12. package/dist/auth/index.js.map +1 -1
  13. package/dist/auth/issuer-mint.d.ts +9 -9
  14. package/dist/auth/issuer-mint.d.ts.map +1 -1
  15. package/dist/auth/issuer-mint.js +3 -3
  16. package/dist/auth/issuer-mint.js.map +1 -1
  17. package/dist/auth/kernel-client.d.ts +5 -17
  18. package/dist/auth/kernel-client.d.ts.map +1 -1
  19. package/dist/auth/kernel-client.js +12 -34
  20. package/dist/auth/kernel-client.js.map +1 -1
  21. package/dist/cli/publish.js +1 -1
  22. package/dist/cli/publish.js.map +1 -1
  23. package/dist/config/define-domain.d.ts +1 -1
  24. package/dist/define/index.d.ts +1 -1
  25. package/dist/define/index.d.ts.map +1 -1
  26. package/dist/define/remote-function.d.ts +22 -30
  27. package/dist/define/remote-function.d.ts.map +1 -1
  28. package/dist/define/remote-function.js.map +1 -1
  29. package/dist/define/view.d.ts +6 -10
  30. package/dist/define/view.d.ts.map +1 -1
  31. package/dist/define/view.js.map +1 -1
  32. package/dist/dispatch/dispatcher.d.ts +1 -1
  33. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  34. package/dist/dispatch/dispatcher.js +31 -7
  35. package/dist/dispatch/dispatcher.js.map +1 -1
  36. package/dist/dispatch/execute.d.ts +10 -6
  37. package/dist/dispatch/execute.d.ts.map +1 -1
  38. package/dist/dispatch/execute.js +5 -3
  39. package/dist/dispatch/execute.js.map +1 -1
  40. package/dist/dispatch/index.d.ts +0 -1
  41. package/dist/dispatch/index.d.ts.map +1 -1
  42. package/dist/dispatch/index.js +0 -1
  43. package/dist/dispatch/index.js.map +1 -1
  44. package/dist/index.d.ts +6 -5
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +1 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/method/context.d.ts +34 -28
  49. package/dist/method/context.d.ts.map +1 -1
  50. package/dist/method/context.js +2 -1
  51. package/dist/method/context.js.map +1 -1
  52. package/dist/method/index.d.ts +1 -1
  53. package/dist/method/index.d.ts.map +1 -1
  54. package/dist/method/index.js.map +1 -1
  55. package/dist/method/single.d.ts +2 -2
  56. package/dist/method/single.d.ts.map +1 -1
  57. package/dist/server/auxiliary-routes.d.ts.map +1 -1
  58. package/dist/server/auxiliary-routes.js +40 -21
  59. package/dist/server/auxiliary-routes.js.map +1 -1
  60. package/dist/server/create.js +1 -1
  61. package/dist/server/create.js.map +1 -1
  62. package/dist/step/index.d.ts +5 -0
  63. package/dist/step/index.d.ts.map +1 -0
  64. package/dist/step/index.js +3 -0
  65. package/dist/step/index.js.map +1 -0
  66. package/dist/step/inline.d.ts +8 -0
  67. package/dist/step/inline.d.ts.map +1 -0
  68. package/dist/step/inline.js +22 -0
  69. package/dist/step/inline.js.map +1 -0
  70. package/dist/step/step.d.ts +28 -0
  71. package/dist/step/step.d.ts.map +1 -0
  72. package/dist/step/step.js +53 -0
  73. package/dist/step/step.js.map +1 -0
  74. package/package.json +5 -1
  75. package/src/auth/compose.ts +2 -2
  76. package/src/auth/function-context.ts +108 -0
  77. package/src/auth/index.ts +9 -3
  78. package/src/auth/issuer-mint.ts +10 -9
  79. package/src/auth/kernel-client.ts +19 -44
  80. package/src/cli/publish.ts +1 -1
  81. package/src/config/define-domain.ts +1 -1
  82. package/src/define/index.ts +1 -0
  83. package/src/define/remote-function.ts +34 -30
  84. package/src/define/view.ts +6 -10
  85. package/src/dispatch/dispatcher.ts +34 -8
  86. package/src/dispatch/execute.ts +15 -9
  87. package/src/dispatch/index.ts +0 -1
  88. package/src/index.ts +20 -5
  89. package/src/method/context.ts +50 -28
  90. package/src/method/index.ts +8 -1
  91. package/src/method/single.ts +2 -2
  92. package/src/server/auxiliary-routes.ts +41 -23
  93. package/src/server/create.ts +1 -1
  94. package/src/step/index.ts +4 -0
  95. package/src/step/inline.ts +38 -0
  96. package/src/step/step.ts +95 -0
  97. package/dist/dispatch/call-remote.d.ts +0 -39
  98. package/dist/dispatch/call-remote.d.ts.map +0 -1
  99. package/dist/dispatch/call-remote.js +0 -37
  100. package/dist/dispatch/call-remote.js.map +0 -1
  101. package/src/dispatch/call-remote.ts +0 -53
@@ -36,16 +36,15 @@ import { buildCorsHeaders, type CorsConfig } from '@astrale-os/kernel-server'
36
36
  import type { RemoteIdentityConfig } from '../auth/identity'
37
37
  import type { AnyRemoteFunctionDef } from '../define/remote-function'
38
38
  import type { ViewDef } from '../define/view'
39
- import type { CallRemoteFn } from '../dispatch/call-remote'
40
39
  import type { AuxIdentityMap } from '../dispatch/identity'
41
40
 
42
- import { makeIssuerMint } from '../auth/issuer-mint'
43
- import { makeSelfKernel } from '../auth/kernel-client'
41
+ import { makeFunctionContext } from '../auth/function-context'
42
+ import { makeDomainAuthority } from '../auth/issuer-mint'
44
43
  import { resolveInboundAuth } from '../auth/resolve'
45
44
  import { runAuthorize } from '../dispatch/authorize'
46
- import { makeCallRemote } from '../dispatch/call-remote'
47
45
  import { SdkResultValidationError, SdkValidationError } from '../dispatch/errors'
48
46
  import { validateParams, validateResult } from '../dispatch/validate'
47
+ import { createInlineStep } from '../step'
49
48
 
50
49
  export type AuxiliaryRoutesConfig<TDeps> = {
51
50
  app: Hono
@@ -100,16 +99,23 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
100
99
  auth: def.auth,
101
100
  identity,
102
101
  corsHeaders,
103
- // Views are transport-only (iframe HTML/redirect) — the kernel client
104
- // built by `resolveInboundAuth` is intentionally NOT forwarded. Code
105
- // inside the loaded iframe talks back to the kernel via the shell
106
- // (WebSocket), not via this worker route. SERVER-rendered views that
107
- // read the graph use `selfKernel` instead: the view's OWN identity,
108
- // narrow grants (the cold-agent gate run had to deep-import SDK
109
- // internals for exactly this — now it's the supported seam).
110
- run: async ({ c, params, auth }) => {
111
- const selfKernel = makeSelfKernel(identity, deps)
112
- const ctx = { c, params, auth, deps, selfKernel }
102
+ // Views are transport-only (iframe HTML/redirect). SERVER-rendered
103
+ // views that read the graph use `ctx.fn.kernel()`: the view's own
104
+ // identity, with narrow grants.
105
+ run: async ({ c, params, auth, kernel }) => {
106
+ const inboundIss = auth?.credential?.verified?.iss as string | undefined
107
+ const kernelUrl = kernel?.default ?? inboundIss
108
+ const ctx = {
109
+ c,
110
+ params,
111
+ auth,
112
+ deps,
113
+ env: { url },
114
+ fn: makeFunctionContext(identity, deps, {
115
+ ref: `view.${slug}`,
116
+ ...(kernelUrl !== undefined ? { defaultKernelUrl: kernelUrl } : {}),
117
+ }),
118
+ }
113
119
  if (def.authorize) await runAuthorize(def.authorize, ctx)
114
120
  return def.render!(ctx)
115
121
  },
@@ -130,26 +136,40 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
130
136
  auth: def.auth,
131
137
  identity,
132
138
  corsHeaders,
133
- run: async ({ c, auth, kernel, callRemote }) => {
134
- const selfKernel = makeSelfKernel(identity, deps)
139
+ run: async ({ c, auth, kernel }) => {
135
140
  const rawBody: unknown = await c.req.json().catch(() => ({}))
136
141
  const validation = validateParams(def.inputSchema, rawBody)
137
142
  if (!validation.ok) {
138
143
  throw new SdkValidationError(validation.issues as SdkValidationError['issues'])
139
144
  }
140
- const issuer = makeIssuerMint(identity)
145
+ const inboundIss = auth?.credential?.verified?.iss as string | undefined
146
+ const kernelUrl = kernel?.default ?? inboundIss
147
+ const caller =
148
+ auth && inboundIss
149
+ ? {
150
+ iss: inboundIss,
151
+ url: kernelUrl ?? inboundIss,
152
+ }
153
+ : undefined
141
154
  const ctx = {
142
155
  params: validation.data,
143
156
  c,
144
157
  auth,
145
158
  deps,
159
+ env: { url },
146
160
  kernel,
147
- issuer,
148
- callRemote,
149
- selfKernel,
161
+ ...(caller !== undefined ? { caller } : {}),
162
+ domain: makeDomainAuthority(identity),
163
+ fn: makeFunctionContext(identity, deps, {
164
+ ref: `function.${slug}`,
165
+ ...(kernelUrl !== undefined ? { defaultKernelUrl: kernelUrl } : {}),
166
+ }),
150
167
  }
151
168
  if (def.authorize) await runAuthorize(def.authorize, ctx)
152
- const result = await def.execute(ctx)
169
+ const result = await def.execute({
170
+ ...ctx,
171
+ step: createInlineStep({ scope: { kind: 'function', ref: `function.${slug}`, slug } }),
172
+ })
153
173
  const outValidation = validateResult(def.outputSchema, result)
154
174
  if (!outValidation.ok) {
155
175
  throw new SdkResultValidationError(
@@ -188,7 +208,6 @@ type RunArgs = {
188
208
  params: Record<string, string>
189
209
  auth: AuthContext | null
190
210
  kernel: BoundClientSessionView<FnMap> | null
191
- callRemote: CallRemoteFn
192
211
  }
193
212
 
194
213
  type MountEntryArgs = {
@@ -264,7 +283,6 @@ function mountEntry(args: MountEntryArgs): void {
264
283
  params: { ...hostParams, ...pathParams },
265
284
  auth: resolvedAuth,
266
285
  kernel,
267
- callRemote: makeCallRemote(kernel),
268
286
  })
269
287
  return applyCorsToResponse(response, corsHeaders)
270
288
  } catch (err) {
@@ -47,7 +47,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
47
47
  deps: config.deps,
48
48
  privateKey: config.privateKey,
49
49
  issuer: iss,
50
- // The canonicalized serving URL, NOT the raw config.url: `ctx.url` is
50
+ // The canonicalized serving URL, NOT the raw config.url: `ctx.env.url` is
51
51
  // documented as the worker's `iss` identity, so the two must be one value.
52
52
  url: iss,
53
53
  })
@@ -0,0 +1,4 @@
1
+ export { createInlineStep } from './inline'
2
+ export type { InlineStepOptions, StepExecutor } from './inline'
3
+ export { buildStepRun } from './step'
4
+ export type { FunctionStepScope, MethodStepScope, Step, StepRun, StepScope } from './step'
@@ -0,0 +1,38 @@
1
+ import type { Step, StepRun, StepScope } from './step'
2
+
3
+ import { buildStepRun, roundTripStepResult } from './step'
4
+
5
+ export type StepExecutor = <T>(run: StepRun, handler: () => T | Promise<T>) => Promise<T>
6
+
7
+ export type InlineStepOptions = {
8
+ scope?: StepScope
9
+ execute?: StepExecutor
10
+ }
11
+
12
+ export function createInlineStep(options: InlineStepOptions = {}): Step {
13
+ const occurrences = new Map<string, number>()
14
+ const execute = options.execute ?? inlineExecute
15
+
16
+ return {
17
+ async run(id, handler) {
18
+ const run = buildStepRun(id, options.scope, nextOccurrence(id, options.scope, occurrences))
19
+ return execute(run, handler)
20
+ },
21
+ }
22
+ }
23
+
24
+ async function inlineExecute<T>(run: StepRun, handler: () => T | Promise<T>): Promise<T> {
25
+ const result = await handler()
26
+ return roundTripStepResult(run.key, result)
27
+ }
28
+
29
+ function nextOccurrence(
30
+ id: string,
31
+ scope: StepScope | undefined,
32
+ occurrences: Map<string, number>,
33
+ ): number {
34
+ const run = buildStepRun(id, scope, 0)
35
+ const occurrence = occurrences.get(run.key) ?? 0
36
+ occurrences.set(run.key, occurrence + 1)
37
+ return occurrence
38
+ }
@@ -0,0 +1,95 @@
1
+ export type Step = {
2
+ run<T>(id: string, handler: () => T | Promise<T>): Promise<T>
3
+ }
4
+
5
+ export type MethodStepScope = {
6
+ kind: 'method'
7
+ ref: string
8
+ owner: string
9
+ method: string
10
+ }
11
+
12
+ export type FunctionStepScope = {
13
+ kind: 'function'
14
+ ref: string
15
+ slug: string
16
+ }
17
+
18
+ export type StepScope = MethodStepScope | FunctionStepScope
19
+
20
+ export type StepRun = {
21
+ /** The author-provided id, normalized to the local id inside this scope. */
22
+ id: string
23
+ /** Backend-facing durable key, qualified by the method/function scope. */
24
+ key: string
25
+ /** Zero-based occurrence for repeated ids in one execution. */
26
+ occurrence: number
27
+ scope?: StepScope
28
+ }
29
+
30
+ export function requireStepId(id: string): string {
31
+ if (typeof id !== 'string' || id.trim() === '') {
32
+ throw new TypeError('step.run: id must be a non-empty string')
33
+ }
34
+ return id
35
+ }
36
+
37
+ export function buildStepRun(
38
+ id: string,
39
+ scope: StepScope | undefined,
40
+ occurrence: number,
41
+ ): StepRun {
42
+ const stepId = requireStepId(localStepId(id, scope))
43
+ const key = scope ? `${canonicalScope(scope)}.${stepId}` : stepId
44
+ return {
45
+ id: stepId,
46
+ key,
47
+ occurrence,
48
+ ...(scope ? { scope } : {}),
49
+ }
50
+ }
51
+
52
+ export function roundTripStepResult<T>(id: string, value: T): T {
53
+ if (value === undefined) return value
54
+
55
+ let serialized: string | undefined
56
+ try {
57
+ serialized = JSON.stringify(value)
58
+ } catch (err) {
59
+ throw new TypeError(
60
+ `step.run("${id}"): result must be JSON-serializable: ${
61
+ err instanceof Error ? err.message : String(err)
62
+ }`,
63
+ )
64
+ }
65
+
66
+ if (serialized === undefined) {
67
+ throw new TypeError(`step.run("${id}"): result must be JSON-serializable`)
68
+ }
69
+
70
+ return JSON.parse(serialized) as T
71
+ }
72
+
73
+ function localStepId(id: string, scope: StepScope | undefined): string {
74
+ if (!scope) return id
75
+
76
+ for (const prefix of scopePrefixes(scope)) {
77
+ const scoped = `${prefix}.`
78
+ if (id.startsWith(scoped) && id.length > scoped.length) return id.slice(scoped.length)
79
+ }
80
+
81
+ return id
82
+ }
83
+
84
+ function scopePrefixes(scope: StepScope): string[] {
85
+ switch (scope.kind) {
86
+ case 'method':
87
+ return [scope.ref, `${scope.owner}.${scope.method}`]
88
+ case 'function':
89
+ return [scope.ref, `function.${scope.slug}`]
90
+ }
91
+ }
92
+
93
+ function canonicalScope(scope: StepScope): string {
94
+ return scope.ref
95
+ }
@@ -1,39 +0,0 @@
1
- /**
2
- * `ctx.callRemote` — call ANOTHER worker's remote method from a handler.
3
- *
4
- * A worker's `kernel.call('/dom/class.X/method', …)` signs `aud = <kernel>`,
5
- * which is correct for a **kernel syscall** (it runs in the kernel) but is
6
- * rejected by a **remote method** (a Function with `binding.remoteUrl`, i.e.
7
- * another worker) — the target worker verifies `aud` against its own identity
8
- * and throws `Credential audience mismatch` at authentication.
9
- *
10
- * `callRemote` is now a thin wrapper over the bound kernel session: the kernel
11
- * resolves a remote-bound path to a redirect carrying the worker's URL and its
12
- * `iss`, and the session (configured in `bindKernel`) follows it reactively —
13
- * minting a kernel-signed delegation envelope scoped to that `iss` via the
14
- * caller's own `@<subject>::mintDelegationCredential`, then dialing the worker.
15
- * Instance-method `self` is injected by the kernel resolver into the redirect,
16
- * so a bare `kernel.call(path, params)` is all that's needed here.
17
- *
18
- * Prerequisite (authorization, unchanged): the calling Function's identity must
19
- * hold `USE` on `Identity.mintDelegationCredential` AND the target method's own
20
- * grants — the session only fixes the audience (authentication), not grants.
21
- */
22
- import type { ClientSessionCallOptions } from '@astrale-os/kernel-client/session';
23
- /**
24
- * The minimal kernel-call surface: dynamic method dispatch by path string. The
25
- * bound kernel session view (`BoundClientSessionView`) satisfies it structurally,
26
- * as does any test double. Exported as the public narrow caller contract so
27
- * handlers can depend on it instead of the wide, generic session view.
28
- */
29
- export type KernelCaller = {
30
- call: (method: string, params: unknown, opts?: ClientSessionCallOptions) => Promise<unknown>;
31
- };
32
- export type CallRemoteFn = (path: string, params?: Record<string, unknown>) => Promise<unknown>;
33
- /**
34
- * Build the `callRemote` helper for one handler invocation. `kernel` is the
35
- * handler's bound kernel view (or `null` for a public/unauthenticated request,
36
- * where `callRemote` cannot mint and throws on use).
37
- */
38
- export declare function makeCallRemote(kernel: KernelCaller | null): CallRemoteFn;
39
- //# sourceMappingURL=call-remote.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"call-remote.d.ts","sourceRoot":"","sources":["../../src/dispatch/call-remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAA;AAEjF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,wBAAwB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAC7F,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE/F;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,YAAY,CAWxE"}
@@ -1,37 +0,0 @@
1
- /**
2
- * `ctx.callRemote` — call ANOTHER worker's remote method from a handler.
3
- *
4
- * A worker's `kernel.call('/dom/class.X/method', …)` signs `aud = <kernel>`,
5
- * which is correct for a **kernel syscall** (it runs in the kernel) but is
6
- * rejected by a **remote method** (a Function with `binding.remoteUrl`, i.e.
7
- * another worker) — the target worker verifies `aud` against its own identity
8
- * and throws `Credential audience mismatch` at authentication.
9
- *
10
- * `callRemote` is now a thin wrapper over the bound kernel session: the kernel
11
- * resolves a remote-bound path to a redirect carrying the worker's URL and its
12
- * `iss`, and the session (configured in `bindKernel`) follows it reactively —
13
- * minting a kernel-signed delegation envelope scoped to that `iss` via the
14
- * caller's own `@<subject>::mintDelegationCredential`, then dialing the worker.
15
- * Instance-method `self` is injected by the kernel resolver into the redirect,
16
- * so a bare `kernel.call(path, params)` is all that's needed here.
17
- *
18
- * Prerequisite (authorization, unchanged): the calling Function's identity must
19
- * hold `USE` on `Identity.mintDelegationCredential` AND the target method's own
20
- * grants — the session only fixes the audience (authentication), not grants.
21
- */
22
- /**
23
- * Build the `callRemote` helper for one handler invocation. `kernel` is the
24
- * handler's bound kernel view (or `null` for a public/unauthenticated request,
25
- * where `callRemote` cannot mint and throws on use).
26
- */
27
- export function makeCallRemote(kernel) {
28
- return async (path, params = {}) => {
29
- if (!kernel) {
30
- throw new Error('callRemote requires an authenticated request — no kernel credential (public method?)');
31
- }
32
- // The bound session auto-follows the kernel's redirect and mints for the
33
- // worker's `iss`; a remote-bound path therefore just works as a kernel.call.
34
- return kernel.call(path, params);
35
- };
36
- }
37
- //# sourceMappingURL=call-remote.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"call-remote.js","sourceRoot":"","sources":["../../src/dispatch/call-remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAA2B;IACxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,sFAAsF,CACvF,CAAA;QACH,CAAC;QACD,yEAAyE;QACzE,6EAA6E;QAC7E,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAClC,CAAC,CAAA;AACH,CAAC"}
@@ -1,53 +0,0 @@
1
- /**
2
- * `ctx.callRemote` — call ANOTHER worker's remote method from a handler.
3
- *
4
- * A worker's `kernel.call('/dom/class.X/method', …)` signs `aud = <kernel>`,
5
- * which is correct for a **kernel syscall** (it runs in the kernel) but is
6
- * rejected by a **remote method** (a Function with `binding.remoteUrl`, i.e.
7
- * another worker) — the target worker verifies `aud` against its own identity
8
- * and throws `Credential audience mismatch` at authentication.
9
- *
10
- * `callRemote` is now a thin wrapper over the bound kernel session: the kernel
11
- * resolves a remote-bound path to a redirect carrying the worker's URL and its
12
- * `iss`, and the session (configured in `bindKernel`) follows it reactively —
13
- * minting a kernel-signed delegation envelope scoped to that `iss` via the
14
- * caller's own `@<subject>::mintDelegationCredential`, then dialing the worker.
15
- * Instance-method `self` is injected by the kernel resolver into the redirect,
16
- * so a bare `kernel.call(path, params)` is all that's needed here.
17
- *
18
- * Prerequisite (authorization, unchanged): the calling Function's identity must
19
- * hold `USE` on `Identity.mintDelegationCredential` AND the target method's own
20
- * grants — the session only fixes the audience (authentication), not grants.
21
- */
22
-
23
- import type { ClientSessionCallOptions } from '@astrale-os/kernel-client/session'
24
-
25
- /**
26
- * The minimal kernel-call surface: dynamic method dispatch by path string. The
27
- * bound kernel session view (`BoundClientSessionView`) satisfies it structurally,
28
- * as does any test double. Exported as the public narrow caller contract so
29
- * handlers can depend on it instead of the wide, generic session view.
30
- */
31
- export type KernelCaller = {
32
- call: (method: string, params: unknown, opts?: ClientSessionCallOptions) => Promise<unknown>
33
- }
34
-
35
- export type CallRemoteFn = (path: string, params?: Record<string, unknown>) => Promise<unknown>
36
-
37
- /**
38
- * Build the `callRemote` helper for one handler invocation. `kernel` is the
39
- * handler's bound kernel view (or `null` for a public/unauthenticated request,
40
- * where `callRemote` cannot mint and throws on use).
41
- */
42
- export function makeCallRemote(kernel: KernelCaller | null): CallRemoteFn {
43
- return async (path, params = {}) => {
44
- if (!kernel) {
45
- throw new Error(
46
- 'callRemote requires an authenticated request — no kernel credential (public method?)',
47
- )
48
- }
49
- // The bound session auto-follows the kernel's redirect and mints for the
50
- // worker's `iss`; a remote-bound path therefore just works as a kernel.call.
51
- return kernel.call(path, params)
52
- }
53
- }