@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
@@ -4,12 +4,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  * projects that box onto a stage slot (ResizeObserver); the iframe lives in the
5
5
  * stage, so reorder/re-key never reloads a healthy frame. It drives the
6
6
  * `WindowState` machine, owns teardown (unmount closes its window), survives
7
- * StrictMode double-mount, and downgrades a fallbackable handshake to plain.
7
+ * StrictMode double-mount. The complete target-bound View is the sole source
8
+ * of mount identity, route, target, and handshake behavior.
8
9
  */
9
10
  import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from 'react';
10
11
  import { useShell } from '../session/shell.hook.js';
11
12
  import { getStageHub } from './hub.js';
12
- import { isFallbackableHandshakeError, refToId, resolveSource, sendSetTarget, sourceKey, } from './lifecycle/mount.js';
13
+ import { viewMountKey } from './lifecycle/mount.js';
13
14
  import { rectOf, sameRect } from './placement.js';
14
15
  const MOUNTING = { phase: 'mounting' };
15
16
  export const ViewHost = forwardRef(function ViewHost(props, ref) {
@@ -30,9 +31,7 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
30
31
  propsRef.current = props;
31
32
  const [state, setState] = useState(MOUNTING);
32
33
  const [refreshNonce, setRefreshNonce] = useState(0);
33
- const mountKey = sourceKey(props);
34
- const targetId = refToId(props.node);
35
- const lastTargetRef = useRef(undefined);
34
+ const mountKey = viewMountKey(props.view);
36
35
  const setStateSafe = (next) => {
37
36
  if (!aliveRef.current)
38
37
  return;
@@ -83,10 +82,9 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
83
82
  lastRectRef.current = rect;
84
83
  slot.place(rect);
85
84
  }, []);
86
- // ── Mount lifecycle. Keyed on the source identity + refresh nonce only — a
87
- // node (targetNodeId) change retargets via setTarget, never a remount. A
88
- // re-key/re-parent unmount+remount ADOPTS the live window through the hub
89
- // (deferred close), so a healthy iframe is never reloaded (§10).
85
+ // ── Mount lifecycle. Keyed on complete target + route provenance and
86
+ // refresh nonce. A same-selection re-key/re-parent can adopt the live
87
+ // window; any target or provenance change necessarily remounts.
90
88
  useEffect(() => {
91
89
  let cancelled = false;
92
90
  let controllerWaiter = null;
@@ -123,7 +121,6 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
123
121
  window: live.window,
124
122
  slot,
125
123
  state: live.state,
126
- targetId: live.targetId,
127
124
  dispose: () => closeAndRelease(live.windowId, slot),
128
125
  };
129
126
  if (refreshingRef.current)
@@ -131,6 +128,14 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
131
128
  else
132
129
  hub.stashForAdopt(mountKey, record);
133
130
  }
131
+ /** A pre-ready failure must not leave an empty stage slot over the fallback UI. */
132
+ function crashAndRelease(error) {
133
+ const slot = slotRef.current;
134
+ slotRef.current = null;
135
+ lastRectRef.current = null;
136
+ slot?.release();
137
+ setStateSafe({ phase: 'crashed', windowId: windowIdRef.current ?? undefined, error });
138
+ }
134
139
  // Adopt a live window a just-unmounted same-source host left behind.
135
140
  if (!wasRefresh) {
136
141
  const adopted = hub.takeAdopt(mountKey);
@@ -141,9 +146,7 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
141
146
  windowId: adopted.windowId,
142
147
  window: adopted.window,
143
148
  state: adopted.state,
144
- targetId: adopted.targetId,
145
149
  };
146
- lastTargetRef.current = adopted.targetId;
147
150
  hub.registry.upsert(adopted.windowId, adopted.window, adopted.state);
148
151
  lastRectRef.current = null;
149
152
  measure(); // reposition the surviving slot onto this host's anchor
@@ -151,34 +154,38 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
151
154
  return cleanup;
152
155
  }
153
156
  }
