@astrale-os/shell-react 0.2.10-beta.2 → 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.
- package/dist/application/apps/app-host.d.ts +5 -5
- package/dist/application/apps/app-host.d.ts.map +1 -1
- package/dist/application/apps/app-host.js +18 -39
- package/dist/application/apps/apps.hook.d.ts +4 -11
- package/dist/application/apps/apps.hook.d.ts.map +1 -1
- package/dist/application/apps/apps.hook.js +9 -49
- package/dist/application/apps/open-app.hook.d.ts +4 -7
- package/dist/application/apps/open-app.hook.d.ts.map +1 -1
- package/dist/application/apps/open-app.hook.js +2 -2
- package/dist/application/index.d.ts +1 -3
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +1 -2
- package/dist/application/spaces/space.hook.d.ts.map +1 -1
- package/dist/application/spaces/space.hook.js +4 -1
- package/dist/auth/act-as.context.d.ts +1 -1
- package/dist/auth/act-as.context.d.ts.map +1 -1
- package/dist/auth/auth.hook.d.ts +1 -1
- package/dist/auth/auth.hook.d.ts.map +1 -1
- package/dist/auth/can.hook.d.ts +2 -2
- package/dist/auth/can.hook.d.ts.map +1 -1
- package/dist/auth/self.hook.d.ts +1 -1
- package/dist/auth/self.hook.d.ts.map +1 -1
- package/dist/components/astrale.d.ts +14 -0
- package/dist/components/astrale.d.ts.map +1 -0
- package/dist/components/astrale.js +12 -0
- package/dist/components/can.d.ts +2 -2
- package/dist/components/can.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -2
- package/dist/graph/call/call.hook.d.ts +2 -2
- package/dist/graph/call/call.hook.d.ts.map +1 -1
- package/dist/graph/call/stream.hook.d.ts +2 -2
- package/dist/graph/call/stream.hook.d.ts.map +1 -1
- package/dist/graph/graph.context.d.ts +1 -1
- package/dist/graph/graph.context.d.ts.map +1 -1
- package/dist/graph/guards.error.d.ts +4 -4
- package/dist/graph/guards.error.d.ts.map +1 -1
- package/dist/graph/guards.error.js +4 -4
- package/dist/graph/query-definition.hook.d.ts +1 -2
- package/dist/graph/query-definition.hook.d.ts.map +1 -1
- package/dist/graph/query-definition.hook.js +4 -10
- package/dist/graph/resource.d.ts +1 -1
- package/dist/graph/resource.d.ts.map +1 -1
- package/dist/graph/store.d.ts +2 -2
- package/dist/graph/store.d.ts.map +1 -1
- package/dist/graph/store.js +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/navigation/adapters/url.adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/url.adapter.js +2 -5
- package/dist/navigation/codec.d.ts.map +1 -1
- package/dist/navigation/codec.js +1 -7
- package/dist/navigation/link.js +1 -1
- package/dist/navigation/scope.context.d.ts +1 -1
- package/dist/navigation/scope.context.d.ts.map +1 -1
- package/dist/navigation/scope.context.js +3 -6
- package/dist/navigation/snapshot.d.ts +1 -1
- package/dist/navigation/snapshot.d.ts.map +1 -1
- package/dist/navigation/snapshot.js +2 -8
- package/dist/navigation/view-key.d.ts +3 -0
- package/dist/navigation/view-key.d.ts.map +1 -0
- package/dist/navigation/view-key.js +8 -0
- package/dist/schema/action.hook.d.ts +11 -7
- package/dist/schema/action.hook.d.ts.map +1 -1
- package/dist/schema/action.hook.js +11 -6
- package/dist/schema/domain.hook.js +12 -13
- package/dist/schema/kernel.hook.d.ts +1 -1
- package/dist/schema/kernel.hook.d.ts.map +1 -1
- package/dist/schema/kernel.hook.js +1 -1
- package/dist/schema/mutation.hook.d.ts +2 -2
- package/dist/schema/mutation.hook.d.ts.map +1 -1
- package/dist/schema/mutation.hook.js +6 -5
- package/dist/schema/object.hook.d.ts +3 -6
- package/dist/schema/object.hook.d.ts.map +1 -1
- package/dist/schema/object.hook.js +4 -5
- package/dist/schema/relation.hook.d.ts +29 -19
- package/dist/schema/relation.hook.d.ts.map +1 -1
- package/dist/schema/relation.hook.js +45 -63
- package/dist/selection/live.d.ts.map +1 -1
- package/dist/selection/live.js +1 -2
- package/dist/session/session.context.d.ts +3 -4
- package/dist/session/session.context.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/application/apps/app-host.tsx +22 -52
- package/src/application/apps/apps.hook.ts +14 -64
- package/src/application/apps/open-app.hook.ts +5 -10
- package/src/application/index.ts +2 -12
- package/src/application/spaces/space.hook.ts +6 -1
- package/src/auth/act-as.context.tsx +1 -1
- package/src/auth/auth.hook.ts +1 -1
- package/src/auth/can.hook.ts +3 -3
- package/src/auth/self.hook.ts +1 -1
- package/src/components/astrale.tsx +40 -0
- package/src/components/can.tsx +2 -2
- package/src/components/index.ts +2 -3
- package/src/graph/call/call.hook.ts +2 -2
- package/src/graph/call/stream.hook.ts +2 -2
- package/src/graph/graph.context.ts +1 -1
- package/src/graph/guards.error.ts +4 -4
- package/src/graph/query-definition.hook.ts +13 -23
- package/src/graph/resource.ts +1 -1
- package/src/graph/store.ts +3 -3
- package/src/index.ts +5 -14
- package/src/navigation/adapters/url.adapter.ts +3 -4
- package/src/navigation/codec.ts +1 -7
- package/src/navigation/link.tsx +1 -1
- package/src/navigation/scope.context.tsx +5 -6
- package/src/navigation/snapshot.ts +4 -9
- package/src/navigation/view-key.ts +11 -0
- package/src/schema/action.hook.ts +45 -28
- package/src/schema/domain.hook.ts +9 -10
- package/src/schema/kernel.hook.ts +1 -1
- package/src/schema/mutation.hook.ts +13 -9
- package/src/schema/object.hook.ts +10 -18
- package/src/schema/relation.hook.ts +127 -145
- package/src/selection/live.ts +1 -2
- package/src/session/session.context.ts +3 -4
- package/dist/application/apps/domains.hook.d.ts +0 -7
- package/dist/application/apps/domains.hook.d.ts.map +0 -1
- package/dist/application/apps/domains.hook.js +0 -18
- package/dist/components/mount-domain-frontend.d.ts +0 -22
- package/dist/components/mount-domain-frontend.d.ts.map +0 -1
- package/dist/components/mount-domain-frontend.js +0 -85
- package/dist/components/view-app.d.ts +0 -16
- package/dist/components/view-app.d.ts.map +0 -1
- package/dist/components/view-app.js +0 -16
- package/src/application/apps/domains.hook.ts +0 -37
- package/src/components/mount-domain-frontend.tsx +0 -158
- package/src/components/view-app.tsx +0 -49
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
1
2
|
import type { NodeTarget, SpaceNode } from '@astrale-os/shell'
|
|
2
3
|
|
|
4
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
3
5
|
/**
|
|
4
6
|
* useSpace(ref) (API §12.2) — a single space by ref; `null`/`undefined` (or an
|
|
5
7
|
* unavailable OS plane) is idle (§1.3). One-shot today (§12 flag).
|
|
@@ -11,7 +13,6 @@ import type { CurrentSpaceResult } from './current-space.hook.js'
|
|
|
11
13
|
import { useAsyncResource } from '../../graph/async.js'
|
|
12
14
|
import { isIdleInput } from '../../graph/memory/read/idle.js'
|
|
13
15
|
import { useShell } from '../../session/shell.hook.js'
|
|
14
|
-
import { targetKey } from '../apps/apps.hook.js'
|
|
15
16
|
import { isOsPlaneAvailable } from '../user/user.store.js'
|
|
16
17
|
|
|
17
18
|
export type SpaceResult = CurrentSpaceResult
|
|
@@ -38,3 +39,7 @@ export function useSpace(ref: NodeTarget | null | undefined): SpaceResult {
|
|
|
38
39
|
[idle, res],
|
|
39
40
|
)
|
|
40
41
|
}
|
|
42
|
+
|
|
43
|
+
function targetKey(input: NodeTarget): string {
|
|
44
|
+
return typeof input === 'string' ? `text:${input}` : `path:${Path.from(input as PathLike).raw}`
|
|
45
|
+
}
|
package/src/auth/auth.hook.ts
CHANGED
package/src/auth/can.hook.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NodeId } from '@astrale-os/sdk/graph/node'
|
|
2
2
|
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
3
|
-
import type {
|
|
3
|
+
import type { PolicyRef } from '@astrale-os/sdk/schema'
|
|
4
4
|
|
|
5
5
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
6
6
|
import { useEffect, useState } from 'react'
|
|
@@ -14,7 +14,7 @@ export type PolicyObject = NodeId | { readonly id: NodeId }
|
|
|
14
14
|
* invoked callable remains the authorization enforcement point.
|
|
15
15
|
*/
|
|
16
16
|
export function useCan(
|
|
17
|
-
policy: PathLike |
|
|
17
|
+
policy: PathLike | PolicyRef | null | undefined,
|
|
18
18
|
object: PolicyObject | null | undefined,
|
|
19
19
|
): boolean | undefined {
|
|
20
20
|
const { kernel } = useIdentityBinding()
|
|
@@ -45,7 +45,7 @@ export function useCan(
|
|
|
45
45
|
return decision
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
function policyPath(policy: PathLike |
|
|
48
|
+
function policyPath(policy: PathLike | PolicyRef | null | undefined) {
|
|
49
49
|
if (policy === null || policy === undefined) return null
|
|
50
50
|
return policy instanceof Path || typeof policy === 'string'
|
|
51
51
|
? Path.from(policy).raw
|
package/src/auth/self.hook.ts
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Shell, ShellConfig } from '@astrale-os/shell'
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Suspense } from 'react'
|
|
5
|
+
|
|
6
|
+
import type { ShellProviderProps } from '../session/provider.js'
|
|
7
|
+
|
|
8
|
+
import { ShellProvider } from '../session/provider.js'
|
|
9
|
+
import { ViewStage } from '../window/stage.js'
|
|
10
|
+
import { defaultViewError, defaultViewLoading, ViewErrorBoundary } from './boundaries.js'
|
|
11
|
+
|
|
12
|
+
const SANDBOXED_SHELL = Object.freeze({ mode: 'sandboxed' as const })
|
|
13
|
+
|
|
14
|
+
export interface AstraleProps {
|
|
15
|
+
/** Defaults to the child-side sandbox handshake; pass a Shell for an embedded host or test. */
|
|
16
|
+
readonly config?: ShellConfig | Shell
|
|
17
|
+
readonly fallback?: ReactNode
|
|
18
|
+
readonly errorFallback?: ShellProviderProps['errorFallback']
|
|
19
|
+
readonly onError?: (error: unknown) => void
|
|
20
|
+
readonly children: ReactNode
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Complete Astrale runtime root without taking ownership of application routing. */
|
|
24
|
+
export function Astrale(props: AstraleProps): ReactElement {
|
|
25
|
+
const loading = props.fallback ?? defaultViewLoading()
|
|
26
|
+
const errorFallback = props.errorFallback ?? defaultViewError
|
|
27
|
+
return (
|
|
28
|
+
<ShellProvider
|
|
29
|
+
config={props.config ?? SANDBOXED_SHELL}
|
|
30
|
+
fallback={loading}
|
|
31
|
+
errorFallback={errorFallback}
|
|
32
|
+
onError={({ error }) => props.onError?.(error)}
|
|
33
|
+
>
|
|
34
|
+
<ViewErrorBoundary fallback={errorFallback} onError={props.onError}>
|
|
35
|
+
<Suspense fallback={loading}>{props.children}</Suspense>
|
|
36
|
+
</ViewErrorBoundary>
|
|
37
|
+
<ViewStage />
|
|
38
|
+
</ShellProvider>
|
|
39
|
+
)
|
|
40
|
+
}
|
package/src/components/can.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
2
|
-
import type {
|
|
2
|
+
import type { PolicyRef } from '@astrale-os/sdk/schema'
|
|
3
3
|
import type { ReactElement, ReactNode } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { PolicyObject } from '../auth/can.hook.js'
|
|
@@ -7,7 +7,7 @@ import type { PolicyObject } from '../auth/can.hook.js'
|
|
|
7
7
|
import { useCan } from '../auth/can.hook.js'
|
|
8
8
|
|
|
9
9
|
export function Can(props: {
|
|
10
|
-
readonly policy: PathLike |
|
|
10
|
+
readonly policy: PathLike | PolicyRef
|
|
11
11
|
readonly object: PolicyObject
|
|
12
12
|
readonly children: ReactNode
|
|
13
13
|
readonly fallback?: ReactNode
|
package/src/components/index.ts
CHANGED
|
@@ -9,6 +9,5 @@ export {
|
|
|
9
9
|
ViewBoundary,
|
|
10
10
|
} from './boundaries.js'
|
|
11
11
|
export type { ViewErrorBoundaryProps } from './boundaries.js'
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export type { MountedDomainFrontend, MountDomainFrontendOptions } from './mount-domain-frontend.js'
|
|
12
|
+
export { Astrale } from './astrale.js'
|
|
13
|
+
export type { AstraleProps } from './astrale.js'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Call } from '@astrale-os/
|
|
2
|
-
import type { SessionRequestOptions } from '@astrale-os/
|
|
1
|
+
import type { Call } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { SessionRequestOptions } from '@astrale-os/sdk/client/session'
|
|
3
3
|
import type { Value } from '@astrale-os/sdk/value'
|
|
4
4
|
|
|
5
5
|
import type { Action } from '../action.js'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Call } from '@astrale-os/
|
|
2
|
-
import type { SessionRequestOptions } from '@astrale-os/
|
|
1
|
+
import type { Call } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { SessionRequestOptions } from '@astrale-os/sdk/client/session'
|
|
3
3
|
import type { Value } from '@astrale-os/sdk/value'
|
|
4
4
|
import type { KernelClient } from '@astrale-os/shell'
|
|
5
5
|
|
|
@@ -4,12 +4,12 @@ export {
|
|
|
4
4
|
ResponseError,
|
|
5
5
|
StaleRouteError,
|
|
6
6
|
TransportError,
|
|
7
|
-
} from '@astrale-os/
|
|
8
|
-
export { NodeUnavailableError } from '@astrale-os/
|
|
7
|
+
} from '@astrale-os/sdk/client'
|
|
8
|
+
export { NodeUnavailableError } from '@astrale-os/sdk/client'
|
|
9
9
|
export { UnsupportedFeatureError } from './memory/read/envelope.js'
|
|
10
10
|
|
|
11
|
-
import { ClientError } from '@astrale-os/
|
|
12
|
-
import { NodeUnavailableError } from '@astrale-os/
|
|
11
|
+
import { ClientError } from '@astrale-os/sdk/client'
|
|
12
|
+
import { NodeUnavailableError } from '@astrale-os/sdk/client'
|
|
13
13
|
|
|
14
14
|
export function isClientError(error: unknown): error is ClientError {
|
|
15
15
|
return error instanceof ClientError
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
QueryDefinition,
|
|
5
|
-
QueryValueBinder,
|
|
6
|
-
} from '@astrale-os/sdk/query'
|
|
7
|
-
import type { QueryAST } from '@astrale-os/sdk/query'
|
|
8
|
-
import type { DomainBinding } from '@astrale-os/shell'
|
|
1
|
+
import type { QueryPageRequest } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { GraphStore, StoreQueryResponse, StoreSnapshot } from '@astrale-os/sdk/client/store'
|
|
3
|
+
import type { QueryAST, QueryDefinition } from '@astrale-os/sdk/query'
|
|
9
4
|
|
|
10
|
-
import {
|
|
5
|
+
import { executeQuery } from '@astrale-os/sdk/query'
|
|
11
6
|
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'
|
|
12
7
|
|
|
13
8
|
import type { GraphResource, GraphResourceState } from './resource.js'
|
|
14
9
|
|
|
15
|
-
import { useDynamicDomain } from '../schema/domain.hook.js'
|
|
16
10
|
import { retainRefreshScheduler } from './resource.js'
|
|
17
11
|
import { useGraphStore } from './store.js'
|
|
18
12
|
|
|
@@ -24,7 +18,7 @@ export interface QueryResourceOptions {
|
|
|
24
18
|
|
|
25
19
|
interface TouchedShape {
|
|
26
20
|
readonly ast: QueryAST
|
|
27
|
-
readonly options:
|
|
21
|
+
readonly options: { readonly page: QueryPageRequest }
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
interface DefinitionSnapshot<Output> {
|
|
@@ -33,7 +27,7 @@ interface DefinitionSnapshot<Output> {
|
|
|
33
27
|
readonly error?: unknown
|
|
34
28
|
}
|
|
35
29
|
|
|
36
|
-
class DefinitionObserver<Input, Output
|
|
30
|
+
class DefinitionObserver<Input, Output> {
|
|
37
31
|
private readonly listeners = new Set<() => void>()
|
|
38
32
|
private subscriptions: readonly (() => void)[] = Object.freeze([])
|
|
39
33
|
private touched: readonly TouchedShape[] = Object.freeze([])
|
|
@@ -45,8 +39,7 @@ class DefinitionObserver<Input, Output, Query extends QueryAST> {
|
|
|
45
39
|
|
|
46
40
|
constructor(
|
|
47
41
|
private readonly store: GraphStore,
|
|
48
|
-
private readonly
|
|
49
|
-
private readonly definition: QueryDefinition<Input, Output, Query>,
|
|
42
|
+
private readonly definition: QueryDefinition<Input, Output>,
|
|
50
43
|
private readonly input: Input,
|
|
51
44
|
) {}
|
|
52
45
|
|
|
@@ -82,14 +75,12 @@ class DefinitionObserver<Input, Output, Query extends QueryAST> {
|
|
|
82
75
|
})
|
|
83
76
|
const execute = async <Ast extends QueryAST>(
|
|
84
77
|
ast: Ast,
|
|
85
|
-
options:
|
|
78
|
+
options: { readonly page: QueryPageRequest },
|
|
86
79
|
): Promise<StoreQueryResponse<Ast>> => {
|
|
87
80
|
touched.push(Object.freeze({ ast, options }))
|
|
88
81
|
return this.store.read(ast, { ...options, freshness })
|
|
89
82
|
}
|
|
90
|
-
const
|
|
91
|
-
const query = bindQueries(execute, { bind })
|
|
92
|
-
const running = query(this.definition, this.input)
|
|
83
|
+
const running = executeQuery({ query: execute }, this.definition, this.input)
|
|
93
84
|
.then((data) => {
|
|
94
85
|
if (!this.started || generation !== this.generation) return
|
|
95
86
|
this.touched = dedupeShapes(touched)
|
|
@@ -157,17 +148,16 @@ class DefinitionObserver<Input, Output, Query extends QueryAST> {
|
|
|
157
148
|
}
|
|
158
149
|
|
|
159
150
|
/** Execute one named SDK Query definition exclusively through the current GraphStore. */
|
|
160
|
-
export function useQuery<Input, Output
|
|
161
|
-
definition: QueryDefinition<Input, Output
|
|
151
|
+
export function useQuery<Input, Output>(
|
|
152
|
+
definition: QueryDefinition<Input, Output> | null,
|
|
162
153
|
input: Input,
|
|
163
154
|
options: QueryResourceOptions = {},
|
|
164
155
|
): GraphResource<Output> {
|
|
165
156
|
const store = useGraphStore()
|
|
166
|
-
const binding = useDynamicDomain()
|
|
167
157
|
const inputIdentity = stableIdentity(input)
|
|
168
158
|
const observer = useMemo(
|
|
169
|
-
() => (definition === null ? null : new DefinitionObserver(store,
|
|
170
|
-
[store,
|
|
159
|
+
() => (definition === null ? null : new DefinitionObserver(store, definition, input)),
|
|
160
|
+
[store, definition, inputIdentity],
|
|
171
161
|
)
|
|
172
162
|
const idle = useMemo<DefinitionSnapshot<Output>>(() => Object.freeze({ state: 'idle' }), [])
|
|
173
163
|
const subscribe = useCallback(
|
package/src/graph/resource.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GraphStore, StoreQueryResponse, StoreSnapshot } from '@astrale-os/
|
|
1
|
+
import type { GraphStore, StoreQueryResponse, StoreSnapshot } from '@astrale-os/sdk/client/store'
|
|
2
2
|
import type { QueryAST } from '@astrale-os/sdk/query'
|
|
3
3
|
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'
|
package/src/graph/store.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { GraphApi } from '@astrale-os/
|
|
2
|
-
import type { GraphStore as GraphStoreValue } from '@astrale-os/
|
|
1
|
+
import type { GraphApi } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { GraphStore as GraphStoreValue } from '@astrale-os/sdk/client/store'
|
|
3
3
|
import type { Shell } from '@astrale-os/shell'
|
|
4
4
|
|
|
5
|
-
import { GraphStore } from '@astrale-os/
|
|
5
|
+
import { GraphStore } from '@astrale-os/sdk/client/store'
|
|
6
6
|
import { useContext } from 'react'
|
|
7
7
|
|
|
8
8
|
import { GraphContext } from './graph.context.js'
|
package/src/index.ts
CHANGED
|
@@ -166,16 +166,11 @@ export {
|
|
|
166
166
|
defaultViewError,
|
|
167
167
|
defaultViewLoading,
|
|
168
168
|
GraphBoundary,
|
|
169
|
-
|
|
170
|
-
ViewApp,
|
|
169
|
+
Astrale,
|
|
171
170
|
ViewBoundary,
|
|
172
171
|
ViewErrorBoundary,
|
|
173
172
|
} from './components/index.js'
|
|
174
|
-
export type {
|
|
175
|
-
MountedDomainFrontend,
|
|
176
|
-
MountDomainFrontendOptions,
|
|
177
|
-
ViewErrorBoundaryProps,
|
|
178
|
-
} from './components/index.js'
|
|
173
|
+
export type { AstraleProps, ViewErrorBoundaryProps } from './components/index.js'
|
|
179
174
|
|
|
180
175
|
// Shell application conveniences.
|
|
181
176
|
export {
|
|
@@ -183,7 +178,6 @@ export {
|
|
|
183
178
|
AppLogo,
|
|
184
179
|
useApps,
|
|
185
180
|
useCurrentSpace,
|
|
186
|
-
useInstalledDomains,
|
|
187
181
|
useOpenApp,
|
|
188
182
|
useSpace,
|
|
189
183
|
useSpaces,
|
|
@@ -195,7 +189,6 @@ export type {
|
|
|
195
189
|
AppLogoProps,
|
|
196
190
|
AppsResult,
|
|
197
191
|
CurrentSpaceResult,
|
|
198
|
-
DomainsResult,
|
|
199
192
|
SpaceResult,
|
|
200
193
|
SpacesResult,
|
|
201
194
|
} from './application/index.js'
|
|
@@ -205,16 +198,14 @@ export { NodeId } from '@astrale-os/sdk/graph/node'
|
|
|
205
198
|
export { NodePath, Path } from '@astrale-os/sdk/graph/path'
|
|
206
199
|
export type { Node } from '@astrale-os/sdk/graph/node'
|
|
207
200
|
export type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
208
|
-
export type { AuthApi, Identity, PolicyDecisionInput } from '@astrale-os/
|
|
209
|
-
export type { DomainBinding
|
|
201
|
+
export type { AuthApi, Identity, PolicyDecisionInput } from '@astrale-os/sdk/auth'
|
|
202
|
+
export type { DomainBinding } from '@astrale-os/shell'
|
|
210
203
|
|
|
211
204
|
// Shell contracts retained at the package root for custom full-app composition.
|
|
212
205
|
export type {
|
|
213
|
-
|
|
214
|
-
AppSession,
|
|
206
|
+
App,
|
|
215
207
|
CloseResult,
|
|
216
208
|
CredentialSnapshot,
|
|
217
|
-
DomainSearchResult,
|
|
218
209
|
HostCapabilities,
|
|
219
210
|
IntentRegistry,
|
|
220
211
|
KernelClient,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { NodePath } from '@astrale-os/sdk/graph/path'
|
|
2
|
-
import { Key } from '@astrale-os/sdk/schema'
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* urlAdapter (API §8.1) — the History-API adapter: mirrors the location into the
|
|
@@ -9,6 +8,8 @@ import { Key } from '@astrale-os/sdk/schema'
|
|
|
9
8
|
import type { SerializedLocation } from '../history.js'
|
|
10
9
|
import type { NavAdapter } from './adapter.js'
|
|
11
10
|
|
|
11
|
+
import { viewKey } from '../view-key.js'
|
|
12
|
+
|
|
12
13
|
const VIEW_KEY = 'astrale.view'
|
|
13
14
|
const PARAM_PREFIX = 'astrale.param.'
|
|
14
15
|
|
|
@@ -55,9 +56,7 @@ export function urlAdapter(opts?: { param?: string }): NavAdapter {
|
|
|
55
56
|
node = NodePath(rawNode)
|
|
56
57
|
const rawView = sp.get(VIEW_KEY)
|
|
57
58
|
if (rawView !== null) {
|
|
58
|
-
|
|
59
|
-
if (!Key.ref(admitted).startsWith('view.')) throw new TypeError('Invalid View key.')
|
|
60
|
-
view = admitted as Key.View
|
|
59
|
+
view = viewKey(rawView)
|
|
61
60
|
}
|
|
62
61
|
} catch {
|
|
63
62
|
return null
|
package/src/navigation/codec.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { Key } from '@astrale-os/sdk/schema'
|
|
|
2
2
|
import type { ReactElement, ReactNode } from 'react'
|
|
3
3
|
|
|
4
4
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
5
|
-
import { Key as admitKey } from '@astrale-os/sdk/schema'
|
|
6
5
|
import { createElement } from 'react'
|
|
7
6
|
|
|
8
7
|
import type { NavAdapter } from './adapters/adapter.js'
|
|
@@ -11,6 +10,7 @@ import type { NavSnapshot, SerializedLocation } from './history.js'
|
|
|
11
10
|
import { NavScope } from './scope.context.js'
|
|
12
11
|
import { useNavCtx } from './scope.context.js'
|
|
13
12
|
import { useHistoryVersion } from './scope.context.js'
|
|
13
|
+
import { viewKey } from './view-key.js'
|
|
14
14
|
|
|
15
15
|
export interface NavigationParam<Value> {
|
|
16
16
|
encode(value: Value): string
|
|
@@ -182,9 +182,3 @@ export function useNavigation<Routes extends NavigationRoutes>(
|
|
|
182
182
|
|
|
183
183
|
export type NavigationTarget<Definition> =
|
|
184
184
|
Definition extends NavigationDefinition<infer Routes> ? RouteTarget<Routes> : never
|
|
185
|
-
|
|
186
|
-
function viewKey(input: string): Key.View {
|
|
187
|
-
const key = admitKey(input)
|
|
188
|
-
if (!admitKey.ref(key).startsWith('view.')) throw new TypeError(`Invalid View key '${input}'.`)
|
|
189
|
-
return key as Key.View
|
|
190
|
-
}
|
package/src/navigation/link.tsx
CHANGED
|
@@ -50,7 +50,7 @@ export function Link(props: LinkProps): ReactElement {
|
|
|
50
50
|
const nodeRef = target.node
|
|
51
51
|
|
|
52
52
|
const preload = useCallback(() => {
|
|
53
|
-
const query = Query.from({
|
|
53
|
+
const query = Query.from({ nodes: [nodeRef] }).select({ kind: 'graph' })
|
|
54
54
|
void ctx.store.preload(query, { page: { size: 1 } }).catch(() => {})
|
|
55
55
|
}, [ctx.store, nodeRef])
|
|
56
56
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { GraphStore, StoreQueryResponse } from '@astrale-os/
|
|
1
|
+
import type { GraphStore, StoreQueryResponse } from '@astrale-os/sdk/client/store'
|
|
2
|
+
import type { Key } from '@astrale-os/sdk/schema'
|
|
2
3
|
import type { ReactElement, ReactNode } from 'react'
|
|
3
4
|
|
|
4
5
|
import { NodePath } from '@astrale-os/sdk/graph/path'
|
|
5
6
|
import { Query } from '@astrale-os/sdk/query'
|
|
6
|
-
import { Key } from '@astrale-os/sdk/schema'
|
|
7
7
|
import {
|
|
8
8
|
createContext,
|
|
9
9
|
startTransition,
|
|
@@ -28,6 +28,7 @@ import { memoryAdapter } from './adapters/memory.adapter.js'
|
|
|
28
28
|
import { NavHistory, seedEntries } from './history.js'
|
|
29
29
|
import { buildLocation } from './location.js'
|
|
30
30
|
import { revalidateAndPrune, serializeTarget } from './snapshot.js'
|
|
31
|
+
import { viewKey } from './view-key.js'
|
|
31
32
|
|
|
32
33
|
export interface Navigate {
|
|
33
34
|
(to: NavTarget, opts?: { replace?: boolean }): void
|
|
@@ -85,7 +86,7 @@ export function useGraphPoint(target: NodePath | null): PointResult | null {
|
|
|
85
86
|
() =>
|
|
86
87
|
target === null
|
|
87
88
|
? null
|
|
88
|
-
: Query.from({
|
|
89
|
+
: Query.from({ nodes: [target], binding: 'navigation_target' }).select({
|
|
89
90
|
kind: 'graph',
|
|
90
91
|
}),
|
|
91
92
|
[target],
|
|
@@ -253,9 +254,7 @@ function admitLocation(location: SerializedLocation): SerializedLocation {
|
|
|
253
254
|
const node = NodePath(location.node)
|
|
254
255
|
let view: Key.View | undefined
|
|
255
256
|
if (location.view !== undefined) {
|
|
256
|
-
|
|
257
|
-
if (!Key.ref(key).startsWith('view.')) throw new TypeError('Navigation View key is invalid.')
|
|
258
|
-
view = key as Key.View
|
|
257
|
+
view = viewKey(location.view)
|
|
259
258
|
}
|
|
260
259
|
const params: Record<string, string> = {}
|
|
261
260
|
if (location.params !== undefined) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GraphStore } from '@astrale-os/
|
|
1
|
+
import type { GraphStore } from '@astrale-os/sdk/client/store'
|
|
2
2
|
import type { Node } from '@astrale-os/sdk/graph/node'
|
|
3
3
|
import type { NodePath, PathLike } from '@astrale-os/sdk/graph/path'
|
|
4
4
|
import type { Key } from '@astrale-os/sdk/schema'
|
|
@@ -6,10 +6,11 @@ import type { Shell } from '@astrale-os/shell'
|
|
|
6
6
|
|
|
7
7
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
8
8
|
import { Query } from '@astrale-os/sdk/query'
|
|
9
|
-
import { Key as admitKey } from '@astrale-os/sdk/schema'
|
|
10
9
|
|
|
11
10
|
import type { NavHistory, SerializedLocation } from './history.js'
|
|
12
11
|
|
|
12
|
+
import { viewKey } from './view-key.js'
|
|
13
|
+
|
|
13
14
|
/** Canonical navigation inputs; there is no implicit slash-tree or bare-id grammar. */
|
|
14
15
|
export type NavTarget =
|
|
15
16
|
| PathLike
|
|
@@ -44,12 +45,6 @@ export function serializeTarget(target: NavTarget): SerializedLocation {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
function viewKey(input: string): Key.View {
|
|
48
|
-
const key = admitKey(input)
|
|
49
|
-
if (!admitKey.ref(key).startsWith('view.')) throw new TypeError(`Invalid View key '${input}'.`)
|
|
50
|
-
return key as Key.View
|
|
51
|
-
}
|
|
52
|
-
|
|
53
48
|
/** Drop restored entries that no longer resolve under the authenticated Graph capability. */
|
|
54
49
|
export async function revalidateAndPrune(
|
|
55
50
|
history: NavHistory,
|
|
@@ -81,7 +76,7 @@ async function resolves(
|
|
|
81
76
|
entry: SerializedLocation,
|
|
82
77
|
): Promise<boolean> {
|
|
83
78
|
try {
|
|
84
|
-
const query = Query.from({
|
|
79
|
+
const query = Query.from({ nodes: [entry.node] }).select({ kind: 'graph' })
|
|
85
80
|
const response = await store.read(query, { page: { size: 1 } })
|
|
86
81
|
if (
|
|
87
82
|
response.result.kind !== 'graph' ||
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Key } from '@astrale-os/sdk/schema'
|
|
2
|
+
|
|
3
|
+
import { Key as admitKey } from '@astrale-os/sdk/schema'
|
|
4
|
+
|
|
5
|
+
export function viewKey(input: string): Key.View {
|
|
6
|
+
const key = admitKey(input)
|
|
7
|
+
if (!key.slice(key.indexOf(':') + 1).startsWith('view.')) {
|
|
8
|
+
throw new TypeError(`Invalid View key '${input}'.`)
|
|
9
|
+
}
|
|
10
|
+
return key as Key.View
|
|
11
|
+
}
|
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
InstanceMethod,
|
|
4
|
-
OutputOf,
|
|
5
|
-
ReceiverOf,
|
|
6
|
-
RemoteCallableOf,
|
|
7
|
-
} from '@astrale-os/sdk/domain'
|
|
1
|
+
import type { BoundNodeFor } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { SessionRequestOptions } from '@astrale-os/sdk/client/session'
|
|
8
3
|
import type { NodeId } from '@astrale-os/sdk/graph/node'
|
|
9
4
|
import type { QueryAST } from '@astrale-os/sdk/query'
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
import type {
|
|
6
|
+
CallableInputOf,
|
|
7
|
+
CallableResultOf,
|
|
8
|
+
MethodOwnerKeyOf,
|
|
9
|
+
MethodInheritance,
|
|
10
|
+
ResolvedFunction,
|
|
11
|
+
ResolvedMethod,
|
|
12
|
+
} from '@astrale-os/sdk/schema'
|
|
13
|
+
|
|
14
|
+
import { reference } from '@astrale-os/sdk/client/session'
|
|
13
15
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
14
16
|
import { useCallback, useMemo, useRef } from 'react'
|
|
15
17
|
|
|
16
18
|
import type { Action as AsyncAction } from '../graph/action.js'
|
|
17
19
|
|
|
20
|
+
import { useSessionKernel } from '../auth/session-kernel.js'
|
|
18
21
|
import { useAction as useAsyncAction } from '../graph/action.js'
|
|
19
22
|
import { useGraphStore } from '../graph/store.js'
|
|
20
23
|
import { bindingFor } from './domain.hook.js'
|
|
21
24
|
|
|
22
|
-
type
|
|
25
|
+
type ExecutableInheritance = Exclude<MethodInheritance, 'abstract'>
|
|
26
|
+
type InstanceMethod = ResolvedMethod<unknown, unknown, false, ExecutableInheritance>
|
|
27
|
+
type StaticMethod = ResolvedMethod<unknown, unknown, true, ExecutableInheritance>
|
|
28
|
+
type Callable = ResolvedFunction | InstanceMethod | StaticMethod
|
|
29
|
+
type Receiver = { readonly id: NodeId }
|
|
30
|
+
type MethodReceiver<C extends InstanceMethod> = BoundNodeFor<MethodOwnerKeyOf<C>>
|
|
23
31
|
|
|
24
32
|
const QUERY_REFRESH = Symbol('shell-react.query-refresh')
|
|
25
33
|
|
|
@@ -59,14 +67,14 @@ export type ActionRefresh =
|
|
|
59
67
|
readonly queries: readonly QueryRefresh[]
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
export interface ActionOptions extends
|
|
70
|
+
export interface ActionOptions extends SessionRequestOptions {
|
|
63
71
|
readonly refresh?: ActionRefresh
|
|
64
72
|
readonly onError?: (error: unknown) => void
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
type ActionArgs<C extends Callable> = C extends InstanceMethod
|
|
68
|
-
? [receiver:
|
|
69
|
-
: [input:
|
|
76
|
+
? [receiver: MethodReceiver<C>, input: CallableInputOf<C>]
|
|
77
|
+
: [input: CallableInputOf<C>]
|
|
70
78
|
|
|
71
79
|
export interface DomainAction<Args extends unknown[], Result> {
|
|
72
80
|
run(...args: Args): Promise<Result>
|
|
@@ -80,8 +88,9 @@ export interface DomainAction<Args extends unknown[], Result> {
|
|
|
80
88
|
export function useAction<C extends Callable>(
|
|
81
89
|
callable: C,
|
|
82
90
|
options: ActionOptions = {},
|
|
83
|
-
): DomainAction<ActionArgs<C>,
|
|
91
|
+
): DomainAction<ActionArgs<C>, CallableResultOf<C>> {
|
|
84
92
|
const binding = bindingFor(callable)
|
|
93
|
+
const kernel = useSessionKernel()
|
|
85
94
|
const store = useGraphStore()
|
|
86
95
|
const callableRef = useRef(callable)
|
|
87
96
|
callableRef.current = callable
|
|
@@ -91,18 +100,22 @@ export function useAction<C extends Callable>(
|
|
|
91
100
|
optionsRef.current = options
|
|
92
101
|
|
|
93
102
|
const invoke = useCallback(
|
|
94
|
-
async (...args: ActionArgs<C>): Promise<
|
|
103
|
+
async (...args: ActionArgs<C>): Promise<CallableResultOf<C>> => {
|
|
95
104
|
const current = callableRef.current
|
|
96
105
|
const policy = optionsRef.current
|
|
97
106
|
const { refresh: _refresh, onError: _onError, ...request } = policy
|
|
98
107
|
try {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
const result = isInstanceMethod(current)
|
|
109
|
+
? await kernel.session.invoke(
|
|
110
|
+
reference(bindingRef.current.domain, current)(receiverPath(args[0] as Receiver)),
|
|
111
|
+
args[1] as never,
|
|
112
|
+
request,
|
|
113
|
+
)
|
|
114
|
+
: await kernel.session.invoke(
|
|
115
|
+
reference(bindingRef.current.domain, current),
|
|
116
|
+
args[0] as never,
|
|
117
|
+
request,
|
|
118
|
+
)
|
|
106
119
|
const refresh = policy.refresh ?? 'all'
|
|
107
120
|
if (refresh === 'all') store.invalidate()
|
|
108
121
|
else if (refresh !== 'none') {
|
|
@@ -111,15 +124,15 @@ export function useAction<C extends Callable>(
|
|
|
111
124
|
store.invalidate(target.ast, { page: target.page })
|
|
112
125
|
})
|
|
113
126
|
}
|
|
114
|
-
return result
|
|
127
|
+
return result as CallableResultOf<C>
|
|
115
128
|
} catch (error) {
|
|
116
129
|
policy.onError?.(error)
|
|
117
130
|
throw error
|
|
118
131
|
}
|
|
119
132
|
},
|
|
120
|
-
[store],
|
|
133
|
+
[kernel, store],
|
|
121
134
|
)
|
|
122
|
-
const action = useAsyncAction(invoke
|
|
135
|
+
const action = useAsyncAction(invoke)
|
|
123
136
|
return useMemo(() => projectAction(action), [action])
|
|
124
137
|
}
|
|
125
138
|
|
|
@@ -141,6 +154,10 @@ function projectAction<Args extends unknown[], Result>(
|
|
|
141
154
|
}
|
|
142
155
|
}
|
|
143
156
|
|
|
144
|
-
function
|
|
145
|
-
return
|
|
157
|
+
function isInstanceMethod(callable: Callable): callable is InstanceMethod {
|
|
158
|
+
return 'owner' in callable && callable.static === false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function receiverPath(receiver: Receiver): Path {
|
|
162
|
+
return Path.id(receiver.id)
|
|
146
163
|
}
|