@astrale-os/sdk 0.3.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 (113) 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/deploy/hash-spec.d.ts +0 -9
  98. package/dist/deploy/hash-spec.d.ts.map +0 -1
  99. package/dist/deploy/hash-spec.js +0 -29
  100. package/dist/deploy/hash-spec.js.map +0 -1
  101. package/dist/dispatch/call-remote.d.ts +0 -39
  102. package/dist/dispatch/call-remote.d.ts.map +0 -1
  103. package/dist/dispatch/call-remote.js +0 -37
  104. package/dist/dispatch/call-remote.js.map +0 -1
  105. package/dist/domain/extend-core.d.ts +0 -49
  106. package/dist/domain/extend-core.d.ts.map +0 -1
  107. package/dist/domain/extend-core.js +0 -190
  108. package/dist/domain/extend-core.js.map +0 -1
  109. package/dist/server/worker-meta.d.ts +0 -6
  110. package/dist/server/worker-meta.d.ts.map +0 -1
  111. package/dist/server/worker-meta.js +0 -10
  112. package/dist/server/worker-meta.js.map +0 -1
  113. 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,9 +0,0 @@
1
- /**
2
- * Hash a `spec.json` file deterministically. Only the schema payload
3
- * (`nodes` + `edges`) is hashed — the `meta` block carries `builtAt`
4
- * timestamps that would poison drift detection.
5
- *
6
- * Returns `sha256:<hex>`.
7
- */
8
- export declare function hashSpecFile(path: string): string;
9
- //# sourceMappingURL=hash-spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hash-spec.d.ts","sourceRoot":"","sources":["../../src/deploy/hash-spec.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKjD"}
@@ -1,29 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { readFileSync } from 'node:fs';
3
- const HASH_ALGORITHM = 'sha256';
4
- /**
5
- * Hash a `spec.json` file deterministically. Only the schema payload
6
- * (`nodes` + `edges`) is hashed — the `meta` block carries `builtAt`
7
- * timestamps that would poison drift detection.
8
- *
9
- * Returns `sha256:<hex>`.
10
- */
11
- export function hashSpecFile(path) {
12
- const raw = readFileSync(path, 'utf-8');
13
- const parsed = JSON.parse(raw);
14
- const canonical = canonicalJson({ nodes: parsed.nodes ?? [], edges: parsed.edges ?? [] });
15
- return `${HASH_ALGORITHM}:${createHash(HASH_ALGORITHM).update(canonical).digest('hex')}`;
16
- }
17
- function canonicalJson(value) {
18
- if (value === null || typeof value !== 'object')
19
- return JSON.stringify(value);
20
- if (Array.isArray(value))
21
- return '[' + value.map(canonicalJson).join(',') + ']';
22
- const keys = Object.keys(value).sort();
23
- return ('{' +
24
- keys
25
- .map((k) => JSON.stringify(k) + ':' + canonicalJson(value[k]))
26
- .join(',') +
27
- '}');
28
- }
29
- //# sourceMappingURL=hash-spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hash-spec.js","sourceRoot":"","sources":["../../src/deploy/hash-spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEtC,MAAM,cAAc,GAAG,QAAQ,CAAA;AAE/B;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyC,CAAA;IACtE,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAA;IACzF,OAAO,GAAG,cAAc,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AAC1F,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,CAAA;IACjE,OAAO,CACL,GAAG;QACH,IAAI;aACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,aAAa,CAAE,KAAiC,CAAC,CAAC,CAAC,CAAC,CAAC;aAC1F,IAAI,CAAC,GAAG,CAAC;QACZ,GAAG,CACJ,CAAA;AACH,CAAC"}
@@ -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,49 +0,0 @@
1
- /**
2
- * Build an augmented `Core<S>` that includes auto-materialized View and
3
- * standalone-function nodes (one per entry in `defineRemoteDomain`'s `views` /
4
- * `remoteFunctions` config), plus their parent Folder + optional `view_for`
5
- * edges. Returns the resolved effective bindings alongside so callers don't
6
- * recompute them.
7
- *
8
- * Standalone callables (the former `RemoteFunction` class) are materialized as
9
- * the canonical kernel `Function` node class — "remote" is just a `binding`,
10
- * not a distinct kind, so there is no per-domain class to configure.
11
- *
12
- * Conflicts: if the user core already declares a top-level node at
13
- * `<viewsFolder>` or `<functionsFolder>`, throws — the auto-materialization
14
- * reserves those slugs.
15
- */
16
- import type { FunctionBinding } from '@astrale-os/kernel-api/routed';
17
- import type { AnyEdgeDef, AnyNodeDef, Core, Schema } from '@astrale-os/kernel-dsl';
18
- import type { AnyRemoteFunctionDef } from '../define/remote-function';
19
- import type { ViewDef } from '../define/view';
20
- export declare const DEFAULT_VIEWS_FOLDER = "views";
21
- export declare const DEFAULT_FUNCTIONS_FOLDER = "functions";
22
- export type ExtendCoreConfig = {
23
- schema: Schema;
24
- origin: string;
25
- userCore?: Core;
26
- /**
27
- * The worker's serving URL — the base every auto-materialized binding
28
- * resolves against. OMITTED at define time (the URL is known only to the
29
- * spec producers): the aux nodes then materialize structure-only (paths,
30
- * names, refs — what identity/subs resolution needs) with no `binding`
31
- * stamped and empty binding maps. Every install graph comes from a
32
- * `materializeRemoteDomain(domain, url)` call where it is present.
33
- */
34
- url?: string;
35
- viewClass?: AnyNodeDef;
36
- viewForEdgeClass?: AnyEdgeDef;
37
- viewsFolder?: string;
38
- views?: Record<string, ViewDef<any>>;
39
- functionsFolder?: string;
40
- remoteFunctions?: Record<string, AnyRemoteFunctionDef>;
41
- };
42
- export type ExtendCoreResult = {
43
- core: Core;
44
- viewBindings: Record<string, FunctionBinding>;
45
- remoteFunctionBindings: Record<string, FunctionBinding>;
46
- };
47
- export declare function extendCore(config: ExtendCoreConfig): ExtendCoreResult;
48
- export declare function resolveBinding(override: FunctionBinding | undefined, url: string, folderSlug: string, slug: string): FunctionBinding;
49
- //# sourceMappingURL=extend-core.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extend-core.d.ts","sourceRoot":"","sources":["../../src/domain/extend-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AACpE,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,IAAI,EAGJ,MAAM,EACP,MAAM,wBAAwB,CAAA;AAY/B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAE7C,eAAO,MAAM,oBAAoB,UAAU,CAAA;AAC3C,eAAO,MAAM,wBAAwB,cAAc,CAAA;AAGnD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,IAAI,CAAA;IACf;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,SAAS,CAAC,EAAE,UAAU,CAAA;IACtB,gBAAgB,CAAC,EAAE,UAAU,CAAA;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IAEpC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;CACvD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,IAAI,CAAA;IACV,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC7C,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;CACxD,CAAA;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAuFrE;AAQD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,eAAe,CAWjB"}
@@ -1,190 +0,0 @@
1
- /**
2
- * Build an augmented `Core<S>` that includes auto-materialized View and
3
- * standalone-function nodes (one per entry in `defineRemoteDomain`'s `views` /
4
- * `remoteFunctions` config), plus their parent Folder + optional `view_for`
5
- * edges. Returns the resolved effective bindings alongside so callers don't
6
- * recompute them.
7
- *
8
- * Standalone callables (the former `RemoteFunction` class) are materialized as
9
- * the canonical kernel `Function` node class — "remote" is just a `binding`,
10
- * not a distinct kind, so there is no per-domain class to configure.
11
- *
12
- * Conflicts: if the user core already declares a top-level node at
13
- * `<viewsFolder>` or `<functionsFolder>`, throws — the auto-materialization
14
- * reserves those slugs.
15
- */
16
- import { Folder, K, KernelSchema } from '@astrale-os/kernel-core';
17
- import { buildCorePath } from '@astrale-os/kernel-dsl';
18
- /** Canonical node class for standalone callables (the former `RemoteFunction`). */
19
- const FUNCTION_CLASS = KernelSchema.classes.Function;
20
- /** Canonical node class for GUI views. */
21
- const VIEW_CLASS = KernelSchema.classes.View;
22
- /** Canonical edge class linking a View to its target. */
23
- const VIEW_FOR_EDGE_CLASS = KernelSchema.classes.view_for;
24
- export const DEFAULT_VIEWS_FOLDER = 'views';
25
- export const DEFAULT_FUNCTIONS_FOLDER = 'functions';
26
- const SLUG_RE = /^[a-z][a-z0-9-]*$/;
27
- export function extendCore(config) {
28
- const { schema, origin, userCore, url, viewClass = VIEW_CLASS, viewForEdgeClass = VIEW_FOR_EDGE_CLASS, viewsFolder = DEFAULT_VIEWS_FOLDER, views, functionsFolder = DEFAULT_FUNCTIONS_FOLDER, remoteFunctions, } = config;
29
- validateInputs(config);
30
- const nodes = userCore ? [...userCore.__nodes] : [];
31
- const edges = userCore ? [...userCore.__edges] : [];
32
- const viewBindings = {};
33
- const remoteFunctionBindings = {};
34
- const enableUiHandshake = viewClass === VIEW_CLASS;
35
- if (views && viewClass) {
36
- // Enforce ViewDef's documented exclusivity: `mount` (SPA route) cannot be
37
- // combined with `render` (inline HTML handler) or an explicit `binding` —
38
- // silently ignoring one of them would deploy a view that behaves
39
- // differently than its definition reads.
40
- for (const [slug, def] of Object.entries(views)) {
41
- if (def.mount && (def.render || def.binding)) {
42
- throw new Error(`defineRemoteDomain: view "${slug}" sets \`mount\` together with ` +
43
- `${def.render ? '`render`' : '`binding`'} — they are mutually exclusive.`);
44
- }
45
- }
46
- assertNoConflict(nodes, origin, viewsFolder);
47
- addFolderAndEntries({
48
- origin,
49
- folderSlug: viewsFolder,
50
- nodes,
51
- entries: Object.entries(views).map(([slug, def]) => {
52
- const binding = url
53
- ? def.mount
54
- ? { remoteUrl: joinWorkerPath(url, def.mount) }
55
- : resolveBinding(def.binding, url, viewsFolder, slug)
56
- : undefined;
57
- if (binding)
58
- viewBindings[slug] = binding;
59
- return {
60
- slug,
61
- nodeClass: viewClass,
62
- data: buildViewData(slug, binding, def, enableUiHandshake),
63
- edges: buildViewForEdges(slug, def, viewForEdgeClass, viewsFolder, origin),
64
- };
65
- }),
66
- edges,
67
- });
68
- }
69
- if (remoteFunctions) {
70
- assertNoConflict(nodes, origin, functionsFolder);
71
- addFolderAndEntries({
72
- origin,
73
- folderSlug: functionsFolder,
74
- nodes,
75
- entries: Object.entries(remoteFunctions).map(([slug, def]) => {
76
- const binding = url ? resolveBinding(def.binding, url, functionsFolder, slug) : undefined;
77
- if (binding)
78
- remoteFunctionBindings[slug] = binding;
79
- return {
80
- slug,
81
- // Standalone callables materialize as the canonical kernel Function class.
82
- nodeClass: FUNCTION_CLASS,
83
- data: {
84
- ...buildFunctionData(slug, binding),
85
- [K.$.i('Function').ref.key]: def.ref ?? `function.${slug}`,
86
- },
87
- edges: [],
88
- };
89
- }),
90
- edges,
91
- });
92
- }
93
- return {
94
- core: { schema, domain: origin, __nodes: nodes, __edges: edges },
95
- viewBindings,
96
- remoteFunctionBindings,
97
- };
98
- }
99
- /** Join a worker-relative mount path onto the serving url (single slash). */
100
- function joinWorkerPath(url, mount) {
101
- const base = url.replace(/\/+$/, '');
102
- return `${base}/${mount.replace(/^\/+/, '')}`;
103
- }
104
- export function resolveBinding(override, url, folderSlug, slug) {
105
- // Same trailing-slash discipline as `joinWorkerPath` — a `url` ending in `/`
106
- // must not produce `//` in the binding (the kernel pins iss by exact string).
107
- const base = url.replace(/\/+$/, '');
108
- if (override) {
109
- return {
110
- ...override,
111
- remoteUrl: override.remoteUrl ?? `${base}/${folderSlug}/${slug}`,
112
- };
113
- }
114
- return { remoteUrl: `${base}/${folderSlug}/${slug}` };
115
- }
116
- // ── Internal ───────────────────────────────────────────────────────────────
117
- function validateInputs(config) {
118
- if (config.views) {
119
- for (const slug of Object.keys(config.views)) {
120
- if (!SLUG_RE.test(slug)) {
121
- throw new Error(`defineRemoteDomain: invalid view slug "${slug}" — must match ${SLUG_RE}.`);
122
- }
123
- }
124
- }
125
- if (config.remoteFunctions) {
126
- for (const slug of Object.keys(config.remoteFunctions)) {
127
- if (!SLUG_RE.test(slug)) {
128
- throw new Error(`defineRemoteDomain: invalid remote-function slug "${slug}" — must match ${SLUG_RE}.`);
129
- }
130
- }
131
- }
132
- }
133
- function assertNoConflict(nodes, origin, folderSlug) {
134
- const folderPath = buildCorePath(origin, [folderSlug]);
135
- if (nodes.some((n) => n.path === folderPath)) {
136
- throw new Error(`defineRemoteDomain: top-level core slug "${folderSlug}" is reserved by ` +
137
- 'SDK auto-materialization. Move the conflicting node or rename the ' +
138
- '`viewsFolder` / `functionsFolder` config.');
139
- }
140
- }
141
- function addFolderAndEntries(args) {
142
- const { origin, folderSlug, entries, nodes, edges } = args;
143
- const folderPath = buildCorePath(origin, [folderSlug]);
144
- nodes.push({
145
- path: folderPath,
146
- def: Folder,
147
- data: { [K.Named.name.key]: folderSlug },
148
- });
149
- for (const entry of entries) {
150
- nodes.push({
151
- path: buildCorePath(origin, [folderSlug, entry.slug]),
152
- def: entry.nodeClass,
153
- data: entry.data,
154
- parent: folderPath,
155
- });
156
- for (const e of entry.edges)
157
- edges.push(e);
158
- }
159
- }
160
- function buildFunctionData(slug, binding) {
161
- // Match the kernel-core schema serializer which JSON-stringifies the
162
- // `Function.binding` value on Function nodes (see
163
- // kernel/core/domain/serialize/schema.ts). The kernel install validator
164
- // checks `typeof binding === 'string'` then parses; storing a raw object
165
- // trips the validator with "missing remote binding" even when the object IS
166
- // the binding. No binding at all (define-time, no url) → no prop stamped.
167
- return {
168
- [K.Named.name.key]: slug,
169
- ...(binding ? { [K.$.i('Function').binding.key]: JSON.stringify(binding) } : {}),
170
- };
171
- }
172
- function buildViewData(slug, binding, def, enableUiHandshake) {
173
- const handshake = def.handshake ?? (def.render ? 'none' : undefined);
174
- return {
175
- ...buildFunctionData(slug, binding),
176
- ...(enableUiHandshake && handshake ? { [K.UI.handshake.key]: handshake } : {}),
177
- };
178
- }
179
- function buildViewForEdges(slug, def, edgeClass, viewsFolder, origin) {
180
- if (!def.viewFor || !edgeClass)
181
- return [];
182
- const targets = Array.isArray(def.viewFor) ? def.viewFor : [def.viewFor];
183
- const from = buildCorePath(origin, [viewsFolder, slug]);
184
- return targets.map((target) => ({
185
- from,
186
- edge: edgeClass,
187
- to: target,
188
- }));
189
- }
190
- //# sourceMappingURL=extend-core.js.map