@astrale-os/shell-react 0.2.8 → 0.2.10-beta.2
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 +7 -15
- package/dist/application/apps/app-host.d.ts.map +1 -1
- package/dist/application/apps/app-host.js +3 -2
- package/dist/application/apps/apps.hook.d.ts +7 -4
- package/dist/application/apps/apps.hook.d.ts.map +1 -1
- package/dist/application/apps/apps.hook.js +10 -2
- package/dist/application/apps/domains.hook.d.ts +1 -1
- package/dist/application/apps/domains.hook.d.ts.map +1 -1
- package/dist/application/apps/domains.hook.js +1 -1
- package/dist/application/apps/open-app.hook.d.ts +2 -3
- package/dist/application/apps/open-app.hook.d.ts.map +1 -1
- package/dist/application/index.d.ts +2 -4
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +2 -3
- package/dist/application/spaces/space.hook.d.ts +2 -2
- package/dist/application/spaces/space.hook.d.ts.map +1 -1
- package/dist/application/spaces/space.hook.js +2 -2
- package/dist/application/spaces/spaces.hook.d.ts +2 -3
- package/dist/application/spaces/spaces.hook.d.ts.map +1 -1
- package/dist/application/spaces/spaces.hook.js +10 -3
- package/dist/auth/act-as.context.d.ts +15 -32
- package/dist/auth/act-as.context.d.ts.map +1 -1
- package/dist/auth/act-as.context.js +22 -66
- package/dist/auth/auth.hook.d.ts +1 -0
- package/dist/auth/auth.hook.d.ts.map +1 -1
- package/dist/auth/auth.hook.js +2 -22
- package/dist/auth/can.hook.d.ts +10 -38
- package/dist/auth/can.hook.d.ts.map +1 -1
- package/dist/auth/can.hook.js +35 -110
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/self.hook.d.ts +2 -2
- package/dist/auth/self.hook.d.ts.map +1 -1
- package/dist/auth/self.hook.js +0 -7
- package/dist/auth/session-kernel.d.ts +2 -3
- package/dist/auth/session-kernel.d.ts.map +1 -1
- package/dist/auth/session-kernel.js +4 -13
- package/dist/components/boundaries.d.ts +47 -0
- package/dist/components/boundaries.d.ts.map +1 -0
- package/dist/components/boundaries.js +55 -0
- package/dist/components/can.d.ts +11 -0
- package/dist/components/can.d.ts.map +1 -0
- package/dist/components/can.js +5 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +4 -0
- package/dist/components/mount-domain-frontend.d.ts +22 -0
- package/dist/components/mount-domain-frontend.d.ts.map +1 -0
- package/dist/components/mount-domain-frontend.js +85 -0
- package/dist/components/view-app.d.ts +16 -0
- package/dist/components/view-app.d.ts.map +1 -0
- package/dist/components/view-app.js +16 -0
- package/dist/graph/call/call.hook.d.ts +8 -18
- package/dist/graph/call/call.hook.d.ts.map +1 -1
- package/dist/graph/call/call.hook.js +7 -13
- package/dist/graph/call/stream.hook.d.ts +12 -20
- package/dist/graph/call/stream.hook.d.ts.map +1 -1
- package/dist/graph/call/stream.hook.js +55 -111
- package/dist/graph/graph.context.d.ts +4 -3
- package/dist/graph/graph.context.d.ts.map +1 -1
- package/dist/graph/graph.context.js +2 -8
- package/dist/graph/guards.error.d.ts +7 -8
- package/dist/graph/guards.error.d.ts.map +1 -1
- package/dist/graph/guards.error.js +11 -7
- package/dist/graph/index.d.ts +8 -16
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +3 -7
- package/dist/graph/memory/read/envelope.d.ts +22 -54
- package/dist/graph/memory/read/envelope.d.ts.map +1 -1
- package/dist/graph/memory/read/envelope.js +26 -20
- package/dist/graph/memory/read/idle.d.ts +1 -15
- package/dist/graph/memory/read/idle.d.ts.map +1 -1
- package/dist/graph/memory/read/idle.js +4 -13
- package/dist/graph/query-definition.hook.d.ts +11 -0
- package/dist/graph/query-definition.hook.d.ts.map +1 -0
- package/dist/graph/query-definition.hook.js +205 -0
- package/dist/graph/resource.d.ts +34 -0
- package/dist/graph/resource.d.ts.map +1 -0
- package/dist/graph/resource.js +244 -0
- package/dist/graph/store.d.ts +25 -0
- package/dist/graph/store.d.ts.map +1 -0
- package/dist/graph/store.js +69 -0
- package/dist/host/actions.hook.d.ts +34 -0
- package/dist/host/actions.hook.d.ts.map +1 -0
- package/dist/host/actions.hook.js +125 -0
- package/dist/host/components.d.ts +15 -0
- package/dist/host/components.d.ts.map +1 -0
- package/dist/host/components.js +14 -0
- package/dist/host/index.d.ts +5 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +3 -0
- package/dist/index.d.ts +26 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -31
- package/dist/intents/envelope.d.ts +7 -19
- package/dist/intents/envelope.d.ts.map +1 -1
- package/dist/intents/envelope.js +2 -3
- package/dist/intents/send-intent.hook.d.ts +5 -7
- package/dist/intents/send-intent.hook.d.ts.map +1 -1
- package/dist/intents/send-intent.hook.js +17 -75
- package/dist/navigation/adapters/adapter.d.ts +5 -0
- package/dist/navigation/adapters/adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/memory.adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/memory.adapter.js +4 -0
- package/dist/navigation/adapters/url.adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/url.adapter.js +57 -10
- package/dist/navigation/codec.d.ts +60 -0
- package/dist/navigation/codec.d.ts.map +1 -0
- package/dist/navigation/codec.js +121 -0
- package/dist/navigation/history.d.ts +11 -9
- package/dist/navigation/history.d.ts.map +1 -1
- package/dist/navigation/history.js +6 -8
- package/dist/navigation/hooks/ancestors.hook.d.ts +5 -7
- package/dist/navigation/hooks/ancestors.hook.d.ts.map +1 -1
- package/dist/navigation/hooks/ancestors.hook.js +7 -51
- package/dist/navigation/hooks/location.hook.d.ts.map +1 -1
- package/dist/navigation/hooks/location.hook.js +4 -6
- package/dist/navigation/hooks/scope.hook.d.ts.map +1 -1
- package/dist/navigation/hooks/scope.hook.js +4 -6
- package/dist/navigation/index.d.ts +4 -2
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +2 -1
- package/dist/navigation/link.d.ts +12 -0
- package/dist/navigation/link.d.ts.map +1 -1
- package/dist/navigation/link.js +13 -2
- package/dist/navigation/location.d.ts +13 -40
- package/dist/navigation/location.d.ts.map +1 -1
- package/dist/navigation/location.js +14 -76
- package/dist/navigation/scope.context.d.ts +17 -26
- package/dist/navigation/scope.context.d.ts.map +1 -1
- package/dist/navigation/scope.context.js +140 -108
- package/dist/navigation/snapshot.d.ts +14 -19
- package/dist/navigation/snapshot.d.ts.map +1 -1
- package/dist/navigation/snapshot.js +48 -44
- package/dist/schema/action.hook.d.ts +44 -0
- package/dist/schema/action.hook.d.ts.map +1 -0
- package/dist/schema/action.hook.js +72 -0
- package/dist/schema/domain.hook.d.ts +12 -7
- package/dist/schema/domain.hook.d.ts.map +1 -1
- package/dist/schema/domain.hook.js +101 -4
- package/dist/schema/index.d.ts +9 -8
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +5 -7
- package/dist/schema/kernel.hook.d.ts +4 -9
- package/dist/schema/kernel.hook.d.ts.map +1 -1
- package/dist/schema/kernel.hook.js +4 -6
- package/dist/schema/mutation.hook.d.ts +9 -0
- package/dist/schema/mutation.hook.d.ts.map +1 -0
- package/dist/schema/mutation.hook.js +41 -0
- package/dist/schema/object.hook.d.ts +16 -0
- package/dist/schema/object.hook.d.ts.map +1 -0
- package/dist/schema/object.hook.js +46 -0
- package/dist/schema/relation.hook.d.ts +46 -0
- package/dist/schema/relation.hook.d.ts.map +1 -0
- package/dist/schema/relation.hook.js +136 -0
- package/dist/selection/live.d.ts +9 -17
- package/dist/selection/live.d.ts.map +1 -1
- package/dist/selection/live.js +65 -112
- package/dist/selection/scope.context.js +5 -4
- package/dist/session/boot/boot.d.ts +1 -13
- package/dist/session/boot/boot.d.ts.map +1 -1
- package/dist/session/boot/boot.js +9 -27
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/kernel-instance.hook.d.ts +1 -4
- package/dist/session/kernel-instance.hook.d.ts.map +1 -1
- package/dist/session/kernel-instance.hook.js +1 -5
- package/dist/session/provider.d.ts +9 -23
- package/dist/session/provider.d.ts.map +1 -1
- package/dist/session/provider.js +29 -75
- package/dist/session/session.context.d.ts +5 -0
- package/dist/session/session.context.d.ts.map +1 -1
- package/dist/window/adopt.d.ts +0 -1
- package/dist/window/adopt.d.ts.map +1 -1
- package/dist/window/hooks/target-node.hook.d.ts +10 -6
- package/dist/window/hooks/target-node.hook.d.ts.map +1 -1
- package/dist/window/hooks/target-node.hook.js +14 -72
- package/dist/window/hooks/views-for.hook.d.ts +4 -3
- package/dist/window/hooks/views-for.hook.d.ts.map +1 -1
- package/dist/window/hooks/views-for.hook.js +6 -22
- package/dist/window/hooks/windows.hook.d.ts.map +1 -1
- package/dist/window/hooks/windows.hook.js +3 -4
- package/dist/window/host.d.ts +11 -11
- package/dist/window/host.d.ts.map +1 -1
- package/dist/window/host.js +38 -54
- package/dist/window/hub.d.ts +2 -0
- package/dist/window/hub.d.ts.map +1 -1
- package/dist/window/hub.js +33 -0
- package/dist/window/index.d.ts +3 -3
- package/dist/window/index.d.ts.map +1 -1
- package/dist/window/index.js +2 -2
- package/dist/window/lifecycle/mount.d.ts +5 -44
- package/dist/window/lifecycle/mount.d.ts.map +1 -1
- package/dist/window/lifecycle/mount.js +14 -120
- package/dist/window/lifecycle/states.d.ts +0 -5
- package/dist/window/lifecycle/states.d.ts.map +1 -1
- package/dist/window/stage.d.ts.map +1 -1
- package/dist/window/stage.js +1 -0
- package/package.json +9 -8
- package/src/application/apps/app-host.tsx +17 -18
- package/src/application/apps/apps.hook.ts +13 -6
- package/src/application/apps/domains.hook.ts +1 -1
- package/src/application/apps/open-app.hook.ts +3 -4
- package/src/application/index.ts +4 -6
- package/src/application/spaces/space.hook.ts +5 -6
- package/src/application/spaces/spaces.hook.ts +14 -9
- package/src/auth/act-as.context.tsx +37 -91
- package/src/auth/auth.hook.ts +2 -30
- package/src/auth/can.hook.ts +44 -135
- package/src/auth/index.ts +1 -0
- package/src/auth/self.hook.ts +2 -9
- package/src/auth/session-kernel.ts +6 -16
- package/src/components/boundaries.tsx +113 -0
- package/src/components/can.tsx +16 -0
- package/src/components/index.ts +14 -0
- package/src/components/mount-domain-frontend.tsx +158 -0
- package/src/components/view-app.tsx +49 -0
- package/src/graph/call/call.hook.ts +22 -27
- package/src/graph/call/stream.hook.ts +66 -143
- package/src/graph/graph.context.ts +6 -11
- package/src/graph/guards.error.ts +17 -55
- package/src/graph/index.ts +23 -17
- package/src/graph/memory/read/envelope.ts +40 -65
- package/src/graph/memory/read/idle.ts +5 -18
- package/src/graph/query-definition.hook.ts +270 -0
- package/src/graph/resource.ts +358 -0
- package/src/graph/store.ts +97 -0
- package/src/host/actions.hook.ts +165 -0
- package/src/host/components.tsx +29 -0
- package/src/host/index.ts +17 -0
- package/src/index.ts +137 -123
- package/src/intents/envelope.ts +7 -23
- package/src/intents/send-intent.hook.ts +24 -79
- package/src/navigation/adapters/adapter.ts +3 -0
- package/src/navigation/adapters/memory.adapter.ts +4 -0
- package/src/navigation/adapters/url.adapter.ts +48 -8
- package/src/navigation/codec.ts +190 -0
- package/src/navigation/history.ts +20 -11
- package/src/navigation/hooks/ancestors.hook.ts +10 -60
- package/src/navigation/hooks/location.hook.ts +4 -6
- package/src/navigation/hooks/scope.hook.ts +4 -6
- package/src/navigation/index.ts +18 -2
- package/src/navigation/link.tsx +27 -2
- package/src/navigation/location.ts +26 -93
- package/src/navigation/scope.context.tsx +151 -136
- package/src/navigation/snapshot.ts +69 -50
- package/src/schema/action.hook.ts +146 -0
- package/src/schema/domain.hook.ts +127 -11
- package/src/schema/index.ts +9 -30
- package/src/schema/kernel.hook.ts +6 -13
- package/src/schema/mutation.hook.ts +63 -0
- package/src/schema/object.hook.ts +85 -0
- package/src/schema/relation.hook.ts +253 -0
- package/src/selection/live.ts +75 -125
- package/src/selection/scope.context.tsx +4 -4
- package/src/session/boot/boot.ts +8 -33
- package/src/session/index.ts +1 -0
- package/src/session/kernel-instance.hook.ts +2 -11
- package/src/session/provider.tsx +34 -103
- package/src/session/session.context.ts +6 -0
- package/src/window/adopt.ts +0 -1
- package/src/window/hooks/target-node.hook.ts +20 -85
- package/src/window/hooks/views-for.hook.ts +12 -25
- package/src/window/hooks/windows.hook.ts +3 -4
- package/src/window/host.tsx +51 -68
- package/src/window/hub.ts +33 -0
- package/src/window/index.ts +4 -4
- package/src/window/lifecycle/mount.ts +15 -160
- package/src/window/lifecycle/states.ts +1 -5
- package/src/window/stage.tsx +1 -0
- package/dist/application/roles.hook.d.ts +0 -7
- package/dist/application/roles.hook.d.ts.map +0 -1
- package/dist/application/roles.hook.js +0 -23
- package/dist/graph/memory/base.d.ts +0 -21
- package/dist/graph/memory/base.d.ts.map +0 -1
- package/dist/graph/memory/base.js +0 -33
- package/dist/graph/memory/read/read.d.ts +0 -9
- package/dist/graph/memory/read/read.d.ts.map +0 -1
- package/dist/graph/memory/read/read.js +0 -87
- package/dist/graph/memory/scope.d.ts +0 -7
- package/dist/graph/memory/scope.d.ts.map +0 -1
- package/dist/graph/memory/scope.js +0 -25
- package/dist/graph/mutate/move.hook.d.ts +0 -7
- package/dist/graph/mutate/move.hook.d.ts.map +0 -1
- package/dist/graph/mutate/move.hook.js +0 -49
- package/dist/graph/mutate/mutate.hook.d.ts +0 -17
- package/dist/graph/mutate/mutate.hook.d.ts.map +0 -1
- package/dist/graph/mutate/mutate.hook.js +0 -45
- package/dist/graph/query/children.hook.d.ts +0 -29
- package/dist/graph/query/children.hook.d.ts.map +0 -1
- package/dist/graph/query/children.hook.js +0 -64
- package/dist/graph/query/graph.hook.d.ts +0 -9
- package/dist/graph/query/graph.hook.d.ts.map +0 -1
- package/dist/graph/query/graph.hook.js +0 -5
- package/dist/graph/query/neighbors.hook.d.ts +0 -7
- package/dist/graph/query/neighbors.hook.d.ts.map +0 -1
- package/dist/graph/query/neighbors.hook.js +0 -104
- package/dist/graph/query/node.hook.d.ts +0 -14
- package/dist/graph/query/node.hook.d.ts.map +0 -1
- package/dist/graph/query/node.hook.js +0 -43
- package/dist/graph/query/query.hook.d.ts +0 -14
- package/dist/graph/query/query.hook.d.ts.map +0 -1
- package/dist/graph/query/query.hook.js +0 -23
- package/dist/schema/bind-read.d.ts +0 -37
- package/dist/schema/bind-read.d.ts.map +0 -1
- package/dist/schema/bind-read.js +0 -205
- package/dist/schema/binding.d.ts +0 -21
- package/dist/schema/binding.d.ts.map +0 -1
- package/dist/schema/binding.js +0 -99
- package/dist/schema/function.hook.d.ts +0 -9
- package/dist/schema/function.hook.d.ts.map +0 -1
- package/dist/schema/function.hook.js +0 -40
- package/dist/schema/method.hook.d.ts +0 -14
- package/dist/schema/method.hook.d.ts.map +0 -1
- package/dist/schema/method.hook.js +0 -49
- package/dist/schema/tag.d.ts +0 -11
- package/dist/schema/tag.d.ts.map +0 -1
- package/dist/schema/tag.js +0 -11
- package/dist/schema/types.d.ts +0 -73
- package/dist/schema/types.d.ts.map +0 -1
- package/dist/schema/types.js +0 -1
- package/dist/schema/write.d.ts +0 -16
- package/dist/schema/write.d.ts.map +0 -1
- package/dist/schema/write.js +0 -56
- package/src/application/roles.hook.ts +0 -41
- package/src/graph/memory/base.ts +0 -50
- package/src/graph/memory/read/read.ts +0 -103
- package/src/graph/memory/scope.ts +0 -29
- package/src/graph/mutate/move.hook.ts +0 -70
- package/src/graph/mutate/mutate.hook.ts +0 -69
- package/src/graph/query/children.hook.ts +0 -136
- package/src/graph/query/graph.hook.ts +0 -13
- package/src/graph/query/neighbors.hook.ts +0 -159
- package/src/graph/query/node.hook.ts +0 -83
- package/src/graph/query/query.hook.ts +0 -42
- package/src/schema/bind-read.ts +0 -319
- package/src/schema/binding.ts +0 -146
- package/src/schema/function.hook.ts +0 -68
- package/src/schema/method.hook.ts +0 -103
- package/src/schema/tag.ts +0 -19
- package/src/schema/types.ts +0 -150
- package/src/schema/write.ts +0 -96
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { CredentialSnapshot } from '@astrale-os/shell'
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Component, Fragment } from 'react'
|
|
5
|
+
|
|
6
|
+
import type { GraphResource } from '../graph/resource.js'
|
|
7
|
+
|
|
8
|
+
import { useConnection } from '../session/connection/connection.hook.js'
|
|
9
|
+
|
|
10
|
+
export function ViewBoundary<T>(props: {
|
|
11
|
+
readonly resource: GraphResource<T>
|
|
12
|
+
readonly children: (value: T) => ReactNode
|
|
13
|
+
readonly loading?: ReactNode
|
|
14
|
+
readonly empty?: ReactNode
|
|
15
|
+
readonly error?: (error: unknown) => ReactNode
|
|
16
|
+
}): ReactElement {
|
|
17
|
+
const { resource } = props
|
|
18
|
+
if (resource.state === 'failed') {
|
|
19
|
+
if (props.error !== undefined) return <>{props.error(resource.error)}</>
|
|
20
|
+
throw resource.error
|
|
21
|
+
}
|
|
22
|
+
if (resource.data === undefined) return <>{props.loading ?? null}</>
|
|
23
|
+
if (resource.data === null) return <>{props.empty ?? null}</>
|
|
24
|
+
return <>{props.children(resource.data)}</>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const GraphBoundary = ViewBoundary
|
|
28
|
+
|
|
29
|
+
export interface ViewErrorBoundaryProps {
|
|
30
|
+
/** A render function receives the retry generation and can recreate rejected lazy resources. */
|
|
31
|
+
readonly children: ReactNode | ((generation: number) => ReactNode)
|
|
32
|
+
readonly fallback?: (error: unknown, retry: () => void) => ReactNode
|
|
33
|
+
readonly onError?: (error: unknown) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Catch Shell View render/lazy failures and remount the failed subtree on explicit retry. */
|
|
37
|
+
export class ViewErrorBoundary extends Component<
|
|
38
|
+
ViewErrorBoundaryProps,
|
|
39
|
+
{ readonly error: unknown; readonly generation: number }
|
|
40
|
+
> {
|
|
41
|
+
override state = { error: undefined as unknown, generation: 0 }
|
|
42
|
+
|
|
43
|
+
static getDerivedStateFromError(error: unknown) {
|
|
44
|
+
return { error }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
override componentDidCatch(error: unknown): void {
|
|
48
|
+
this.props.onError?.(error)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private readonly retry = (): void => {
|
|
52
|
+
this.setState((state) => ({ error: undefined, generation: state.generation + 1 }))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override render(): ReactNode {
|
|
56
|
+
if (this.state.error !== undefined) {
|
|
57
|
+
return (
|
|
58
|
+
this.props.fallback?.(this.state.error, this.retry) ??
|
|
59
|
+
defaultViewError(this.state.error, this.retry)
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
const content =
|
|
63
|
+
typeof this.props.children === 'function'
|
|
64
|
+
? this.props.children(this.state.generation)
|
|
65
|
+
: this.props.children
|
|
66
|
+
return <Fragment key={this.state.generation}>{content}</Fragment>
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function defaultViewLoading(): ReactElement {
|
|
71
|
+
return (
|
|
72
|
+
<div role="status" aria-live="polite">
|
|
73
|
+
Loading view…
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function defaultViewError(_error: unknown, retry: () => void): ReactElement {
|
|
79
|
+
return (
|
|
80
|
+
<section role="alert">
|
|
81
|
+
<p>View could not load.</p>
|
|
82
|
+
<button type="button" onClick={retry}>
|
|
83
|
+
Retry
|
|
84
|
+
</button>
|
|
85
|
+
</section>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function ConnectionBoundary(props: {
|
|
90
|
+
readonly children: ReactNode
|
|
91
|
+
readonly fallback?: (status: ReturnType<typeof useConnection>) => ReactNode
|
|
92
|
+
}): ReactElement {
|
|
93
|
+
const connection = useConnection()
|
|
94
|
+
return connection.status === 'ready' ? (
|
|
95
|
+
<>{props.children}</>
|
|
96
|
+
) : (
|
|
97
|
+
<>{props.fallback?.(connection) ?? null}</>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function CredentialBoundary(props: {
|
|
102
|
+
readonly credential: CredentialSnapshot
|
|
103
|
+
readonly children: ReactNode
|
|
104
|
+
readonly fallback?: (credential: CredentialSnapshot) => ReactNode
|
|
105
|
+
}): ReactElement {
|
|
106
|
+
return props.credential.state === 'valid' ||
|
|
107
|
+
props.credential.state === 'refreshing' ||
|
|
108
|
+
props.credential.state === 'degraded' ? (
|
|
109
|
+
<>{props.children}</>
|
|
110
|
+
) : (
|
|
111
|
+
<>{props.fallback?.(props.credential) ?? null}</>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import type { schema } from '@astrale-os/sdk/schema'
|
|
3
|
+
import type { ReactElement, ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
import type { PolicyObject } from '../auth/can.hook.js'
|
|
6
|
+
|
|
7
|
+
import { useCan } from '../auth/can.hook.js'
|
|
8
|
+
|
|
9
|
+
export function Can(props: {
|
|
10
|
+
readonly policy: PathLike | schema.PolicyRef
|
|
11
|
+
readonly object: PolicyObject
|
|
12
|
+
readonly children: ReactNode
|
|
13
|
+
readonly fallback?: ReactNode
|
|
14
|
+
}): ReactElement {
|
|
15
|
+
return <>{useCan(props.policy, props.object) === true ? props.children : props.fallback}</>
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { Can } from './can.js'
|
|
2
|
+
export {
|
|
3
|
+
ConnectionBoundary,
|
|
4
|
+
CredentialBoundary,
|
|
5
|
+
defaultViewError,
|
|
6
|
+
defaultViewLoading,
|
|
7
|
+
GraphBoundary,
|
|
8
|
+
ViewErrorBoundary,
|
|
9
|
+
ViewBoundary,
|
|
10
|
+
} from './boundaries.js'
|
|
11
|
+
export type { ViewErrorBoundaryProps } from './boundaries.js'
|
|
12
|
+
export { ViewApp } from './view-app.js'
|
|
13
|
+
export { mountDomainFrontend } from './mount-domain-frontend.js'
|
|
14
|
+
export type { MountedDomainFrontend, MountDomainFrontendOptions } from './mount-domain-frontend.js'
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { Domain, Definition } from '@astrale-os/sdk/domain'
|
|
2
|
+
import type { ReactRouteModule, ReactViewModule, ReactViewProps } from '@astrale-os/sdk/react'
|
|
3
|
+
import type { schema } from '@astrale-os/sdk/schema'
|
|
4
|
+
import type { Shell, ShellConfig } from '@astrale-os/shell'
|
|
5
|
+
import type { ComponentType, LazyExoticComponent, ReactElement, ReactNode } from 'react'
|
|
6
|
+
|
|
7
|
+
import { createElement, lazy, Suspense } from 'react'
|
|
8
|
+
import { createRoot } from 'react-dom/client'
|
|
9
|
+
|
|
10
|
+
import { useHistoryVersion, useOptionalNavCtx } from '../navigation/scope.context.js'
|
|
11
|
+
import { useDomain } from '../schema/domain.hook.js'
|
|
12
|
+
import { useTargetPath } from '../window/hooks/target-node.hook.js'
|
|
13
|
+
import { defaultViewError, defaultViewLoading, ViewErrorBoundary } from './boundaries.js'
|
|
14
|
+
import { ViewApp } from './view-app.js'
|
|
15
|
+
|
|
16
|
+
type ViewName<S extends schema.DomainSchema> = Extract<keyof ReactRouteModule<S>['routes'], string>
|
|
17
|
+
type ViewOf<S extends schema.DomainSchema, Name extends ViewName<S>> = Extract<
|
|
18
|
+
Domain<S>[Name],
|
|
19
|
+
Definition<'view'>
|
|
20
|
+
>
|
|
21
|
+
|
|
22
|
+
export interface MountDomainFrontendOptions<
|
|
23
|
+
S extends schema.DomainSchema,
|
|
24
|
+
Name extends ViewName<S>,
|
|
25
|
+
> {
|
|
26
|
+
readonly root: Element | string
|
|
27
|
+
readonly module: ReactRouteModule<S>
|
|
28
|
+
readonly view: Name
|
|
29
|
+
/** Defaults to the child-side sandbox handshake. A prebuilt Shell is the full-app escape hatch. */
|
|
30
|
+
readonly config?: ShellConfig | Shell
|
|
31
|
+
readonly fallback?: ReactNode
|
|
32
|
+
readonly errorFallback?: (error: unknown, retry: () => void) => ReactNode
|
|
33
|
+
readonly onError?: (error: unknown) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MountedDomainFrontend {
|
|
37
|
+
unmount(): void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Browser bootstrap used by SDK-generated route entries and custom full React apps alike. */
|
|
41
|
+
export function mountDomainFrontend<
|
|
42
|
+
const S extends schema.DomainSchema,
|
|
43
|
+
const Name extends ViewName<S>,
|
|
44
|
+
>(options: MountDomainFrontendOptions<S, Name>): MountedDomainFrontend {
|
|
45
|
+
const rootElement =
|
|
46
|
+
typeof options.root === 'string' ? document.querySelector(options.root) : options.root
|
|
47
|
+
if (rootElement === null) throw new Error(`React frontend root '${options.root}' was not found.`)
|
|
48
|
+
const route = options.module.routes[options.view]
|
|
49
|
+
if (route === undefined) throw new Error(`React View '${options.view}' is not implemented.`)
|
|
50
|
+
const loading = options.fallback ?? defaultViewLoading()
|
|
51
|
+
const errorFallback = options.errorFallback ?? defaultViewError
|
|
52
|
+
const routeRuntime = createRouteRuntime()
|
|
53
|
+
const root = createRoot(rootElement)
|
|
54
|
+
root.render(
|
|
55
|
+
<ViewApp
|
|
56
|
+
config={options.config ?? { mode: 'sandboxed' }}
|
|
57
|
+
fallback={loading}
|
|
58
|
+
errorFallback={errorFallback}
|
|
59
|
+
onError={({ error }) => options.onError?.(error)}
|
|
60
|
+
>
|
|
61
|
+
<ViewErrorBoundary fallback={errorFallback} onError={options.onError}>
|
|
62
|
+
{(generation) => (
|
|
63
|
+
<Suspense fallback={loading}>
|
|
64
|
+
<DomainRoute
|
|
65
|
+
module={options.module}
|
|
66
|
+
name={options.view}
|
|
67
|
+
generation={generation}
|
|
68
|
+
runtime={routeRuntime}
|
|
69
|
+
/>
|
|
70
|
+
</Suspense>
|
|
71
|
+
)}
|
|
72
|
+
</ViewErrorBoundary>
|
|
73
|
+
</ViewApp>,
|
|
74
|
+
)
|
|
75
|
+
return Object.freeze({ unmount: () => root.unmount() })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function DomainRoute<S extends schema.DomainSchema, Name extends ViewName<S>>(props: {
|
|
79
|
+
readonly module: ReactRouteModule<S>
|
|
80
|
+
readonly name: Name
|
|
81
|
+
readonly generation: number
|
|
82
|
+
readonly runtime: RouteRuntime
|
|
83
|
+
}): ReactElement {
|
|
84
|
+
const domain = useDomain(props.module.schema)
|
|
85
|
+
const navigation = useOptionalNavCtx()
|
|
86
|
+
useHistoryVersion(navigation?.history ?? null)
|
|
87
|
+
const view = domain[props.name] as ViewOf<S, Name>
|
|
88
|
+
// Domain's schema-indexed conditional retains the exact authored View type,
|
|
89
|
+
// while the runtime member has Core's common rich Definition shape.
|
|
90
|
+
const runtimeView = view as unknown as Definition<'view'>
|
|
91
|
+
const targetPath = useTargetPath()
|
|
92
|
+
const implementation = props.module.routes[props.name]!
|
|
93
|
+
const Component = props.runtime.component<ViewOf<S, Name>>(props.generation, implementation)
|
|
94
|
+
const declaration = runtimeView.definition as { readonly target: { readonly kind: string } }
|
|
95
|
+
const target =
|
|
96
|
+
declaration.target.kind === 'domain'
|
|
97
|
+
? Object.freeze({ kind: 'domain' as const, origin: runtimeView.origin })
|
|
98
|
+
: targetPath === null
|
|
99
|
+
? missingTarget(runtimeView.name)
|
|
100
|
+
: targetPath
|
|
101
|
+
const current = new URL(window.location.href)
|
|
102
|
+
const routeParams = navigation?.history.current?.params ?? directRouteParams(current)
|
|
103
|
+
const route = Object.freeze({
|
|
104
|
+
name: props.name,
|
|
105
|
+
href: current.href,
|
|
106
|
+
params: Object.freeze({ ...routeParams }),
|
|
107
|
+
})
|
|
108
|
+
return createElement(Component, { view, target, route } as unknown as ReactViewProps<
|
|
109
|
+
ViewOf<S, Name>
|
|
110
|
+
>)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function directRouteParams(url: URL): Readonly<Record<string, string>> {
|
|
114
|
+
const params: Record<string, string> = {}
|
|
115
|
+
const prefix = 'astrale.param.'
|
|
116
|
+
for (const key of new Set(url.searchParams.keys())) {
|
|
117
|
+
if (!key.startsWith(prefix)) continue
|
|
118
|
+
const name = key.slice(prefix.length)
|
|
119
|
+
if (name.length === 0 || /token|credential|authorization|secret/i.test(name)) {
|
|
120
|
+
throw new TypeError(`React View route param '${name}' is invalid.`)
|
|
121
|
+
}
|
|
122
|
+
const values = url.searchParams.getAll(key)
|
|
123
|
+
if (values.length !== 1) throw new TypeError(`React View route param '${name}' is duplicated.`)
|
|
124
|
+
params[name] = values[0]!
|
|
125
|
+
}
|
|
126
|
+
return Object.freeze(params)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface RouteRuntime {
|
|
130
|
+
component<View extends Definition<'view'>>(
|
|
131
|
+
generation: number,
|
|
132
|
+
implementation: { readonly component: () => Promise<ReactViewModule<View>> },
|
|
133
|
+
): ComponentType<ReactViewProps<View>>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function createRouteRuntime(): RouteRuntime {
|
|
137
|
+
const generations = new Map<number, LazyExoticComponent<ComponentType<ReactViewProps<never>>>>()
|
|
138
|
+
return {
|
|
139
|
+
component<View extends Definition<'view'>>(
|
|
140
|
+
generation: number,
|
|
141
|
+
implementation: { readonly component: () => Promise<ReactViewModule<View>> },
|
|
142
|
+
): ComponentType<ReactViewProps<View>> {
|
|
143
|
+
let Component = generations.get(generation)
|
|
144
|
+
if (Component === undefined) {
|
|
145
|
+
Component = lazy(async () => {
|
|
146
|
+
const loaded = await implementation.component()
|
|
147
|
+
return { default: loaded.default as ComponentType<ReactViewProps<never>> }
|
|
148
|
+
})
|
|
149
|
+
generations.set(generation, Component)
|
|
150
|
+
}
|
|
151
|
+
return Component as unknown as ComponentType<ReactViewProps<View>>
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function missingTarget(name: string): never {
|
|
157
|
+
throw new Error(`React View '${name}' requires a resource target from the Shell handshake.`)
|
|
158
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Shell, ShellConfig } from '@astrale-os/shell'
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import type { NavAdapter } from '../navigation/adapters/adapter.js'
|
|
5
|
+
import type { NavTarget } from '../navigation/snapshot.js'
|
|
6
|
+
import type { ShellProviderProps } from '../session/provider.js'
|
|
7
|
+
|
|
8
|
+
import { memoryAdapter } from '../navigation/adapters/memory.adapter.js'
|
|
9
|
+
import { urlAdapter } from '../navigation/adapters/url.adapter.js'
|
|
10
|
+
import { NavScope } from '../navigation/scope.context.js'
|
|
11
|
+
import { ShellProvider } from '../session/provider.js'
|
|
12
|
+
import { useShell } from '../session/shell.hook.js'
|
|
13
|
+
import { ViewStage } from '../window/stage.js'
|
|
14
|
+
|
|
15
|
+
/** One generated/default root; custom React apps may compose the lower-level providers directly. */
|
|
16
|
+
export function ViewApp(props: {
|
|
17
|
+
readonly config: ShellConfig | Shell
|
|
18
|
+
readonly navigation?: NavAdapter
|
|
19
|
+
readonly initial?: NavTarget
|
|
20
|
+
readonly fallback?: ReactNode
|
|
21
|
+
readonly errorFallback?: ShellProviderProps['errorFallback']
|
|
22
|
+
readonly onError?: ShellProviderProps['onError']
|
|
23
|
+
readonly children: ReactNode
|
|
24
|
+
}): ReactElement {
|
|
25
|
+
return (
|
|
26
|
+
<ShellProvider
|
|
27
|
+
config={props.config}
|
|
28
|
+
fallback={props.fallback}
|
|
29
|
+
errorFallback={props.errorFallback}
|
|
30
|
+
onError={props.onError}
|
|
31
|
+
>
|
|
32
|
+
<ViewAppRuntime {...props} />
|
|
33
|
+
</ShellProvider>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ViewAppRuntime(
|
|
38
|
+
props: Omit<Parameters<typeof ViewApp>[0], 'config' | 'fallback'>,
|
|
39
|
+
): ReactElement {
|
|
40
|
+
const shell = useShell()
|
|
41
|
+
const defaultAdapter =
|
|
42
|
+
shell.hostCapabilities.navigation.history === 'browser' ? urlAdapter() : memoryAdapter()
|
|
43
|
+
return (
|
|
44
|
+
<NavScope adapter={props.navigation ?? defaultAdapter} initial={props.initial}>
|
|
45
|
+
{props.children}
|
|
46
|
+
<ViewStage />
|
|
47
|
+
</NavScope>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -1,41 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* structurally invalidatable (FRONTEND §5.7); a consumer that needs freshness
|
|
5
|
-
* owns its own refetch policy. Dispatch is a graph operation (rule 2).
|
|
6
|
-
*/
|
|
7
|
-
import type { CallFns, FnMap } from '@astrale-os/kernel-client'
|
|
1
|
+
import type { Call } from '@astrale-os/kernel-client'
|
|
2
|
+
import type { SessionRequestOptions } from '@astrale-os/kernel-client/session'
|
|
3
|
+
import type { Value } from '@astrale-os/sdk/value'
|
|
8
4
|
|
|
9
5
|
import type { Action } from '../action.js'
|
|
10
6
|
|
|
11
7
|
import { useSessionKernel } from '../../auth/session-kernel.js'
|
|
12
8
|
import { useAction } from '../action.js'
|
|
13
9
|
|
|
14
|
-
export interface CallOptions {
|
|
15
|
-
|
|
16
|
-
timeout?: number
|
|
17
|
-
/** Side-channel notified on rejection (the Action also records `.error`). */
|
|
18
|
-
onError?(error: unknown): void
|
|
10
|
+
export interface CallOptions extends SessionRequestOptions {
|
|
11
|
+
readonly onError?: (error: unknown) => void
|
|
19
12
|
}
|
|
20
13
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
export type CallResult = Value
|
|
15
|
+
type CallAuthor<A extends unknown[]> = Call | ((...args: A) => Call)
|
|
16
|
+
|
|
17
|
+
export function useCall(call: Call | null, options?: CallOptions): Action<[], CallResult>
|
|
18
|
+
export function useCall<A extends unknown[]>(
|
|
19
|
+
author: ((...args: A) => Call) | null,
|
|
20
|
+
options?: CallOptions,
|
|
21
|
+
): Action<A, CallResult>
|
|
22
|
+
export function useCall<A extends unknown[]>(
|
|
23
|
+
author: CallAuthor<A> | null,
|
|
24
|
+
options?: CallOptions,
|
|
25
|
+
): Action<A, CallResult> {
|
|
30
26
|
const kernel = useSessionKernel()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (method === null) throw new Error('useCall: method is null (idle action invoked)')
|
|
27
|
+
return useAction(async (...args: A) => {
|
|
28
|
+
if (author === null) throw new Error('useCall: idle action invoked')
|
|
29
|
+
const call = typeof author === 'function' ? author(...args) : author
|
|
35
30
|
try {
|
|
36
|
-
return await kernel.call(
|
|
31
|
+
return await kernel.call(call, options)
|
|
37
32
|
} catch (error) {
|
|
38
|
-
|
|
33
|
+
options?.onError?.(error)
|
|
39
34
|
throw error
|
|
40
35
|
}
|
|
41
36
|
})
|