@astrale-os/shell-react 0.2.9 → 0.2.10-beta.3

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 (335) hide show
  1. package/dist/application/apps/app-host.d.ts +4 -15
  2. package/dist/application/apps/app-host.d.ts.map +1 -1
  3. package/dist/application/apps/app-host.js +1 -1
  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/astrale.d.ts +14 -0
  39. package/dist/components/astrale.d.ts.map +1 -0
  40. package/dist/components/astrale.js +12 -0
  41. package/dist/components/boundaries.d.ts +47 -0
  42. package/dist/components/boundaries.d.ts.map +1 -0
  43. package/dist/components/boundaries.js +55 -0
  44. package/dist/components/can.d.ts +11 -0
  45. package/dist/components/can.d.ts.map +1 -0
  46. package/dist/components/can.js +5 -0
  47. package/dist/components/index.d.ts +6 -0
  48. package/dist/components/index.d.ts.map +1 -0
  49. package/dist/components/index.js +3 -0
  50. package/dist/graph/call/call.hook.d.ts +8 -18
  51. package/dist/graph/call/call.hook.d.ts.map +1 -1
  52. package/dist/graph/call/call.hook.js +7 -13
  53. package/dist/graph/call/stream.hook.d.ts +12 -20
  54. package/dist/graph/call/stream.hook.d.ts.map +1 -1
  55. package/dist/graph/call/stream.hook.js +55 -111
  56. package/dist/graph/graph.context.d.ts +4 -3
  57. package/dist/graph/graph.context.d.ts.map +1 -1
  58. package/dist/graph/graph.context.js +2 -8
  59. package/dist/graph/guards.error.d.ts +7 -8
  60. package/dist/graph/guards.error.d.ts.map +1 -1
  61. package/dist/graph/guards.error.js +11 -7
  62. package/dist/graph/index.d.ts +8 -16
  63. package/dist/graph/index.d.ts.map +1 -1
  64. package/dist/graph/index.js +3 -7
  65. package/dist/graph/memory/read/envelope.d.ts +22 -54
  66. package/dist/graph/memory/read/envelope.d.ts.map +1 -1
  67. package/dist/graph/memory/read/envelope.js +26 -20
  68. package/dist/graph/memory/read/idle.d.ts +1 -15
  69. package/dist/graph/memory/read/idle.d.ts.map +1 -1
  70. package/dist/graph/memory/read/idle.js +4 -13
  71. package/dist/graph/query-definition.hook.d.ts +11 -0
  72. package/dist/graph/query-definition.hook.d.ts.map +1 -0
  73. package/dist/graph/query-definition.hook.js +205 -0
  74. package/dist/graph/resource.d.ts +34 -0
  75. package/dist/graph/resource.d.ts.map +1 -0
  76. package/dist/graph/resource.js +244 -0
  77. package/dist/graph/store.d.ts +25 -0
  78. package/dist/graph/store.d.ts.map +1 -0
  79. package/dist/graph/store.js +69 -0
  80. package/dist/host/actions.hook.d.ts +34 -0
  81. package/dist/host/actions.hook.d.ts.map +1 -0
  82. package/dist/host/actions.hook.js +125 -0
  83. package/dist/host/components.d.ts +15 -0
  84. package/dist/host/components.d.ts.map +1 -0
  85. package/dist/host/components.js +14 -0
  86. package/dist/host/index.d.ts +5 -0
  87. package/dist/host/index.d.ts.map +1 -0
  88. package/dist/host/index.js +3 -0
  89. package/dist/index.d.ts +26 -37
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +20 -31
  92. package/dist/intents/envelope.d.ts +7 -19
  93. package/dist/intents/envelope.d.ts.map +1 -1
  94. package/dist/intents/envelope.js +2 -3
  95. package/dist/intents/send-intent.hook.d.ts +5 -7
  96. package/dist/intents/send-intent.hook.d.ts.map +1 -1
  97. package/dist/intents/send-intent.hook.js +17 -75
  98. package/dist/navigation/adapters/adapter.d.ts +5 -0
  99. package/dist/navigation/adapters/adapter.d.ts.map +1 -1
  100. package/dist/navigation/adapters/memory.adapter.d.ts.map +1 -1
  101. package/dist/navigation/adapters/memory.adapter.js +4 -0
  102. package/dist/navigation/adapters/url.adapter.d.ts.map +1 -1
  103. package/dist/navigation/adapters/url.adapter.js +57 -10
  104. package/dist/navigation/codec.d.ts +60 -0
  105. package/dist/navigation/codec.d.ts.map +1 -0
  106. package/dist/navigation/codec.js +121 -0
  107. package/dist/navigation/history.d.ts +11 -9
  108. package/dist/navigation/history.d.ts.map +1 -1
  109. package/dist/navigation/history.js +6 -8
  110. package/dist/navigation/hooks/ancestors.hook.d.ts +5 -7
  111. package/dist/navigation/hooks/ancestors.hook.d.ts.map +1 -1
  112. package/dist/navigation/hooks/ancestors.hook.js +7 -51
  113. package/dist/navigation/hooks/location.hook.d.ts.map +1 -1
  114. package/dist/navigation/hooks/location.hook.js +4 -6
  115. package/dist/navigation/hooks/scope.hook.d.ts.map +1 -1
  116. package/dist/navigation/hooks/scope.hook.js +4 -6
  117. package/dist/navigation/index.d.ts +4 -2
  118. package/dist/navigation/index.d.ts.map +1 -1
  119. package/dist/navigation/index.js +2 -1
  120. package/dist/navigation/link.d.ts +12 -0
  121. package/dist/navigation/link.d.ts.map +1 -1
  122. package/dist/navigation/link.js +13 -2
  123. package/dist/navigation/location.d.ts +13 -40
  124. package/dist/navigation/location.d.ts.map +1 -1
  125. package/dist/navigation/location.js +14 -76
  126. package/dist/navigation/scope.context.d.ts +17 -26
  127. package/dist/navigation/scope.context.d.ts.map +1 -1
  128. package/dist/navigation/scope.context.js +140 -108
  129. package/dist/navigation/snapshot.d.ts +14 -19
  130. package/dist/navigation/snapshot.d.ts.map +1 -1
  131. package/dist/navigation/snapshot.js +48 -44
  132. package/dist/schema/action.hook.d.ts +44 -0
  133. package/dist/schema/action.hook.d.ts.map +1 -0
  134. package/dist/schema/action.hook.js +72 -0
  135. package/dist/schema/domain.hook.d.ts +12 -7
  136. package/dist/schema/domain.hook.d.ts.map +1 -1
  137. package/dist/schema/domain.hook.js +101 -4
  138. package/dist/schema/index.d.ts +9 -8
  139. package/dist/schema/index.d.ts.map +1 -1
  140. package/dist/schema/index.js +5 -7
  141. package/dist/schema/kernel.hook.d.ts +4 -9
  142. package/dist/schema/kernel.hook.d.ts.map +1 -1
  143. package/dist/schema/kernel.hook.js +4 -6
  144. package/dist/schema/mutation.hook.d.ts +9 -0
  145. package/dist/schema/mutation.hook.d.ts.map +1 -0
  146. package/dist/schema/mutation.hook.js +41 -0
  147. package/dist/schema/object.hook.d.ts +16 -0
  148. package/dist/schema/object.hook.d.ts.map +1 -0
  149. package/dist/schema/object.hook.js +46 -0
  150. package/dist/schema/relation.hook.d.ts +46 -0
  151. package/dist/schema/relation.hook.d.ts.map +1 -0
  152. package/dist/schema/relation.hook.js +136 -0
  153. package/dist/selection/live.d.ts +9 -17
  154. package/dist/selection/live.d.ts.map +1 -1
  155. package/dist/selection/live.js +65 -112
  156. package/dist/selection/scope.context.js +5 -4
  157. package/dist/session/boot/boot.d.ts +1 -13
  158. package/dist/session/boot/boot.d.ts.map +1 -1
  159. package/dist/session/boot/boot.js +9 -27
  160. package/dist/session/index.d.ts +1 -0
  161. package/dist/session/index.d.ts.map +1 -1
  162. package/dist/session/kernel-instance.hook.d.ts +1 -4
  163. package/dist/session/kernel-instance.hook.d.ts.map +1 -1
  164. package/dist/session/kernel-instance.hook.js +1 -5
  165. package/dist/session/provider.d.ts +9 -23
  166. package/dist/session/provider.d.ts.map +1 -1
  167. package/dist/session/provider.js +29 -75
  168. package/dist/session/session.context.d.ts +5 -0
  169. package/dist/session/session.context.d.ts.map +1 -1
  170. package/dist/window/adopt.d.ts +0 -1
  171. package/dist/window/adopt.d.ts.map +1 -1
  172. package/dist/window/hooks/target-node.hook.d.ts +10 -6
  173. package/dist/window/hooks/target-node.hook.d.ts.map +1 -1
  174. package/dist/window/hooks/target-node.hook.js +14 -72
  175. package/dist/window/hooks/views-for.hook.d.ts +4 -3
  176. package/dist/window/hooks/views-for.hook.d.ts.map +1 -1
  177. package/dist/window/hooks/views-for.hook.js +6 -22
  178. package/dist/window/hooks/windows.hook.d.ts.map +1 -1
  179. package/dist/window/hooks/windows.hook.js +3 -4
  180. package/dist/window/host.d.ts +9 -11
  181. package/dist/window/host.d.ts.map +1 -1
  182. package/dist/window/host.js +26 -62
  183. package/dist/window/hub.d.ts +2 -0
  184. package/dist/window/hub.d.ts.map +1 -1
  185. package/dist/window/hub.js +33 -0
  186. package/dist/window/index.d.ts +2 -2
  187. package/dist/window/index.d.ts.map +1 -1
  188. package/dist/window/index.js +2 -2
  189. package/dist/window/lifecycle/mount.d.ts +5 -44
  190. package/dist/window/lifecycle/mount.d.ts.map +1 -1
  191. package/dist/window/lifecycle/mount.js +14 -120
  192. package/dist/window/lifecycle/states.d.ts +0 -5
  193. package/dist/window/lifecycle/states.d.ts.map +1 -1
  194. package/package.json +20 -8
  195. package/src/application/apps/app-host.tsx +11 -15
  196. package/src/application/apps/apps.hook.ts +13 -6
  197. package/src/application/apps/domains.hook.ts +1 -1
  198. package/src/application/apps/open-app.hook.ts +3 -4
  199. package/src/application/index.ts +4 -6
  200. package/src/application/spaces/space.hook.ts +5 -6
  201. package/src/application/spaces/spaces.hook.ts +14 -9
  202. package/src/auth/act-as.context.tsx +37 -91
  203. package/src/auth/auth.hook.ts +2 -30
  204. package/src/auth/can.hook.ts +44 -135
  205. package/src/auth/index.ts +1 -0
  206. package/src/auth/self.hook.ts +2 -9
  207. package/src/auth/session-kernel.ts +6 -16
  208. package/src/components/astrale.tsx +40 -0
  209. package/src/components/boundaries.tsx +113 -0
  210. package/src/components/can.tsx +16 -0
  211. package/src/components/index.ts +13 -0
  212. package/src/graph/call/call.hook.ts +22 -27
  213. package/src/graph/call/stream.hook.ts +66 -143
  214. package/src/graph/graph.context.ts +6 -11
  215. package/src/graph/guards.error.ts +17 -55
  216. package/src/graph/index.ts +23 -17
  217. package/src/graph/memory/read/envelope.ts +40 -65
  218. package/src/graph/memory/read/idle.ts +5 -18
  219. package/src/graph/query-definition.hook.ts +270 -0
  220. package/src/graph/resource.ts +358 -0
  221. package/src/graph/store.ts +97 -0
  222. package/src/host/actions.hook.ts +165 -0
  223. package/src/host/components.tsx +29 -0
  224. package/src/host/index.ts +17 -0
  225. package/src/index.ts +131 -124
  226. package/src/intents/envelope.ts +7 -23
  227. package/src/intents/send-intent.hook.ts +24 -79
  228. package/src/navigation/adapters/adapter.ts +3 -0
  229. package/src/navigation/adapters/memory.adapter.ts +4 -0
  230. package/src/navigation/adapters/url.adapter.ts +48 -8
  231. package/src/navigation/codec.ts +190 -0
  232. package/src/navigation/history.ts +20 -11
  233. package/src/navigation/hooks/ancestors.hook.ts +10 -60
  234. package/src/navigation/hooks/location.hook.ts +4 -6
  235. package/src/navigation/hooks/scope.hook.ts +4 -6
  236. package/src/navigation/index.ts +18 -2
  237. package/src/navigation/link.tsx +27 -2
  238. package/src/navigation/location.ts +26 -93
  239. package/src/navigation/scope.context.tsx +151 -136
  240. package/src/navigation/snapshot.ts +69 -50
  241. package/src/schema/action.hook.ts +146 -0
  242. package/src/schema/domain.hook.ts +127 -11
  243. package/src/schema/index.ts +9 -30
  244. package/src/schema/kernel.hook.ts +6 -13
  245. package/src/schema/mutation.hook.ts +63 -0
  246. package/src/schema/object.hook.ts +85 -0
  247. package/src/schema/relation.hook.ts +253 -0
  248. package/src/selection/live.ts +75 -125
  249. package/src/selection/scope.context.tsx +4 -4
  250. package/src/session/boot/boot.ts +8 -33
  251. package/src/session/index.ts +1 -0
  252. package/src/session/kernel-instance.hook.ts +2 -11
  253. package/src/session/provider.tsx +34 -103
  254. package/src/session/session.context.ts +6 -0
  255. package/src/window/adopt.ts +0 -1
  256. package/src/window/hooks/target-node.hook.ts +20 -85
  257. package/src/window/hooks/views-for.hook.ts +12 -25
  258. package/src/window/hooks/windows.hook.ts +3 -4
  259. package/src/window/host.tsx +35 -77
  260. package/src/window/hub.ts +33 -0
  261. package/src/window/index.ts +3 -3
  262. package/src/window/lifecycle/mount.ts +15 -160
  263. package/src/window/lifecycle/states.ts +1 -5
  264. package/dist/application/roles.hook.d.ts +0 -7
  265. package/dist/application/roles.hook.d.ts.map +0 -1
  266. package/dist/application/roles.hook.js +0 -23
  267. package/dist/graph/memory/base.d.ts +0 -21
  268. package/dist/graph/memory/base.d.ts.map +0 -1
  269. package/dist/graph/memory/base.js +0 -33
  270. package/dist/graph/memory/read/read.d.ts +0 -9
  271. package/dist/graph/memory/read/read.d.ts.map +0 -1
  272. package/dist/graph/memory/read/read.js +0 -87
  273. package/dist/graph/memory/scope.d.ts +0 -7
  274. package/dist/graph/memory/scope.d.ts.map +0 -1
  275. package/dist/graph/memory/scope.js +0 -25
  276. package/dist/graph/mutate/move.hook.d.ts +0 -7
  277. package/dist/graph/mutate/move.hook.d.ts.map +0 -1
  278. package/dist/graph/mutate/move.hook.js +0 -49
  279. package/dist/graph/mutate/mutate.hook.d.ts +0 -17
  280. package/dist/graph/mutate/mutate.hook.d.ts.map +0 -1
  281. package/dist/graph/mutate/mutate.hook.js +0 -45
  282. package/dist/graph/query/children.hook.d.ts +0 -29
  283. package/dist/graph/query/children.hook.d.ts.map +0 -1
  284. package/dist/graph/query/children.hook.js +0 -64
  285. package/dist/graph/query/graph.hook.d.ts +0 -9
  286. package/dist/graph/query/graph.hook.d.ts.map +0 -1
  287. package/dist/graph/query/graph.hook.js +0 -5
  288. package/dist/graph/query/neighbors.hook.d.ts +0 -7
  289. package/dist/graph/query/neighbors.hook.d.ts.map +0 -1
  290. package/dist/graph/query/neighbors.hook.js +0 -104
  291. package/dist/graph/query/node.hook.d.ts +0 -14
  292. package/dist/graph/query/node.hook.d.ts.map +0 -1
  293. package/dist/graph/query/node.hook.js +0 -43
  294. package/dist/graph/query/query.hook.d.ts +0 -14
  295. package/dist/graph/query/query.hook.d.ts.map +0 -1
  296. package/dist/graph/query/query.hook.js +0 -23
  297. package/dist/schema/bind-read.d.ts +0 -37
  298. package/dist/schema/bind-read.d.ts.map +0 -1
  299. package/dist/schema/bind-read.js +0 -205
  300. package/dist/schema/binding.d.ts +0 -21
  301. package/dist/schema/binding.d.ts.map +0 -1
  302. package/dist/schema/binding.js +0 -99
  303. package/dist/schema/function.hook.d.ts +0 -9
  304. package/dist/schema/function.hook.d.ts.map +0 -1
  305. package/dist/schema/function.hook.js +0 -40
  306. package/dist/schema/method.hook.d.ts +0 -14
  307. package/dist/schema/method.hook.d.ts.map +0 -1
  308. package/dist/schema/method.hook.js +0 -49
  309. package/dist/schema/tag.d.ts +0 -11
  310. package/dist/schema/tag.d.ts.map +0 -1
  311. package/dist/schema/tag.js +0 -11
  312. package/dist/schema/types.d.ts +0 -73
  313. package/dist/schema/types.d.ts.map +0 -1
  314. package/dist/schema/types.js +0 -1
  315. package/dist/schema/write.d.ts +0 -16
  316. package/dist/schema/write.d.ts.map +0 -1
  317. package/dist/schema/write.js +0 -56
  318. package/src/application/roles.hook.ts +0 -41
  319. package/src/graph/memory/base.ts +0 -50
  320. package/src/graph/memory/read/read.ts +0 -103
  321. package/src/graph/memory/scope.ts +0 -29
  322. package/src/graph/mutate/move.hook.ts +0 -70
  323. package/src/graph/mutate/mutate.hook.ts +0 -69
  324. package/src/graph/query/children.hook.ts +0 -136
  325. package/src/graph/query/graph.hook.ts +0 -13
  326. package/src/graph/query/neighbors.hook.ts +0 -159
  327. package/src/graph/query/node.hook.ts +0 -83
  328. package/src/graph/query/query.hook.ts +0 -42
  329. package/src/schema/bind-read.ts +0 -319
  330. package/src/schema/binding.ts +0 -146
  331. package/src/schema/function.hook.ts +0 -68
  332. package/src/schema/method.hook.ts +0 -103
  333. package/src/schema/tag.ts +0 -19
  334. package/src/schema/types.ts +0 -150
  335. package/src/schema/write.ts +0 -96
