@astrale-os/sdk 0.1.10 → 0.3.0

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 (186) hide show
  1. package/LICENSE +202 -0
  2. package/dist/auth/check.d.ts +1 -2
  3. package/dist/auth/check.d.ts.map +1 -1
  4. package/dist/auth/check.js +2 -6
  5. package/dist/auth/check.js.map +1 -1
  6. package/dist/auth/index.d.ts +3 -1
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js +2 -1
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/auth/issuer-mint.d.ts +53 -0
  11. package/dist/auth/issuer-mint.d.ts.map +1 -0
  12. package/dist/auth/issuer-mint.js +35 -0
  13. package/dist/auth/issuer-mint.js.map +1 -0
  14. package/dist/auth/kernel-client.d.ts +8 -2
  15. package/dist/auth/kernel-client.d.ts.map +1 -1
  16. package/dist/auth/kernel-client.js +22 -46
  17. package/dist/auth/kernel-client.js.map +1 -1
  18. package/dist/cli/dotenv.d.ts +8 -0
  19. package/dist/cli/dotenv.d.ts.map +1 -1
  20. package/dist/cli/dotenv.js +18 -0
  21. package/dist/cli/dotenv.js.map +1 -1
  22. package/dist/cli/index.d.ts +1 -1
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +1 -1
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/log.d.ts +11 -0
  27. package/dist/cli/log.d.ts.map +1 -0
  28. package/dist/cli/log.js +17 -0
  29. package/dist/cli/log.js.map +1 -0
  30. package/dist/cli/publish.d.ts +35 -0
  31. package/dist/cli/publish.d.ts.map +1 -0
  32. package/dist/cli/publish.js +271 -0
  33. package/dist/cli/publish.js.map +1 -0
  34. package/dist/cli/run.d.ts +17 -2
  35. package/dist/cli/run.d.ts.map +1 -1
  36. package/dist/cli/run.js +102 -111
  37. package/dist/cli/run.js.map +1 -1
  38. package/dist/cli/spec.d.ts.map +1 -1
  39. package/dist/cli/spec.js +1 -0
  40. package/dist/cli/spec.js.map +1 -1
  41. package/dist/config/adapter.d.ts +12 -11
  42. package/dist/config/adapter.d.ts.map +1 -1
  43. package/dist/config/adapter.js.map +1 -1
  44. package/dist/config/define-domain.d.ts +88 -27
  45. package/dist/config/define-domain.d.ts.map +1 -1
  46. package/dist/config/define-domain.js +79 -33
  47. package/dist/config/define-domain.js.map +1 -1
  48. package/dist/config/deploy.d.ts +1 -1
  49. package/dist/config/deploy.js +1 -1
  50. package/dist/config/index.d.ts +1 -1
  51. package/dist/config/index.d.ts.map +1 -1
  52. package/dist/define/remote-function.d.ts +32 -17
  53. package/dist/define/remote-function.d.ts.map +1 -1
  54. package/dist/define/remote-function.js +10 -9
  55. package/dist/define/remote-function.js.map +1 -1
  56. package/dist/define/view.d.ts +8 -6
  57. package/dist/define/view.d.ts.map +1 -1
  58. package/dist/define/view.js +8 -6
  59. package/dist/define/view.js.map +1 -1
  60. package/dist/deploy/check.d.ts +6 -6
  61. package/dist/deploy/check.d.ts.map +1 -1
  62. package/dist/deploy/check.js +1 -1
  63. package/dist/deploy/check.js.map +1 -1
  64. package/dist/deploy/index.d.ts +0 -1
  65. package/dist/deploy/index.d.ts.map +1 -1
  66. package/dist/deploy/index.js +0 -1
  67. package/dist/deploy/index.js.map +1 -1
  68. package/dist/deploy/meta.d.ts +11 -0
  69. package/dist/deploy/meta.d.ts.map +1 -1
  70. package/dist/deploy/meta.js +23 -0
  71. package/dist/deploy/meta.js.map +1 -1
  72. package/dist/dispatch/dispatcher.d.ts +17 -4
  73. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  74. package/dist/dispatch/dispatcher.js +15 -3
  75. package/dist/dispatch/dispatcher.js.map +1 -1
  76. package/dist/dispatch/execute.d.ts +6 -2
  77. package/dist/dispatch/execute.d.ts.map +1 -1
  78. package/dist/dispatch/execute.js +1 -0
  79. package/dist/dispatch/execute.js.map +1 -1
  80. package/dist/dispatch/identity.d.ts +12 -10
  81. package/dist/dispatch/identity.d.ts.map +1 -1
  82. package/dist/dispatch/identity.js +10 -15
  83. package/dist/dispatch/identity.js.map +1 -1
  84. package/dist/dispatch/index.d.ts +1 -1
  85. package/dist/dispatch/index.d.ts.map +1 -1
  86. package/dist/dispatch/index.js +1 -1
  87. package/dist/dispatch/index.js.map +1 -1
  88. package/dist/dispatch/self.d.ts +25 -37
  89. package/dist/dispatch/self.d.ts.map +1 -1
  90. package/dist/dispatch/self.js +60 -30
  91. package/dist/dispatch/self.js.map +1 -1
  92. package/dist/domain/binding.d.ts +18 -0
  93. package/dist/domain/binding.d.ts.map +1 -0
  94. package/dist/domain/binding.js +29 -0
  95. package/dist/domain/binding.js.map +1 -0
  96. package/dist/domain/build-spec.d.ts.map +1 -1
  97. package/dist/domain/build-spec.js +22 -7
  98. package/dist/domain/build-spec.js.map +1 -1
  99. package/dist/domain/define.d.ts +28 -43
  100. package/dist/domain/define.d.ts.map +1 -1
  101. package/dist/domain/define.js +23 -60
  102. package/dist/domain/define.js.map +1 -1
  103. package/dist/domain/extend-functions.d.ts +48 -0
  104. package/dist/domain/extend-functions.d.ts.map +1 -0
  105. package/dist/domain/extend-functions.js +84 -0
  106. package/dist/domain/extend-functions.js.map +1 -0
  107. package/dist/domain/extend-views.d.ts +45 -0
  108. package/dist/domain/extend-views.d.ts.map +1 -0
  109. package/dist/domain/extend-views.js +116 -0
  110. package/dist/domain/extend-views.js.map +1 -0
  111. package/dist/index.d.ts +8 -4
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +13 -5
  114. package/dist/index.js.map +1 -1
  115. package/dist/method/class.d.ts +2 -2
  116. package/dist/method/class.d.ts.map +1 -1
  117. package/dist/method/class.js.map +1 -1
  118. package/dist/method/context.d.ts +18 -3
  119. package/dist/method/context.d.ts.map +1 -1
  120. package/dist/method/index.d.ts +2 -2
  121. package/dist/method/index.d.ts.map +1 -1
  122. package/dist/method/index.js.map +1 -1
  123. package/dist/method/single.d.ts +25 -38
  124. package/dist/method/single.d.ts.map +1 -1
  125. package/dist/method/single.js +1 -11
  126. package/dist/method/single.js.map +1 -1
  127. package/dist/server/auxiliary-routes.d.ts.map +1 -1
  128. package/dist/server/auxiliary-routes.js +14 -3
  129. package/dist/server/auxiliary-routes.js.map +1 -1
  130. package/dist/server/create.d.ts +2 -15
  131. package/dist/server/create.d.ts.map +1 -1
  132. package/dist/server/create.js +15 -51
  133. package/dist/server/create.js.map +1 -1
  134. package/dist/server/domain-entry.d.ts +10 -0
  135. package/dist/server/domain-entry.d.ts.map +1 -1
  136. package/dist/server/domain-entry.js +2 -0
  137. package/dist/server/domain-entry.js.map +1 -1
  138. package/dist/server/index.d.ts +0 -1
  139. package/dist/server/index.d.ts.map +1 -1
  140. package/dist/server/index.js +0 -1
  141. package/dist/server/index.js.map +1 -1
  142. package/dist/server/worker-entry.d.ts +3 -3
  143. package/dist/server/worker-entry.js +3 -3
  144. package/package.json +10 -11
  145. package/src/auth/check.ts +2 -6
  146. package/src/auth/index.ts +3 -1
  147. package/src/auth/issuer-mint.ts +75 -0
  148. package/src/auth/kernel-client.ts +31 -63
  149. package/src/cli/dotenv.ts +20 -0
  150. package/src/cli/index.ts +1 -1
  151. package/src/cli/log.ts +18 -0
  152. package/src/cli/publish.ts +341 -0
  153. package/src/cli/run.ts +109 -125
  154. package/src/cli/spec.ts +1 -0
  155. package/src/config/adapter.ts +9 -11
  156. package/src/config/define-domain.ts +184 -53
  157. package/src/config/deploy.ts +1 -1
  158. package/src/config/index.ts +1 -1
  159. package/src/define/remote-function.ts +56 -21
  160. package/src/define/view.ts +8 -6
  161. package/src/deploy/check.ts +7 -7
  162. package/src/deploy/index.ts +0 -1
  163. package/src/deploy/meta.ts +25 -0
  164. package/src/dispatch/dispatcher.ts +32 -9
  165. package/src/dispatch/execute.ts +7 -2
  166. package/src/dispatch/identity.ts +17 -22
  167. package/src/dispatch/index.ts +1 -1
  168. package/src/dispatch/self.ts +117 -41
  169. package/src/domain/binding.ts +37 -0
  170. package/src/domain/build-spec.ts +32 -7
  171. package/src/domain/define.ts +71 -78
  172. package/src/domain/extend-functions.ts +106 -0
  173. package/src/domain/extend-views.ts +151 -0
  174. package/src/index.ts +36 -7
  175. package/src/method/class.ts +11 -7
  176. package/src/method/context.ts +19 -3
  177. package/src/method/index.ts +8 -2
  178. package/src/method/single.ts +35 -42
  179. package/src/server/auxiliary-routes.ts +14 -3
  180. package/src/server/create.ts +15 -51
  181. package/src/server/domain-entry.ts +12 -0
  182. package/src/server/index.ts +0 -1
  183. package/src/server/worker-entry.ts +3 -3
  184. package/src/deploy/hash-spec.ts +0 -31
  185. package/src/domain/extend-core.ts +0 -301
  186. package/src/server/worker-meta.ts +0 -25