154
- async function mountWith(handshake, downgraded) {
157
+ async function mountView() {
155
158
  const p = propsRef.current;
156
- let resolved;
159
+ let sandbox;
157
160
  try {
158
- resolved = await resolveSource(shell.kernel, sourceOf(p));
161
+ sandbox = typeof p.sandbox === 'function' ? p.sandbox(p.view) : p.sandbox;
159
162
  }
160
163
  catch (err) {
161
- // No window yet (source resolution failed) — windowId honestly absent.
162
164
  if (!cancelled)
163
- setStateSafe({ phase: 'crashed', error: err });
165
+ crashAndRelease(err);
164
166
  return;
165
167
  }
166
- if (cancelled)
167
- return;
168
- const usedHandshake = handshake === 'none' ? 'none' : resolved.handshake;
169
- const deliveredTarget = refToId(p.node);
170
168
  const params = {
171
169
  host: slotRef.current.element,
172
- url: resolved.url,
173
- functionId: resolved.functionId,
174
- capabilities: { intents: p.capabilities?.intents ?? [] },
175
- targetNodeId: deliveredTarget,
176
- sandbox: p.sandbox,
177
- handshake: usedHandshake,
170
+ view: p.view,
171
+ capabilities: p.capabilities ?? {
172
+ version: 1,
173
+ navigation: {},
174
+ actions: {},
175
+ browser: {},
176
+ access: {},
177
+ },
178
+ sandbox,
178
179
  credential: p.credential,
180
+ ...(p.handshakeTimeoutMs === undefined
181
+ ? {}
182
+ : { handshakeTimeoutMs: p.handshakeTimeoutMs }),
183
+ ...(p.delegationTtlSeconds === undefined
184
+ ? {}
185
+ : { delegationTtlSeconds: p.delegationTtlSeconds }),
179
186
  };
180
187
  try {
181
- const win = await shell.mount(params);
188
+ const win = await shell.openView(params);
182
189
  // A StrictMode cleanup (or refresh) fired before mount resolved: adopt
183
190
  // then immediately close so no orphan window survives.
184
191
  if (cancelled) {
@@ -186,15 +193,11 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
186
193
  return;
187
194
  }
188
195
  windowIdRef.current = win.windowId;
189
- lastTargetRef.current = deliveredTarget;
190
- const ready = downgraded
191
- ? { phase: 'ready', windowId: win.windowId, downgraded: true }
192
- : { phase: 'ready', windowId: win.windowId };
196
+ const ready = { phase: 'ready', windowId: win.windowId };
193
197
  liveRef.current = {
194
198
  windowId: win.windowId,
195
199
  window: win.window,
196
200
  state: ready,
197
- targetId: deliveredTarget,
198
201
  };
199
202
  hub.registry.upsert(win.windowId, win.window, ready);
200
203
  setStateSafe(ready);
@@ -202,11 +205,7 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
202
205
  catch (err) {
203
206
  if (cancelled)
204
207
  return;
205
- if (usedHandshake === 'shell' && isFallbackableHandshakeError(err)) {
206
- await mountWith('none', true);
207
- return;
208
- }
209
- setStateSafe({ phase: 'crashed', windowId: windowIdRef.current ?? undefined, error: err });
208
+ crashAndRelease(err);
210
209
  }
211
210
  }
212
211
  function begin(controller) {
@@ -217,7 +216,7 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
217
216
  lastRectRef.current = null;
218
217
  measure();
219
218
  setStateSafe(MOUNTING);
220
- void mountWith('shell', false);
219
+ void mountView();
221
220
  }
222
221
  const existing = hub.getController();
223
222
  if (existing !== null)
@@ -231,17 +230,6 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
231
230
  return cleanup;
232
231
  // eslint-disable-next-line react-hooks/exhaustive-deps
233
232
  }, [hub, shell, mountKey, refreshNonce]);
234
- // ── Retarget: a node change after ready hot-swaps via setTarget (no remount).
235
- useEffect(() => {
236
- if (state.phase !== 'ready')
237
- return;
238
- if (targetId === lastTargetRef.current)
239
- return;
240
- lastTargetRef.current = targetId;
241
- if (targetId === undefined)
242
- return;
243
- sendSetTarget(shell, state.windowId, targetId);
244
- }, [shell, state, targetId]);
245
233
  // ── Placement observers: reposition on resize/scroll/anchor-resize.
246
234
  useLayoutEffect(() => {
247
235
  measure();
@@ -264,7 +252,3 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
264
252
  });
265
253
  return (_jsx("div", { ref: anchorRef, className: props.className, style: props.style, children: state.phase !== 'ready' ? props.fallback?.(state) : null }));
266
254
  });
