@astrale-os/sdk 0.1.5 → 0.1.7

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 (108) hide show
  1. package/dist/auth/verify.d.ts +2 -0
  2. package/dist/auth/verify.d.ts.map +1 -1
  3. package/dist/auth/verify.js +81 -26
  4. package/dist/auth/verify.js.map +1 -1
  5. package/dist/cli/bin.d.ts +7 -0
  6. package/dist/cli/bin.d.ts.map +1 -0
  7. package/dist/cli/bin.js +15 -0
  8. package/dist/cli/bin.js.map +1 -0
  9. package/dist/cli/dotenv.d.ts +13 -0
  10. package/dist/cli/dotenv.d.ts.map +1 -0
  11. package/dist/cli/dotenv.js +46 -0
  12. package/dist/cli/dotenv.js.map +1 -0
  13. package/dist/cli/index.d.ts +15 -0
  14. package/dist/cli/index.d.ts.map +1 -0
  15. package/dist/cli/index.js +15 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/run.d.ts +79 -0
  18. package/dist/cli/run.d.ts.map +1 -0
  19. package/dist/cli/run.js +569 -0
  20. package/dist/cli/run.js.map +1 -0
  21. package/dist/cli/spec.d.ts +19 -0
  22. package/dist/cli/spec.d.ts.map +1 -0
  23. package/dist/cli/spec.js +31 -0
  24. package/dist/cli/spec.js.map +1 -0
  25. package/dist/config/adapter.d.ts +140 -0
  26. package/dist/config/adapter.d.ts.map +1 -0
  27. package/dist/config/adapter.js +40 -0
  28. package/dist/config/adapter.js.map +1 -0
  29. package/dist/config/define-domain.d.ts +112 -0
  30. package/dist/config/define-domain.d.ts.map +1 -0
  31. package/dist/config/define-domain.js +98 -0
  32. package/dist/config/define-domain.js.map +1 -0
  33. package/dist/config/deploy.d.ts +28 -0
  34. package/dist/config/deploy.d.ts.map +1 -0
  35. package/dist/config/deploy.js +24 -0
  36. package/dist/config/deploy.js.map +1 -0
  37. package/dist/config/index.d.ts +21 -0
  38. package/dist/config/index.d.ts.map +1 -0
  39. package/dist/config/index.js +18 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/define/remote-function.d.ts +19 -11
  42. package/dist/define/remote-function.d.ts.map +1 -1
  43. package/dist/define/remote-function.js.map +1 -1
  44. package/dist/dispatch/call-remote.d.ts +7 -3
  45. package/dist/dispatch/call-remote.d.ts.map +1 -1
  46. package/dist/dispatch/call-remote.js.map +1 -1
  47. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  48. package/dist/dispatch/dispatcher.js +8 -4
  49. package/dist/dispatch/dispatcher.js.map +1 -1
  50. package/dist/dispatch/index.d.ts +1 -1
  51. package/dist/dispatch/index.d.ts.map +1 -1
  52. package/dist/dispatch/index.js.map +1 -1
  53. package/dist/dispatch/self.d.ts +46 -10
  54. package/dist/dispatch/self.d.ts.map +1 -1
  55. package/dist/dispatch/self.js +65 -8
  56. package/dist/dispatch/self.js.map +1 -1
  57. package/dist/domain/define.d.ts +3 -3
  58. package/dist/domain/define.js +3 -3
  59. package/dist/index.d.ts +5 -4
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +8 -2
  62. package/dist/index.js.map +1 -1
  63. package/dist/method/class.d.ts.map +1 -1
  64. package/dist/method/class.js.map +1 -1
  65. package/dist/method/context.d.ts +32 -7
  66. package/dist/method/context.d.ts.map +1 -1
  67. package/dist/method/index.d.ts +1 -1
  68. package/dist/method/index.d.ts.map +1 -1
  69. package/dist/method/single.d.ts +16 -11
  70. package/dist/method/single.d.ts.map +1 -1
  71. package/dist/method/single.js.map +1 -1
  72. package/dist/server/domain-entry.d.ts +67 -0
  73. package/dist/server/domain-entry.d.ts.map +1 -0
  74. package/dist/server/domain-entry.js +58 -0
  75. package/dist/server/domain-entry.js.map +1 -0
  76. package/dist/server/index.d.ts +3 -1
  77. package/dist/server/index.d.ts.map +1 -1
  78. package/dist/server/index.js +2 -1
  79. package/dist/server/index.js.map +1 -1
  80. package/dist/server/worker-entry.d.ts +57 -5
  81. package/dist/server/worker-entry.d.ts.map +1 -1
  82. package/dist/server/worker-entry.js +108 -24
  83. package/dist/server/worker-entry.js.map +1 -1
  84. package/package.json +12 -3
  85. package/src/auth/verify.ts +89 -28
  86. package/src/cli/bin.ts +15 -0
  87. package/src/cli/dotenv.ts +45 -0
  88. package/src/cli/index.ts +15 -0
  89. package/src/cli/run.ts +675 -0
  90. package/src/cli/spec.ts +42 -0
  91. package/src/config/adapter.ts +172 -0
  92. package/src/config/define-domain.ts +218 -0
  93. package/src/config/deploy.ts +35 -0
  94. package/src/config/index.ts +31 -0
  95. package/src/define/remote-function.ts +42 -13
  96. package/src/dispatch/call-remote.ts +7 -2
  97. package/src/dispatch/dispatcher.ts +8 -4
  98. package/src/dispatch/index.ts +1 -1
  99. package/src/dispatch/self.ts +96 -10
  100. package/src/domain/define.ts +3 -3
  101. package/src/index.ts +25 -4
  102. package/src/method/class.ts +4 -3
  103. package/src/method/context.ts +38 -7
  104. package/src/method/index.ts +1 -1
  105. package/src/method/single.ts +30 -11
  106. package/src/server/domain-entry.ts +113 -0
  107. package/src/server/index.ts +3 -1
  108. package/src/server/worker-entry.ts +122 -23