@@ -1,33 +1,30 @@
1
1
  /**
2
- * `defineRemoteDomain` — turn a typed schema + methods map into a mountable domain.
3
- *
4
- * When `views` and/or `remoteFunctions` entries are provided, the SDK
5
- * auto-materializes graph nodes
6
- * under reserved folders (`viewsFolder` / `functionsFolder`, defaults `'views'`
7
- * / `'functions'`). Views and standalone functions materialize as canonical
8
- * kernel `View` / `Function` classes by default (no per-domain class to
9
- * configure). The same entries'
10
- * `render` / `execute` handlers are mounted as Hono routes by
11
- * `createRemoteServer`. Slug = map key.
12
- *
13
- * The domain definition is deployment-agnostic: it carries NO serving url. The
14
- * url is supplied late by the spec producer (`createRemoteServer({ url })` at
15
- * runtime, the `astrale-domain` CLI offline) and stamped onto every `binding.remoteUrl`
16
- * by `materializeRemoteDomain`. There is exactly one notion of `url` in the SDK
17
- * — the worker serving URL, which is also `iss` and the binding base.
2
+ * Define a remote domain. Views/functions are first-class domain members; their
3
+ * serving URL is stamped later by spec producers and is also the worker issuer.
18
4
  */
19
5
 
20
6
  import type { FunctionBinding } from '@astrale-os/kernel-api/routed'
