@astrale-os/sdk 0.1.9 → 0.2.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 (80) hide show
  1. package/dist/auth/index.d.ts +1 -1
  2. package/dist/auth/index.d.ts.map +1 -1
  3. package/dist/auth/index.js +1 -1
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/cli/run.d.ts +0 -1
  6. package/dist/cli/run.d.ts.map +1 -1
  7. package/dist/cli/run.js +21 -10
  8. package/dist/cli/run.js.map +1 -1
  9. package/dist/config/adapter.d.ts +19 -11
  10. package/dist/config/adapter.d.ts.map +1 -1
  11. package/dist/config/adapter.js.map +1 -1
  12. package/dist/config/define-domain.d.ts +13 -29
  13. package/dist/config/define-domain.d.ts.map +1 -1
  14. package/dist/config/define-domain.js +22 -33
  15. package/dist/config/define-domain.js.map +1 -1
  16. package/dist/config/deploy.d.ts +1 -1
  17. package/dist/config/deploy.js +1 -1
  18. package/dist/config/index.d.ts +1 -1
  19. package/dist/config/index.d.ts.map +1 -1
  20. package/dist/define/remote-function.d.ts +9 -14
  21. package/dist/define/remote-function.d.ts.map +1 -1
  22. package/dist/define/remote-function.js +9 -8
  23. package/dist/define/remote-function.js.map +1 -1
  24. package/dist/define/view.d.ts +14 -6
  25. package/dist/define/view.d.ts.map +1 -1
  26. package/dist/define/view.js +8 -6
  27. package/dist/define/view.js.map +1 -1
  28. package/dist/dispatch/identity.d.ts +10 -9
  29. package/dist/dispatch/identity.d.ts.map +1 -1
  30. package/dist/dispatch/identity.js +9 -15
  31. package/dist/dispatch/identity.js.map +1 -1
  32. package/dist/domain/binding.d.ts +18 -0
  33. package/dist/domain/binding.d.ts.map +1 -0
  34. package/dist/domain/binding.js +29 -0
  35. package/dist/domain/binding.js.map +1 -0
  36. package/dist/domain/build-spec.d.ts.map +1 -1
  37. package/dist/domain/build-spec.js +10 -7
  38. package/dist/domain/build-spec.js.map +1 -1
  39. package/dist/domain/define.d.ts +27 -27
  40. package/dist/domain/define.d.ts.map +1 -1
  41. package/dist/domain/define.js +31 -49
  42. package/dist/domain/define.js.map +1 -1
  43. package/dist/domain/extend-core.d.ts.map +1 -1
  44. package/dist/domain/extend-core.js +9 -1
  45. package/dist/domain/extend-core.js.map +1 -1
  46. package/dist/domain/extend-functions.d.ts +44 -0
  47. package/dist/domain/extend-functions.d.ts.map +1 -0
  48. package/dist/domain/extend-functions.js +69 -0
  49. package/dist/domain/extend-functions.js.map +1 -0
  50. package/dist/domain/extend-views.d.ts +45 -0
  51. package/dist/domain/extend-views.d.ts.map +1 -0
  52. package/dist/domain/extend-views.js +116 -0
  53. package/dist/domain/extend-views.js.map +1 -0
  54. package/dist/index.d.ts +2 -2
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +1 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/server/auxiliary-routes.js +1 -1
  59. package/dist/server/auxiliary-routes.js.map +1 -1
  60. package/dist/server/worker-entry.d.ts +3 -3
  61. package/dist/server/worker-entry.js +3 -3
  62. package/package.json +2 -5
  63. package/src/auth/index.ts +1 -1
  64. package/src/cli/run.ts +21 -12
  65. package/src/config/adapter.ts +16 -11
  66. package/src/config/define-domain.ts +34 -56
  67. package/src/config/deploy.ts +1 -1
  68. package/src/config/index.ts +1 -1
  69. package/src/define/remote-function.ts +9 -14
  70. package/src/define/view.ts +14 -6
  71. package/src/dispatch/identity.ts +15 -21
  72. package/src/domain/binding.ts +37 -0
  73. package/src/domain/build-spec.ts +18 -7
  74. package/src/domain/define.ts +67 -62
  75. package/src/domain/extend-functions.ts +86 -0
  76. package/src/domain/extend-views.ts +151 -0
  77. package/src/index.ts +7 -2
  78. package/src/server/auxiliary-routes.ts +1 -1
  79. package/src/server/worker-entry.ts +3 -3
  80. package/src/domain/extend-core.ts +0 -287
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * `defineRemoteDomain` — turn a typed schema + methods map into a mountable domain.
3
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'
4
+ * `views` and `functions` entries become first-class DOMAIN MEMBERS: a `View` /
5
+ * `Function` node attached to the Domain via an `of_domain` edge (slug
6
+ * `view.<slug>` / `function.<slug>`), laid out at `/<origin>/{views,functions}/<slug>`.
7
+ * Their contract half (the member ref) threads through `compileDomain`; their
8
+ * impl/binding half threads through `serialize`. The same entries'
10
9
  * `render` / `execute` handlers are mounted as Hono routes by