@@ -1,94 +1,54 @@
1
- import type { MemoryEvent } from '@astrale-os/kernel-client/store'
2
1
  import type { Shell, ShellConfig } from '@astrale-os/shell'
3
2
  import type { ReactElement, ReactNode } from 'react'
4
3
 
5
4
  import { createShell } from '@astrale-os/shell'
6
- /**
7
- * ShellProvider — the composition root (API §2, SPEC §5): owns the shell boot
8
- * state machine (a config OR a prebuilt Shell; `createShell → init → whoami →
9
- * ready`), seats `ShellContext` and `GraphContext`, and registers its boot-retry
10
- * into the per-shell connection store. One live shell per mounted provider,
11
- * StrictMode/HMR-safe (deferred teardown), keyed on config identity. Children
12
- * render only at `ready`, so useShell/useSelf are synchronous below.
13
- */
14
- import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
5
+ import { useCallback, useEffect, useReducer, useRef, useState } from 'react'
15
6
 
16
- import type { GraphMemory } from '../graph/graph.context.js'
17
7
  import type { BootRef } from './boot/ownership.js'
18
8
  import type { SelfNode } from './session.context.js'
19
9
 
20
10
  import { primeUser } from '../application/user/user.store.js'
21
11
  import { GraphContext } from '../graph/graph.context.js'