267
- /** Narrow props back to the bare `ViewSource` discriminated union. */
268
- function sourceOf(props) {
269
- return 'url' in props ? { url: props.url } : { view: props.view };
270
- }
@@ -23,6 +23,8 @@ export interface WindowRegistry {
23
23
  upsert(windowId: string, window: Window, state: WindowState): void;
24
24
  patch(windowId: string, state: WindowState): void;
25
25
  remove(windowId: string): void;
26
+ /** Shell writes: replace host and imperatively mounted windows from authoritative snapshots. */
27
+ sync(windows: ReadonlyMap<string, Window>): void;
26
28
  }
27
29
  export interface StageHub {
28
30
  readonly registry: WindowRegistry;
@@ -1 +1 @@
1
- {"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../src/window/hub.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAYrD,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAEzE,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,4EAA4E;IAC5E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,CAAA;IAC3C,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,sDAAsD;IACtD,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IAClE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACjD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAqED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,aAAa,IAAI,eAAe,GAAG,IAAI,CAAA;IACvC,uEAAuE;IACvE,aAAa,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAAA;IACvD,4EAA4E;IAC5E,YAAY,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAC1E,yFAAyF;IACzF,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IACrD,yFAAyF;IACzF,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAA;CAC3C;AAID,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAkClD"}
1
+ {"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../src/window/hub.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAYrD,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAEzE,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,4EAA4E;IAC5E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,CAAA;IAC3C,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,sDAAsD;IACtD,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IAClE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACjD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gGAAgG;IAChG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;CACjD;AAkGD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,aAAa,IAAI,eAAe,GAAG,IAAI,CAAA;IACvC,uEAAuE;IACvE,aAAa,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAAA;IACvD,4EAA4E;IAC5E,YAAY,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAC1E,yFAAyF;IACzF,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IACrD,yFAAyF;IACzF,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAA;CAC3C;AAID,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAoClD"}
@@ -58,8 +58,39 @@ function createWindowRegistry() {
58
58
  return;
59
59
  invalidate();
60
60
  },
61
+ sync(windows) {
62
+ let changed = false;
63
+ for (const windowId of entries.keys()) {
64
+ if (!windows.has(windowId)) {
65
+ entries.delete(windowId);
66
+ changed = true;
67
+ }
68
+ }
69
+ for (const [windowId, window] of windows) {
70
+ const current = entries.get(windowId);
71
+ if (current?.window === window)
72
+ continue;
73
+ entries.set(windowId, {
74
+ window,
75
+ state: current?.state ?? phaseFor(window),
76
+ });
77
+ changed = true;
78
+ }
79
+ if (changed)
80
+ invalidate();
81
+ },
61
82
  };
62
83
  }
84
+ function phaseFor(window) {
85
+ if (window.state === 'opening')
86
+ return { phase: 'mounting' };
87
+ if (window.state === 'failed') {
88
+ return { phase: 'crashed', windowId: window.windowId };
89
+ }
90
+ if (window.state === 'closed')
91
+ return { phase: 'closed', windowId: window.windowId };
92
+ return { phase: 'ready', windowId: window.windowId };
93
+ }
63
94
  const HUBS = new WeakMap();
64
95
  /** The windows hub for a shell — one per shell object, created on first touch. */
65
96
  export function getStageHub(shell) {
@@ -95,6 +126,8 @@ export function getStageHub(shell) {
95
126
  return adopt.take(key);
96
127
  },
97
128
  };
129
+ registry.sync(shell.windows);
130
+ shell.onWindowChange(() => registry.sync(shell.windows));
98
131
  HUBS.set(shell, hub);
99
132
  return hub;
100
133
  }
@@ -1,15 +1,15 @@
1
1
  export { ViewStage } from './stage.js';
2
2
  export { ViewHost } from './host.js';
3
- export type { ViewHostProps, ViewHostHandle } from './host.js';
3
+ export type { SandboxPolicy, ViewHostProps, ViewHostHandle } from './host.js';
4
4
  export { useWindows, useWindowList } from './hooks/windows.hook.js';
5
5
  export type { WindowListEntry } from './hooks/windows.hook.js';
6
6
  export { useWindow } from './hooks/window.hook.js';
7
7
  export type { WindowView } from './hub.js';
8
8
  export { useViewsFor } from './hooks/views-for.hook.js';
9
9
  export type { ViewsForResult } from './hooks/views-for.hook.js';
