@astrale-os/shell-react 0.2.10-beta.3 → 0.3.1
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/can.d.ts +2 -2
- package/dist/components/can.d.ts.map +1 -1
- 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 +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- 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 +4 -14
- 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/can.tsx +2 -2
- 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 +3 -7
- 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/src/application/apps/domains.hook.ts +0 -37
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
|
|
@@ -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
|
@@ -178,7 +178,6 @@ export {
|
|
|
178
178
|
AppLogo,
|
|
179
179
|
useApps,
|
|
180
180
|
useCurrentSpace,
|
|
181
|
-
useInstalledDomains,
|
|
182
181
|
useOpenApp,
|
|
183
182
|
useSpace,
|
|
184
183
|
useSpaces,
|
|
@@ -190,7 +189,6 @@ export type {
|
|
|
190
189
|
AppLogoProps,
|
|
191
190
|
AppsResult,
|
|
192
191
|
CurrentSpaceResult,
|
|
193
|
-
DomainsResult,
|
|
194
192
|
SpaceResult,
|
|
195
193
|
SpacesResult,
|
|
196
194
|
} from './application/index.js'
|
|
@@ -200,16 +198,14 @@ export { NodeId } from '@astrale-os/sdk/graph/node'
|
|
|
200
198
|
export { NodePath, Path } from '@astrale-os/sdk/graph/path'
|
|
201
199
|
export type { Node } from '@astrale-os/sdk/graph/node'
|
|
202
200
|
export type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
203
|
-
export type { AuthApi, Identity, PolicyDecisionInput } from '@astrale-os/
|
|
204
|
-
export type { DomainBinding
|
|
201
|
+
export type { AuthApi, Identity, PolicyDecisionInput } from '@astrale-os/sdk/auth'
|
|
202
|
+
export type { DomainBinding } from '@astrale-os/shell'
|
|
205
203
|
|
|
206
204
|
// Shell contracts retained at the package root for custom full-app composition.
|
|
207
205
|
export type {
|
|
208
|
-
|
|
209
|
-
AppSession,
|
|
206
|
+
App,
|
|
210
207
|
CloseResult,
|
|
211
208
|
CredentialSnapshot,
|
|
212
|
-
DomainSearchResult,
|
|
213
209
|
HostCapabilities,
|
|
214
210
|
IntentRegistry,
|
|
215
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
|
}
|
|
@@ -89,17 +89,16 @@ export function bindingFor(value: object): DomainBinding {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function registerBinding(binding: DomainBinding): void {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
bindingByValue.set(
|
|
97
|
-
for (const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
bindingByValue.set(binding, binding)
|
|
93
|
+
bindingByValue.set(binding.domain, binding)
|
|
94
|
+
for (const definition of Object.values(binding.domain.classes)) {
|
|
95
|
+
bindingByValue.set(definition, binding)
|
|
96
|
+
for (const method of definition.methods) bindingByValue.set(method, binding)
|
|
97
|
+
for (const method of definition.static.methods) bindingByValue.set(method, binding)
|
|
98
|
+
}
|
|
99
|
+
for (const callable of Object.values(binding.domain.functions)) {
|
|
100
|
+
bindingByValue.set(callable, binding)
|
|
101
101
|
}
|
|
102
|
-
visit(binding, 0)
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
function createResource<T>(promise: Promise<T>): Resource<T> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Mutation } from '@astrale-os/sdk/mutation'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { executeMutation } from '@astrale-os/sdk/mutation'
|
|
4
4
|
import { useCallback, useMemo, useRef } from 'react'
|
|
5
5
|
|
|
6
6
|
import type { Action as AsyncAction } from '../graph/action.js'
|
|
@@ -16,7 +16,7 @@ export interface MutationOptions {
|
|
|
16
16
|
|
|
17
17
|
/** Execute one named SDK Mutation definition through GraphStore optimism/invalidation. */
|
|
18
18
|
export function useMutation<Input, Output>(
|
|
19
|
-
definition:
|
|
19
|
+
definition: Mutation<Input, Output>,
|
|
20
20
|
options: MutationOptions = {},
|
|
21
21
|
): DomainAction<[Input], Output> {
|
|
22
22
|
const store = useGraphStore()
|
|
@@ -26,13 +26,17 @@ export function useMutation<Input, Output>(
|
|
|
26
26
|
optionsRef.current = options
|
|
27
27
|
const run = useCallback(
|
|
28
28
|
async (input: Input): Promise<Output> => {
|
|
29
|
-
const execute = bindMutations((ast) =>
|
|
30
|
-
store.mutate(ast, {
|
|
31
|
-
optimistic: optionsRef.current.optimistic ?? true,
|
|
32
|
-
}),
|
|
33
|
-
)
|
|
34
29
|
try {
|
|
35
|
-
return await
|
|
30
|
+
return await executeMutation(
|
|
31
|
+
{
|
|
32
|
+
mutate: (ast) =>
|
|
33
|
+
store.mutate(ast, {
|
|
34
|
+
optimistic: optionsRef.current.optimistic ?? true,
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
definitionRef.current,
|
|
38
|
+
input,
|
|
39
|
+
)
|
|
36
40
|
} catch (error) {
|
|
37
41
|
optionsRef.current.onError?.(error)
|
|
38
42
|
throw error
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { BoundNode } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { StoreQueryResponse } from '@astrale-os/sdk/client/store'
|
|
3
3
|
import type { Node } from '@astrale-os/sdk/graph/node'
|
|
4
4
|
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
5
|
+
import type { ResolvedClass } from '@astrale-os/sdk/schema'
|
|
5
6
|
|
|
6
7
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
7
8
|
import { Query } from '@astrale-os/sdk/query'
|
|
@@ -16,25 +17,20 @@ export interface RichReadOptions {
|
|
|
16
17
|
readonly pageSize?: number
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
type ObjectDefinition = Pick<
|
|
20
|
-
Definition<'class' | 'interface'>,
|
|
21
|
-
'kind' | 'origin' | 'name' | 'key' | 'schemaRef' | 'definition'
|
|
22
|
-
> & { readonly family: 'node' }
|
|
23
|
-
|
|
24
20
|
/** Read and bind one exact rich object definition exclusively through GraphStore. */
|
|
25
|
-
export function useObject<
|
|
26
|
-
definition:
|
|
21
|
+
export function useObject<Class extends ResolvedClass<'node'>>(
|
|
22
|
+
definition: Class,
|
|
27
23
|
target: PathLike | null | undefined,
|
|
28
24
|
options: RichReadOptions = {},
|
|
29
|
-
): GraphResource<
|
|
25
|
+
): GraphResource<BoundNode<Class> | null> {
|
|
30
26
|
const binding = bindingFor(definition)
|
|
31
27
|
const path = target === null || target === undefined ? null : Path.from(target)
|
|
32
28
|
const query = useMemo(
|
|
33
29
|
() =>
|
|
34
30
|
path === null
|
|
35
31
|
? null
|
|
36
|
-
: Query.from({
|
|
37
|
-
.filter({
|
|
32
|
+
: Query.from({ nodes: [path], binding: 'object' })
|
|
33
|
+
.filter({ class: definition })
|
|
38
34
|
.select({ kind: 'graph' }),
|
|
39
35
|
[definition, path?.raw],
|
|
40
36
|
)
|
|
@@ -47,11 +43,7 @@ export function useObject<RichDefinition extends ObjectDefinition>(
|
|
|
47
43
|
id === undefined
|
|
48
44
|
? undefined
|
|
49
45
|
: result.graph.nodes.find((candidate: { readonly id: unknown }) => candidate.id === id)
|
|
50
|
-
|
|
51
|
-
expected: RichDefinition,
|
|
52
|
-
value: Node,
|
|
53
|
-
) => BoundInstanceOf<RichDefinition>
|
|
54
|
-
return node === undefined ? null : bind(definition, node)
|
|
46
|
+
return node === undefined ? null : binding.wrapNode(definition, node)
|
|
55
47
|
},
|
|
56
48
|
[binding, definition],
|
|
57
49
|
)
|
|
@@ -68,7 +60,7 @@ export function useDynamicObject(
|
|
|
68
60
|
() =>
|
|
69
61
|
path === null
|
|
70
62
|
? null
|
|
71
|
-
: Query.from({
|
|
63
|
+
: Query.from({ nodes: [path], binding: 'dynamic_object' }).select({
|
|
72
64
|
kind: 'graph',
|
|
73
65
|
}),
|
|
74
66
|
[path?.raw],
|