11
- * `createRemoteServer`. Slug = map key.
10
+ * `createRemoteServer` at `<url>/<viewsFolder|functionsFolder>/<slug>` (the route
11
+ * URL is decoupled from the graph layout). Slug = map key.
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
@@ -18,8 +18,13 @@
18
18
  */
19
19
 
20
20
  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'
21
+ import type {
22
+ BoundMethod,
23
+ CompiledDomain,
24
+ FunctionSchema,
25
+ ViewSchema,
26
+ } from '@astrale-os/kernel-core/domain'
27
+ import type { Core, Schema } from '@astrale-os/kernel-dsl'
23
28
 
24
29
  import { bindMethods, compileDomain } from '@astrale-os/kernel-core/domain'
25
30
 
@@ -27,7 +32,12 @@ import type { AnyRemoteFunctionDef, ViewDef } from '../define'
27
32
  import type { SchemaMethodsImpl } from '../method/class'
28
33
  import type { AnyRemoteHandler } from '../method/single'
29
34
 
30
- import { DEFAULT_FUNCTIONS_FOLDER, DEFAULT_VIEWS_FOLDER, extendCore } from './extend-core'
35
+ import {
36
+ buildFunctionDeclarations,
37
+ buildFunctionSchemas,
38
+ DEFAULT_FUNCTIONS_FOLDER,
39
+ } from './extend-functions'
40
+ import { buildViewDeclarations, buildViewSchemas, DEFAULT_VIEWS_FOLDER } from './extend-views'
31
41
 