21
- import type { BoundMethod, CompiledDomain } from '@astrale-os/kernel-core/domain'
22
- import type { AnyEdgeDef, AnyNodeDef, Core, Schema } from '@astrale-os/kernel-dsl'
7
+ import type {
8
+ BoundMethod,
9
+ CompiledDomain,
10
+ FunctionSchema,
11
+ ViewSchema,
12
+ } from '@astrale-os/kernel-core/domain'
13
+ import type { Core, Schema } from '@astrale-os/kernel-dsl'
23
14
 
24
15
  import { bindMethods, compileDomain } from '@astrale-os/kernel-core/domain'
25
16
 
17
+ import type { DomainManifest } from '../config/define-domain'
26
18
  import type { AnyRemoteFunctionDef, ViewDef } from '../define'
27
19
  import type { SchemaMethodsImpl } from '../method/class'
28
20
  import type { AnyRemoteHandler } from '../method/single'
29
21
 
30
- import { DEFAULT_FUNCTIONS_FOLDER, DEFAULT_VIEWS_FOLDER, extendCore } from './extend-core'
22
+ import {
23
+ assertFunctionsMatchSchema,
24
+ buildFunctionSchemas,
25
+ DEFAULT_FUNCTIONS_FOLDER,
26
+ } from './extend-functions'
27
+ import { buildViewDeclarations, buildViewSchemas, DEFAULT_VIEWS_FOLDER } from './extend-views'
31
28
 