@@ -2,31 +2,117 @@
2
2
  * Self resolution — pipeline step 3.
3
3
  *
4
4
  * `_self` is a path reference to the node the non-static method runs on.
5
- * Currently a thin parse: the caller's string becomes a `Path`, and handlers
6
- * build recursive paths as `${self.path}::method` (the `Path`'s `toString`
7
- * yields its raw form, so template literals work unchanged).
5
+ * `resolveSelf` is a thin PARSE: the caller's string becomes a `Path`, and
6
+ * handlers build recursive paths as `${self.path}::method` (the `Path`'s
7
+ * `toString` yields its raw form, so template literals work unchanged). It does
8
+ * NOT touch the graph — the whole dispatch pipeline (resolve → authenticate →
9
+ * validate → self → execute) reaches the handler without a single `Node::get`.
8
10
  *
9
11
  * `resolveSelf` receives the bare self target (`@<id>` or a tree path) — the
10
12
  * method ref travels in a separate dispatch channel, so `::method` never
11
13
  * reaches here. For the `@<id>` form the parsed `Path` is an `IdPath` which
12
14
  * already carries the node id; surfacing it on `SelfResult.id` saves every
13
- * worker handler from either parsing `self.path.raw` by hand or doing a
14
- * `Node::get` round-trip just to recover the id of the node it's already
15
- * operating on.
15
+ * worker handler from parsing `self.path.raw` by hand to recover the id of the
16
+ * node it is already operating on.
16
17
  *
17
- * Kept as a distinct step so a future implementation can do a real
18
- * resolution here (e.g. hydrate a node/accessor) without touching callers.
18
+ * `withNode` enriches the parsed self with the lazy `node()` accessor the one
19
+ * place a handler can fetch its own node's full record (`{ id, class, props }`)
20
+ * without hand-rolling a `::get`. Construction stays split from parsing so
21
+ * `resolveSelf` remains pure and trivially testable; the dispatcher attaches the
22
+ * accessor once it holds the request's kernel.
19
23
  */
20
24
 
25
+ import type { Node } from '@astrale-os/kernel-core/graph'
26
+
21
27
  import { IdPath, Path, type NodeId } from '@astrale-os/kernel-core'
28
+ import { classPathSchema } from '@astrale-os/kernel-core/domain'
29
+ import { absolutePathSchema } from '@astrale-os/kernel-core/tree'
30
+ import { z } from 'zod'
31
+
32
+ import type { Kernel } from '../method/context'
22
33
 
