@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.
Files changed (342) hide show
  1. package/dist/application/apps/app-host.d.ts +7 -15
  2. package/dist/application/apps/app-host.d.ts.map +1 -1
  3. package/dist/application/apps/app-host.js +3 -2
  4. package/dist/application/apps/apps.hook.d.ts +7 -4
  5. package/dist/application/apps/apps.hook.d.ts.map +1 -1
  6. package/dist/application/apps/apps.hook.js +10 -2
  7. package/dist/application/apps/domains.hook.d.ts +1 -1
  8. package/dist/application/apps/domains.hook.d.ts.map +1 -1
  9. package/dist/application/apps/domains.hook.js +1 -1
  10. package/dist/application/apps/open-app.hook.d.ts +2 -3
  11. package/dist/application/apps/open-app.hook.d.ts.map +1 -1
  12. package/dist/application/index.d.ts +2 -4
  13. package/dist/application/index.d.ts.map +1 -1
  14. package/dist/application/index.js +2 -3
  15. package/dist/application/spaces/space.hook.d.ts +2 -2
  16. package/dist/application/spaces/space.hook.d.ts.map +1 -1
  17. package/dist/application/spaces/space.hook.js +2 -2
  18. package/dist/application/spaces/spaces.hook.d.ts +2 -3
  19. package/dist/application/spaces/spaces.hook.d.ts.map +1 -1
  20. package/dist/application/spaces/spaces.hook.js +10 -3
  21. package/dist/auth/act-as.context.d.ts +15 -32
  22. package/dist/auth/act-as.context.d.ts.map +1 -1
  23. package/dist/auth/act-as.context.js +22 -66
  24. package/dist/auth/auth.hook.d.ts +1 -0
  25. package/dist/auth/auth.hook.d.ts.map +1 -1
  26. package/dist/auth/auth.hook.js +2 -22
  27. package/dist/auth/can.hook.d.ts +10 -38
  28. package/dist/auth/can.hook.d.ts.map +1 -1
  29. package/dist/auth/can.hook.js +35 -110
  30. package/dist/auth/index.d.ts +1 -0
  31. package/dist/auth/index.d.ts.map +1 -1
  32. package/dist/auth/self.hook.d.ts +2 -2
  33. package/dist/auth/self.hook.d.ts.map +1 -1
  34. package/dist/auth/self.hook.js +0 -7
  35. package/dist/auth/session-kernel.d.ts +2 -3
  36. package/dist/auth/session-kernel.d.ts.map +1 -1
  37. package/dist/auth/session-kernel.js +4 -13
  38. package/dist/components/boundaries.d.ts +47 -0
  39. package/dist/components/boundaries.d.ts.map +1 -0
  40. package/dist/components/boundaries.js +55 -0
  41. package/dist/components/can.d.ts +11 -0
  42. package/dist/components/can.d.ts.map +1 -0
  43. package/dist/components/can.js +5 -0
  44. package/dist/components/index.d.ts +7 -0
  45. package/dist/components/index.d.ts.map +1 -0
  46. package/dist/components/index.js +4 -0
  47. package/dist/components/mount-domain-frontend.d.ts +22 -0
  48. package/dist/components/mount-domain-frontend.d.ts.map +1 -0
  49. package/dist/components/mount-domain-frontend.js +85 -0
  50. package/dist/components/view-app.d.ts +16 -0
  51. package/dist/components/view-app.d.ts.map +1 -0
  52. package/dist/components/view-app.js +16 -0
  53. package/dist/graph/call/call.hook.d.ts +8 -18
  54. package/dist/graph/call/call.hook.d.ts.map +1 -1
  55. package/dist/graph/call/call.hook.js +7 -13
  56. package/dist/graph/call/stream.hook.d.ts +12 -20
  57. package/dist/graph/call/stream.hook.d.ts.map +1 -1
  58. package/dist/graph/call/stream.hook.js +55 -111
  59. package/dist/graph/graph.context.d.ts +4 -3
  60. package/dist/graph/graph.context.d.ts.map +1 -1
  61. package/dist/graph/graph.context.js +2 -8
  62. package/dist/graph/guards.error.d.ts +7 -8
  63. package/dist/graph/guards.error.d.ts.map +1 -1
  64. package/dist/graph/guards.error.js +11 -7
  65. package/dist/graph/index.d.ts +8 -16
  66. package/dist/graph/index.d.ts.map +1 -1
  67. package/dist/graph/index.js +3 -7
  68. package/dist/graph/memory/read/envelope.d.ts +22 -54
  69. package/dist/graph/memory/read/envelope.d.ts.map +1 -1
  70. package/dist/graph/memory/read/envelope.js +26 -20
  71. package/dist/graph/memory/read/idle.d.ts +1 -15
  72. package/dist/graph/memory/read/idle.d.ts.map +1 -1
  73. package/dist/graph/memory/read/idle.js +4 -13
  74. package/dist/graph/query-definition.hook.d.ts +11 -0
  75. package/dist/graph/query-definition.hook.d.ts.map +1 -0
  76. package/dist/graph/query-definition.hook.js +205 -0
  77. package/dist/graph/resource.d.ts +34 -0
  78. package/dist/graph/resource.d.ts.map +1 -0
  79. package/dist/graph/resource.js +244 -0
  80. package/dist/graph/store.d.ts +25 -0
  81. package/dist/graph/store.d.ts.map +1 -0
  82. package/dist/graph/store.js +69 -0
  83. package/dist/host/actions.hook.d.ts +34 -0
  84. package/dist/host/actions.hook.d.ts.map +1 -0
  85. package/dist/host/actions.hook.js +125 -0
  86. package/dist/host/components.d.ts +15 -0
  87. package/dist/host/components.d.ts.map +1 -0
  88. package/dist/host/components.js +14 -0
  89. package/dist/host/index.d.ts +5 -0
  90. package/dist/host/index.d.ts.map +1 -0
  91. package/dist/host/index.js +3 -0
  92. package/dist/index.d.ts +26 -37
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +20 -31
  95. package/dist/intents/envelope.d.ts +7 -19
  96. package/dist/intents/envelope.d.ts.map +1 -1
  97. package/dist/intents/envelope.js +2 -3
  98. package/dist/intents/send-intent.hook.d.ts +5 -7
  99. package/dist/intents/send-intent.hook.d.ts.map +1 -1
  100. package/dist/intents/send-intent.hook.js +17 -75
  101. package/dist/navigation/adapters/adapter.d.ts +5 -0
  102. package/dist/navigation/adapters/adapter.d.ts.map +1 -1
  103. package/dist/navigation/adapters/memory.adapter.d.ts.map +1 -1
  104. package/dist/navigation/adapters/memory.adapter.js +4 -0
  105. package/dist/navigation/adapters/url.adapter.d.ts.map +1 -1
  106. package/dist/navigation/adapters/url.adapter.js +57 -10
  107. package/dist/navigation/codec.d.ts +60 -0
  108. package/dist/navigation/codec.d.ts.map +1 -0
  109. package/dist/navigation/codec.js +121 -0
  110. package/dist/navigation/history.d.ts +11 -9
  111. package/dist/navigation/history.d.ts.map +1 -1
  112. package/dist/navigation/history.js +6 -8
  113. package/dist/navigation/hooks/ancestors.hook.d.ts +5 -7
  114. package/dist/navigation/hooks/ancestors.hook.d.ts.map +1 -1
  115. package/dist/navigation/hooks/ancestors.hook.js +7 -51
  116. package/dist/navigation/hooks/location.hook.d.ts.map +1 -1
  117. package/dist/navigation/hooks/location.hook.js +4 -6
  118. package/dist/navigation/hooks/scope.hook.d.ts.map +1 -1
  119. package/dist/navigation/hooks/scope.hook.js +4 -6
  120. package/dist/navigation/index.d.ts +4 -2
  121. package/dist/navigation/index.d.ts.map +1 -1
  122. package/dist/navigation/index.js +2 -1
  123. package/dist/navigation/link.d.ts +12 -0
  124. package/dist/navigation/link.d.ts.map +1 -1
  125. package/dist/navigation/link.js +13 -2
  126. package/dist/navigation/location.d.ts +13 -40
  127. package/dist/navigation/location.d.ts.map +1 -1
  128. package/dist/navigation/location.js +14 -76
  129. package/dist/navigation/scope.context.d.ts +17 -26
  130. package/dist/navigation/scope.context.d.ts.map +1 -1
  131. package/dist/navigation/scope.context.js +140 -108
  132. package/dist/navigation/snapshot.d.ts +14 -19
  133. package/dist/navigation/snapshot.d.ts.map +1 -1
  134. package/dist/navigation/snapshot.js +48 -44
  135. package/dist/schema/action.hook.d.ts +44 -0
  136. package/dist/schema/action.hook.d.ts.map +1 -0
  137. package/dist/schema/action.hook.js +72 -0
  138. package/dist/schema/domain.hook.d.ts +12 -7
  139. package/dist/schema/domain.hook.d.ts.map +1 -1
  140. package/dist/schema/domain.hook.js +101 -4
  141. package/dist/schema/index.d.ts +9 -8
  142. package/dist/schema/index.d.ts.map +1 -1
  143. package/dist/schema/index.js +5 -7
  144. package/dist/schema/kernel.hook.d.ts +4 -9
  145. package/dist/schema/kernel.hook.d.ts.map +1 -1
  146. package/dist/schema/kernel.hook.js +4 -6
  147. package/dist/schema/mutation.hook.d.ts +9 -0
  148. package/dist/schema/mutation.hook.d.ts.map +1 -0
  149. package/dist/schema/mutation.hook.js +41 -0
  150. package/dist/schema/object.hook.d.ts +16 -0
  151. package/dist/schema/object.hook.d.ts.map +1 -0
  152. package/dist/schema/object.hook.js +46 -0
  153. package/dist/schema/relation.hook.d.ts +46 -0
  154. package/dist/schema/relation.hook.d.ts.map +1 -0
  155. package/dist/schema/relation.hook.js +136 -0
  156. package/dist/selection/live.d.ts +9 -17
  157. package/dist/selection/live.d.ts.map +1 -1
  158. package/dist/selection/live.js +65 -112
  159. package/dist/selection/scope.context.js +5 -4
  160. package/dist/session/boot/boot.d.ts +1 -13
  161. package/dist/session/boot/boot.d.ts.map +1 -1
  162. package/dist/session/boot/boot.js +9 -27
  163. package/dist/session/index.d.ts +1 -0
  164. package/dist/session/index.d.ts.map +1 -1
  165. package/dist/session/kernel-instance.hook.d.ts +1 -4
  166. package/dist/session/kernel-instance.hook.d.ts.map +1 -1
  167. package/dist/session/kernel-instance.hook.js +1 -5
  168. package/dist/session/provider.d.ts +9 -23
  169. package/dist/session/provider.d.ts.map +1 -1
  170. package/dist/session/provider.js +29 -75
  171. package/dist/session/session.context.d.ts +5 -0
  172. package/dist/session/session.context.d.ts.map +1 -1
  173. package/dist/window/adopt.d.ts +0 -1
  174. package/dist/window/adopt.d.ts.map +1 -1
  175. package/dist/window/hooks/target-node.hook.d.ts +10 -6
  176. package/dist/window/hooks/target-node.hook.d.ts.map +1 -1
  177. package/dist/window/hooks/target-node.hook.js +14 -72
  178. package/dist/window/hooks/views-for.hook.d.ts +4 -3
  179. package/dist/window/hooks/views-for.hook.d.ts.map +1 -1
  180. package/dist/window/hooks/views-for.hook.js +6 -22
  181. package/dist/window/hooks/windows.hook.d.ts.map +1 -1
  182. package/dist/window/hooks/windows.hook.js +3 -4
  183. package/dist/window/host.d.ts +11 -11
  184. package/dist/window/host.d.ts.map +1 -1
  185. package/dist/window/host.js +38 -54
  186. package/dist/window/hub.d.ts +2 -0
  187. package/dist/window/hub.d.ts.map +1 -1
  188. package/dist/window/hub.js +33 -0
  189. package/dist/window/index.d.ts +3 -3
  190. package/dist/window/index.d.ts.map +1 -1
  191. package/dist/window/index.js +2 -2
  192. package/dist/window/lifecycle/mount.d.ts +5 -44
  193. package/dist/window/lifecycle/mount.d.ts.map +1 -1
  194. package/dist/window/lifecycle/mount.js +14 -120
  195. package/dist/window/lifecycle/states.d.ts +0 -5
  196. package/dist/window/lifecycle/states.d.ts.map +1 -1
  197. package/dist/window/stage.d.ts.map +1 -1
  198. package/dist/window/stage.js +1 -0
  199. package/package.json +9 -8
  200. package/src/application/apps/app-host.tsx +17 -18
  201. package/src/application/apps/apps.hook.ts +13 -6
  202. package/src/application/apps/domains.hook.ts +1 -1
  203. package/src/application/apps/open-app.hook.ts +3 -4
  204. package/src/application/index.ts +4 -6
  205. package/src/application/spaces/space.hook.ts +5 -6
  206. package/src/application/spaces/spaces.hook.ts +14 -9
  207. package/src/auth/act-as.context.tsx +37 -91
  208. package/src/auth/auth.hook.ts +2 -30
  209. package/src/auth/can.hook.ts +44 -135
  210. package/src/auth/index.ts +1 -0
  211. package/src/auth/self.hook.ts +2 -9
  212. package/src/auth/session-kernel.ts +6 -16
  213. package/src/components/boundaries.tsx +113 -0
  214. package/src/components/can.tsx +16 -0
  215. package/src/components/index.ts +14 -0
  216. package/src/components/mount-domain-frontend.tsx +158 -0
  217. package/src/components/view-app.tsx +49 -0
  218. package/src/graph/call/call.hook.ts +22 -27
  219. package/src/graph/call/stream.hook.ts +66 -143
  220. package/src/graph/graph.context.ts +6 -11
  221. package/src/graph/guards.error.ts +17 -55
  222. package/src/graph/index.ts +23 -17
  223. package/src/graph/memory/read/envelope.ts +40 -65
  224. package/src/graph/memory/read/idle.ts +5 -18
  225. package/src/graph/query-definition.hook.ts +270 -0
  226. package/src/graph/resource.ts +358 -0
  227. package/src/graph/store.ts +97 -0
  228. package/src/host/actions.hook.ts +165 -0
  229. package/src/host/components.tsx +29 -0
  230. package/src/host/index.ts +17 -0
  231. package/src/index.ts +137 -123
  232. package/src/intents/envelope.ts +7 -23
  233. package/src/intents/send-intent.hook.ts +24 -79
  234. package/src/navigation/adapters/adapter.ts +3 -0
  235. package/src/navigation/adapters/memory.adapter.ts +4 -0
  236. package/src/navigation/adapters/url.adapter.ts +48 -8
  237. package/src/navigation/codec.ts +190 -0
  238. package/src/navigation/history.ts +20 -11
  239. package/src/navigation/hooks/ancestors.hook.ts +10 -60
  240. package/src/navigation/hooks/location.hook.ts +4 -6
  241. package/src/navigation/hooks/scope.hook.ts +4 -6
  242. package/src/navigation/index.ts +18 -2
  243. package/src/navigation/link.tsx +27 -2
  244. package/src/navigation/location.ts +26 -93
  245. package/src/navigation/scope.context.tsx +151 -136
  246. package/src/navigation/snapshot.ts +69 -50
  247. package/src/schema/action.hook.ts +146 -0
  248. package/src/schema/domain.hook.ts +127 -11
  249. package/src/schema/index.ts +9 -30
  250. package/src/schema/kernel.hook.ts +6 -13
  251. package/src/schema/mutation.hook.ts +63 -0
  252. package/src/schema/object.hook.ts +85 -0
  253. package/src/schema/relation.hook.ts +253 -0
  254. package/src/selection/live.ts +75 -125
  255. package/src/selection/scope.context.tsx +4 -4
  256. package/src/session/boot/boot.ts +8 -33
  257. package/src/session/index.ts +1 -0
  258. package/src/session/kernel-instance.hook.ts +2 -11
  259. package/src/session/provider.tsx +34 -103
  260. package/src/session/session.context.ts +6 -0
  261. package/src/window/adopt.ts +0 -1
  262. package/src/window/hooks/target-node.hook.ts +20 -85
  263. package/src/window/hooks/views-for.hook.ts +12 -25
  264. package/src/window/hooks/windows.hook.ts +3 -4
  265. package/src/window/host.tsx +51 -68
  266. package/src/window/hub.ts +33 -0
  267. package/src/window/index.ts +4 -4
  268. package/src/window/lifecycle/mount.ts +15 -160
  269. package/src/window/lifecycle/states.ts +1 -5
  270. package/src/window/stage.tsx +1 -0
  271. package/dist/application/roles.hook.d.ts +0 -7
  272. package/dist/application/roles.hook.d.ts.map +0 -1
  273. package/dist/application/roles.hook.js +0 -23
  274. package/dist/graph/memory/base.d.ts +0 -21
  275. package/dist/graph/memory/base.d.ts.map +0 -1
  276. package/dist/graph/memory/base.js +0 -33
  277. package/dist/graph/memory/read/read.d.ts +0 -9
  278. package/dist/graph/memory/read/read.d.ts.map +0 -1
  279. package/dist/graph/memory/read/read.js +0 -87
  280. package/dist/graph/memory/scope.d.ts +0 -7
  281. package/dist/graph/memory/scope.d.ts.map +0 -1
  282. package/dist/graph/memory/scope.js +0 -25
  283. package/dist/graph/mutate/move.hook.d.ts +0 -7
  284. package/dist/graph/mutate/move.hook.d.ts.map +0 -1
  285. package/dist/graph/mutate/move.hook.js +0 -49
  286. package/dist/graph/mutate/mutate.hook.d.ts +0 -17
  287. package/dist/graph/mutate/mutate.hook.d.ts.map +0 -1
  288. package/dist/graph/mutate/mutate.hook.js +0 -45
  289. package/dist/graph/query/children.hook.d.ts +0 -29
  290. package/dist/graph/query/children.hook.d.ts.map +0 -1
  291. package/dist/graph/query/children.hook.js +0 -64
  292. package/dist/graph/query/graph.hook.d.ts +0 -9
  293. package/dist/graph/query/graph.hook.d.ts.map +0 -1
  294. package/dist/graph/query/graph.hook.js +0 -5
  295. package/dist/graph/query/neighbors.hook.d.ts +0 -7
  296. package/dist/graph/query/neighbors.hook.d.ts.map +0 -1
  297. package/dist/graph/query/neighbors.hook.js +0 -104
  298. package/dist/graph/query/node.hook.d.ts +0 -14
  299. package/dist/graph/query/node.hook.d.ts.map +0 -1
  300. package/dist/graph/query/node.hook.js +0 -43
  301. package/dist/graph/query/query.hook.d.ts +0 -14
  302. package/dist/graph/query/query.hook.d.ts.map +0 -1
  303. package/dist/graph/query/query.hook.js +0 -23
  304. package/dist/schema/bind-read.d.ts +0 -37
  305. package/dist/schema/bind-read.d.ts.map +0 -1
  306. package/dist/schema/bind-read.js +0 -205
  307. package/dist/schema/binding.d.ts +0 -21
  308. package/dist/schema/binding.d.ts.map +0 -1
  309. package/dist/schema/binding.js +0 -99
  310. package/dist/schema/function.hook.d.ts +0 -9
  311. package/dist/schema/function.hook.d.ts.map +0 -1
  312. package/dist/schema/function.hook.js +0 -40
  313. package/dist/schema/method.hook.d.ts +0 -14
  314. package/dist/schema/method.hook.d.ts.map +0 -1
  315. package/dist/schema/method.hook.js +0 -49
  316. package/dist/schema/tag.d.ts +0 -11
  317. package/dist/schema/tag.d.ts.map +0 -1
  318. package/dist/schema/tag.js +0 -11
  319. package/dist/schema/types.d.ts +0 -73
  320. package/dist/schema/types.d.ts.map +0 -1
  321. package/dist/schema/types.js +0 -1
  322. package/dist/schema/write.d.ts +0 -16
  323. package/dist/schema/write.d.ts.map +0 -1
  324. package/dist/schema/write.js +0 -56
  325. package/src/application/roles.hook.ts +0 -41
  326. package/src/graph/memory/base.ts +0 -50
  327. package/src/graph/memory/read/read.ts +0 -103
  328. package/src/graph/memory/scope.ts +0 -29
  329. package/src/graph/mutate/move.hook.ts +0 -70
  330. package/src/graph/mutate/mutate.hook.ts +0 -69
  331. package/src/graph/query/children.hook.ts +0 -136
  332. package/src/graph/query/graph.hook.ts +0 -13
  333. package/src/graph/query/neighbors.hook.ts +0 -159
  334. package/src/graph/query/node.hook.ts +0 -83
  335. package/src/graph/query/query.hook.ts +0 -42
  336. package/src/schema/bind-read.ts +0 -319
  337. package/src/schema/binding.ts +0 -146
  338. package/src/schema/function.hook.ts +0 -68
  339. package/src/schema/method.hook.ts +0 -103
  340. package/src/schema/tag.ts +0 -19
  341. package/src/schema/types.ts +0 -150
  342. 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
- * useCall (API §7) — unary domain-function dispatch over `kernel.call`, in the
3
- * Action envelope. No result caching, no liveness — domain function reads are not
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
- /** Per-call timeout (ms), forwarded to the kernel dispatch. */
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
- * Unary domain-function dispatch as an {@link Action}. `method` is a typed FnMap
23
- * `call` path or any string (the untyped floor); `null` yields an idle action
24
- * that rejects if invoked. Params/result are typed from the FnMap entry.
25
- */
26
- export function useCall<K extends CallFns<FnMap>>(
27
- method: K | null,
28
- opts?: CallOptions,
29
- ): Action<[FnMap[K]['params']], FnMap[K]['result']> {
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
- // A fresh closure per render captures the current method/opts; `useAction`
32
- // keeps ONE stable callable and always dispatches this latest closure.
33
- return useAction(async (params: FnMap[K]['params']): Promise<FnMap[K]['result']> => {
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(method, params, { timeout: opts?.timeout })
31
+ return await kernel.call(call, options)
37
32
  } catch (error) {
38
- opts?.onError?.(error)
33
+ options?.onError?.(error)
39
34
  throw error
40
35
  }
41
36
  })