32
29
  export type RemoteDomainConfig<S extends Schema, TDeps> = {
33
30
  schema: S
@@ -35,72 +32,72 @@ export type RemoteDomainConfig<S extends Schema, TDeps> = {
35
32
  core?: Core<S>
36
33
 
37
34
  views?: Record<string, ViewDef<TDeps>>
38
- viewClass?: AnyNodeDef
39
- viewForEdgeClass?: AnyEdgeDef
40
35
  viewsFolder?: string
41
36
 
42
37
  remoteFunctions?: Record<string, AnyRemoteFunctionDef>
43
38
  functionsFolder?: string
39
+
40
+ /** Presentation metadata for `/meta` (logo / entry view). Carried verbatim. */
41
+ manifest?: DomainManifest
42
+
43
+ /** Physical tree path for the Domain node (absolute, e.g. `/domains/acme`); default `/domains/<origin>`. */
44
+ path?: string
44
45
  }
45
46
 
46
- /** Effective bindings + folder layout for auxiliary routes, resolved against `url`. */
47
+ /** Effective bindings + folder layout + member schemas for auxiliary routes, resolved against `url`. */
47
48
  export type AuxiliaryMetadata = {
48
49
  url: string
49
50
  viewsFolder: string
50
51
  functionsFolder: string
51
52
  viewBindings: Record<string, FunctionBinding>
52
53
  remoteFunctionBindings: Record<string, FunctionBinding>
54
+ /** Function members' impl/binding half (`function.<slug>`) for `serialize`. */
55
+ functionSchemas: FunctionSchema[]
56
+ /** View members' impl/binding half (`view.<slug>`) for `serialize`. */
57
+ viewSchemas: ViewSchema[]
53
58
  }
54
59
 
55
60
  export type RemoteDomain<S extends Schema = Schema> = {
56
- /**
57
- * Define-time compile: full domain STRUCTURE (classes, methods, and the aux
58
- * View/Function nodes' paths/names/refs — what identity, subs, and contract
59
- * resolution need). It carries NO `binding` values: bindings derive from the
60
- * serving url, which only the spec producers know — they call
61
- * `materializeRemoteDomain(domain, url)` for the install-ready compile.
62
- */
61
+ /** Define-time compile has structure only; bindings are stamped at materialization. */
63
62
  compiled: CompiledDomain<S>
64
63
  methods: BoundMethod<AnyRemoteHandler>[]
65
64
  // oxlint-disable-next-line no-explicit-any
66
65
  views?: Record<string, ViewDef<any>>
67
66
  remoteFunctions?: Record<string, AnyRemoteFunctionDef>
68
- /** The original config, so `materializeRemoteDomain` re-extends from source. */
69
- config: ExtendInputs
67
+ /** Presentation metadata for `/meta` (logo / entry view); `createRemoteServer` stamps it. */
68
+ manifest?: DomainManifest
69
+ /** The original config, so `materializeRemoteDomain` re-materializes from source. */
70
+ config: MaterializeInputs
71
+ /**
72
+ * Physical tree path for the Domain node (absolute string, e.g. `/domains/acme`).
73
+ * Read by `buildInstallGraph` and passed to `serialize` as `treeMount`; unset =>
74
+ * `/domains/<origin>` (applied in build-spec). Addressing is unaffected.
75
+ */
76
+ path?: string
70
77
  }
71
78
 
72
- /** The defineRemoteDomain inputs `materializeRemoteDomain` needs to re-extend. */
73
- type ExtendInputs = {
79
+ /** The defineRemoteDomain inputs `materializeRemoteDomain` needs to re-materialize. */
80
+ type MaterializeInputs = {
74
81
  userCore?: Core
75
- viewClass?: AnyNodeDef
76
- viewForEdgeClass?: AnyEdgeDef
77
82
  viewsFolder: string
78
83
  functionsFolder: string
79
84
  }
80
85
 
81
86
  export function defineRemoteDomain<TDeps>() {
82
87
  return function <S extends Schema>(config: RemoteDomainConfig<S, TDeps>): RemoteDomain<S> {
83
- const hasAux = Boolean(config.views || config.remoteFunctions)
84
88
  const viewsFolder = config.viewsFolder ?? DEFAULT_VIEWS_FOLDER
85
89
  const functionsFolder = config.functionsFolder ?? DEFAULT_FUNCTIONS_FOLDER
86
90
 
87
- // Structure-only extension (no url no bindings stamped): the aux nodes
88
- // exist with their paths so subs/identity/contract resolution see them.
89
- const effectiveCore: Core | undefined = hasAux
90
- ? extendCore({
91
- schema: config.schema,
92
- origin: config.schema.domain,
93
- userCore: config.core,
94
- viewClass: config.viewClass,
95
- viewForEdgeClass: config.viewForEdgeClass,
96
- viewsFolder,
97
- views: config.views,
98
- functionsFolder,
99
- remoteFunctions: config.remoteFunctions,
100
- }).core
101
- : config.core
102
-
103
- const compiled = compileDomain(config.schema, effectiveCore as Core<S> | undefined)
91
+ // Standalone function handlers must match schema-declared function members.
92
+ assertFunctionsMatchSchema(config.schema, config.remoteFunctions)
93
+ // Standalone function handlers must match schema-declared function members.
94
+ assertFunctionsMatchSchema(config.schema, config.remoteFunctions)
95
+ const compiled = compileDomain(
96
+ config.schema,
97
+ config.core,
98
+ undefined,
99
+ config.views ? buildViewDeclarations(config.views) : undefined,
100
+ )
104
101
  const methods = bindMethods<AnyRemoteHandler>(
105
102
  compiled.$.schema,
106
103
  compiled.$.methods,
@@ -113,10 +110,10 @@ export function defineRemoteDomain<TDeps>() {
113
110
  methods,
114
111
  ...(config.views ? { views: config.views } : {}),
115
112
  ...(config.remoteFunctions ? { remoteFunctions: config.remoteFunctions } : {}),
113
+ ...(config.manifest ? { manifest: config.manifest } : {}),
114
+ ...(config.path ? { path: config.path } : {}),
116
115
  config: {
117
116
  userCore: config.core,
118
- viewClass: config.viewClass,
119
- viewForEdgeClass: config.viewForEdgeClass,
120
117
  viewsFolder,
121
118
  functionsFolder,
122
119
  },
@@ -124,15 +121,7 @@ export function defineRemoteDomain<TDeps>() {
124
121
  }
125
122
  }
126
123
 
127
- /**
128
- * Materialize a `RemoteDomain` at its real serving `url`: re-runs `extendCore`
129
- * so every aux View/Function `binding.remoteUrl` points at the actual host,
130
- * and returns the binding maps the auxiliary routes mount from. Called by the
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
- * stamp. `extendCore`/`compileDomain` are pure, so this is safe to call
134
- * repeatedly (and is memoized per cold isolate by the callers).
135
- */
124
+ /** Materialize aux member bindings against the real serving URL. Pure and repeatable. */
136
125
  export function materializeRemoteDomain<S extends Schema>(
137
126
  domain: RemoteDomain<S>,
138
127
  url: string,
@@ -142,27 +131,31 @@ export function materializeRemoteDomain<S extends Schema>(
142
131
 
143
132
  const { config } = domain
144
133
  const schema = domain.compiled.$.schema as S
145
- const result = extendCore({
134
+
135
+ const compiled = compileDomain(
146
136
  schema,
147
- origin: schema.domain,
148
- userCore: config.userCore,
149
- url,
150
- viewClass: config.viewClass,
151
- viewForEdgeClass: config.viewForEdgeClass,
152
- viewsFolder: config.viewsFolder,
153
- views: domain.views,
154
- functionsFolder: config.functionsFolder,
155
- remoteFunctions: domain.remoteFunctions,
156
- })
157
- const compiled = compileDomain(schema, result.core as Core<S>)
137
+ config.userCore as Core<S> | undefined,
138
+ undefined,
139
+ domain.views ? buildViewDeclarations(domain.views) : undefined,
140
+ )
141
+
142
+ const fns = domain.remoteFunctions
143
+ ? buildFunctionSchemas(domain.remoteFunctions, url, config.functionsFolder)
144
+ : { schemas: [], bindings: {} }
145
+ const vws = domain.views
146
+ ? buildViewSchemas(domain.views, url, config.viewsFolder, schema)
147
+ : { schemas: [], bindings: {} }
148
+
158
149
  return {
159
150
  compiled,
160
151
  auxiliary: {
161
152
  url,
162
153
  viewsFolder: config.viewsFolder,
163
154
  functionsFolder: config.functionsFolder,
164
- viewBindings: result.viewBindings,
165
- remoteFunctionBindings: result.remoteFunctionBindings,
155
+ viewBindings: vws.bindings,
156
+ remoteFunctionBindings: fns.bindings,
157
+ functionSchemas: fns.schemas,
158
+ viewSchemas: vws.schemas,
166
159
  },
167
160
  }
168
161
  }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Materialize the `functions` map (`defineRemoteFunction` entries) into the two
3
+ * halves a standalone-function DOMAIN MEMBER contributes — mirroring how a class
4
+ * method splits across compile + serialize:
5
+ *
6
+ * - the CONTRACT half — a `func({ input, output })` per slug, declared in the
7
+ * schema's `functions` group so the compiled domain carries `function.<slug>`
8
+ * on `refs.functions` (the kernel owns the ref/layout/of_domain mapping).
9
+ * - `FunctionSchema[]` + per-slug `FunctionBinding` — the IMPL/BINDING half,
10
+ * fed to `serialize` (url-stamped). The worker route is
11
+ * `<url>/<functionsFolder>/<slug>`, DECOUPLED from the graph layout — so
12
+ * `functionsFolder` only names the URL segment, not the node position.
13
+ *
14
+ * A standalone function is NOT a core/instance node: it is attached directly to
15
+ * the Domain via `of_domain` by the kernel-core serializer, so this module never
16
+ * touches `Core`.
17
+ */
18
+
19
+ import type { FunctionBinding } from '@astrale-os/kernel-api/routed'
20
+ import type { FunctionSchema } from '@astrale-os/kernel-core/domain'
21
+ import type { Schema } from '@astrale-os/kernel-dsl'
22
+
23
+ import { zodToJsonSchema } from '@astrale-os/kernel-core/domain'
24
+
25
+ import type { AnyRemoteFunctionDef } from '../define/remote-function'
26
+
27
+ import { resolveBinding } from './binding'
28
+
29
+ /**
30
+ * Default URL segment for a function's worker route (`<url>/functions/<slug>`).
31
+ * Names the HTTP route only — the graph layout (`/<origin>/functions/<slug>`) is
32
+ * a fixed kernel convention, independent of this.
33
+ */
34
+ export const DEFAULT_FUNCTIONS_FOLDER = 'functions'
35
+ const SLUG_RE = /^[a-z][a-z0-9-]*$/
36
+
37
+ function assertValidSlugs(functions: Record<string, AnyRemoteFunctionDef>): void {
38
+ for (const slug of Object.keys(functions)) {
39
+ if (!SLUG_RE.test(slug)) {
40
+ throw new Error(
41
+ `defineRemoteDomain: invalid function slug "${slug}" — must match ${SLUG_RE}.`,
42
+ )
43
+ }
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Contract half — since "functions as first-class defineSchema members", a
49
+ * standalone function's CONTRACT is declared in the SCHEMA (`defineSchema(...,
50
+ * { functions: { seed: func({ input, output }) } })`) and `compileDomain` builds
51
+ * the member refs from it; the SDK map supplies the HANDLER half. This check
52
+ * pins the two halves together at define time: every schema-declared function
53
+ * needs a handler, every handler a declaration — a drifted key fails loudly at
54
+ * authoring, never as a boot-time missing-identity or a dangling graph member.
55
+ */
56
+ export function assertFunctionsMatchSchema(
57
+ schema: Schema,
58
+ functions: Record<string, AnyRemoteFunctionDef> | undefined,
59
+ ): void {
60
+ if (functions) assertValidSlugs(functions)
61
+ const declared = Object.keys(schema.functions ?? {})
62
+ const handled = Object.keys(functions ?? {})
63
+ const missingHandler = declared.filter((slug) => !handled.includes(slug))
64
+ const missingDecl = handled.filter((slug) => !declared.includes(slug))
65
+ if (missingHandler.length === 0 && missingDecl.length === 0) return
66
+ const parts: string[] = []
67
+ if (missingHandler.length > 0) {
68
+ parts.push(
69
+ `schema declares function(s) with no handler: ${missingHandler.map((s) => `"${s}"`).join(', ')}`,
70
+ )
71
+ }
72
+ if (missingDecl.length > 0) {
73
+ parts.push(
74
+ `handler(s) with no schema declaration: ${missingDecl.map((s) => `"${s}"`).join(', ')} ` +
75
+ `(declare them in defineSchema's \`functions\` group via \`func({ input, output })\`)`,
76
+ )
77
+ }
78
+ throw new Error(`defineRemoteDomain: functions/schema mismatch — ${parts.join('; ')}.`)
79
+ }
80
+
81
+ /**
82
+ * Impl/binding half — `FunctionSchema[]` for `serialize` plus the per-slug
83
+ * `FunctionBinding` map the worker mounts routes from. `ref` is the canonical
84
+ * `function.<slug>` member ref (matching `compiled.$.refs.functions`), so the
85
+ * serializer's per-member impl lookup finds it.
86
+ */
87
+ export function buildFunctionSchemas(
88
+ functions: Record<string, AnyRemoteFunctionDef>,
89
+ url: string,
90
+ functionsFolder: string,
91
+ ): { schemas: FunctionSchema[]; bindings: Record<string, FunctionBinding> } {
92
+ const schemas: FunctionSchema[] = []
93
+ const bindings: Record<string, FunctionBinding> = {}
94
+ for (const [slug, def] of Object.entries(functions)) {
95
+ const binding = resolveBinding(def.binding, url, functionsFolder, slug)
96
+ bindings[slug] = binding
97
+ schemas.push({
98
+ ref: `function.${slug}`,
99
+ inputSchema: zodToJsonSchema(def.inputSchema),
100
+ outputSchema: zodToJsonSchema(def.outputSchema),
101
+ output: 'value',
102
+ binding,
103
+ })
104
+ }
105
+ return { schemas, bindings }
106
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Materialize the `views` map (`defineView` entries) into the two halves a view
3
+ * DOMAIN MEMBER contributes — mirroring `extend-functions.ts`:
4
+ *
5
+ * - `ViewDeclarations` — the CONTRACT half, fed to `compileDomain`'s members
6
+ * arg. The member ref (`view.<slug>`), the layout (`/<origin>/views/<slug>`),
7
+ * and the `of_domain` edge slug all derive from the map key.
8
+ * - `ViewSchema[]` + per-slug `FunctionBinding` — the IMPL half, fed to
9
+ * `serialize`. A view carries more than a function: a `UI.handshake` mode and
10
+ * `view_for` target edges (resolved here to graph paths). The worker route is
11
+ * `<url>/<viewsFolder>/<slug>`, DECOUPLED from the graph layout.
12
+ *
13
+ * A view is NOT a core/instance node: the kernel-core serializer attaches it to
14
+ * the Domain via `of_domain` (slug `view.<slug>`) and emits its `view_for` edges,
15
+ * so this module never touches `Core`.
16
+ */
17
+
18
+ import type { FunctionBinding } from '@astrale-os/kernel-api/routed'
19
+ import type { ViewDeclarations, ViewSchema } from '@astrale-os/kernel-core/domain'
20
+ import type { Schema } from '@astrale-os/kernel-dsl'
21
+
22
+ import { DomainPaths } from '@astrale-os/kernel-core/domain'
23
+ import { buildDefOriginMap, scanImportedDefs } from '@astrale-os/kernel-core/domain'
24
+ import {
25
+ buildDefDescriptorMap,
26
+ isAbstract,
27
+ isCorePath,
28
+ isSelfMarker,
29
+ type EdgeEndpoint,
30
+ } from '@astrale-os/kernel-dsl'
31
+
32
+ import type { ViewDef } from '../define/view'
33
+
34
+ import { joinWorkerPath, resolveBinding } from './binding'
35
+
36
+ /**
37
+ * Default URL segment for a view's worker route (`<url>/views/<slug>`). Names the
38
+ * HTTP route only — the graph layout (`/<origin>/views/<slug>`) is a fixed kernel
39
+ * convention, independent of this.
40
+ */
41
+ export const DEFAULT_VIEWS_FOLDER = 'views'
42
+ const SLUG_RE = /^[a-z][a-z0-9-]*$/
43
+
44
+ // oxlint-disable-next-line no-explicit-any
45
+ type AnyViewDef = ViewDef<any>
46
+
47
+ function assertValidSlugs(views: Record<string, AnyViewDef>): void {
48
+ for (const slug of Object.keys(views)) {
49
+ if (!SLUG_RE.test(slug)) {
50
+ throw new Error(`defineRemoteDomain: invalid view slug "${slug}" — must match ${SLUG_RE}.`)
51
+ }
52
+ }
53
+ }
54
+
55
+ /** Enforce ViewDef's documented exclusivity: `mount` cannot combine with `render`/`binding`. */
56
+ function assertExclusivity(slug: string, def: AnyViewDef): void {
57
+ if (def.mount && (def.render || def.binding)) {
58
+ throw new Error(
59
+ `defineRemoteDomain: view "${slug}" sets \`mount\` together with ` +
60
+ `${def.render ? '`render`' : '`binding`'} — they are mutually exclusive.`,
61
+ )
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Contract half — one declaration per `views` map key, for `compileDomain`'s
67
+ * `views` arg. The slug drives ref/path/of_domain; impl/binding/view_for arrive
68
+ * at serialize.
69
+ */
70
+ export function buildViewDeclarations(views: Record<string, AnyViewDef>): ViewDeclarations {
71
+ assertValidSlugs(views)
72
+ const out: Record<string, { slug: string }> = {}
73
+ for (const slug of Object.keys(views)) out[slug] = { slug }
74
+ return out
75
+ }
76
+
77
+ /**
78
+ * Impl half — `ViewSchema[]` for `serialize` plus the per-slug `FunctionBinding`
79
+ * map the worker mounts routes from. `viewFor` markers are resolved here to
80
+ * concrete target paths (class meta-nodes) because the kernel-core serializer is
81
+ * pure and does not resolve `defineCore` endpoints.
82
+ */
83
+ export function buildViewSchemas(
84
+ views: Record<string, AnyViewDef>,
85
+ url: string,
86
+ viewsFolder: string,
87
+ schema: Schema,
88
+ ): { schemas: ViewSchema[]; bindings: Record<string, FunctionBinding> } {
89
+ const schemas: ViewSchema[] = []
90
+ const bindings: Record<string, FunctionBinding> = {}
91
+ for (const [slug, def] of Object.entries(views)) {
92
+ assertExclusivity(slug, def)
93
+ // `mount` is a worker-relative SPA path → `<url><mount>`; otherwise the
94
+ // default/overridden `<url>/<viewsFolder>/<slug>`.
95
+ const binding = def.mount
96
+ ? { remoteUrl: joinWorkerPath(url, def.mount) }
97
+ : resolveBinding(def.binding, url, viewsFolder, slug)
98
+ bindings[slug] = binding
99
+ // Inline `render` views ship no shell client → default handshake 'none'.
100
+ const handshake = def.handshake ?? (def.render ? 'none' : undefined)
101
+ schemas.push({
102
+ ref: `view.${slug}`,
103
+ name: slug,
104
+ binding,
105
+ ...(handshake ? { handshake } : {}),
106
+ viewFor: resolveViewForTargets(def.viewFor, schema),
107
+ })
108
+ }
109
+ return { schemas, bindings }
110
+ }
111
+
112
+ /**
113
+ * Resolve `defineView`'s `viewFor` endpoints to concrete graph path raws.
114
+ * Mirrors kernel-core `compileCore`'s endpoint resolution (the old channel that
115
+ * carried view_for as core edges): `selfOf(Def)` → the def's class/interface
116
+ * meta-node `/<declaringDomain>/{class,interface}.<Name>/self`; a `CorePath`
117
+ * string → its anchored graph path. Empty when the view targets nothing.
118
+ */
119
+ function resolveViewForTargets(
120
+ viewFor: EdgeEndpoint | readonly EdgeEndpoint[] | undefined,
121
+ schema: Schema,
122
+ ): string[] {
123
+ if (!viewFor) return []
124
+ const targets = Array.isArray(viewFor) ? viewFor : [viewFor]
125
+ if (targets.length === 0) return []
126
+
127
+ const descriptorMap = buildDefDescriptorMap(schema)
128
+ const defOrigins = buildDefOriginMap(schema, scanImportedDefs(schema))
129
+
130
+ return targets.map((target) => {
131
+ if (isSelfMarker(target)) {
132
+ const def = target.__def
133
+ const desc = descriptorMap.get(def)
134
+ if (!desc) {
135
+ throw new Error(
136
+ 'defineView: `viewFor: selfOf(...)` references a def that is neither in this ' +
137
+ "schema nor any of its `imports`. Add the def's schema to `imports`.",
138
+ )
139
+ }
140
+ const origin = defOrigins.get(def) ?? schema.domain
141
+ const dp = DomainPaths.of(origin)
142
+ return isAbstract(def) ? dp.interface(desc.name).raw : dp.class(desc.name).raw
143
+ }
144
+ if (isCorePath(target)) {
145
+ return DomainPaths.of(schema.domain).corePath(target).raw
146
+ }
147
+ throw new Error(
148
+ 'defineView: unsupported `viewFor` endpoint — use `selfOf(Class)` or a CorePath.',
149
+ )
150
+ })
151
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  // ─── Method authoring ────────────────────────────────────────────────────
2
2
  export type {
3
+ MethodParams,
4
+ MethodResult,
3
5
  AuthForPolicy,
6
+ GraphApi,
4
7
  Kernel,
5
8
  KernelForAuth,
6
9
  RemoteContext,
@@ -19,9 +22,9 @@ export { remoteMethod, remoteClassMethods, remoteInterfaceMethods } from './meth
19
22
  // (preferred) use `domainWorkerEntry` from `@astrale-os/sdk/server`.
20
23
  export { defineDomain, deploy, defineAdapter } from './config'
21
24
  export type {
22
- ClientBinding,
23
25
  DefineDomainConfig,
24
26
  DomainDefinition,
27
+ DomainManifest,
25
28
  DeployConfig,
26
29
  AdapterSpec,
27
30
  DeployCtx,
@@ -35,6 +38,25 @@ export type {
35
38
  // ─── Install graph (advanced) ────────────────────────────────────────────
36
39
  export { buildInstallGraph, buildInstallGraphHash } from './domain'
37
40
 
41
+ // ─── Graph patching (advanced) ────────────────────────────────────────
42
+ // Author-facing patch types, re-exported from kernel-core (the single source).
43
+ // Build a `Patch` and feed it straight to `ctx.kernel.mutate(patch)` (it implements
44
+ // `PatchData`); the graph sugar itself lives in `@astrale-os/kernel-client/graph`,
45
+ // flattened onto `ctx.kernel`, and `function.mutate` validates every patch kernel-side
46
+ // against kernel-core's `patchDataSchema` — no client-side re-validation needed.
47
+ export { AbsolutePath, Patch, Path } from '@astrale-os/kernel-core'
48
+ export { ClassPath } from '@astrale-os/kernel-core/domain'
49
+ export type {
50
+ EdgeCreate,
51
+ EdgeDelete,
52
+ EdgeMatcher,
53
+ EdgeUpdate,
54
+ NodeCreate,
55
+ NodeDelete,
56
+ NodeUpdate,
57
+ PatchData,
58
+ } from '@astrale-os/kernel-core'
59
+
38
60
  // ─── Declarative resource helpers ────────────────────────────────────────
39
61
  // Author Views (iframe-mountable) and RemoteFunctions (standalone callables)
40
62
  // at the domain definition site. Each `defineView` / `defineRemoteFunction`
@@ -54,10 +76,10 @@ export type { ViewDef, ViewRenderContext, RemoteFunctionDef, RemoteFunctionConte
54
76
  // runtime import from '@astrale-os/sdk/server' directly.
55
77
 
56
78
  // ─── Deploy ──────────────────────────────────────────────────────────────
57
- // `deployCheck` and `hashSpecFile` use Node-only modules (`node:crypto`,
58
- // `node:fs`, `node:child_process`) at module load time. Exporting them from
59
- // the barrel poisons browser bundlers (Vite/esbuild) that transitively load
60
- // every re-export. Consumers that need them import from './deploy' directly.
79
+ // `deployCheck` uses Node-only modules (`node:path`, `node:child_process`) at
80
+ // module load time. Exporting it from the barrel poisons browser bundlers
81
+ // (Vite/esbuild) that transitively load every re-export. Consumers that need it
82
+ // import from './deploy' directly.
61
83
  export { MetaSchema } from './deploy/meta'
62
84
  export type { Meta } from './deploy/meta'
63
85
 
@@ -75,7 +97,14 @@ export type {
75
97
 
76
98
  // ─── Auth ────────────────────────────────────────────────────────────────
77
99
  export type { RemoteIdentityConfig, AuthenticateResult } from './auth'
78
- export { authenticateRequest, buildComposedGrant, signCredential } from './auth'
100
+ export type { DomainIssuer, MintIssuedParams } from './auth'
101
+ export {
102
+ authenticateRequest,
103
+ bindSelfKernel,
104
+ buildComposedGrant,
105
+ makeSelfKernel,
106
+ signCredential,
107
+ } from './auth'
79
108
  export { AuthMissingError, AuthInvalidError } from './auth'
80
109
  export { assertPerm, requireOwnership, READ, EDIT, USE, SHARE, ALL } from './auth'
81
110
  export type {
@@ -96,4 +125,4 @@ export {
96
125
  SdkValidationError,
97
126
  SdkResultValidationError,
98
127
  } from './dispatch'
99
- export type { SelfResult, CallRemoteFn, KernelCaller } from './dispatch'
128
+ export type { SelfResult, TypedSelf, CallRemoteFn, KernelCaller } from './dispatch'
@@ -25,7 +25,7 @@ import type {
25
25
  } from '@astrale-os/kernel-core/domain'
26
26
  import type { ImplementableOwnKeys, Schema } from '@astrale-os/kernel-dsl'
27
27
 
28
- import type { SelfResult } from '../dispatch/self'
28
+ import type { TypedSelf } from '../dispatch/self'
29
29
  import type { MethodImpl, RemoteHandler } from './single'
30
30
 
31
31
  /** Per-class method implementations for a remote domain. */
@@ -51,12 +51,16 @@ type InterfaceMethodHandler<
51
51
  ? InterfaceMethodDefs<S, K>[M] extends { readonly config: infer MC }
52
52
  ? MC extends { readonly static: true }
53
53
  ? RemoteHandler<ResolveParams<MC>, ResolveReturn<MC>, undefined, TDeps>
54
- : // Non-static interface methods receive the same `self` the dispatcher
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()`.
59
- RemoteHandler<ResolveParams<MC>, ResolveReturn<MC>, SelfResult, TDeps>
54
+ : // Non-static interface methods receive the schema-typed `self` keyed by
55
+ // THIS interface's def — `self.node()` binds to a `BoundNode<D>` with
56
+ // short-keyed, def-typed `props`, `self.call` is typed, exactly as the
57
+ // class-method path (`MethodImpl`) does.
58
+ RemoteHandler<
59
+ ResolveParams<MC>,
60
+ ResolveReturn<MC>,
61
+ TypedSelf<DefForInterface<S, K>, S>,
62
+ TDeps
63
+ >
60
64
  : never
61
65
  : never
62
66
 
@@ -12,8 +12,11 @@ import type { FnMap } from '@astrale-os/kernel-client'
12
12
  import type { BoundClientSessionView } from '@astrale-os/kernel-client/session'
13
13
  import type { AuthContext } from '@astrale-os/kernel-core'
14
14
 
15
+ import type { DomainIssuer } from '../auth/issuer-mint'
15
16
  import type { CallRemoteFn } from '../dispatch/call-remote'
16
17
 
18
+ export type { GraphApi } from '@astrale-os/kernel-client/graph'
19
+
17
20
  /**
18
21
  * The kernel session a handler holds: a credential-bound view over the parent
19
22
  * kernel — the FULL dispatch surface (`call`/`stream`/`binary`/`signal`,
@@ -80,11 +83,24 @@ export type RemoteContext<
80
83
  * credential `union(delegation, self)`. Its nullability is `{@link
81
84
  * KernelForAuth}` of the method's `auth`: non-null for the default
82
85
  * `'required'`, `… | null` for `'optional'`, `null` for `'public'`. Use for
83
- * kernel syscalls + same-domain methods. For ANOTHER worker's remote method
84
- * use {@link RemoteContext.callRemote} `kernel.call` to a remote method
85
- * fails the audience check.
86
+ * kernel syscalls + same-domain methods, and for the typed graph read/write
87
+ * sugar flattened onto it (`kernel.get` / `kernel.children` / `kernel.query`,
88
+ * plus `kernel.createNode` / `kernel.updateNode` /
89
+ * `kernel.deleteNode` / `kernel.createEdge` / `kernel.updateEdge` /
90
+ * `kernel.deleteEdge` / `kernel.mutate(patch)`) over the `function.get` /
91
+ * `function.mutate` syscalls. For ANOTHER worker's remote method use
92
+ * {@link RemoteContext.callRemote} — `kernel.call` to a remote method fails the
93
+ * audience check.
86
94
  */
87
95
  kernel: TKernel
96
+ /**
97
+ * Mint seam for identities THIS DOMAIN issues (`iss` = this worker): sign a
98
+ * credential for a domain-owned principal (an App identity, a role umbrella,
99
+ * a managed user) and optionally open a kernel session AS it. See
100
+ * {@link DomainIssuer}. The kernel enforces trust/frozen/grant rules — this
101
+ * only signs under the worker's own issuer.
102
+ */
103
+ issuer: DomainIssuer
88
104
  /**
89
105
  * Call another worker's remote method (a Function with `binding.remoteUrl`),
90
106
  * re-minting the credential for the target's audience so it isn't rejected at
@@ -1,5 +1,11 @@
1
- export type { AuthForPolicy, Kernel, KernelForAuth, RemoteContext } from './context'
2
- export type { RemoteHandler, AnyRemoteHandler, MethodImpl } from './single'
1
+ export type { AuthForPolicy, GraphApi, Kernel, KernelForAuth, RemoteContext } from './context'
2
+ export type {
3
+ RemoteHandler,
4
+ AnyRemoteHandler,
5
+ MethodImpl,
6
+ MethodParams,
7
+ MethodResult,
8
+ } from './single'
3
9
  export { remoteMethod } from './single'
4
10
  export type { ClassMethodsImpl, InterfaceMethodsImpl, SchemaMethodsImpl } from './class'
5
11
  export { remoteClassMethods, remoteInterfaceMethods } from './class'