22
- import { baseMemoryFor, setMemoryErrorSink } from '../graph/memory/base.js'
12
+ import { resolveGraphStoreScope, retainStore, storeFor } from '../graph/store.js'
23
13
  import { configIdentity, isShellConfig, resolveSelf } from './boot/boot.js'
24
14
  import { cancelTeardown, disposeShell, scheduleTeardown } from './boot/ownership.js'
25
15
  import { bootReducer, INITIAL_BOOT } from './boot/status.js'
26
16
  import { connectionStore } from './connection/connection.store.js'
27
17
  import { ShellContext } from './session.context.js'
28
18
 
29
- /** Global error channel payload (API §2). */
30
- export type ShellError =
31
- | { kind: 'refresh'; shape: unknown; error: unknown }
32
- | { kind: 'mutation'; mutationId: string; error: unknown }
33
- | { kind: 'connection'; error: unknown }
19
+ export type ShellError = { readonly kind: 'connection'; readonly error: unknown }
34
20
 
35
21
  export interface ShellProviderProps {
36
- /**
37
- * Shell config — or a prebuilt Shell (tests, storybook). Keyed by identity: a
38
- * config change disposes and reboots.
39
- */
40
- config: ShellConfig | Shell
41
- /** Rendered until status === 'ready'. Default: null. */
42
- fallback?: ReactNode
43
- /** Rendered when boot fails; `retry` re-runs init. Default: rethrow to the nearest boundary. */
44
- errorFallback?: (error: unknown, retry: () => void) => ReactNode
45
- /** Global error channel: warm-read refresh failures, rolled-back mutations, watch drops. */
46
- onError?: (error: ShellError) => void
47
- children: ReactNode
48
- }
49
-
50
- /**
51
- * Map a memory event to the documented `ShellError` union (API §2). `refresh`
52
- * (warm-read revalidation failure) and `mutation` (rolled-back write) are the
53
- * two the channel promises; `divergence`/`invariant` are Tier-0/dev tripwires
54
- * outside the union and are not bridged here.
55
- */
56
- function toShellError(event: MemoryEvent): ShellError | null {
57
- switch (event.kind) {
58
- case 'refresh':
59
- return { kind: 'refresh', shape: event.shape, error: event.error }
60
- case 'mutation':
61
- return { kind: 'mutation', mutationId: event.mutationId, error: event.error }
62
- default:
63
- return null
64
- }
22
+ /** A Shell configuration or an already-constructed Shell capability. */
23
+ readonly config: ShellConfig | Shell
24
+ readonly fallback?: ReactNode
25
+ readonly errorFallback?: (error: unknown, retry: () => void) => ReactNode
26
+ readonly onError?: (error: ShellError) => void
27
+ readonly children: ReactNode
65
28
  }