32
42
  export type RemoteDomainConfig<S extends Schema, TDeps> = {
33
43
  schema: S
@@ -35,27 +45,29 @@ export type RemoteDomainConfig<S extends Schema, TDeps> = {
35
45
  core?: Core<S>
36
46
 
37
47
  views?: Record<string, ViewDef<TDeps>>
38
- viewClass?: AnyNodeDef
39
- viewForEdgeClass?: AnyEdgeDef
40
48
  viewsFolder?: string
41
49
 
42
50
  remoteFunctions?: Record<string, AnyRemoteFunctionDef>
43
51
  functionsFolder?: string
44
52
  }
45
53
 
46
- /** Effective bindings + folder layout for auxiliary routes, resolved against `url`. */
54
+ /** Effective bindings + folder layout + member schemas for auxiliary routes, resolved against `url`. */
47
55
  export type AuxiliaryMetadata = {
48
56
  url: string
49
57
  viewsFolder: string
50
58
  functionsFolder: string
51
59
  viewBindings: Record<string, FunctionBinding>
52
60
  remoteFunctionBindings: Record<string, FunctionBinding>
61
+ /** Function members' impl/binding half (`function.<slug>`) for `serialize`. */
62
+ functionSchemas: FunctionSchema[]
63
+ /** View members' impl/binding half (`view.<slug>`) for `serialize`. */
64
+ viewSchemas: ViewSchema[]
53
65
  }
54
66
 
55
67
  export type RemoteDomain<S extends Schema = Schema> = {
56
68
  /**
57
- * Define-time compile: full domain STRUCTURE (classes, methods, and the aux
58
- * View/Function nodes' paths/names/refs — what identity, subs, and contract
69
+ * Define-time compile: full domain STRUCTURE (classes, methods, and the
70
+ * view/function members' paths/names/refs — what identity, subs, and contract
59
71
  * resolution need). It carries NO `binding` values: bindings derive from the
60
72
  * serving url, which only the spec producers know — they call
61
73
  * `materializeRemoteDomain(domain, url)` for the install-ready compile.
@@ -65,42 +77,32 @@ export type RemoteDomain<S extends Schema = Schema> = {
65
77
  // oxlint-disable-next-line no-explicit-any
66
78
  views?: Record<string, ViewDef<any>>
67
79
  remoteFunctions?: Record<string, AnyRemoteFunctionDef>
68
- /** The original config, so `materializeRemoteDomain` re-extends from source. */
69
- config: ExtendInputs
80
+ /** The original config, so `materializeRemoteDomain` re-materializes from source. */
81
+ config: MaterializeInputs
70
82
  }
71
83
 
72
- /** The defineRemoteDomain inputs `materializeRemoteDomain` needs to re-extend. */
73
- type ExtendInputs = {
84
+ /** The defineRemoteDomain inputs `materializeRemoteDomain` needs to re-materialize. */
85
+ type MaterializeInputs = {
74
86
  userCore?: Core
75
- viewClass?: AnyNodeDef
76
- viewForEdgeClass?: AnyEdgeDef
77
87
  viewsFolder: string
78
88
  functionsFolder: string
79
89
  }
80
90
 
81
91
  export function defineRemoteDomain<TDeps>() {
82
92
  return function <S extends Schema>(config: RemoteDomainConfig<S, TDeps>): RemoteDomain<S> {
83
- const hasAux = Boolean(config.views || config.remoteFunctions)
84
93
  const viewsFolder = config.viewsFolder ?? DEFAULT_VIEWS_FOLDER
85
94
  const functionsFolder = config.functionsFolder ?? DEFAULT_FUNCTIONS_FOLDER
86
95
 
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)
96
+ // Views + functions are domain MEMBERS their contract enters compile via
97
+ // the `functions` / `views` args (no Core involvement). `config.core` is the
98
+ // author's own genuine genesis instance data, untouched.
99
+ const compiled = compileDomain(
100
+ config.schema,
101
+ config.core,
102
+ undefined,
103
+ config.remoteFunctions ? buildFunctionDeclarations(config.remoteFunctions) : undefined,
104
+ config.views ? buildViewDeclarations(config.views) : undefined,
105
+ )
104
106
  const methods = bindMethods<AnyRemoteHandler>(
105
107
  compiled.$.schema,
106
108
  compiled.$.methods,
@@ -115,8 +117,6 @@ export function defineRemoteDomain<TDeps>() {
115
117
  ...(config.remoteFunctions ? { remoteFunctions: config.remoteFunctions } : {}),
116
118
  config: {
117
119
  userCore: config.core,
118
- viewClass: config.viewClass,
119
- viewForEdgeClass: config.viewForEdgeClass,
120
120
  viewsFolder,
121
121
  functionsFolder,
122
122
  },
@@ -125,13 +125,13 @@ export function defineRemoteDomain<TDeps>() {
125
125
  }
126
126
 
127
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).
128
+ * Materialize a `RemoteDomain` at its real serving `url`: re-compiles the domain
129
+ * and builds every member's url-stamped impl schema + binding, returning the
130
+ * binding maps the auxiliary routes mount from and the member schemas `serialize`
131
+ * emits from. Called by the only two spec producers — `createRemoteServer`
132
+ * (`config.url`) and the `astrale-domain` CLI. Returns the define-time `compiled`
133
+ * untouched when there is no aux to stamp. Pure, so safe to call repeatedly (and
134
+ * memoized per cold isolate by the callers).
135
135
  */
136
136
  export function materializeRemoteDomain<S extends Schema>(
137
137
  domain: RemoteDomain<S>,
@@ -142,27 +142,32 @@ export function materializeRemoteDomain<S extends Schema>(
142
142
 
143
143
  const { config } = domain
144
144
  const schema = domain.compiled.$.schema as S
145
- const result = extendCore({
145
+
146
+ const compiled = compileDomain(
146
147
  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>)
148
+ config.userCore as Core<S> | undefined,
149
+ undefined,
150
+ domain.remoteFunctions ? buildFunctionDeclarations(domain.remoteFunctions) : undefined,
151
+ domain.views ? buildViewDeclarations(domain.views) : undefined,
152
+ )
153
+
154
+ const fns = domain.remoteFunctions
155
+ ? buildFunctionSchemas(domain.remoteFunctions, url, config.functionsFolder)
156
+ : { schemas: [], bindings: {} }
157
+ const vws = domain.views
158
+ ? buildViewSchemas(domain.views, url, config.viewsFolder, schema)
159
+ : { schemas: [], bindings: {} }
160
+
158
161
  return {
159
162
  compiled,
160
163
  auxiliary: {
161
164
  url,
162
165
  viewsFolder: config.viewsFolder,
163
166
  functionsFolder: config.functionsFolder,
164
- viewBindings: result.viewBindings,
165
- remoteFunctionBindings: result.remoteFunctionBindings,
167
+ viewBindings: vws.bindings,
168
+ remoteFunctionBindings: fns.bindings,
169
+ functionSchemas: fns.schemas,
170
+ viewSchemas: vws.schemas,
166
171
  },
167
172
  }
168
173
  }
@@ -0,0 +1,86 @@
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
+ * - `FunctionDeclarations` — the CONTRACT half, fed to `compileDomain`'s 4th
7
+ * arg. The member ref (`function.<slug>`), the layout
8
+ * (`/<origin>/functions/<slug>`), and the `of_domain` edge slug all derive
9
+ * from the map key; the kernel owns that mapping.
10
+ * - `FunctionSchema[]` + per-slug `FunctionBinding` — the IMPL/BINDING half,
11
+ * fed to `serialize` (url-stamped). The worker route is
12
+ * `<url>/<functionsFolder>/<slug>`, DECOUPLED from the graph layout — so
13
+ * `functionsFolder` only names the URL segment, not the node position.
14
+ *
15
+ * A standalone function is NOT a core/instance node: it is attached directly to
16
+ * the Domain via `of_domain` by the kernel-core serializer, so this module never
17
+ * touches `Core`.
18
+ */
19
+
20
+ import type { FunctionBinding } from '@astrale-os/kernel-api/routed'
21
+ import type { FunctionDeclarations, FunctionSchema } from '@astrale-os/kernel-core/domain'
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 — one declaration per `functions` map key, for `compileDomain`'s
49
+ * `functions` arg. The slug drives ref/path/of_domain; impl/binding arrive at
50
+ * serialize.
51
+ */
52
+ export function buildFunctionDeclarations(
53
+ functions: Record<string, AnyRemoteFunctionDef>,
54
+ ): FunctionDeclarations {
55
+ assertValidSlugs(functions)
56
+ const out: Record<string, { slug: string }> = {}
57
+ for (const slug of Object.keys(functions)) out[slug] = { slug }
58
+ return out
59
+ }
60
+
61
+ /**
62
+ * Impl/binding half — `FunctionSchema[]` for `serialize` plus the per-slug
63
+ * `FunctionBinding` map the worker mounts routes from. `ref` is the canonical
64
+ * `function.<slug>` member ref (matching `compiled.$.refs.functions`), so the
65
+ * serializer's per-member impl lookup finds it.
66
+ */
67
+ export function buildFunctionSchemas(
68
+ functions: Record<string, AnyRemoteFunctionDef>,
69
+ url: string,
70
+ functionsFolder: string,
71
+ ): { schemas: FunctionSchema[]; bindings: Record<string, FunctionBinding> } {
72
+ const schemas: FunctionSchema[] = []
73
+ const bindings: Record<string, FunctionBinding> = {}
74
+ for (const [slug, def] of Object.entries(functions)) {
75
+ const binding = resolveBinding(def.binding, url, functionsFolder, slug)
76
+ bindings[slug] = binding
77
+ schemas.push({
78
+ ref: `function.${slug}`,
79
+ inputSchema: zodToJsonSchema(def.inputSchema),
80
+ outputSchema: zodToJsonSchema(def.outputSchema),
81
+ output: 'value',
82
+ binding,
83
+ })
84
+ }
85
+ return { schemas, bindings }
86
+ }
@@ -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
@@ -19,7 +19,6 @@ export { remoteMethod, remoteClassMethods, remoteInterfaceMethods } from './meth
19
19
  // (preferred) use `domainWorkerEntry` from `@astrale-os/sdk/server`.
20
20
  export { defineDomain, deploy, defineAdapter } from './config'
21
21
  export type {
22
- ClientBinding,
23
22
  DefineDomainConfig,
24
23
  DomainDefinition,
25
24
  DeployConfig,
@@ -75,7 +74,13 @@ export type {
75
74
 
76
75
  // ─── Auth ────────────────────────────────────────────────────────────────
77
76
  export type { RemoteIdentityConfig, AuthenticateResult } from './auth'
78
- export { authenticateRequest, buildComposedGrant, signCredential } from './auth'
77
+ export {
78
+ authenticateRequest,
79
+ bindSelfKernel,
80
+ buildComposedGrant,
81
+ makeSelfKernel,
82
+ signCredential,
83
+ } from './auth'
79
84
  export { AuthMissingError, AuthInvalidError } from './auth'
80
85
  export { assertPerm, requireOwnership, READ, EDIT, USE, SHARE, ALL } from './auth'
81
86
  export type {
@@ -143,7 +143,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
143
143
  if (!outValidation.ok) {
144
144
  throw new SdkResultValidationError(
145
145
  outValidation.issues as SdkResultValidationError['issues'],
146
- def.ref,
146
+ `function.${slug}`,
147
147
  )
148
148
  }
149
149
  return c.json({ result: outValidation.data })
@@ -154,9 +154,9 @@ export function clientOrigin(url: URL, request: Request): string {
154
154
 
155
155
  /**
156
156
  * Build a `before` hook that serves a static-asset `binding` (e.g. a Workers
157
- * Assets binding) mounted under `base` (default `/ui`) — the runtime half of a
158
- * domain's `client` binding. Returns `undefined` for non-matching paths (and
159
- * when no binding is present) so the request falls through to domain dispatch.
157
+ * Assets binding) mounted under `base` (default `/ui`) — the runtime half of an
158
+ * adapter env's client-asset config. Returns `undefined` for non-matching paths
159
+ * (and when no binding is present) so the request falls through to domain dispatch.
160
160
  *
161
161
  * Asset URLs are rooted at `base` (a client bundler sets `base: '<base>/'`);
162
162
  * this hook strips that prefix before delegating to the binding, so