@astrale-os/sdk 0.2.0 → 0.4.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/LICENSE +202 -0
- package/dist/auth/check.d.ts +1 -2
- package/dist/auth/check.d.ts.map +1 -1
- package/dist/auth/check.js +2 -6
- package/dist/auth/check.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/issuer-mint.d.ts +53 -0
- package/dist/auth/issuer-mint.d.ts.map +1 -0
- package/dist/auth/issuer-mint.js +35 -0
- package/dist/auth/issuer-mint.js.map +1 -0
- package/dist/auth/kernel-client.d.ts +8 -2
- package/dist/auth/kernel-client.d.ts.map +1 -1
- package/dist/auth/kernel-client.js +22 -46
- package/dist/auth/kernel-client.js.map +1 -1
- package/dist/cli/dotenv.d.ts +8 -0
- package/dist/cli/dotenv.d.ts.map +1 -1
- package/dist/cli/dotenv.js +18 -0
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/log.d.ts +11 -0
- package/dist/cli/log.d.ts.map +1 -0
- package/dist/cli/log.js +17 -0
- package/dist/cli/log.js.map +1 -0
- package/dist/cli/publish.d.ts +35 -0
- package/dist/cli/publish.d.ts.map +1 -0
- package/dist/cli/publish.js +271 -0
- package/dist/cli/publish.js.map +1 -0
- package/dist/cli/run.d.ts +17 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +85 -101
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/spec.d.ts.map +1 -1
- package/dist/cli/spec.js +1 -0
- package/dist/cli/spec.js.map +1 -1
- package/dist/config/define-domain.d.ts +79 -2
- package/dist/config/define-domain.d.ts.map +1 -1
- package/dist/config/define-domain.js +58 -1
- package/dist/config/define-domain.js.map +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 +23 -3
- package/dist/define/remote-function.d.ts.map +1 -1
- package/dist/define/remote-function.js +1 -1
- package/dist/define/remote-function.js.map +1 -1
- package/dist/deploy/check.d.ts +6 -6
- package/dist/deploy/check.d.ts.map +1 -1
- package/dist/deploy/check.js +1 -1
- package/dist/deploy/check.js.map +1 -1
- package/dist/deploy/index.d.ts +0 -1
- package/dist/deploy/index.d.ts.map +1 -1
- package/dist/deploy/index.js +0 -1
- package/dist/deploy/index.js.map +1 -1
- package/dist/deploy/meta.d.ts +11 -0
- package/dist/deploy/meta.d.ts.map +1 -1
- package/dist/deploy/meta.js +23 -0
- package/dist/deploy/meta.js.map +1 -1
- package/dist/dispatch/dispatcher.d.ts +17 -4
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +15 -3
- package/dist/dispatch/dispatcher.js.map +1 -1
- package/dist/dispatch/execute.d.ts +6 -2
- package/dist/dispatch/execute.d.ts.map +1 -1
- package/dist/dispatch/execute.js +1 -0
- package/dist/dispatch/execute.js.map +1 -1
- package/dist/dispatch/identity.d.ts +11 -10
- package/dist/dispatch/identity.d.ts.map +1 -1
- package/dist/dispatch/identity.js +4 -3
- package/dist/dispatch/identity.js.map +1 -1
- package/dist/dispatch/index.d.ts +1 -1
- package/dist/dispatch/index.d.ts.map +1 -1
- package/dist/dispatch/index.js +1 -1
- package/dist/dispatch/index.js.map +1 -1
- package/dist/dispatch/self.d.ts +25 -37
- package/dist/dispatch/self.d.ts.map +1 -1
- package/dist/dispatch/self.js +60 -30
- package/dist/dispatch/self.js.map +1 -1
- package/dist/domain/build-spec.d.ts.map +1 -1
- package/dist/domain/build-spec.js +15 -3
- package/dist/domain/build-spec.js.map +1 -1
- package/dist/domain/define.d.ts +17 -32
- package/dist/domain/define.d.ts.map +1 -1
- package/dist/domain/define.js +12 -31
- package/dist/domain/define.js.map +1 -1
- package/dist/domain/extend-functions.d.ts +13 -9
- package/dist/domain/extend-functions.d.ts.map +1 -1
- package/dist/domain/extend-functions.js +28 -13
- package/dist/domain/extend-functions.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/method/class.d.ts +2 -2
- package/dist/method/class.d.ts.map +1 -1
- package/dist/method/class.js.map +1 -1
- package/dist/method/context.d.ts +18 -3
- package/dist/method/context.d.ts.map +1 -1
- package/dist/method/index.d.ts +2 -2
- package/dist/method/index.d.ts.map +1 -1
- package/dist/method/index.js.map +1 -1
- package/dist/method/single.d.ts +25 -38
- package/dist/method/single.d.ts.map +1 -1
- package/dist/method/single.js +1 -11
- package/dist/method/single.js.map +1 -1
- package/dist/server/auxiliary-routes.d.ts.map +1 -1
- package/dist/server/auxiliary-routes.js +13 -2
- package/dist/server/auxiliary-routes.js.map +1 -1
- package/dist/server/create.d.ts +2 -15
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/create.js +15 -51
- package/dist/server/create.js.map +1 -1
- package/dist/server/domain-entry.d.ts +10 -0
- package/dist/server/domain-entry.d.ts.map +1 -1
- package/dist/server/domain-entry.js +2 -0
- package/dist/server/domain-entry.js.map +1 -1
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +10 -8
- package/src/auth/check.ts +2 -6
- package/src/auth/index.ts +2 -0
- package/src/auth/issuer-mint.ts +75 -0
- package/src/auth/kernel-client.ts +31 -63
- package/src/cli/dotenv.ts +20 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/log.ts +18 -0
- package/src/cli/publish.ts +341 -0
- package/src/cli/run.ts +92 -113
- package/src/cli/spec.ts +1 -0
- package/src/config/define-domain.ts +158 -5
- package/src/config/index.ts +1 -1
- package/src/define/remote-function.ts +47 -7
- package/src/deploy/check.ts +7 -7
- package/src/deploy/index.ts +0 -1
- package/src/deploy/meta.ts +25 -0
- package/src/dispatch/dispatcher.ts +32 -9
- package/src/dispatch/execute.ts +7 -2
- package/src/dispatch/identity.ts +11 -10
- package/src/dispatch/index.ts +1 -1
- package/src/dispatch/self.ts +117 -41
- package/src/domain/build-spec.ts +15 -1
- package/src/domain/define.ts +26 -38
- package/src/domain/extend-functions.ts +35 -15
- package/src/index.ts +29 -5
- package/src/method/class.ts +11 -7
- package/src/method/context.ts +19 -3
- package/src/method/index.ts +8 -2
- package/src/method/single.ts +35 -42
- package/src/server/auxiliary-routes.ts +13 -2
- package/src/server/create.ts +15 -51
- package/src/server/domain-entry.ts +12 -0
- package/src/server/index.ts +0 -1
- package/dist/deploy/hash-spec.d.ts +0 -9
- package/dist/deploy/hash-spec.d.ts.map +0 -1
- package/dist/deploy/hash-spec.js +0 -29
- package/dist/deploy/hash-spec.js.map +0 -1
- package/dist/domain/extend-core.d.ts +0 -49
- package/dist/domain/extend-core.d.ts.map +0 -1
- package/dist/domain/extend-core.js +0 -190
- package/dist/domain/extend-core.js.map +0 -1
- package/dist/server/worker-meta.d.ts +0 -6
- package/dist/server/worker-meta.d.ts.map +0 -1
- package/dist/server/worker-meta.js +0 -10
- package/dist/server/worker-meta.js.map +0 -1
- package/src/deploy/hash-spec.ts +0 -31
- package/src/server/worker-meta.ts +0 -25
package/src/dispatch/self.ts
CHANGED
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Self resolution
|
|
3
|
-
*
|
|
4
|
-
* `_self` is a path reference to the node the non-static method runs on.
|
|
5
|
-
* `resolveSelf` is a thin PARSE: the caller's string becomes a `Path`, and
|
|
6
|
-
* handlers build recursive paths as `${self.path}::method` (the `Path`'s
|
|
7
|
-
* `toString` yields its raw form, so template literals work unchanged). It does
|
|
8
|
-
* NOT touch the graph — the whole dispatch pipeline (resolve → authenticate →
|
|
9
|
-
* validate → self → execute) reaches the handler without a single `Node::get`.
|
|
10
|
-
*
|
|
11
|
-
* `resolveSelf` receives the bare self target (`@<id>` or a tree path) — the
|
|
12
|
-
* method ref travels in a separate dispatch channel, so `::method` never
|
|
13
|
-
* reaches here. For the `@<id>` form the parsed `Path` is an `IdPath` which
|
|
14
|
-
* already carries the node id; surfacing it on `SelfResult.id` saves every
|
|
15
|
-
* worker handler from parsing `self.path.raw` by hand to recover the id of the
|
|
16
|
-
* node it is already operating on.
|
|
17
|
-
*
|
|
18
|
-
* `withNode` enriches the parsed self with the lazy `node()` accessor — the one
|
|
19
|
-
* place a handler can fetch its own node's full record (`{ id, class, props }`)
|
|
20
|
-
* without hand-rolling a `::get`. Construction stays split from parsing so
|
|
21
|
-
* `resolveSelf` remains pure and trivially testable; the dispatcher attaches the
|
|
22
|
-
* accessor once it holds the request's kernel.
|
|
2
|
+
* Self resolution stays parse-only: `_self` becomes a Path/IdPath with no graph
|
|
3
|
+
* read. The dispatcher later attaches lazy node()/call helpers once it has a kernel.
|
|
23
4
|
*/
|
|
24
5
|
|
|
6
|
+
import type {
|
|
7
|
+
BindCtx,
|
|
8
|
+
BoundNode,
|
|
9
|
+
InstanceDispatch,
|
|
10
|
+
NodeBinder,
|
|
11
|
+
ProxyCaller,
|
|
12
|
+
TypeMethods,
|
|
13
|
+
TypeNames,
|
|
14
|
+
} from '@astrale-os/kernel-client/schema'
|
|
25
15
|
import type { Node } from '@astrale-os/kernel-core/graph'
|
|
16
|
+
import type { Schema } from '@astrale-os/kernel-dsl'
|
|
26
17
|
|
|
27
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
createInstanceProxy,
|
|
20
|
+
defaultCaller,
|
|
21
|
+
extractMethodsByType,
|
|
22
|
+
} from '@astrale-os/kernel-client/schema'
|
|
23
|
+
import { IdPath, K, Path, type NodeId } from '@astrale-os/kernel-core'
|
|
28
24
|
import { classPathSchema } from '@astrale-os/kernel-core/domain'
|
|
29
25
|
import { absolutePathSchema } from '@astrale-os/kernel-core/tree'
|
|
30
26
|
import { z } from 'zod'
|
|
@@ -43,32 +39,31 @@ export type ParsedSelf = {
|
|
|
43
39
|
|
|
44
40
|
export type SelfResult = ParsedSelf & {
|
|
45
41
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* single in-flight fetch, and a failure clears the cache so it stays
|
|
49
|
-
* retryable. Pass `{ reload: true }` to force a fresh read.
|
|
50
|
-
*
|
|
51
|
-
* The result is a point-in-time SNAPSHOT, not a live view — for
|
|
52
|
-
* read-modify-write, treat it as the value as of the read (or `reload`).
|
|
53
|
-
* `props` is returned structurally validated only, NOT typed against the
|
|
54
|
-
* node's declared schema: the kernel does not hydrate declared props at
|
|
55
|
-
* runtime, so a node-typed `self` would be unsound. Read the concrete backend
|
|
56
|
-
* off `node.class` (a `ClassPath`) — that is the persisted discriminator.
|
|
42
|
+
* Lazy point-in-time self read. Memoized per dispatch; `{ reload: true }`
|
|
43
|
+
* forces a fresh `function.get`.
|
|
57
44
|
*/
|
|
58
45
|
node(opts?: { reload?: boolean }): Promise<ResolvedSelfNode>
|
|
59
46
|
}
|
|
60
47
|
|
|
48
|
+
/** Schema-typed self: short-keyed props and typed same-instance call proxy. */
|
|
49
|
+
export type TypedSelf<D, S extends Schema = Schema> = ParsedSelf & {
|
|
50
|
+
/**
|
|
51
|
+
* Fetch THIS node as a typed `BoundNode<D>`. Memoized exactly like
|
|
52
|
+
* {@link SelfResult.node} — one in-flight `function.get`, rejection-clearing,
|
|
53
|
+
* `{ reload: true }` refetches — so the zero-extra-graph-read dispatch
|
|
54
|
+
* invariant holds: nothing is read until called, then at most once.
|
|
55
|
+
*/
|
|
56
|
+
node(opts?: { reload?: boolean }): Promise<BoundNode<D, S>>
|
|
57
|
+
/** Own instance methods — typed, address-only (`<selfPath>::method`), no read. */
|
|
58
|
+
readonly call: InstanceDispatch<D>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
61
|
export function resolveSelf(ref: string): ParsedSelf {
|
|
62
62
|
const path = Path.parse(ref)
|
|
63
63
|
return path instanceof IdPath ? { path, id: path.id } : { path }
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/**
|
|
67
|
-
* Attach the memoized `node()` accessor, binding it to the handler's kernel.
|
|
68
|
-
* `kernel` is `null` for methods whose auth policy yields none (`public`, or an
|
|
69
|
-
* unauthenticated `optional`) — there `node()` rejects with a clear error
|
|
70
|
-
* rather than reading the graph unauthenticated.
|
|
71
|
-
*/
|
|
66
|
+
/** Attach memoized node(); unauthenticated contexts reject instead of reading. */
|
|
72
67
|
export function withNode(parsed: ParsedSelf, kernel: Kernel | null): SelfResult {
|
|
73
68
|
let cached: Promise<ResolvedSelfNode> | undefined
|
|
74
69
|
const node = (opts?: { reload?: boolean }): Promise<ResolvedSelfNode> => {
|
|
@@ -84,6 +79,73 @@ export function withNode(parsed: ParsedSelf, kernel: Kernel | null): SelfResult
|
|
|
84
79
|
return { ...parsed, node }
|
|
85
80
|
}
|
|
86
81
|
|
|
82
|
+
const EMPTY_METHODS: TypeMethods = { kind: 'class', methods: new Set() }
|
|
83
|
+
const methodMapCache = new WeakMap<object, Map<string, TypeMethods>>()
|
|
84
|
+
|
|
85
|
+
/** The type's method allow-list + kind, computed once per binder. */
|
|
86
|
+
function methodsFor<S extends Schema>(binder: NodeBinder<S>, typeName: string): TypeMethods {
|
|
87
|
+
let map = methodMapCache.get(binder)
|
|
88
|
+
if (!map) {
|
|
89
|
+
map = extractMethodsByType(binder.schema)
|
|
90
|
+
methodMapCache.set(binder, map)
|
|
91
|
+
}
|
|
92
|
+
return map.get(typeName) ?? EMPTY_METHODS
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function noKernelCaller(ref: string): ProxyCaller {
|
|
96
|
+
return () =>
|
|
97
|
+
Promise.reject(
|
|
98
|
+
new Error(
|
|
99
|
+
`self.call needs an authenticated kernel to dispatch on "${ref}", but this method ran ` +
|
|
100
|
+
`without one (its auth policy yields no kernel — e.g. 'public').`,
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Schema-typed self helper: lazy BoundNode plus address-only call proxy. With no
|
|
107
|
+
* kernel, both node() and call thunks fail loudly.
|
|
108
|
+
*/
|
|
109
|
+
export function withBoundNode<S extends Schema>(
|
|
110
|
+
parsed: ParsedSelf,
|
|
111
|
+
kernel: Kernel | null,
|
|
112
|
+
binder: NodeBinder<S>,
|
|
113
|
+
typeName: string,
|
|
114
|
+
): TypedSelf<unknown, S> {
|
|
115
|
+
const ctx: BindCtx = kernel
|
|
116
|
+
? { caller: defaultCaller(kernel), read: kernel }
|
|
117
|
+
: { caller: null, read: null }
|
|
118
|
+
|
|
119
|
+
let cached: Promise<BoundNode<unknown, S>> | undefined
|
|
120
|
+
const node = (opts?: { reload?: boolean }): Promise<BoundNode<unknown, S>> => {
|
|
121
|
+
if (opts?.reload) cached = undefined
|
|
122
|
+
if (!cached) {
|
|
123
|
+
cached = fetchSelfNode(parsed.path, kernel)
|
|
124
|
+
.then(
|
|
125
|
+
(record) =>
|
|
126
|
+
binder.bind(typeName as TypeNames<S>, record, ctx, {
|
|
127
|
+
validate: 'warn',
|
|
128
|
+
}) as unknown as BoundNode<unknown, S>,
|
|
129
|
+
)
|
|
130
|
+
.catch((err: unknown) => {
|
|
131
|
+
cached = undefined // never cache a rejection — a later call may succeed
|
|
132
|
+
throw err
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
return cached
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const info = methodsFor(binder, typeName)
|
|
139
|
+
const call = createInstanceProxy(
|
|
140
|
+
ctx.caller ?? noKernelCaller(parsed.path.raw),
|
|
141
|
+
parsed.path.raw,
|
|
142
|
+
info.methods,
|
|
143
|
+
info.kind === 'class' ? typeName : undefined,
|
|
144
|
+
) as InstanceDispatch<unknown>
|
|
145
|
+
|
|
146
|
+
return { ...parsed, node, call }
|
|
147
|
+
}
|
|
148
|
+
|
|
87
149
|
/**
|
|
88
150
|
* Boundary schema for a node record off the wire. Structurally identical to
|
|
89
151
|
* kernel-core's `nodeSchema`, re-declared here from its public coercers
|
|
@@ -108,9 +170,23 @@ async function fetchSelfNode(path: Path, kernel: Kernel | null): Promise<Resolve
|
|
|
108
170
|
`method whose auth is 'required', or supply a credential.`,
|
|
109
171
|
)
|
|
110
172
|
}
|
|
111
|
-
|
|
173
|
+
// function.get soft-masks missing/unreadable roots; empty page means no self node.
|
|
174
|
+
const result = (await kernel.call(K.$.f('get').path.domain.raw, {
|
|
175
|
+
roots: [path.raw],
|
|
176
|
+
depth: 0,
|
|
177
|
+
})) as {
|
|
178
|
+
nodes?: unknown[]
|
|
179
|
+
}
|
|
180
|
+
const first = result?.nodes?.[0]
|
|
181
|
+
if (first === undefined) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`self.node(): "${path.raw}" was not found or is not visible to the caller ` +
|
|
184
|
+
`(function.get returned no node).`,
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
const record = nodeRecordSchema.parse(first)
|
|
112
188
|
if (record.id === undefined) {
|
|
113
|
-
throw new Error(`self.node(): "${path.raw}
|
|
189
|
+
throw new Error(`self.node(): "${path.raw}" returned a record with no id`)
|
|
114
190
|
}
|
|
115
191
|
// Validated at the wire boundary: id is now known-present, props are
|
|
116
192
|
// structurally checked. Bridge the parse output to the canonical `Node`.
|
package/src/domain/build-spec.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { Graph, WireGraph } from '@astrale-os/kernel-core'
|
|
|
21
21
|
import type { BoundMethod, FunctionSchema, ViewSchema } from '@astrale-os/kernel-core/domain'
|
|
22
22
|
import type { Schema } from '@astrale-os/kernel-dsl'
|
|
23
23
|
|
|
24
|
+
import { AbsolutePath } from '@astrale-os/kernel-core'
|
|
24
25
|
import { hashInstallGraph, serialize, zodToJsonSchema } from '@astrale-os/kernel-core/domain'
|
|
25
26
|
|
|
26
27
|
import type { AnyRemoteHandler } from '../method/single'
|
|
@@ -42,12 +43,21 @@ export function buildInstallGraph<S extends Schema>(
|
|
|
42
43
|
url: string,
|
|
43
44
|
): WireGraph {
|
|
44
45
|
const { compiled, auxiliary } = materializeRemoteDomain(domain, url)
|
|
46
|
+
// Two-axis placement: `parent` = the folder the Domain node physically lives
|
|
47
|
+
// under. `path` names the FULL node path (basename === origin);
|
|
48
|
+
// unset defaults to /domains/<origin> — the "/domains" placement policy lives
|
|
49
|
+
// HERE (the kernel default is Root → /<origin>). `mount` is deliberately NOT
|
|
50
|
+
// passed: installed_in + typed addresses stay root-mounted (kernel default).
|
|
51
|
+
const parent = domain.path
|
|
52
|
+
? AbsolutePath.parse(domain.path).parent()
|
|
53
|
+
: AbsolutePath.from('domains')
|
|
45
54
|
return buildSpecInternal(
|
|
46
55
|
compiled,
|
|
47
56
|
domain.methods,
|
|
48
57
|
url,
|
|
49
58
|
auxiliary?.functionSchemas ?? [],
|
|
50
59
|
auxiliary?.viewSchemas ?? [],
|
|
60
|
+
parent,
|
|
51
61
|
).toWire() as WireGraph
|
|
52
62
|
}
|
|
53
63
|
|
|
@@ -71,6 +81,7 @@ function buildSpecInternal(
|
|
|
71
81
|
url: string,
|
|
72
82
|
functionSchemas: FunctionSchema[],
|
|
73
83
|
viewSchemas: ViewSchema[],
|
|
84
|
+
parent: AbsolutePath,
|
|
74
85
|
): Graph {
|
|
75
86
|
const serialized = serializeMethodsWithStubs(compiled, methods, url)
|
|
76
87
|
// Method impls + standalone-function-member impls travel in the same callable
|
|
@@ -79,7 +90,10 @@ function buildSpecInternal(
|
|
|
79
90
|
// view_for, View class). The serializer emits method nodes from the IR,
|
|
80
91
|
// function/view members from `compiled.$.refs.{functions,views}`, pulling each
|
|
81
92
|
// impl by ref.
|
|
82
|
-
const tree = serialize(compiled, [...serialized, ...functionSchemas], {
|
|
93
|
+
const tree = serialize(compiled, [...serialized, ...functionSchemas], {
|
|
94
|
+
views: viewSchemas,
|
|
95
|
+
parent,
|
|
96
|
+
})
|
|
83
97
|
return tree.toGraph()
|
|
84
98
|
}
|
|
85
99
|
|
package/src/domain/define.ts
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
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'
|
|
9
|
-
* `render` / `execute` handlers are mounted as Hono routes by
|
|
10
|
-
* `createRemoteServer` at `<url>/<viewsFolder|functionsFolder>/<slug>` (the route
|
|
11
|
-
* URL is decoupled from the graph layout). 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'
|
|
@@ -28,12 +14,13 @@ import type { Core, Schema } from '@astrale-os/kernel-dsl'
|
|
|
28
14
|
|
|
29
15
|
import { bindMethods, compileDomain } from '@astrale-os/kernel-core/domain'
|
|
30
16
|
|
|
17
|
+
import type { DomainManifest } from '../config/define-domain'
|
|
31
18
|
import type { AnyRemoteFunctionDef, ViewDef } from '../define'
|
|
32
19
|
import type { SchemaMethodsImpl } from '../method/class'
|
|
33
20
|
import type { AnyRemoteHandler } from '../method/single'
|
|
34
21
|
|
|
35
22
|
import {
|
|
36
|
-
|
|
23
|
+
assertFunctionsMatchSchema,
|
|
37
24
|
buildFunctionSchemas,
|
|
38
25
|
DEFAULT_FUNCTIONS_FOLDER,
|
|
39
26
|
} from './extend-functions'
|
|
@@ -49,6 +36,12 @@ export type RemoteDomainConfig<S extends Schema, TDeps> = {
|
|
|
49
36
|
|
|
50
37
|
remoteFunctions?: Record<string, AnyRemoteFunctionDef>
|
|
51
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
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
/** Effective bindings + folder layout + member schemas for auxiliary routes, resolved against `url`. */
|
|
@@ -65,20 +58,22 @@ export type AuxiliaryMetadata = {
|
|
|
65
58
|
}
|
|
66
59
|
|
|
67
60
|
export type RemoteDomain<S extends Schema = Schema> = {
|
|
68
|
-
/**
|
|
69
|
-
* Define-time compile: full domain STRUCTURE (classes, methods, and the
|
|
70
|
-
* view/function members' paths/names/refs — what identity, subs, and contract
|
|
71
|
-
* resolution need). It carries NO `binding` values: bindings derive from the
|
|
72
|
-
* serving url, which only the spec producers know — they call
|
|
73
|
-
* `materializeRemoteDomain(domain, url)` for the install-ready compile.
|
|
74
|
-
*/
|
|
61
|
+
/** Define-time compile has structure only; bindings are stamped at materialization. */
|
|
75
62
|
compiled: CompiledDomain<S>
|
|
76
63
|
methods: BoundMethod<AnyRemoteHandler>[]
|
|
77
64
|
// oxlint-disable-next-line no-explicit-any
|
|
78
65
|
views?: Record<string, ViewDef<any>>
|
|
79
66
|
remoteFunctions?: Record<string, AnyRemoteFunctionDef>
|
|
67
|
+
/** Presentation metadata for `/meta` (logo / entry view); `createRemoteServer` stamps it. */
|
|
68
|
+
manifest?: DomainManifest
|
|
80
69
|
/** The original config, so `materializeRemoteDomain` re-materializes from source. */
|
|
81
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
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
/** The defineRemoteDomain inputs `materializeRemoteDomain` needs to re-materialize. */
|
|
@@ -93,14 +88,14 @@ export function defineRemoteDomain<TDeps>() {
|
|
|
93
88
|
const viewsFolder = config.viewsFolder ?? DEFAULT_VIEWS_FOLDER
|
|
94
89
|
const functionsFolder = config.functionsFolder ?? DEFAULT_FUNCTIONS_FOLDER
|
|
95
90
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
//
|
|
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)
|
|
99
95
|
const compiled = compileDomain(
|
|
100
96
|
config.schema,
|
|
101
97
|
config.core,
|
|
102
98
|
undefined,
|
|
103
|
-
config.remoteFunctions ? buildFunctionDeclarations(config.remoteFunctions) : undefined,
|
|
104
99
|
config.views ? buildViewDeclarations(config.views) : undefined,
|
|
105
100
|
)
|
|
106
101
|
const methods = bindMethods<AnyRemoteHandler>(
|
|
@@ -115,6 +110,8 @@ export function defineRemoteDomain<TDeps>() {
|
|
|
115
110
|
methods,
|
|
116
111
|
...(config.views ? { views: config.views } : {}),
|
|
117
112
|
...(config.remoteFunctions ? { remoteFunctions: config.remoteFunctions } : {}),
|
|
113
|
+
...(config.manifest ? { manifest: config.manifest } : {}),
|
|
114
|
+
...(config.path ? { path: config.path } : {}),
|
|
118
115
|
config: {
|
|
119
116
|
userCore: config.core,
|
|
120
117
|
viewsFolder,
|
|
@@ -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-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
|
-
*/
|
|
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,
|
|
@@ -147,7 +136,6 @@ export function materializeRemoteDomain<S extends Schema>(
|
|
|
147
136
|
schema,
|
|
148
137
|
config.userCore as Core<S> | undefined,
|
|
149
138
|
undefined,
|
|
150
|
-
domain.remoteFunctions ? buildFunctionDeclarations(domain.remoteFunctions) : undefined,
|
|
151
139
|
domain.views ? buildViewDeclarations(domain.views) : undefined,
|
|
152
140
|
)
|
|
153
141
|
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* halves a standalone-function DOMAIN MEMBER contributes — mirroring how a class
|
|
4
4
|
* method splits across compile + serialize:
|
|
5
5
|
*
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* from the map key; the kernel owns that mapping.
|
|
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).
|
|
10
9
|
* - `FunctionSchema[]` + per-slug `FunctionBinding` — the IMPL/BINDING half,
|
|
11
10
|
* fed to `serialize` (url-stamped). The worker route is
|
|
12
11
|
* `<url>/<functionsFolder>/<slug>`, DECOUPLED from the graph layout — so
|
|
@@ -18,7 +17,8 @@
|
|
|
18
17
|
*/
|
|
19
18
|
|
|
20
19
|
import type { FunctionBinding } from '@astrale-os/kernel-api/routed'
|
|
21
|
-
import type {
|
|
20
|
+
import type { FunctionSchema } from '@astrale-os/kernel-core/domain'
|
|
21
|
+
import type { Schema } from '@astrale-os/kernel-dsl'
|
|
22
22
|
|
|
23
23
|
import { zodToJsonSchema } from '@astrale-os/kernel-core/domain'
|
|
24
24
|
|
|
@@ -45,17 +45,37 @@ function assertValidSlugs(functions: Record<string, AnyRemoteFunctionDef>): void
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* Contract half —
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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.
|
|
51
55
|
*/
|
|
52
|
-
export function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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('; ')}.`)
|
|
59
79
|
}
|
|
60
80
|
|
|
61
81
|
/**
|
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,
|
|
@@ -21,6 +24,7 @@ export { defineDomain, deploy, defineAdapter } from './config'
|
|
|
21
24
|
export type {
|
|
22
25
|
DefineDomainConfig,
|
|
23
26
|
DomainDefinition,
|
|
27
|
+
DomainManifest,
|
|
24
28
|
DeployConfig,
|
|
25
29
|
AdapterSpec,
|
|
26
30
|
DeployCtx,
|
|
@@ -34,6 +38,25 @@ export type {
|
|
|
34
38
|
// ─── Install graph (advanced) ────────────────────────────────────────────
|
|
35
39
|
export { buildInstallGraph, buildInstallGraphHash } from './domain'
|
|
36
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
|
+
|
|
37
60
|
// ─── Declarative resource helpers ────────────────────────────────────────
|
|
38
61
|
// Author Views (iframe-mountable) and RemoteFunctions (standalone callables)
|
|
39
62
|
// at the domain definition site. Each `defineView` / `defineRemoteFunction`
|
|
@@ -53,10 +76,10 @@ export type { ViewDef, ViewRenderContext, RemoteFunctionDef, RemoteFunctionConte
|
|
|
53
76
|
// runtime import from '@astrale-os/sdk/server' directly.
|
|
54
77
|
|
|
55
78
|
// ─── Deploy ──────────────────────────────────────────────────────────────
|
|
56
|
-
// `deployCheck`
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
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.
|
|
60
83
|
export { MetaSchema } from './deploy/meta'
|
|
61
84
|
export type { Meta } from './deploy/meta'
|
|
62
85
|
|
|
@@ -74,6 +97,7 @@ export type {
|
|
|
74
97
|
|
|
75
98
|
// ─── Auth ────────────────────────────────────────────────────────────────
|
|
76
99
|
export type { RemoteIdentityConfig, AuthenticateResult } from './auth'
|
|
100
|
+
export type { DomainIssuer, MintIssuedParams } from './auth'
|
|
77
101
|
export {
|
|
78
102
|
authenticateRequest,
|
|
79
103
|
bindSelfKernel,
|
|
@@ -101,4 +125,4 @@ export {
|
|
|
101
125
|
SdkValidationError,
|
|
102
126
|
SdkResultValidationError,
|
|
103
127
|
} from './dispatch'
|
|
104
|
-
export type { SelfResult, CallRemoteFn, KernelCaller } from './dispatch'
|
|
128
|
+
export type { SelfResult, TypedSelf, CallRemoteFn, KernelCaller } from './dispatch'
|
package/src/method/class.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
package/src/method/context.ts
CHANGED
|
@@ -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
|
|
84
|
-
*
|
|
85
|
-
*
|
|
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
|
package/src/method/index.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
export type { AuthForPolicy, Kernel, KernelForAuth, RemoteContext } from './context'
|
|
2
|
-
export type {
|
|
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'
|