66
29
 
30
+ /** Own and initialize exactly one Shell for a React subtree. */
67
31
  export function ShellProvider(props: ShellProviderProps): ReactElement | null {
68
32
  const { config, fallback, errorFallback, onError, children } = props
69
-
70
33
  const [state, dispatch] = useReducer(bootReducer, INITIAL_BOOT)
71
34
  const [attempt, setAttempt] = useState(0)
72
-
73
35
  const bootRef = useRef<BootRef>({ key: null, shell: null, generation: 0 })
74
36
  const configRef = useRef(config)
75
37
  configRef.current = config
76
38
  const onErrorRef = useRef(onError)
77
39
  onErrorRef.current = onError
78
-
79
40
  const configKey = configIdentity(config)
80
41
 
81
42
  const startBoot = useCallback((ref: BootRef) => {
82
43
  const generation = (ref.generation += 1)
83
44
  dispatch({ type: 'connecting' })
84
-
85
45
  let shell: Shell
86
- const current = configRef.current
87
46
  try {
47
+ const current = configRef.current
88
48
  shell = isShellConfig(current) ? createShell(current) : current
89
- } catch (err) {
90
- dispatch({ type: 'error', error: err })
91
- onErrorRef.current?.({ kind: 'connection', error: err })
49
+ } catch (error) {
50
+ dispatch({ type: 'error', error })
51
+ onErrorRef.current?.({ kind: 'connection', error })
92
52
  return
93
53
  }
94
54
  ref.shell = shell
@@ -99,33 +59,29 @@ export function ShellProvider(props: ShellProviderProps): ReactElement | null {
99
59
  if (generation !== ref.generation) return
100
60
  const self: SelfNode | null = await resolveSelf(shell)
101
61
  if (generation !== ref.generation) return
102
- // Pre-resolve the OS-plane user binding (API §2/§12.2) — NON-FATAL: a
103
- // missing OS plane marks it unavailable without failing boot (whoami above
104
- // stays the fatal gate). Seeds the user store so `useUser` is synchronous.
105
62
  await primeUser(shell)
106
63
  if (generation !== ref.generation) return
107
- dispatch({ type: 'ready', runtime: { shell, self } })
108
- } catch (err) {
64
+ const storeScope = await resolveGraphStoreScope(shell, self)
109
65
  if (generation !== ref.generation) return
110
- dispatch({ type: 'error', error: err })
111
- onErrorRef.current?.({ kind: 'connection', error: err })
66
+ const graph = storeFor(shell, storeScope)
67
+ dispatch({
68
+ type: 'ready',
69
+ runtime: { shell, self, storeScope, store: graph.store, ownsStore: graph.owned },
70
+ })
71
+ } catch (error) {
72
+ if (generation !== ref.generation) return
73
+ dispatch({ type: 'error', error })
74
+ onErrorRef.current?.({ kind: 'connection', error })
112
75
  }
113
76
  })()
114
77
  }, [])