10
- export { useTargetNode } from './hooks/target-node.hook.js';
10
+ export { useTargetNode, useTargetPath } from './hooks/target-node.hook.js';
11
11
  export { useCloseGuard } from './hooks/close-guard.hook.js';
12
12
  export type { CloseGuard, CloseVerdict } from './hooks/close-guard.hook.js';
13
- export type { ViewDescriptor, ViewSource, Node } from './lifecycle/mount.js';
14
13
  export type { WindowState } from './lifecycle/states.js';
14
+ export type { ResolvedView } from '@astrale-os/shell';
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/window/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC3E,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC5E,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/window/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC3E,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA"}
@@ -1,10 +1,10 @@
1
1
  // window — API §10. ViewStage/ViewHost (the physical iframe-slot layer + a host
2
- // that projects placement into a slot), the reactive window hooks, the child-side
2
+ // that projects stage geometry into a slot), the reactive window hooks, the child-side
3
3
  // target-node + close-guard, and the (G5) view-resolution hook.
4
4
  export { ViewStage } from './stage.js';
5
5
  export { ViewHost } from './host.js';
6
6
  export { useWindows, useWindowList } from './hooks/windows.hook.js';
7
7
  export { useWindow } from './hooks/window.hook.js';
8
8
  export { useViewsFor } from './hooks/views-for.hook.js';
9
- export { useTargetNode } from './hooks/target-node.hook.js';
9
+ export { useTargetNode, useTargetPath } from './hooks/target-node.hook.js';
10
10
  export { useCloseGuard } from './hooks/close-guard.hook.js';
@@ -1,47 +1,8 @@
1
- import type { NodeRefInput } from '@astrale-os/kernel-client';
1
+ import type { ResolvedView } from '@astrale-os/shell';
2
2
  /**
3
- * Mount orchestration (API §10) — resolve a `ViewSource` to something `shell.mount`
4
- * accepts (url + handshake + functionId), the stable remount key, the delivered
5
- * target id, the handshake-fallback predicate, and the parent→child setTarget
6
- * hot-swap. A one-call `View.resolve` kernel contract does not exist yet — the
7
- * bare-ref path is the honest subset, G5.
3
+ * Stable identity for one target-bound, provenance-complete View route.
4
+ * A change to any routing or semantic witness forces a fresh mount; an old
5
+ * iframe and credential can never be adopted under a different route.
8
6
  */
9
- import type { KernelClient, Shell } from '@astrale-os/shell';
10
- /** A live graph node, derived from the shell kernel read surface (G8 seam). */
11
- export type Node = NonNullable<Awaited<ReturnType<KernelClient['get']>>>;
12
- /** A View function resolved to something mountable (url + optional handshake). */
13
- export interface ViewDescriptor {
14
- readonly id: string;
15
- readonly path: string;
16
- readonly url?: string;
17
- readonly handshake?: string;
18
- }
19
- /**
20
- * What a `ViewHost` mounts:
21
- * - `{ view }` — a View function (descriptor or ref), resolved to url + handshake.
22
- * - `{ url }` — a handshake-less hosted view; mounts immediately (no 5s wait).
23
- */
24
- export type ViewSource = {
25
- readonly view: NodeRefInput | ViewDescriptor;
26
- } | {
27
- readonly url: string;
28
- };
29
- export interface ResolvedSource {
30
- readonly url: string;
31
- readonly handshake: 'shell' | 'none';
32
- readonly functionId: string;
33
- }
34
- /** Resolve a source; may hit the kernel for a bare/urless View ref (G5). */
35
- export declare function resolveSource(kernel: KernelClient, source: ViewSource): Promise<ResolvedSource>;
36
- /** A stable remount key: the same source identity must not remount. */
37
- export declare function sourceKey(source: ViewSource): string;
38
- /** The bare node id delivered as `targetNodeId` at mount / retarget. */
39
- export declare function refToId(ref: NodeRefInput | undefined): string | undefined;
40
- /**
41
- * Fallbackable = the view didn't speak the shell protocol (timeout/failed), not a
42
- * security refusal (origin mismatch) — only the former downgrades to plain.
43
- */
44
- export declare function isFallbackableHandshakeError(err: unknown): boolean;
45
- /** Parent → child hot-swap of the target node (no remount) — API §10 setTarget. */
46
- export declare function sendSetTarget(shell: Shell, windowId: string, nodeId: string): void;
7
+ export declare function viewMountKey(view: ResolvedView): string;
47
8
  //# sourceMappingURL=mount.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/window/lifecycle/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE5D,+EAA+E;AAC/E,MAAM,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAExE,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAA;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AA2ED,4EAA4E;AAC5E,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAgBzB;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAOpD;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGlE;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAMlF"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/window/lifecycle/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAWvD"}