23
- export type SelfResult = {
34
+ /** The node a non-static method runs on — its full record, id guaranteed. */
35
+ export type ResolvedSelfNode = Node & { id: NodeId }
36
+
37
+ /** The bare parse of a self target — no kernel, no fetch. */
38
+ export type ParsedSelf = {
24
39
  path: Path
25
40
  /** Set when `path` is an `IdPath` (i.e. `@<id>::method` calls). */
26
41
  id?: NodeId
27
42
  }
28
43
 
29
- export function resolveSelf(ref: string): SelfResult {
44
+ export type SelfResult = ParsedSelf & {
45
+ /**
46
+ * Lazily read THIS node's full record via one `::get` on the handler's
47
+ * kernel. Memoized for the dispatch: repeated and concurrent calls share a
48
+ * single in-flight fetch, and a failure clears the cache so it stays
49
+ * retryable. Pass `{ reload: true }` to force a fresh read.
50
+ *
51
+ * The result is a point-in-time SNAPSHOT, not a live view — for
52
+ * read-modify-write, treat it as the value as of the read (or `reload`).
53
+ * `props` is returned structurally validated only, NOT typed against the
54
+ * node's declared schema: the kernel does not hydrate declared props at
55
+ * runtime, so a node-typed `self` would be unsound. Read the concrete backend
56
+ * off `node.class` (a `ClassPath`) — that is the persisted discriminator.
57
+ */
58
+ node(opts?: { reload?: boolean }): Promise<ResolvedSelfNode>
59
+ }
60
+
61
+ export function resolveSelf(ref: string): ParsedSelf {
30
62
  const path = Path.parse(ref)
31
63
  return path instanceof IdPath ? { path, id: path.id } : { path }
32
64
  }
65
+
66
+ /**
67
+ * Attach the memoized `node()` accessor, binding it to the handler's kernel.
68
+ * `kernel` is `null` for methods whose auth policy yields none (`public`, or an
69
+ * unauthenticated `optional`) — there `node()` rejects with a clear error
70
+ * rather than reading the graph unauthenticated.
71
+ */
72
+ export function withNode(parsed: ParsedSelf, kernel: Kernel | null): SelfResult {
73
+ let cached: Promise<ResolvedSelfNode> | undefined
74
+ const node = (opts?: { reload?: boolean }): Promise<ResolvedSelfNode> => {
75
+ if (opts?.reload) cached = undefined
76
+ if (!cached) {
77
+ cached = fetchSelfNode(parsed.path, kernel).catch((err: unknown) => {
78
+ cached = undefined // never cache a rejection — a later call may succeed
79
+ throw err
80
+ })
81
+ }
82
+ return cached
83
+ }
84
+ return { ...parsed, node }
85
+ }
86
+
87
+ /**
88
+ * Boundary schema for a node record off the wire. Structurally identical to
89
+ * kernel-core's `nodeSchema`, re-declared here from its public coercers
90
+ * (`classPathSchema` from `/domain`, `absolutePathSchema` from `/tree`) on
91
+ * purpose: importing the schema from `#graph` would pull zod into the graph
92
+ * barrel and cycle with `AbsolutePath` init (see `kernel/core/graph/index.ts`).
93
+ * Coercing through the canonical schemas yields a real `ClassPath` / `AbsolutePath`.
94
+ */
95
+ const nodeRecordSchema = z.object({
96
+ class: classPathSchema(),
97
+ path: absolutePathSchema(),
98
+ props: z.record(z.string(), z.unknown()),
99
+ id: z.string().optional(),
100
+ __labels: z.array(z.string()).optional(),
101
+ })
102
+
103
+ async function fetchSelfNode(path: Path, kernel: Kernel | null): Promise<ResolvedSelfNode> {
104
+ if (!kernel) {
105
+ throw new Error(
106
+ `self.node() needs an authenticated kernel to read "${path.raw}", but this method ran ` +
107
+ `without one (its auth policy yields no kernel — e.g. 'public'). Read the node from a ` +
108
+ `method whose auth is 'required', or supply a credential.`,
109
+ )
110
+ }
111
+ const record = nodeRecordSchema.parse(await kernel.call(`${path.raw}::get`, {}))
112
+ if (record.id === undefined) {
113
+ throw new Error(`self.node(): "${path.raw}::get" returned a record with no id`)
114
+ }
115
+ // Validated at the wire boundary: id is now known-present, props are
116
+ // structurally checked. Bridge the parse output to the canonical `Node`.
117
+ return { ...record, id: record.id as NodeId } as ResolvedSelfNode
118
+ }
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * The domain definition is deployment-agnostic: it carries NO serving url. The
14
14
  * url is supplied late by the spec producer (`createRemoteServer({ url })` at
15
- * runtime, the devkit CLI offline) and stamped onto every `binding.remoteUrl`
15
+ * runtime, the `astrale-domain` CLI offline) and stamped onto every `binding.remoteUrl`
16
16
  * by `materializeRemoteDomain`. There is exactly one notion of `url` in the SDK
17
17
  * — the worker serving URL, which is also `iss` and the binding base.
18
18
  */
@@ -128,8 +128,8 @@ export function defineRemoteDomain<TDeps>() {
128
128
  * Materialize a `RemoteDomain` at its real serving `url`: re-runs `extendCore`
129
129
  * so every aux View/Function `binding.remoteUrl` points at the actual host,
130
130
  * and returns the binding maps the auxiliary routes mount from. Called by the
131
- * only two spec producers — `createRemoteServer` (`config.url`) and the devkit
132
- * CLI. Returns the define-time `compiled` untouched when there is no aux to
131
+ * only two spec producers — `createRemoteServer` (`config.url`) and the
132
+ * `astrale-domain` CLI. Returns the define-time `compiled` untouched when there is no aux to
133
133
  * stamp. `extendCore`/`compileDomain` are pure, so this is safe to call
134
134
  * repeatedly (and is memoized per cold isolate by the callers).
135
135
  */
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  // ─── Method authoring ────────────────────────────────────────────────────
2
2
  export type {
3
+ Kernel,
4
+ KernelForAuth,
3
5
  RemoteContext,
4
6
  RemoteHandler,
5
7
  MethodImpl,
@@ -9,9 +11,28 @@ export type {
9
11
  } from './method'
10
12
  export { remoteMethod, remoteClassMethods, remoteInterfaceMethods } from './method'
11
13
 
12
- // ─── Domain ──────────────────────────────────────────────────────────────
13
- export { defineRemoteDomain, buildInstallGraph, buildInstallGraphHash } from './domain'
14
- export type { RemoteDomain, RemoteDomainConfig } from './domain'
14
+ // ─── Domain configuration (astrale.config.ts) ────────────────────────────
15
+ // The author-facing way to declare a standalone domain + how it deploys. The
16
+ // runtime-domain COMPILER (`defineRemoteDomain`) is internal machinery — reach
17
+ // it via the `@astrale-os/sdk/domain` subpath when hand-rolling a worker, or
18
+ // (preferred) use `domainWorkerEntry` from `@astrale-os/sdk/server`.
19
+ export { defineDomain, deploy, defineAdapter } from './config'
20
+ export type {
21
+ ClientBinding,
22
+ DefineDomainConfig,
23
+ DomainDefinition,
24
+ DeployConfig,
25
+ AdapterSpec,
26
+ DeployCtx,
27
+ DeployResult,
28
+ DomainAdapter,
29
+ DomainInfo,
30
+ WatchCtx,
31
+ WatchHandle,
32
+ } from './config'
33
+
34
+ // ─── Install graph (advanced) ────────────────────────────────────────────
35
+ export { buildInstallGraph, buildInstallGraphHash } from './domain'
15
36
 
16
37
  // ─── Declarative resource helpers ────────────────────────────────────────
17
38
  // Author Views (iframe-mountable) and RemoteFunctions (standalone callables)
@@ -74,4 +95,4 @@ export {
74
95
  SdkValidationError,
75
96
  SdkResultValidationError,
76
97
  } from './dispatch'
77
- export type { SelfResult, CallRemoteFn } from './dispatch'
98
+ export type { SelfResult, CallRemoteFn, KernelCaller } from './dispatch'
@@ -52,9 +52,10 @@ type InterfaceMethodHandler<
52
52
  ? MC extends { readonly static: true }
53
53
  ? RemoteHandler<ResolveParams<MC>, ResolveReturn<MC>, undefined, TDeps>
54
54
  : // Non-static interface methods receive the same `self` the dispatcher
55
- // actually delivers — `SelfResult` ({ path, id? }) — exactly as the
56
- // class-method path (`MethodImpl`) does. The node's declared props are
57
- // NOT hydrated at runtime, so a node-typed self would be unsound.
55
+ // actually delivers — `SelfResult` ({ path, id?, node() }) — exactly as
56
+ // the class-method path (`MethodImpl`) does. Declared props are NOT
57
+ // hydrated onto `self` (a node-typed self would be unsound); a handler
58
+ // that needs the node's class/props reads them via `self.node()`.
58
59
  RemoteHandler<ResolveParams<MC>, ResolveReturn<MC>, SelfResult, TDeps>
59
60
  : never
60
61
  : never
@@ -7,13 +7,43 @@
7
7
  * `BoundClientSessionView` to call back into the parent kernel.
8
8
  */
9
9
 
10
+ import type { AuthPolicy } from '@astrale-os/kernel-api/routed'
10
11
  import type { FnMap } from '@astrale-os/kernel-client'
11
12
  import type { BoundClientSessionView } from '@astrale-os/kernel-client/session'
12
13
  import type { AuthContext } from '@astrale-os/kernel-core'
13
14
 
14
15
  import type { CallRemoteFn } from '../dispatch/call-remote'
15
16
 
16
- export type RemoteContext<TParams, TSelf, TDeps> = {
17
+ /**
18
+ * The kernel session a handler holds: a credential-bound view over the parent
19
+ * kernel — the FULL dispatch surface (`call`/`stream`/`binary`/`signal`,
20
+ * `withSchema` typed dispatch, `as` to rebind the acting credential), not just
21
+ * `call`. Name it from `@astrale-os/sdk` instead of reaching into
22
+ * `@astrale-os/kernel-client`. (For the narrow `{ call }` contract — e.g. a
23
+ * tiny test double — see {@link KernelCaller}.)
24
+ */
25
+ export type Kernel = BoundClientSessionView<FnMap>
26
+
27
+ /**
28
+ * The `kernel` a handler receives, derived from its declared `auth` policy.
29
+ * The runtime guarantee (see `auth/resolve.ts`) is exact:
30
+ * - `'required'` (the default) — the dispatcher rejects a credentialless call
31
+ * before `execute`, so the kernel is ALWAYS present → non-null.
32
+ * - `'optional'` — a kernel only when a credential was supplied → `… | null`.
33
+ * - `'public'` — never any inbound credential → `null`. (Public handlers that
34
+ * must touch the graph use `selfKernel` after verifying their own upstream.)
35
+ *
36
+ * Distributes over a `AuthPolicy` union (e.g. when `auth` is typed but not a
37
+ * literal) to the widened `Kernel | null` — the safe superset, matching the
38
+ * pre-conditional behaviour.
39
+ */
40
+ export type KernelForAuth<TAuth extends AuthPolicy> = TAuth extends 'public'
41
+ ? null
42
+ : TAuth extends 'optional'
43
+ ? Kernel | null
44
+ : Kernel
45
+
46
+ export type RemoteContext<TParams, TSelf, TDeps, TKernel = Kernel | null> = {
17
47
  /** Validated params (Zod-checked against the method's `inputSchema`). */
18
48
  params: TParams
19
49
  /** Auth context resolved from the inbound delegation credential. `null` for public or unauthenticated optional methods. */
@@ -26,13 +56,14 @@ export type RemoteContext<TParams, TSelf, TDeps> = {
26
56
  url: string
27
57
  /**
28
58
  * `BoundClientSessionView` to the parent kernel, bound to the composed
29
- * credential `union(delegation, self)`. `null` when `auth: 'public'`, or
30
- * `auth: 'optional'` with no inbound credential. Use for kernel syscalls +
31
- * same-domain methods. For ANOTHER worker's remote method use
32
- * {@link RemoteContext.callRemote} `kernel.call` to a remote method fails
33
- * the audience check.
59
+ * credential `union(delegation, self)`. Its nullability is `{@link
60
+ * KernelForAuth}` of the method's `auth`: non-null for the default
61
+ * `'required'`, `… | null` for `'optional'`, `null` for `'public'`. Use for
62
+ * kernel syscalls + same-domain methods. For ANOTHER worker's remote method
63
+ * use {@link RemoteContext.callRemote} — `kernel.call` to a remote method
64
+ * fails the audience check.
34
65
  */
35
- kernel: BoundClientSessionView<FnMap> | null
66
+ kernel: TKernel
36
67
  /**
37
68
  * Call another worker's remote method (a Function with `binding.remoteUrl`),
38
69
  * re-minting the credential for the target's audience so it isn't rejected at
@@ -1,4 +1,4 @@
1
- export type { RemoteContext } from './context'
1
+ export type { Kernel, KernelForAuth, RemoteContext } from './context'
2
2
  export type { RemoteHandler, AnyRemoteHandler, MethodImpl } from './single'
3
3
  export { remoteMethod } from './single'
4
4
  export type { ClassMethodsImpl, InterfaceMethodsImpl, SchemaMethodsImpl } from './class'
@@ -20,7 +20,7 @@ import type {
20
20
  import type { Schema } from '@astrale-os/kernel-dsl'
21
21
 
22
22
  import type { SelfResult } from '../dispatch/self'
23
- import type { RemoteContext } from './context'
23
+ import type { KernelForAuth, RemoteContext } from './context'
24
24
 
25
25
  /**
26
26
  * Remote function handler — execute with full typed context.
@@ -35,10 +35,10 @@ import type { RemoteContext } from './context'
35
35
  * dispatcher throws — that's a programmer error indicating a stub leaked into
36
36
  * a code path that was supposed to execute it.
37
37
  */
38
- export type RemoteHandler<TParams, TResult, TSelf, TDeps> = {
38
+ export type RemoteHandler<TParams, TResult, TSelf, TDeps, TAuth extends AuthPolicy = 'required'> = {
39
39
  /** The handler body. May be async or an async generator (for `output: 'stream'`). */
40
40
  execute?: (
41
- ctx: RemoteContext<TParams, TSelf, TDeps>,
41
+ ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>>,
42
42
  ) => TResult | Promise<TResult> | AsyncGenerator<TResult>
43
43
  /**
44
44
  * Optional REST binding — attaches a native HTTP route to this method.
@@ -53,8 +53,13 @@ export type RemoteHandler<TParams, TResult, TSelf, TDeps> = {
53
53
  remoteUrl?: string
54
54
  /** Optional human-readable description. Appears in generated docs. */
55
55
  description?: string
56
- /** Authentication policy. Defaults to `'required'` when absent. */
57
- auth?: AuthPolicy
56
+ /**
57
+ * Authentication policy. Defaults to `'required'` when absent. Captured as a
58
+ * literal type so it drives {@link KernelForAuth} on the `execute`/`authorize`
59
+ * context: omit it (or set `'required'`) and `ctx.kernel` is non-null;
60
+ * `'optional'` widens it to `… | null`; `'public'` makes it `null`.
61
+ */
62
+ auth?: TAuth
58
63
  /**
59
64
  * Optional pre-execute authorization check. Runs after auth resolution and
60
65
  * `_self` resolution, before `execute`. Throw any error to deny the call —
@@ -69,11 +74,17 @@ export type RemoteHandler<TParams, TResult, TSelf, TDeps> = {
69
74
  * The kernel still enforces `has_perm` independently — `authorize` is
70
75
  * additive ergonomic gating on top, not a replacement.
71
76
  */
72
- authorize?: (ctx: RemoteContext<TParams, TSelf, TDeps>) => void | Promise<void>
77
+ authorize?: (
78
+ ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>>,
79
+ ) => void | Promise<void>
73
80
  }
74
81
 
82
+ // `TAuth = AuthPolicy` (the full union, not the `'required'` default) keeps this
83
+ // maximally permissive: it accepts a handler of ANY declared policy, and its
84
+ // `ctx.kernel` widens to `BoundClientSessionView<FnMap> | null`. Used by the
85
+ // dispatcher/index where the concrete policy is erased.
75
86
  // oxlint-disable-next-line no-explicit-any
76
- export type AnyRemoteHandler = RemoteHandler<any, any, any, any>
87
+ export type AnyRemoteHandler = RemoteHandler<any, any, any, any, AuthPolicy>
77
88
 
78
89
  /**
79
90
  * Fully typed method implementation — resolves params/result/self from the
@@ -84,6 +95,7 @@ export type MethodImpl<
84
95
  K extends MethodClassKeys<S> & string,
85
96
  M extends string,
86
97
  TDeps = unknown,
98
+ TAuth extends AuthPolicy = 'required',
87
99
  > =
88
100
  ClassMethodConfig<S, K, M> extends {
89
101
  params: infer P
@@ -91,7 +103,7 @@ export type MethodImpl<
91
103
  self: unknown
92
104
  isStatic: infer St
93
105
  }
94
- ? RemoteHandler<P, R, St extends true ? undefined : SelfResult, TDeps>
106
+ ? RemoteHandler<P, R, St extends true ? undefined : SelfResult, TDeps, TAuth>
95
107
  : never
96
108
 
97
109
  type ImplementableMethodName<S extends Schema, K extends MethodClassKeys<S> & string> = (
@@ -113,17 +125,24 @@ export function remoteMethod<TDeps>(): <
113
125
  S extends Schema,
114
126
  K extends MethodClassKeys<S> & string,
115
127
  M extends ImplementableMethodName<S, K>,
128
+ TAuth extends AuthPolicy = 'required',
116
129
  >(
117
130
  schema: S,
118
131
  className: K,
119
132
  methodName: M,
120
- impl: MethodImpl<S, K, M, TDeps>,
121
- ) => MethodImpl<S, K, M, TDeps>
133
+ impl: MethodImpl<S, K, M, TDeps, TAuth>,
134
+ ) => MethodImpl<S, K, M, TDeps, TAuth>
122
135
  export function remoteMethod<
123
136
  S extends Schema,
124
137
  K extends MethodClassKeys<S> & string,
125
138
  M extends ImplementableMethodName<S, K>,
126
- >(schema: S, className: K, methodName: M, impl: MethodImpl<S, K, M>): MethodImpl<S, K, M>
139
+ TAuth extends AuthPolicy = 'required',
140
+ >(
141
+ schema: S,
142
+ className: K,
143
+ methodName: M,
144
+ impl: MethodImpl<S, K, M, unknown, TAuth>,
145
+ ): MethodImpl<S, K, M, unknown, TAuth>
127
146
  export function remoteMethod(...args: unknown[]) {
128
147
  if (args.length === 0) {
129
148
  return (...innerArgs: unknown[]) => innerArgs[3]
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `domainWorkerEntry` — the one-call worker entry for a standalone domain.
3
+ *
4
+ * It folds the three steps every hand-rolled worker used to wire by hand —
5
+ * compile the runtime domain (`defineRemoteDomain`), build the server
6
+ * (`createRemoteServer`), and wrap it in the shared fetch plumbing
7
+ * (`createWorkerEntry`) — into a single declaration. The author passes the raw
8
+ * modules (`schema` / `methods` / `views` / `functions`) plus the identity and
9
+ * deploy bits; the helper does the rest. This is what the cloudflare adapter's
10
+ * codegen emits, and the recommended surface for a hand-rolled worker.
11
+ *
12
+ * Drop down to `createWorkerEntry` + `defineRemoteDomain` (from
13
+ * `@astrale-os/sdk/domain`) only when the worker needs something this config
14
+ * can't express — e.g. a serving-URL-dependent domain build (views stamped with
15
+ * the live url) or a globally-injected signing key resolved per request.
16
+ */
17
+
18
+ import type { Core, Schema } from '@astrale-os/kernel-dsl'
19
+ import type { Hono } from 'hono'
20
+
21
+ import type { AnyRemoteFunctionDef, ViewDef } from '../define'
22
+ import type { SchemaMethodsImpl } from '../method'
23
+ import type { RemoteServerConfig } from './config'
24
+ import type { WorkerEntry, WorkerEntryConfig } from './worker-entry'
25
+
26
+ import { defineRemoteDomain } from '../domain'
27
+ import { createWorkerEntry } from './worker-entry'
28
+
29
+ export interface DomainWorkerEntryConfig<S extends Schema, TEnv, TDeps> {
30
+ /** The domain schema. */
31
+ schema: S
32
+ /** Method implementations, typed against `schema` under `TDeps`. */
33
+ methods: SchemaMethodsImpl<S, TDeps>
34
+ /** Optional Core override (extra genesis nodes / wiring). */
35
+ core?: Core<S>
36
+ /** Views (iframe-mountable UIs) keyed by slug. */
37
+ views?: Record<string, ViewDef<TDeps>>
38
+ /** Standalone Functions (callables not bound to a class) keyed by slug. */
39
+ functions?: Record<string, AnyRemoteFunctionDef>
40
+
41
+ /**
42
+ * The worker's signing key (its `iss` identity material). A static JWK, or a
43
+ * resolver from `env` for keys injected as a secret / shared globally.
44
+ */
45
+ privateKey: JsonWebKey | ((env: TEnv) => JsonWebKey)
46
+ /** Cross-domain deps by origin — verified present at install. */
47
+ requires?: readonly string[]
48
+ /** Typed colon-path the kernel calls once as __SYSTEM__ after install. */
49
+ postInstall?: string
50
+ /** Provenance stamped on `/meta`. */
51
+ meta?: RemoteServerConfig<TDeps>['meta']
52
+
53
+ /**
54
+ * Map the worker `env` to the handler dependency container. Defaults to
55
+ * passing `env` straight through (the common case where handlers read
56
+ * bindings directly).
57
+ */
58
+ deps?: (env: TEnv, url: string) => TDeps
59
+ /** Optional pre-built host Hono app (CORS / logging / extra routes). */
60
+ app?: (env: TEnv) => Hono
61
+
62
+ // ── createWorkerEntry plumbing (passed through verbatim) ──────────────────
63
+ resolveUrl?: WorkerEntryConfig<TEnv>['resolveUrl']
64
+ selfBinding?: WorkerEntryConfig<TEnv>['selfBinding']
65
+ routeSubrequest?: WorkerEntryConfig<TEnv>['routeSubrequest']
66
+ before?: WorkerEntryConfig<TEnv>['before']
67
+ rewriteRequest?: WorkerEntryConfig<TEnv>['rewriteRequest']
68
+ }
69
+
70
+ /**
71
+ * Curried so `TDeps` can be fixed explicitly (it types `methods`/`views`) while
72
+ * `S` is inferred from `schema` — mirroring `defineRemoteDomain`. The common
73
+ * case `domainWorkerEntry<Env>()({ … })` leaves `TDeps = TEnv` (handlers read
74
+ * the env directly); pass both — `domainWorkerEntry<Env, Deps>()` — when the
75
+ * handler deps differ from the worker bindings and a `deps` mapper is supplied.
76
+ */
77
+ export function domainWorkerEntry<TEnv, TDeps = TEnv>() {
78
+ return function <S extends Schema>(
79
+ config: DomainWorkerEntryConfig<S, TEnv, TDeps>,
80
+ ): WorkerEntry<TEnv> {
81
+ const domain = defineRemoteDomain<TDeps>()({
82
+ schema: config.schema,
83
+ methods: config.methods,
84
+ ...(config.core ? { core: config.core } : {}),
85
+ ...(config.views ? { views: config.views } : {}),
86
+ ...(config.functions ? { remoteFunctions: config.functions } : {}),
87
+ })
88
+
89
+ return createWorkerEntry<TEnv>({
90
+ ...(config.resolveUrl ? { resolveUrl: config.resolveUrl } : {}),
91
+ ...(config.selfBinding ? { selfBinding: config.selfBinding } : {}),
92
+ ...(config.routeSubrequest ? { routeSubrequest: config.routeSubrequest } : {}),
93
+ ...(config.before ? { before: config.before } : {}),
94
+ ...(config.rewriteRequest ? { rewriteRequest: config.rewriteRequest } : {}),
95
+ // `createWorkerEntry` conflates the worker env and the handler deps into a
96
+ // single type param; we keep them distinct in the public config and bridge
97
+ // here. The runtime deps (and the methods typed against them) are correct;
98
+ // only this assembly is cast.
99
+ build: (url, env) =>
100
+ ({
101
+ domain,
102
+ deps: config.deps ? config.deps(env, url) : (env as unknown as TDeps),
103
+ url,
104
+ privateKey:
105
+ typeof config.privateKey === 'function' ? config.privateKey(env) : config.privateKey,
106
+ ...(config.requires && config.requires.length > 0 ? { requires: config.requires } : {}),
107
+ ...(config.postInstall ? { postInstall: config.postInstall } : {}),
108
+ ...(config.meta ? { meta: config.meta } : {}),
109
+ ...(config.app ? { app: config.app(env) } : {}),
110
+ }) as unknown as RemoteServerConfig<TEnv>,
111
+ })
112
+ }
113
+ }
@@ -4,7 +4,9 @@ export type { RemoteServer, RemoteServerHandle } from './handle'
4
4
  export { derivePublicJwk } from './jwks'
5
5
  export { requireEnv } from './require-env'
6
6
  export { canonicalizeServingUrl } from './serving-url'
7
- export { createWorkerEntry } from './worker-entry'
7
+ export { assets, createWorkerEntry } from './worker-entry'
8
8
  export type { WorkerEntry, WorkerEntryConfig } from './worker-entry'
9
+ export { domainWorkerEntry } from './domain-entry'
10
+ export type { DomainWorkerEntryConfig } from './domain-entry'
9
11
  export { workerMeta } from './worker-meta'
10
12
  export { startNodeServer } from './start'