115
78
 
116
79
  useEffect(() => {
117
80
  const ref = bootRef.current
118
- // The shell this provider will run with once the effect settles: a prebuilt
119
- // Shell is the config object itself; a config-built one is whatever this
120
- // instance already booted (adopted on a StrictMode/HMR re-setup). Cancel any
121
- // pending deferred teardown for it before deciding — this also cancels a
122
- // teardown another provider scheduled for the SAME prebuilt Shell.
123
81
  const current = configRef.current
124
82
  const adopting = isShellConfig(current) ? ref.shell : current
125
83
  if (adopting !== null) cancelTeardown(adopting)
126
-
127
84
  if (ref.key !== configKey) {
128
- // First boot, config change, or a retry that cleared the key: reboot fresh.
129
85
  if (ref.shell !== null) {
130
86
  cancelTeardown(ref.shell)
131
87
  disposeShell(ref.shell)
@@ -134,12 +90,7 @@ export function ShellProvider(props: ShellProviderProps): ReactElement | null {
134
90
  ref.shell = null
135
91
  startBoot(ref)
136
92
  }
137
- // Same key already booting/booted → adopt (StrictMode's second setup).
138
-
139
93
  return () => {
140
- // Defer teardown one macrotask; a synchronous re-setup (StrictMode/HMR or a
141
- // different provider adopting the same prebuilt Shell) cancels it first and
142
- // keeps the same live shell.
143
94
  if (ref.shell !== null) scheduleTeardown(ref.shell, ref)
144
95
  else ref.key = null
145
96
  }
@@ -154,52 +105,32 @@ export function ShellProvider(props: ShellProviderProps): ReactElement | null {
154
105
  }
155
106
  ref.key = null
156
107
  dispatch({ type: 'connecting' })
157
- setAttempt((a) => a + 1)
108
+ setAttempt((value) => value + 1)
158
109
  }, [])
159
110
 
160
- // The base (default-scope) memory for the booted Shell — the SAME instance
161
- // the graph/schema/nav hooks read through `graph/memory/scope.ts`. Seated in
162
- // `GraphContext` so selection's liveness has a source; null until ready.
163
111
  const shell = state.runtime?.shell ?? null
164
- const memory = useMemo<GraphMemory | null>(
165
- () => (shell === null ? null : baseMemoryFor(shell)),
166
- [shell],
167
- )
168
-
169
- // Bridge the memory's event channel onto the provider `onError` (API §2/§8):
170
- // warm-refresh failures and rolled-back mutations surface here rather than being
171
- // swallowed. Registered while the booted shell is live; cleared on teardown.
172
112
  useEffect(() => {
173
- if (shell === null) return
174
- setMemoryErrorSink(shell, (event) => {
175
- const mapped = toShellError(event)
176
- if (mapped !== null) onErrorRef.current?.(mapped)
177
- })
178
- return () => setMemoryErrorSink(shell, null)
179
- }, [shell])
113
+ if (shell !== null) connectionStore(shell).setRetry(retry)
114
+ }, [shell, retry])
180
115
 
