@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.
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/cli/run.d.ts +0 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +21 -10
- package/dist/cli/run.js.map +1 -1
- package/dist/config/adapter.d.ts +19 -11
- package/dist/config/adapter.d.ts.map +1 -1
- package/dist/config/adapter.js.map +1 -1
- package/dist/config/define-domain.d.ts +13 -29
- package/dist/config/define-domain.d.ts.map +1 -1
- package/dist/config/define-domain.js +22 -33
- package/dist/config/define-domain.js.map +1 -1
- package/dist/config/deploy.d.ts +1 -1
- package/dist/config/deploy.js +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/define/remote-function.d.ts +9 -14
- package/dist/define/remote-function.d.ts.map +1 -1
- package/dist/define/remote-function.js +9 -8
- package/dist/define/remote-function.js.map +1 -1
- package/dist/define/view.d.ts +14 -6
- package/dist/define/view.d.ts.map +1 -1
- package/dist/define/view.js +8 -6
- package/dist/define/view.js.map +1 -1
- package/dist/dispatch/identity.d.ts +10 -9
- package/dist/dispatch/identity.d.ts.map +1 -1
- package/dist/dispatch/identity.js +9 -15
- package/dist/dispatch/identity.js.map +1 -1
- package/dist/domain/binding.d.ts +18 -0
- package/dist/domain/binding.d.ts.map +1 -0
- package/dist/domain/binding.js +29 -0
- package/dist/domain/binding.js.map +1 -0
- package/dist/domain/build-spec.d.ts.map +1 -1
- package/dist/domain/build-spec.js +10 -7
- package/dist/domain/build-spec.js.map +1 -1
- package/dist/domain/define.d.ts +27 -27
- package/dist/domain/define.d.ts.map +1 -1
- package/dist/domain/define.js +31 -49
- package/dist/domain/define.js.map +1 -1
- package/dist/domain/extend-core.d.ts.map +1 -1
- package/dist/domain/extend-core.js +9 -1
- package/dist/domain/extend-core.js.map +1 -1
- package/dist/domain/extend-functions.d.ts +44 -0
- package/dist/domain/extend-functions.d.ts.map +1 -0
- package/dist/domain/extend-functions.js +69 -0
- package/dist/domain/extend-functions.js.map +1 -0
- package/dist/domain/extend-views.d.ts +45 -0
- package/dist/domain/extend-views.d.ts.map +1 -0
- package/dist/domain/extend-views.js +116 -0
- package/dist/domain/extend-views.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server/auxiliary-routes.js +1 -1
- package/dist/server/auxiliary-routes.js.map +1 -1
- package/dist/server/worker-entry.d.ts +3 -3
- package/dist/server/worker-entry.js +3 -3
- package/package.json +2 -5
- package/src/auth/index.ts +1 -1
- package/src/cli/run.ts +21 -12
- package/src/config/adapter.ts +16 -11
- package/src/config/define-domain.ts +34 -56
- package/src/config/deploy.ts +1 -1
- package/src/config/index.ts +1 -1
- package/src/define/remote-function.ts +9 -14
- package/src/define/view.ts +14 -6
- package/src/dispatch/identity.ts +15 -21
- package/src/domain/binding.ts +37 -0
- package/src/domain/build-spec.ts +18 -7
- package/src/domain/define.ts +67 -62
- package/src/domain/extend-functions.ts +86 -0
- package/src/domain/extend-views.ts +151 -0
- package/src/index.ts +7 -2
- package/src/server/auxiliary-routes.ts +1 -1
- package/src/server/worker-entry.ts +3 -3
- package/src/domain/extend-core.ts +0 -287
package/src/domain/define.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `defineRemoteDomain` — turn a typed schema + methods map into a mountable domain.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
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 {
|
|
22
|
-
|
|
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 {
|
|
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
|
|
58
|
-
*
|
|
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-
|
|
69
|
-
config:
|
|
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-
|
|
73
|
-
type
|
|
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
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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-
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* only two spec producers — `createRemoteServer`
|
|
132
|
-
* `astrale-domain` CLI. Returns the define-time `compiled`
|
|
133
|
-
* stamp.
|
|
134
|
-
*
|
|
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
|
-
|
|
145
|
+
|
|
146
|
+
const compiled = compileDomain(
|
|
146
147
|
schema,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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:
|
|
165
|
-
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 {
|
|
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
|
-
|
|
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
|
|
158
|
-
*
|
|
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
|