@@ -1,123 +1,17 @@
1
- function isViewDescriptor(view) {
2
- return typeof view === 'object' && view !== null && 'id' in view && 'path' in view;
3
- }
4
- /** Last path segment or host, alnum-only — a stable-ish functionId from a url. */
5
- function slugOfUrl(url) {
6
- const trimmed = url.replace(/[#?].*$/, '').replace(/\/+$/, '');
7
- const tail = trimmed.split('/').filter(Boolean).pop() ?? 'view';
8
- return tail.replace(/[^a-z0-9]/gi, '').slice(-32) || 'view';
9
- }
10
- /** A ref addressable by the kernel read surface (`get`). `@<id>` for bare ids. */
11
- function toPath(ref) {
12
- if (typeof ref === 'string') {
13
- return ref.startsWith('/') || ref.startsWith('@') ? ref : `@${ref}`;
14
- }
15
- const obj = ref;
16
- if (obj.path?.raw !== undefined)
17
- return obj.path.raw;
18
- if (obj.id !== undefined)
19
- return `@${obj.id}`;
20
- throw new Error('ViewHost: unresolvable view ref');
21
- }
22
- async function resolveViewRef(kernel, ref, handshakeHint) {
23
- const node = await kernel.get(toPath(ref));
24
- if (node === null)
25
- throw new Error(`ViewHost: view not found (${toPath(ref)})`);
26
- const props = node.props ?? {};
27
- const url = viewUrl(props);
28
- if (typeof url !== 'string' || url.length === 0) {
29
- throw new Error('ViewHost: resolved view has no url');
30
- }
31
- const handshake = normalizeHandshake(handshakeHint ?? props.handshake);
32
- const id = node.id ?? slugOfUrl(url);
33
- return { url, handshake, functionId: id };
34
- }
35
- /** Resolve both the legacy flat url and the canonical Function binding. */
36
- function viewUrl(props) {
37
- if (typeof props.url === 'string' && props.url.length > 0)
38
- return props.url;
39
- const direct = remoteUrlOf(props.binding);
40
- if (direct !== undefined)
41
- return direct;
42
- // Raw graph props can be keyed by the property's canonical graph key rather
43
- // than its local slug. The binding value remains self-describing.
44
- for (const value of Object.values(props)) {
45
- const remoteUrl = remoteUrlOf(value);
46
- if (remoteUrl !== undefined)
47
- return remoteUrl;
48
- }
49
- return undefined;
50
- }
51
- function remoteUrlOf(value) {
52
- let binding = value;
53
- if (typeof binding === 'string') {
54
- try {
55
- binding = JSON.parse(binding);
56
- }
57
- catch {
58
- return undefined;
59
- }
60
- }
61
- if (binding === null || typeof binding !== 'object')
62
- return undefined;
63
- const remoteUrl = binding.remoteUrl;
64
- return typeof remoteUrl === 'string' && remoteUrl.length > 0 ? remoteUrl : undefined;
65
- }
66
- function normalizeHandshake(value) {
67
- return value === 'none' ? 'none' : 'shell';
68
- }
69
- /** Resolve a source; may hit the kernel for a bare/urless View ref (G5). */
70
- export async function resolveSource(kernel, source) {
71
- if ('url' in source) {
72
- return { url: source.url, handshake: 'none', functionId: slugOfUrl(source.url) };
73
- }
74
- const view = source.view;
75
- if (isViewDescriptor(view)) {
76
- if (view.url !== undefined && view.url.length > 0) {
77
- return {
78
- url: view.url,
79
- handshake: normalizeHandshake(view.handshake),
80
- functionId: view.id,
81
- };
82
- }
83
- return resolveViewRef(kernel, view.id, view.handshake);
84
- }
85
- return resolveViewRef(kernel, view);
86
- }
87
- /** A stable remount key: the same source identity must not remount. */
88
- export function sourceKey(source) {
89
- if ('url' in source)
90
- return `url:${source.url}`;
91
- const view = source.view;
92
- if (isViewDescriptor(view))
93
- return `view:${view.id}:${view.url ?? ''}:${view.handshake ?? ''}`;
94
- if (typeof view === 'string')
95
- return `ref:${view}`;
96
- const obj = view;
97
- return `ref:${obj.id ?? obj.path?.raw ?? '?'}`;
98
- }
99
- /** The bare node id delivered as `targetNodeId` at mount / retarget. */
100
- export function refToId(ref) {
101
- if (ref === undefined)
102
- return undefined;
103
- if (typeof ref === 'string')
104
- return ref.startsWith('@') ? ref.slice(1) : ref;
105
- const obj = ref;
106
- return obj.id;
107
- }
108
1
  /**
109
- * Fallbackable = the view didn't speak the shell protocol (timeout/failed), not a
110
- * security refusal (origin mismatch) — only the former downgrades to plain.
2
+ * Stable identity for one target-bound, provenance-complete View route.
3
+ * A change to any routing or semantic witness forces a fresh mount; an old
4
+ * iframe and credential can never be adopted under a different route.
111
5
  */
112
- export function isFallbackableHandshakeError(err) {
113
- const code = err?.code;
114
- return code === 'HANDSHAKE_TIMEOUT' || code === 'HANDSHAKE_FAILED';
115
- }
116
- /** Parent → child hot-swap of the target node (no remount) — API §10 setTarget. */
117
- export function sendSetTarget(shell, windowId, nodeId) {
118
- shell.children.send(windowId, {
119
- type: 'intent',
120
- version: 1,
121
- envelope: { name: 'setTarget', payload: { nodeId }, sender: { windowId: '<parent>' } },
122
- });
6
+ export function viewMountKey(view) {
7
+ const { route, target } = view;
8
+ return [
9
+ target,
10
+ route.key,
11
+ route.href,
12
+ route.handshake,
13
+ route.issuer,
14
+ route.etag,
15
+ route.revision,
16
+ ].join('\u0000');
123
17
  }
@@ -7,10 +7,6 @@
7
7
  /**
8
8
  * Lifecycle of a hosted view as the React face observes it (API §10).
9
9
  *
10
- * `ready.downgraded` marks a mount that fell back to a plain iframe after a
11
- * fallbackable handshake error. It rides here (not on the shell `Window`) until
12
- * the shell `Window` grows a first-class flag — flagged in the module report.
13
- *
14
10
  * `crashed.windowId` is OPTIONAL: a crash before a window exists (a session-open
15
11
  * failure, a pre-mount source-resolution failure) has no id to report — the type
16
12
  * says so rather than carrying a placeholder `''`.
@@ -20,7 +16,6 @@ export type WindowState = {
20
16
  } | {
21
17
  readonly phase: 'ready';
22
18
  readonly windowId: string;
23
- readonly downgraded?: boolean;
24
19
  } | {
25
20
  readonly phase: 'crashed';
26
21
  readonly windowId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../src/window/lifecycle/states.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACrF;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACnF;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA"}
1
+ {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../src/window/lifecycle/states.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACnF;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/window/stage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AA4BxD,wBAAgB,SAAS,IAAI,YAAY,CAiBxC"}
1
+ {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/window/stage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AA6BxD,wBAAgB,SAAS,IAAI,YAAY,CAiBxC"}
@@ -20,6 +20,7 @@ const STAGE_STYLE = {
20
20
  left: 0,
21
21
  right: 0,
22
22
  bottom: 0,
23
+ zIndex: 20,
23
24
  pointerEvents: 'none',
24
25
  };
25
26
  export function ViewStage() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/shell-react",
3
- "version": "0.2.8",
3
+ "version": "0.2.10-beta.2",
4
4
  "description": "Astrale shell-react — the React face of the shell (provider, hooks, view components)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -25,10 +25,8 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@astrale-domains/shell-schema": ">=0.3.2 <1.0.0",
29
- "@astrale-os/kernel-client": ">=0.4.7 <1.0.0",
30
- "@astrale-os/kernel-core": ">=0.7.5 <1.0.0",
31
- "@astrale-os/kernel-dsl": ">=0.1.4 <1.0.0",
28
+ "@astrale-os/kernel-client": ">=0.6.0-beta.4 <1.0.0",
29
+ "@astrale-os/sdk": ">=0.5.0-beta.4 <1.0.0",
32
30
  "jose": "^6.2.3",
33
31
  "zod": "^4.3.6"
34
32
  },
@@ -43,14 +41,17 @@
43
41
  "react": "^19.2.0",
44
42
  "react-dom": "^19.2.0",
45
43
  "typescript": "~6.0.1-rc",
44
+ "vite": "^7.3.6",
46
45
  "vitest": "^3.2.4",
47
- "@astrale-os/shell": "0.3.6"
46
+ "@astrale-os/shell": "0.3.8-beta.2"
48
47
  },
49
48
  "peerDependencies": {
50
- "@astrale-os/shell": ">=0.2.0 <1.0.0",
51
- "react": ">=18"
49
+ "@astrale-os/shell": ">=0.3.8-beta.2 <1.0.0",
50
+ "react": ">=18",
51
+ "react-dom": ">=18"
52
52
  },
53
53
  "scripts": {
54
+ "prebuild": "pnpm clean",
54
55
  "build": "tsgo",
55
56
  "clean": "rm -rf dist *.tsbuildinfo",
56
57
  "typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.test.json",
@@ -1,5 +1,10 @@
1
- import type { NodeRefInput } from '@astrale-os/kernel-client'
2
- import type { ApplicationNode, AppSession, IntentRegistry, SandboxProfile } from '@astrale-os/shell'
1
+ import type {
2
+ ApplicationNode,
3
+ AppSession,
4
+ HostCapabilities,
5
+ NodeTarget,
6
+ ResolvedView,
7
+ } from '@astrale-os/shell'
3
8
  import type { CSSProperties, ReactElement, ReactNode } from 'react'
4
9
 
5
10
  /**
@@ -11,8 +16,7 @@ import type { CSSProperties, ReactElement, ReactNode } from 'react'
11
16
  */
12
17
  import { forwardRef, useEffect, useRef, useState } from 'react'
13
18
 
14
- import type { ViewHostHandle } from '../../window/host.js'
15
- import type { ViewDescriptor } from '../../window/lifecycle/mount.js'
19
+ import type { SandboxPolicy, ViewHostHandle } from '../../window/host.js'
16
20
  import type { WindowState } from '../../window/lifecycle/states.js'
17
21
 
18
22
  import { ViewHost } from '../../window/host.js'
@@ -26,16 +30,11 @@ export type AppHostFallbackState =
26
30
  | { readonly phase: 'no-view' }
27
31
 
28
32
  export interface AppHostProps {
29
- app: ApplicationNode | NodeRefInput
30
- /**
31
- * Default target node delivered to the app's view as `targetNodeId` (API §12.2).
32
- * Container-anchored apps need one — the dock passes the current space. A deeper
33
- * fix (an install-time app data container + `app::open` returning a default
34
- * target) is a tracked domain-modeling question; until then the host supplies it.
35
- */
36
- node?: NodeRefInput
37
- capabilities?: { intents: readonly (keyof IntentRegistry)[] }
38
- sandbox?: SandboxProfile | null
33
+ app: ApplicationNode | NodeTarget
34
+ /** Override the entry View while keeping the application's delegated session alive. */
35
+ view?: ResolvedView
36
+ capabilities?: HostCapabilities
37
+ sandbox?: SandboxPolicy
39
38
  fallback?: (state: AppHostFallbackState) => ReactNode
40
39
  onSession?: (session: AppSession) => void
41
40
  className?: string
@@ -94,15 +93,15 @@ export const AppHost = forwardRef<ViewHostHandle, AppHostProps>(
94
93
  // A session-open failure has no window yet — `windowId` is honestly absent (§10).
95
94
  if (error !== undefined) return box(fallback?.({ phase: 'crashed', error }) ?? null)
96
95
  if (session === null) return box(fallback?.({ phase: 'opening' }) ?? null)
97
- if (session.view === null) return box(fallback?.({ phase: 'no-view' }) ?? null)
96
+ const view = props.view ?? session.view
97
+ if (view === null) return box(fallback?.({ phase: 'no-view' }) ?? null)
98
98
 
99
99
  return (
100
100
  <ViewHost
101
101
  ref={ref}
102
- view={session.view as ViewDescriptor}
103
- node={props.node}
102
+ view={view}
104
103
  credential={session.credential}
105
- capabilities={props.capabilities ?? { intents: [] }}
104
+ capabilities={props.capabilities}
106
105
  sandbox={props.sandbox}
107
106
  fallback={fallback ? (s: WindowState) => fallback(s) : undefined}
108
107
  className={className}