181
- // Register this provider's boot-retry into the shell's connection store (rule 3),
182
- // so `useConnection().retry` re-runs boot. Status/seq stay a stub ('ready'/0)
183
- // until the transport-status + result-`seq` feed lands, with no surface change.
116
+ const store = state.runtime?.store ?? null
117
+ const storeScope = state.runtime?.storeScope ?? null
184
118
  useEffect(() => {
185
- if (shell === null) return
186
- connectionStore(shell).setRetry(retry)
187
- }, [shell, retry])
119
+ if (shell === null || store === null || storeScope === null) return
120
+ return retainStore(storeFor(shell, storeScope))
121
+ }, [shell, store, storeScope])
188
122
 
189
123
  if (state.status === 'error') {
190
- // Wrapped in a fragment so the component's declared return type stays
191
- // `ReactElement | null` — valid JSX under @types/react 18 AND 19 (the react
192
- // peer range is >=18), even though `errorFallback`/`fallback` yield ReactNode.
193
124
  if (errorFallback !== undefined) return <>{errorFallback(state.error, retry)}</>
194
125
  throw state.error
195
126
  }
196
127
  if (state.status !== 'ready' || state.runtime === null) {
197
- return fallback !== undefined ? <>{fallback}</> : null
128
+ return fallback === undefined ? null : <>{fallback}</>
198
129
  }
199
130
 
200
131
  return (
201
132
  <ShellContext.Provider value={state.runtime}>
202
- <GraphContext.Provider value={memory}>{children}</GraphContext.Provider>
133
+ <GraphContext.Provider value={state.runtime.store}>{children}</GraphContext.Provider>
203
134
  </ShellContext.Provider>
204
135
  )
205
136
  }
@@ -1,3 +1,4 @@
1
+ import type { GraphStore } from '@astrale-os/kernel-client/store'
1
2
  import type { KernelClient, Shell } from '@astrale-os/shell'
2
3
 
3
4
  /**
@@ -7,6 +8,8 @@ import type { KernelClient, Shell } from '@astrale-os/shell'
7
8
  */
8
9
  import { createContext } from 'react'
9
10
 
11
+ import type { GraphStoreScope } from '../graph/store.js'
12
+
10
13
  /**
11
14
  * whoami's node type, derived from the auth surface so shell-react needs no
12
15
  * direct `@astrale-os/kernel-core` dependency (`Node` is not yet re-exported by
@@ -22,6 +25,9 @@ export interface ShellRuntime {
22
25
  * bound kernel exposes no auth surface (a bare prebuilt Shell in tests).
23
26
  */
24
27
  readonly self: SelfNode | null
28
+ readonly storeScope: GraphStoreScope
29
+ readonly store: GraphStore
30
+ readonly ownsStore: boolean
25
31
  }
26
32
  export const ShellContext = createContext<ShellRuntime | null>(null)
27
33
  ShellContext.displayName = 'AstraleShellContext'
@@ -14,7 +14,6 @@ export interface AdoptRecord {
14
14
  readonly window: Window
15
15
  readonly slot: StageSlot
16
16
  readonly state: WindowState
17
- readonly targetId: string | undefined
18
17
  /** Close the window, release its slot, drop it from the registry (fired if unadopted). */
19
18
  dispose(): void
20
19
  }
@@ -1,94 +1,29 @@
1
- import type { KernelClient } from '@astrale-os/shell'
1
+ import type { Node } from '@astrale-os/sdk/graph/node'
2
2
 
3
- /**
4
- * useTargetNode (API §10) — inside a sandboxed view, the node it was opened on.
5
- * The id starts from `shell.targetNodeId` (handshake) and a host `setTarget`
6
- * intent retargets it; the id resolves to a live `Node` through the shell kernel.
7
- *
8
- * Honest subset (flagged): the read is a one-shot `kernel.get` re-run on id
9
- * change, not a memory watch, so `live` is false. Once composed with `useNode(id)`
10
- * it becomes real stale-while-revalidate liveness with no consumer change.
11
- */
12
- import { useCallback, useEffect, useRef, useState } from 'react'
3
+ import { Path } from '@astrale-os/sdk/graph/path'
4
+ import { useCallback, useMemo, useSyncExternalStore } from 'react'
13
5
 
14
- import type { ReadState } from '../../graph/memory/read/envelope.js'
15
- import type { Node } from '../lifecycle/mount.js'
6
+ import type { GraphResource } from '../../graph/resource.js'
16
7
 
17
- import { idleReadState } from '../../graph/memory/read/idle.js'
8
+ import { useDynamicObject } from '../../schema/object.hook.js'
18
9
  import { useShell } from '../../session/shell.hook.js'
19
10
 
20
- export interface NodeResult extends ReadState {
21
- readonly node: Node | null
22
- }
23
-
24
- /** Address a bare node id by graph id (`@<id>`); pass paths/anchors through. */
25
- function toRef(id: string): string {
26
- return id.startsWith('/') || id.startsWith('@') ? id : `@${id}`
27
- }
28
-
29
- interface ReadSnapshot {
30
- readonly node: Node | null
31
- readonly pending: boolean
32
- readonly error: unknown
11
+ /**
12
+ * Read the canonical target admitted by the parent handshake and subsequent
13
+ * parent-owned hot swaps without remounting this View document.
14
+ */
15
+ export function useTargetNode(): GraphResource<Node | null> {
16
+ return useDynamicObject(useTargetPath())
33
17
  }
34
18
 
35
- const IDLE_READ: ReadSnapshot = { node: null, pending: false, error: undefined }
36
-
37
- export function useTargetNode(): NodeResult {
19
+ /** Reactive canonical target Path; null represents an explicit parent clear. */
20
+ export function useTargetPath(): Path | null {
38
21
  const shell = useShell()
39
- const [id, setId] = useState<string | null>(() => shell.targetNodeId ?? null)
40
-
41
- useEffect(() => {
42
- const parent = shell.parent
43
- if (parent === null) return
44
- return parent.on('intent', (message) => {
45
- if (message.envelope.name !== 'setTarget') return
46
- const nodeId = (message.envelope.payload as { nodeId?: string }).nodeId
47
- if (typeof nodeId === 'string') setId(nodeId)
48
- })
49
- }, [shell])
50
-
51
- return useNodeRead(shell.kernel, id)
52
- }
53
-
54
- /** One-shot get + refetch, in the read envelope. The graph-module seam. */
55
- function useNodeRead(kernel: KernelClient, id: string | null): NodeResult {
56
- const [snap, setSnap] = useState<ReadSnapshot>(IDLE_READ)
57
- const aliveRef = useRef(true)
58
- useEffect(() => {
59
- aliveRef.current = true
60
- return () => {
61
- aliveRef.current = false
62
- }
63
- }, [])
64
-
65
- const load = useCallback(async (): Promise<void> => {
66
- if (id === null) {
67
- if (aliveRef.current) setSnap(IDLE_READ)
68
- return
69
- }
70
- if (aliveRef.current) setSnap((s) => ({ node: s.node, pending: true, error: undefined }))
71
- try {
72
- const node = await kernel.get(toRef(id))
73
- if (aliveRef.current) setSnap({ node: node ?? null, pending: false, error: undefined })
74
- } catch (error) {
75
- if (aliveRef.current) setSnap((s) => ({ node: s.node, pending: false, error }))
76
- }
77
- }, [kernel, id])
78
-
79
- useEffect(() => {
80
- void load()
81
- }, [load])
82
-
83
- if (id === null) {
84
- return { ...idleReadState(), node: null }
85
- }
86
- return {
87
- node: snap.node,
88
- pending: snap.pending,
89
- error: snap.error,
90
- live: false,
91
- seq: 0,
92
- refetch: load,
93
- }
22
+ const subscribe = useCallback(
23
+ (listener: () => void) => shell.onTargetChange(() => listener()),
24
+ [shell],
25
+ )
26
+ const snapshot = useCallback(() => shell.targetNodeId, [shell])
27
+ const target = useSyncExternalStore(subscribe, snapshot, snapshot)
28
+ return useMemo(() => (target === undefined ? null : Path.parse(target)), [target])
94
29
  }
@@ -1,6 +1,8 @@
1
- import type { PathLike } from '@astrale-os/kernel-client/graph'
2
- import type { Shell } from '@astrale-os/shell'
1
+ import type { Node } from '@astrale-os/sdk/graph/node'
2
+ import type { NodePath, PathLike } from '@astrale-os/sdk/graph/path'
3
+ import type { ResolvedView } from '@astrale-os/shell'
3
4
 
5
+ import { Path } from '@astrale-os/sdk/graph/path'
4
6
  /**
5
7
  * useViewsFor (API §10) — "what Views can render this node", as mountable
6
8
  * `ViewDescriptor`s for the open-with menu. Backed one-shot (`live:false`) by the
@@ -10,23 +12,22 @@ import type { Shell } from '@astrale-os/shell'
10
12
  import { useMemo } from 'react'
11
13
 
12
14
  import type { ReadState } from '../../graph/memory/read/envelope.js'
13
- import type { Node, ViewDescriptor } from '../lifecycle/mount.js'
14
15
 
15
16
  import { useAsyncResource } from '../../graph/async.js'
16
17
  import { useShell } from '../../session/shell.hook.js'
17
18
 
18
19
  export interface ViewsForResult extends ReadState {
19
- readonly views: readonly ViewDescriptor[]
20
+ readonly views: readonly ResolvedView[]
20
21
  }
21
22
 
22
- const NO_VIEWS: readonly ViewDescriptor[] = Object.freeze([])
23
+ const NO_VIEWS: readonly ResolvedView[] = Object.freeze([])
23
24
 
24
25
  export function useViewsFor(node: Node | PathLike | null | undefined): ViewsForResult {
25
26
  const shell = useShell()
26
27
  const target = resolvePath(node)
27
28
 
28
- const res = useAsyncResource<readonly ViewDescriptor[]>(
29
- target === null ? null : () => resolveViews(shell, target),
29
+ const res = useAsyncResource<readonly ResolvedView[]>(
30
+ target === null ? null : () => shell.views.resolve(target),
30
31
  NO_VIEWS,
31
32
  [shell, target],
32
33
  )
@@ -44,23 +45,9 @@ export function useViewsFor(node: Node | PathLike | null | undefined): ViewsForR
44
45
  )
45
46
  }
46
47
 
47
- /** Dispatch `View.resolve` and project its rows to mountable `ViewDescriptor`s. */
48
- async function resolveViews(shell: Shell, node: string): Promise<readonly ViewDescriptor[]> {
49
- const rows = await shell.views.resolve(node)
50
- if (rows.length === 0) return NO_VIEWS
51
- return rows.map((r) => ({ id: r.id, path: r.path, url: r.url, handshake: r.handshake }))
52
- }
53
-
54
- /** A path/`@id` the kernel read surface accepts, or `null` for an idle subject. */
55
- function resolvePath(node: Node | PathLike | null | undefined): string | null {
48
+ /** One canonical target, or `null` for an idle subject. */
49
+ function resolvePath(node: Node | PathLike | null | undefined): NodePath | null {
56
50
  if (node === null || node === undefined) return null
57
- if (typeof node === 'string') {
58
- if (node.length === 0) return null
59
- return node.startsWith('/') || node.startsWith('@') ? node : `@${node}`
60
- }
61
- const obj = node as { path?: { raw?: unknown }; id?: unknown; raw?: unknown }
62
- if (typeof obj.path?.raw === 'string') return obj.path.raw
63
- if (typeof obj.id === 'string') return `@${obj.id}`
64
- if (typeof obj.raw === 'string') return obj.raw
65
- return null
51
+ if ('id' in node) return Path.id(node.id).raw
52
+ return Path.from(node).raw
66
53
  }
@@ -2,10 +2,9 @@ import type { Window } from '@astrale-os/shell'
2
2
 
3
3
  /**
4
4
  * useWindows / useWindowList (API §10) — reactive views over the windows a
5
- * `ViewHost`/`AppHost` mounts through the per-shell hub. The shell exposes no
6
- * window-change feed, so the hub registry (fed by the hosts) is the reactive
7
- * source; windows mounted imperatively via `shell.mount` outside React are not
8
- * tracked here — flagged in the module report as a shell foundation gap.
5
+ * `ViewHost`/`AppHost` or the framework-neutral Shell mounts. The hub mirrors
6
+ * Shell's immutable window-change feed, so credential, location, lifecycle,
7
+ * and imperative mounts remain reactive through one snapshot authority.
9
8
  */
10
9
  import { useMemo, useSyncExternalStore } from 'react'
11
10