@astrale-os/shell 0.1.1 → 0.2.0

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 (284) hide show
  1. package/LICENSE +202 -0
  2. package/dist/application/apps.d.ts +18 -0
  3. package/dist/application/apps.d.ts.map +1 -0
  4. package/dist/application/apps.js +35 -0
  5. package/dist/application/namespaces.d.ts +21 -0
  6. package/dist/application/namespaces.d.ts.map +1 -0
  7. package/dist/application/namespaces.js +12 -0
  8. package/dist/application/session/open.d.ts +19 -0
  9. package/dist/application/session/open.d.ts.map +1 -0
  10. package/dist/application/session/open.js +46 -0
  11. package/dist/application/session/session.d.ts +41 -0
  12. package/dist/application/session/session.d.ts.map +1 -0
  13. package/dist/application/session/session.js +9 -0
  14. package/dist/application/session/token.d.ts +24 -0
  15. package/dist/application/session/token.d.ts.map +1 -0
  16. package/dist/application/session/token.js +51 -0
  17. package/dist/application/spaces.d.ts +17 -0
  18. package/dist/application/spaces.d.ts.map +1 -0
  19. package/dist/application/spaces.js +30 -0
  20. package/dist/application/user.d.ts +20 -0
  21. package/dist/application/user.d.ts.map +1 -0
  22. package/dist/application/user.js +26 -0
  23. package/dist/application/view.d.ts +20 -0
  24. package/dist/application/view.d.ts.map +1 -0
  25. package/dist/application/view.js +18 -0
  26. package/dist/{application/channels/children-channel.d.ts → channel/children.d.ts} +4 -4
  27. package/dist/channel/children.d.ts.map +1 -0
  28. package/dist/channel/listeners.d.ts +8 -0
  29. package/dist/channel/listeners.d.ts.map +1 -0
  30. package/dist/{adapters/message-port.transport.d.ts → channel/message-port.d.ts} +2 -2
  31. package/dist/channel/message-port.d.ts.map +1 -0
  32. package/dist/{application/channels → channel}/observer.d.ts +2 -1
  33. package/dist/channel/observer.d.ts.map +1 -0
  34. package/dist/{application/channels/parent-channel.d.ts → channel/parent.d.ts} +4 -4
  35. package/dist/channel/parent.d.ts.map +1 -0
  36. package/dist/{application/channels/parent-channel.js → channel/parent.js} +1 -1
  37. package/dist/channel/transport.d.ts +8 -0
  38. package/dist/channel/transport.d.ts.map +1 -0
  39. package/dist/index.d.ts +56 -41
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +23 -20
  42. package/dist/{domain/intent → intent}/middleware/capabilities.d.ts +2 -2
  43. package/dist/intent/middleware/capabilities.d.ts.map +1 -0
  44. package/dist/intent/middleware/logging.d.ts.map +1 -0
  45. package/dist/{domain/intent → intent}/middleware/rate-limit.d.ts +1 -1
  46. package/dist/intent/middleware/rate-limit.d.ts.map +1 -0
  47. package/dist/{application/intent/pipeline-runner.d.ts → intent/pipeline.d.ts} +3 -3
  48. package/dist/intent/pipeline.d.ts.map +1 -0
  49. package/dist/intent/policy.d.ts +40 -0
  50. package/dist/intent/policy.d.ts.map +1 -0
  51. package/dist/intent/policy.js +15 -0
  52. package/dist/{application/intent → intent}/request.d.ts +5 -14
  53. package/dist/intent/request.d.ts.map +1 -0
  54. package/dist/{application/intent → intent}/request.js +6 -20
  55. package/dist/intent/router.d.ts +31 -0
  56. package/dist/intent/router.d.ts.map +1 -0
  57. package/dist/{application/intent → intent}/router.js +1 -15
  58. package/dist/kernel/client.d.ts +33 -0
  59. package/dist/kernel/client.d.ts.map +1 -0
  60. package/dist/kernel/credential.d.ts +35 -0
  61. package/dist/kernel/credential.d.ts.map +1 -0
  62. package/dist/kernel/credential.js +43 -0
  63. package/dist/kernel/delegated.d.ts +17 -0
  64. package/dist/kernel/delegated.d.ts.map +1 -0
  65. package/dist/kernel/delegated.js +29 -0
  66. package/dist/{application/windowing/token-refresh.d.ts → kernel/delegation/refresh.d.ts} +2 -2
  67. package/dist/kernel/delegation/refresh.d.ts.map +1 -0
  68. package/dist/{application/windowing/token-refresh.js → kernel/delegation/refresh.js} +1 -1
  69. package/dist/{domain/identity/delegation.d.ts → kernel/delegation/token.d.ts} +1 -1
  70. package/dist/kernel/delegation/token.d.ts.map +1 -0
  71. package/dist/{adapters/kernel-client.adapter.d.ts → kernel/session.d.ts} +15 -12
  72. package/dist/kernel/session.d.ts.map +1 -0
  73. package/dist/{adapters/kernel-client.adapter.js → kernel/session.js} +10 -15
  74. package/dist/message/ctrl.d.ts +34 -0
  75. package/dist/message/ctrl.d.ts.map +1 -0
  76. package/dist/message/error.d.ts +9 -0
  77. package/dist/message/error.d.ts.map +1 -0
  78. package/dist/{application/windowing/protocol.d.ts → message/init.d.ts} +1 -1
  79. package/dist/message/init.d.ts.map +1 -0
  80. package/dist/{domain/message/registries.d.ts → message/intent.d.ts} +45 -37
  81. package/dist/message/intent.d.ts.map +1 -0
  82. package/dist/message/message.d.ts +10 -0
  83. package/dist/message/message.d.ts.map +1 -0
  84. package/dist/shell/access.d.ts +10 -0
  85. package/dist/shell/access.d.ts.map +1 -0
  86. package/dist/shell/access.js +19 -0
  87. package/dist/shell/build.d.ts +28 -0
  88. package/dist/shell/build.d.ts.map +1 -0
  89. package/dist/shell/build.js +58 -0
  90. package/dist/shell/config.d.ts +29 -0
  91. package/dist/shell/config.d.ts.map +1 -0
  92. package/dist/shell/create.d.ts +4 -0
  93. package/dist/shell/create.d.ts.map +1 -0
  94. package/dist/shell/create.js +5 -0
  95. package/dist/{application/windowing/child-init.d.ts → shell/sandboxed/boot.d.ts} +5 -5
  96. package/dist/shell/sandboxed/boot.d.ts.map +1 -0
  97. package/dist/{application/windowing/child-init.js → shell/sandboxed/boot.js} +5 -6
  98. package/dist/shell/sandboxed/guarded.d.ts +13 -0
  99. package/dist/shell/sandboxed/guarded.d.ts.map +1 -0
  100. package/dist/shell/sandboxed/guarded.js +28 -0
  101. package/dist/shell/sandboxed/pending.d.ts +14 -0
  102. package/dist/shell/sandboxed/pending.d.ts.map +1 -0
  103. package/dist/shell/sandboxed/pending.js +30 -0
  104. package/dist/shell/sandboxed/sandboxed.d.ts +4 -0
  105. package/dist/shell/sandboxed/sandboxed.d.ts.map +1 -0
  106. package/dist/shell/sandboxed/sandboxed.js +100 -0
  107. package/dist/shell/shell.d.ts +36 -0
  108. package/dist/shell/shell.d.ts.map +1 -0
  109. package/dist/shell/standalone.d.ts +4 -0
  110. package/dist/shell/standalone.d.ts.map +1 -0
  111. package/dist/shell/standalone.js +85 -0
  112. package/dist/{domain/window/config.d.ts → window/capabilities.d.ts} +3 -3
  113. package/dist/window/capabilities.d.ts.map +1 -0
  114. package/dist/{ports/shell-adapter.d.ts → window/embed/adapter.d.ts} +3 -2
  115. package/dist/window/embed/adapter.d.ts.map +1 -0
  116. package/dist/{adapters/iframe.shell-adapter.d.ts → window/embed/iframe.d.ts} +2 -2
  117. package/dist/window/embed/iframe.d.ts.map +1 -0
  118. package/dist/{application/windowing/lifecycle-controller.d.ts → window/lifecycle/states.d.ts} +4 -3
  119. package/dist/window/lifecycle/states.d.ts.map +1 -0
  120. package/dist/{application/windowing/lifecycle-controller.js → window/lifecycle/states.js} +1 -1
  121. package/dist/{domain/window/lifecycle.d.ts → window/lifecycle/transition.d.ts} +3 -3
  122. package/dist/window/lifecycle/transition.d.ts.map +1 -0
  123. package/dist/{domain/window/lifecycle.js → window/lifecycle/transition.js} +1 -1
  124. package/dist/window/mount/close.d.ts +27 -0
  125. package/dist/window/mount/close.d.ts.map +1 -0
  126. package/dist/window/mount/close.js +74 -0
  127. package/dist/window/mount/credential.d.ts +41 -0
  128. package/dist/window/mount/credential.d.ts.map +1 -0
  129. package/dist/window/mount/credential.js +74 -0
  130. package/dist/window/mount/handshake.d.ts.map +1 -0
  131. package/dist/{application/windowing → window/mount}/handshake.js +1 -1
  132. package/dist/{application/windowing/mount-controller.d.ts → window/mount/mount.d.ts} +7 -7
  133. package/dist/window/mount/mount.d.ts.map +1 -0
  134. package/dist/window/mount/mount.js +143 -0
  135. package/dist/window/mount/params.d.ts +73 -0
  136. package/dist/window/mount/params.d.ts.map +1 -0
  137. package/dist/window/mount/params.js +1 -0
  138. package/dist/window/mount/plain.d.ts +20 -0
  139. package/dist/window/mount/plain.d.ts.map +1 -0
  140. package/dist/window/mount/plain.js +46 -0
  141. package/dist/{domain/window → window}/window.d.ts +1 -1
  142. package/dist/window/window.d.ts.map +1 -0
  143. package/dist/window/window.js +1 -0
  144. package/package.json +11 -6
  145. package/src/application/apps.ts +60 -0
  146. package/src/application/namespaces.ts +38 -0
  147. package/src/application/session/open.ts +73 -0
  148. package/src/application/session/session.ts +46 -0
  149. package/src/application/session/token.ts +87 -0
  150. package/src/application/spaces.ts +52 -0
  151. package/src/application/user.ts +54 -0
  152. package/src/application/view.ts +42 -0
  153. package/src/{application/channels/children-channel.ts → channel/children.ts} +3 -3
  154. package/src/{application/channels/types.ts → channel/listeners.ts} +2 -7
  155. package/src/{adapters/message-port.transport.ts → channel/message-port.ts} +2 -2
  156. package/src/{application/channels → channel}/observer.ts +2 -1
  157. package/src/{application/channels/parent-channel.ts → channel/parent.ts} +4 -4
  158. package/src/{ports → channel}/transport.ts +2 -1
  159. package/src/index.ts +80 -88
  160. package/src/{domain/intent → intent}/middleware/capabilities.ts +2 -3
  161. package/src/{domain/intent → intent}/middleware/logging.ts +1 -1
  162. package/src/{domain/intent → intent}/middleware/rate-limit.ts +1 -1
  163. package/src/{application/intent/pipeline-runner.ts → intent/pipeline.ts} +3 -2
  164. package/src/intent/policy.ts +46 -0
  165. package/src/{application/intent → intent}/request.ts +14 -34
  166. package/src/{application/intent → intent}/router.ts +12 -49
  167. package/src/kernel/client.ts +33 -0
  168. package/src/kernel/credential.ts +78 -0
  169. package/src/kernel/delegated.ts +49 -0
  170. package/src/{application/windowing/token-refresh.ts → kernel/delegation/refresh.ts} +2 -2
  171. package/src/{adapters/kernel-client.adapter.ts → kernel/session.ts} +26 -33
  172. package/src/message/ctrl.ts +25 -0
  173. package/src/{domain/message/errors.ts → message/error.ts} +8 -0
  174. package/src/message/intent.ts +98 -0
  175. package/src/message/message.ts +11 -0
  176. package/src/shell/access.ts +26 -0
  177. package/src/shell/build.ts +95 -0
  178. package/src/shell/config.ts +32 -0
  179. package/src/shell/create.ts +9 -0
  180. package/src/{application/windowing/child-init.ts → shell/sandboxed/boot.ts} +10 -11
  181. package/src/shell/sandboxed/guarded.ts +32 -0
  182. package/src/shell/sandboxed/pending.ts +52 -0
  183. package/src/shell/sandboxed/sandboxed.ts +131 -0
  184. package/src/shell/shell.ts +37 -0
  185. package/src/shell/standalone.ts +97 -0
  186. package/src/{domain/window/config.ts → window/capabilities.ts} +2 -2
  187. package/src/{ports/shell-adapter.ts → window/embed/adapter.ts} +2 -1
  188. package/src/{adapters/iframe.shell-adapter.ts → window/embed/iframe.ts} +2 -2
  189. package/src/{application/windowing/lifecycle-controller.ts → window/lifecycle/states.ts} +3 -2
  190. package/src/{domain/window/lifecycle.ts → window/lifecycle/transition.ts} +2 -2
  191. package/src/window/mount/close.ts +111 -0
  192. package/src/window/mount/credential.ts +101 -0
  193. package/src/{application/windowing → window/mount}/handshake.ts +3 -2
  194. package/src/window/mount/mount.ts +185 -0
  195. package/src/window/mount/params.ts +64 -0
  196. package/src/window/mount/plain.ts +68 -0
  197. package/src/{domain/window → window}/window.ts +1 -1
  198. package/dist/adapters/iframe.shell-adapter.d.ts.map +0 -1
  199. package/dist/adapters/kernel-client.adapter.d.ts.map +0 -1
  200. package/dist/adapters/message-port.transport.d.ts.map +0 -1
  201. package/dist/application/channels/children-channel.d.ts.map +0 -1
  202. package/dist/application/channels/observer.d.ts.map +0 -1
  203. package/dist/application/channels/parent-channel.d.ts.map +0 -1
  204. package/dist/application/channels/types.d.ts +0 -12
  205. package/dist/application/channels/types.d.ts.map +0 -1
  206. package/dist/application/intent/pipeline-runner.d.ts.map +0 -1
  207. package/dist/application/intent/request.d.ts.map +0 -1
  208. package/dist/application/intent/router.d.ts +0 -56
  209. package/dist/application/intent/router.d.ts.map +0 -1
  210. package/dist/application/kernel-facade.d.ts +0 -33
  211. package/dist/application/kernel-facade.d.ts.map +0 -1
  212. package/dist/application/kernel-facade.js +0 -77
  213. package/dist/application/shell.d.ts +0 -100
  214. package/dist/application/shell.d.ts.map +0 -1
  215. package/dist/application/shell.js +0 -256
  216. package/dist/application/windowing/child-init.d.ts.map +0 -1
  217. package/dist/application/windowing/handshake.d.ts.map +0 -1
  218. package/dist/application/windowing/lifecycle-controller.d.ts.map +0 -1
  219. package/dist/application/windowing/mount-controller.d.ts.map +0 -1
  220. package/dist/application/windowing/mount-controller.js +0 -277
  221. package/dist/application/windowing/protocol.d.ts.map +0 -1
  222. package/dist/application/windowing/token-refresh.d.ts.map +0 -1
  223. package/dist/domain/identity/delegation.d.ts.map +0 -1
  224. package/dist/domain/identity/path.d.ts +0 -11
  225. package/dist/domain/identity/path.d.ts.map +0 -1
  226. package/dist/domain/identity/path.js +0 -13
  227. package/dist/domain/identity/resolve-self.d.ts +0 -21
  228. package/dist/domain/identity/resolve-self.d.ts.map +0 -1
  229. package/dist/domain/identity/resolve-self.js +0 -36
  230. package/dist/domain/intent/middleware/capabilities.d.ts.map +0 -1
  231. package/dist/domain/intent/middleware/logging.d.ts.map +0 -1
  232. package/dist/domain/intent/middleware/rate-limit.d.ts.map +0 -1
  233. package/dist/domain/intent/pipeline.d.ts +0 -3
  234. package/dist/domain/intent/pipeline.d.ts.map +0 -1
  235. package/dist/domain/intent/router.d.ts +0 -6
  236. package/dist/domain/intent/router.d.ts.map +0 -1
  237. package/dist/domain/message/errors.d.ts +0 -2
  238. package/dist/domain/message/errors.d.ts.map +0 -1
  239. package/dist/domain/message/registries.d.ts.map +0 -1
  240. package/dist/domain/message/types.d.ts +0 -30
  241. package/dist/domain/message/types.d.ts.map +0 -1
  242. package/dist/domain/window/config.d.ts.map +0 -1
  243. package/dist/domain/window/lifecycle.d.ts.map +0 -1
  244. package/dist/domain/window/window.d.ts.map +0 -1
  245. package/dist/ports/kernel-client.d.ts +0 -33
  246. package/dist/ports/kernel-client.d.ts.map +0 -1
  247. package/dist/ports/shell-adapter.d.ts.map +0 -1
  248. package/dist/ports/transport.d.ts +0 -7
  249. package/dist/ports/transport.d.ts.map +0 -1
  250. package/src/application/kernel-facade.ts +0 -143
  251. package/src/application/shell.ts +0 -433
  252. package/src/application/windowing/mount-controller.ts +0 -335
  253. package/src/domain/identity/path.ts +0 -14
  254. package/src/domain/identity/resolve-self.ts +0 -38
  255. package/src/domain/intent/pipeline.ts +0 -3
  256. package/src/domain/intent/router.ts +0 -6
  257. package/src/domain/message/registries.ts +0 -76
  258. package/src/domain/message/types.ts +0 -32
  259. package/src/ports/kernel-client.ts +0 -32
  260. /package/dist/{application/channels/children-channel.js → channel/children.js} +0 -0
  261. /package/dist/{application/channels/types.js → channel/listeners.js} +0 -0
  262. /package/dist/{adapters/message-port.transport.js → channel/message-port.js} +0 -0
  263. /package/dist/{application/channels → channel}/observer.js +0 -0
  264. /package/dist/{ports → channel}/transport.js +0 -0
  265. /package/dist/{domain/intent → intent}/middleware/capabilities.js +0 -0
  266. /package/dist/{domain/intent → intent}/middleware/logging.d.ts +0 -0
  267. /package/dist/{domain/intent → intent}/middleware/logging.js +0 -0
  268. /package/dist/{domain/intent → intent}/middleware/rate-limit.js +0 -0
  269. /package/dist/{application/intent/pipeline-runner.js → intent/pipeline.js} +0 -0
  270. /package/dist/{domain/intent/pipeline.js → kernel/client.js} +0 -0
  271. /package/dist/{domain/identity/delegation.js → kernel/delegation/token.js} +0 -0
  272. /package/dist/{domain/intent/router.js → message/ctrl.js} +0 -0
  273. /package/dist/{domain/message/errors.js → message/error.js} +0 -0
  274. /package/dist/{application/windowing/protocol.js → message/init.js} +0 -0
  275. /package/dist/{domain/message/registries.js → message/intent.js} +0 -0
  276. /package/dist/{domain/message/types.js → message/message.js} +0 -0
  277. /package/dist/{domain/window → shell}/config.js +0 -0
  278. /package/dist/{domain/window/window.js → shell/shell.js} +0 -0
  279. /package/dist/{ports/kernel-client.js → window/capabilities.js} +0 -0
  280. /package/dist/{ports/shell-adapter.js → window/embed/adapter.js} +0 -0
  281. /package/dist/{adapters/iframe.shell-adapter.js → window/embed/iframe.js} +0 -0
  282. /package/dist/{application/windowing → window/mount}/handshake.d.ts +0 -0
  283. /package/src/{domain/identity/delegation.ts → kernel/delegation/token.ts} +0 -0
  284. /package/src/{application/windowing/protocol.ts → message/init.ts} +0 -0
@@ -0,0 +1,27 @@
1
+ import type { ChildrenChannelController } from '../../channel/children';
2
+ import type { TokenRefresher } from '../../kernel/delegation/refresh';
3
+ import type { WindowHandle } from '../embed/adapter';
4
+ import type { LifecycleController } from '../lifecycle/states';
5
+ import type { Window } from '../window';
6
+ import type { CloseResult } from './params';
7
+ /** A mounted window's runtime entry — everything `close()` must tear down. */
8
+ export type MountEntry = {
9
+ readonly handle: WindowHandle;
10
+ readonly dispose: () => void;
11
+ readonly refresher: TokenRefresher;
12
+ /** Plain (no-handshake) mount: no transport, no token, no close ack. */
13
+ readonly plain?: boolean;
14
+ };
15
+ export type CloseDeps = {
16
+ entries: Map<string, MountEntry>;
17
+ children: ChildrenChannelController;
18
+ windows: Map<string, Window>;
19
+ lifecycle: LifecycleController;
20
+ };
21
+ export type Closer = (windowId: string, opts?: {
22
+ force?: boolean;
23
+ timeoutMs?: number;
24
+ }) => Promise<CloseResult>;
25
+ /** The close flow: willClose → ack/refuse (unless forced or plain), then ordered teardown. */
26
+ export declare function createCloser(deps: CloseDeps): Closer;
27
+ //# sourceMappingURL=close.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../../src/window/mount/close.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAM3C,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;IAClC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAChC,QAAQ,EAAE,yBAAyB,CAAA;IACnC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,EAAE,mBAAmB,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,MAAM,GAAG,CACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,KAC3C,OAAO,CAAC,WAAW,CAAC,CAAA;AAEzB,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAmCpD"}
@@ -0,0 +1,74 @@
1
+ const DEFAULT_CLOSE_TIMEOUT = 3_000;
2
+ /** Trusted sender id stamped on the parent's own close-protocol intents. */
3
+ const PARENT_SENDER_ID = '<parent>';
4
+ /** The close flow: willClose → ack/refuse (unless forced or plain), then ordered teardown. */
5
+ export function createCloser(deps) {
6
+ const { entries, children, windows, lifecycle } = deps;
7
+ return async (windowId, opts) => {
8
+ const entry = entries.get(windowId);
9
+ if (!entry)
10
+ return { kind: 'noop' };
11
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_CLOSE_TIMEOUT;
12
+ // Plain mounts have no channel to ack a close; always force.
13
+ if (!opts?.force && !entry.plain) {
14
+ // Move the window into `closing` so the state machine accepts the
15
+ // subsequent `closeRefuse`/`forceClose` transition (both are only valid
16
+ // from `closing`).
17
+ lifecycle.apply(windowId, { kind: 'requestClose' });
18
+ const accepted = await requestCloseAck(children, windowId, timeoutMs);
19
+ if (accepted.kind === 'refused') {
20
+ lifecycle.apply(windowId, { kind: 'closeRefuse' });
21
+ return { kind: 'refused', reason: accepted.reason };
22
+ }
23
+ }
24
+ lifecycle.apply(windowId, { kind: 'forceClose' });
25
+ entry.refresher.stop();
26
+ // Drop the entry before detaching: `detach` synchronously closes the
27
+ // transport, whose `onClose` re-enters `close()`. Removing the entry first
28
+ // makes that re-entrant call's `entries.has(windowId)` guard short-circuit,
29
+ // so `entry.dispose()` runs exactly once (adapters need not be idempotent).
30
+ entries.delete(windowId);
31
+ children.detach(windowId);
32
+ windows.delete(windowId);
33
+ entry.dispose();
34
+ lifecycle.remove(windowId);
35
+ return { kind: 'closed' };
36
+ };
37
+ }
38
+ async function requestCloseAck(children, windowId, timeoutMs) {
39
+ return new Promise((resolve) => {
40
+ let settled = false;
41
+ const off = children.on('intent', (from, message) => {
42
+ if (from !== windowId)
43
+ return;
44
+ if (message.envelope.name === 'closeAck') {
45
+ settled = true;
46
+ off();
47
+ clearTimeout(timer);
48
+ resolve({ kind: 'ack' });
49
+ }
50
+ else if (message.envelope.name === 'closeRefuse') {
51
+ settled = true;
52
+ off();
53
+ clearTimeout(timer);
54
+ const reason = message.envelope.payload.reason;
55
+ resolve({ kind: 'refused', reason });
56
+ }
57
+ });
58
+ const timer = setTimeout(() => {
59
+ if (settled)
60
+ return;
61
+ off();
62
+ resolve({ kind: 'timeout' });
63
+ }, timeoutMs);
64
+ children.send(windowId, {
65
+ type: 'intent',
66
+ version: 1,
67
+ envelope: {
68
+ name: 'willClose',
69
+ payload: {},
70
+ sender: { windowId: PARENT_SENDER_ID },
71
+ },
72
+ });
73
+ });
74
+ }
@@ -0,0 +1,41 @@
1
+ import type { ChildrenChannelController } from '../../channel/children';
2
+ import type { KernelClient } from '../../kernel/client';
3
+ import type { Window } from '../window';
4
+ import type { MountCredential } from './params';
5
+ import { type TokenRefresher } from '../../kernel/delegation/refresh';
6
+ export type AcquiredCredential = {
7
+ credential: string;
8
+ expiresAt: number;
9
+ };
10
+ /** No-op refresher for windows with no token to refresh (plain mounts, no-refresh credentials). */
11
+ export declare const NOOP_REFRESHER: TokenRefresher;
12
+ /**
13
+ * Pick where the window's credential comes from — and how it refreshes.
14
+ *
15
+ * A PROVIDED credential (the app-principal path — e.g. `app::open`'s composed
16
+ * credential) is used as-is and refreshes through its own callback (re-calling
17
+ * `app::open`); no callback → no refresh (the window lives at most until
18
+ * `expiresAt`). Otherwise the shell mints — and re-mints — its own delegation.
19
+ */
20
+ export declare function credentialSource(opts: {
21
+ kernel: KernelClient;
22
+ ttlSeconds: number;
23
+ provided?: MountCredential;
24
+ }): {
25
+ acquire: () => Promise<AcquiredCredential>;
26
+ refreshMint: (() => Promise<AcquiredCredential>) | null;
27
+ };
28
+ /**
29
+ * Wire the per-window refresher: on each refresh, update the Window store and
30
+ * push a `ctrl:tokenRefresh` down the child's channel.
31
+ */
32
+ export declare function wireTokenRefresher(opts: {
33
+ windowId: string;
34
+ functionId: string;
35
+ initial: AcquiredCredential;
36
+ refreshMint: (() => Promise<AcquiredCredential>) | null;
37
+ windows: Map<string, Window>;
38
+ children: ChildrenChannelController;
39
+ onFailure: () => void;
40
+ }): TokenRefresher;
41
+ //# sourceMappingURL=credential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../../src/window/mount/credential.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAA;AAE3F,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1E,mGAAmG;AACnG,eAAO,MAAM,cAAc,EAAE,cAI5B,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B,GAAG;IACF,OAAO,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC1C,WAAW,EAAE,CAAC,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC,GAAG,IAAI,CAAA;CACxD,CAkBA;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,kBAAkB,CAAA;IAC3B,WAAW,EAAE,CAAC,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC,GAAG,IAAI,CAAA;IACvD,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,QAAQ,EAAE,yBAAyB,CAAA;IACnC,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,GAAG,cAAc,CAqCjB"}
@@ -0,0 +1,74 @@
1
+ import { createTokenRefresher } from '../../kernel/delegation/refresh';
2
+ /** No-op refresher for windows with no token to refresh (plain mounts, no-refresh credentials). */
3
+ export const NOOP_REFRESHER = {
4
+ start: () => { },
5
+ stop: () => { },
6
+ forceRefreshNow: () => Promise.resolve(),
7
+ };
8
+ /**
9
+ * Pick where the window's credential comes from — and how it refreshes.
10
+ *
11
+ * A PROVIDED credential (the app-principal path — e.g. `app::open`'s composed
12
+ * credential) is used as-is and refreshes through its own callback (re-calling
13
+ * `app::open`); no callback → no refresh (the window lives at most until
14
+ * `expiresAt`). Otherwise the shell mints — and re-mints — its own delegation.
15
+ */
16
+ export function credentialSource(opts) {
17
+ const mint = async () => {
18
+ const credential = await opts.kernel.mintDelegation();
19
+ return { credential, expiresAt: Date.now() + opts.ttlSeconds * 1_000 };
20
+ };
21
+ const provided = opts.provided;
22
+ if (!provided)
23
+ return { acquire: mint, refreshMint: mint };
24
+ return {
25
+ acquire: () => Promise.resolve({ credential: provided.token, expiresAt: provided.expiresAt }),
26
+ refreshMint: provided.refresh
27
+ ? async () => {
28
+ const next = await provided.refresh();
29
+ return { credential: next.token, expiresAt: next.expiresAt };
30
+ }
31
+ : null,
32
+ };
33
+ }
34
+ /**
35
+ * Wire the per-window refresher: on each refresh, update the Window store and
36
+ * push a `ctrl:tokenRefresh` down the child's channel.
37
+ */
38
+ export function wireTokenRefresher(opts) {
39
+ const { windowId, refreshMint } = opts;
40
+ if (!refreshMint)
41
+ return NOOP_REFRESHER;
42
+ // The live expiry advances on every refresh. The Window store only keeps
43
+ // the credential, so track expiry here — otherwise `token()` keeps
44
+ // returning the original expiry and `schedule()` collapses its lead time
45
+ // to 0, busy-looping the mint forever after the first refresh.
46
+ let currentExpiresAt = opts.initial.expiresAt;
47
+ return createTokenRefresher({
48
+ functionId: opts.functionId,
49
+ token: () => {
50
+ const stored = opts.windows.get(windowId);
51
+ return {
52
+ credential: stored?.delegationToken ?? opts.initial.credential,
53
+ expiresAt: currentExpiresAt,
54
+ issuedAt: 0,
55
+ scope: { functionId: opts.functionId },
56
+ };
57
+ },
58
+ setToken: (next) => {
59
+ currentExpiresAt = next.expiresAt;
60
+ const stored = opts.windows.get(windowId);
61
+ if (stored) {
62
+ opts.windows.set(windowId, { ...stored, delegationToken: next.credential });
63
+ }
64
+ opts.children.send(windowId, {
65
+ type: 'ctrl',
66
+ version: 1,
67
+ action: 'tokenRefresh',
68
+ data: { delegationToken: next.credential, tokenExpiresAt: next.expiresAt },
69
+ });
70
+ },
71
+ mint: refreshMint,
72
+ onFailure: opts.onFailure,
73
+ });
74
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../../src/window/mount/handshake.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAC3B,CAAA;AAED,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAsEpF"}
@@ -1,4 +1,4 @@
1
- import { INIT_RESPONSE_TYPE, isInitRequest } from './protocol';
1
+ import { INIT_RESPONSE_TYPE, isInitRequest } from '../../message/init';
2
2
  /** Parent-side handshake: wait for INIT_REQUEST, transfer a MessagePort, complete ctrl:handshake. */
3
3
  export function runParentHandshake(params) {
4
4
  const { iframe, expectedOrigin, timeoutMs } = params;
@@ -1,9 +1,9 @@
1
- import type { Window } from '../../domain/window/window';
2
- import type { KernelClient } from '../../ports/kernel-client';
3
- import type { ShellAdapter } from '../../ports/shell-adapter';
4
- import type { ChildrenChannelController } from '../channels/children-channel';
5
- import type { MountParams, MountedWindow, CloseResult } from '../shell';
6
- import type { LifecycleController } from './lifecycle-controller';
1
+ import type { ChildrenChannelController } from '../../channel/children';
2
+ import type { KernelClient } from '../../kernel/client';
3
+ import type { ShellAdapter } from '../embed/adapter';
4
+ import type { LifecycleController } from '../lifecycle/states';
5
+ import type { Window } from '../window';
6
+ import type { CloseResult, MountedWindow, MountParams } from './params';
7
7
  export type MountControllerConfig = {
8
8
  adapter: ShellAdapter;
9
9
  kernel: KernelClient;
@@ -22,4 +22,4 @@ export interface MountController {
22
22
  dispose(): Promise<void>;
23
23
  }
24
24
  export declare function createMountController(config: MountControllerConfig): MountController;
25
- //# sourceMappingURL=mount-controller.d.ts.map
25
+ //# sourceMappingURL=mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/window/mount/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAWvE,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,YAAY,CAAA;IACrB,MAAM,EAAE,YAAY,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,yBAAyB,CAAA;IAC1C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,EAAE,mBAAmB,CAAA;CAC/B,CAAA;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAClD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAuJpF"}
@@ -0,0 +1,143 @@
1
+ import { createMessagePortTransport } from '../../channel/message-port';
2
+ import { createCloser } from './close';
3
+ import { credentialSource, wireTokenRefresher } from './credential';
4
+ import { runParentHandshake } from './handshake';
5
+ import { mountPlain } from './plain';
6
+ const DEFAULT_HANDSHAKE_TIMEOUT = 5_000;
7
+ const DEFAULT_DELEGATION_TTL = 3_600;
8
+ export function createMountController(config) {
9
+ const { adapter, kernel, kernelUrl, mintIdentity, childrenChannel, windows, lifecycle } = config;
10
+ // The child iframe is always a DIFFERENT origin from this parent window, so a
11
+ // relative kernelUrl (e.g. the SPA's same-origin `/api/`) handed across the
12
+ // handshake would resolve against the iframe's OWN origin and POST to the
13
+ // wrong host. Absolutize it against the parent's location once so every child
14
+ // calls THIS origin's kernel. (The parent's own calls keep using the relative
15
+ // form via its kernel client / fetch.)
16
+ const childKernelUrl = new URL(kernelUrl, window.location.href).href;
17
+ const entries = new Map();
18
+ let seq = 0;
19
+ let disposed = false;
20
+ const close = createCloser({ entries, children: childrenChannel, windows, lifecycle });
21
+ function nextWindowId(functionId) {
22
+ seq += 1;
23
+ const slug = functionId.replace(/[^a-z0-9]/gi, '').slice(-8) || 'fn';
24
+ return `win-${slug}-${seq}`;
25
+ }
26
+ function originOf(url) {
27
+ try {
28
+ return new URL(url, window.location.href).origin;
29
+ }
30
+ catch {
31
+ throw new Error(`Invalid mount url: ${url}`);
32
+ }
33
+ }
34
+ const controller = {
35
+ async mount(params) {
36
+ if (disposed)
37
+ throw new Error('MountController is disposed');
38
+ // Plain (no-handshake) path: hosted views that don't speak the shell
39
+ // protocol. Skip mint/handshake/transport/refresher; the iframe still
40
+ // renders and `close()` falls through to dispose.
41
+ if (params.handshake === 'none') {
42
+ const windowId = nextWindowId(params.functionId);
43
+ return mountPlain({ adapter, windows, lifecycle, entries, close }, windowId, params);
44
+ }
45
+ const ttlSeconds = params.delegationTtlSeconds ?? DEFAULT_DELEGATION_TTL;
46
+ const handshakeTimeout = params.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT;
47
+ const expectedOrigin = originOf(params.url);
48
+ const windowId = nextWindowId(params.functionId);
49
+ const { acquire, refreshMint } = credentialSource({
50
+ kernel,
51
+ ttlSeconds,
52
+ provided: params.credential,
53
+ });
54
+ // Mint the token and create the iframe in parallel — the iframe
55
+ // doesn't need the credential until the handshake fires.
56
+ const [{ credential, expiresAt }, mounted] = await Promise.all([
57
+ acquire(),
58
+ Promise.resolve(adapter.mount({
59
+ functionId: params.functionId,
60
+ isolation: 'sandboxed',
61
+ capabilities: params.capabilities,
62
+ sandbox: params.sandbox,
63
+ })),
64
+ ]);
65
+ const { handle, dispose: disposeHandle } = mounted;
66
+ if (!(handle.element instanceof HTMLIFrameElement)) {
67
+ disposeHandle();
68
+ throw new Error('Shell adapter did not produce an HTMLIFrameElement');
69
+ }
70
+ params.host.appendChild(handle.element);
71
+ handle.element.src = params.url;
72
+ let handshakeResult;
73
+ try {
74
+ handshakeResult = await runParentHandshake({
75
+ iframe: handle.element,
76
+ expectedOrigin,
77
+ windowId,
78
+ delegationToken: credential,
79
+ tokenExpiresAt: expiresAt,
80
+ kernelUrl: childKernelUrl,
81
+ functionId: params.functionId,
82
+ targetNodeId: params.targetNodeId,
83
+ mintIdentity,
84
+ timeoutMs: handshakeTimeout,
85
+ });
86
+ }
87
+ catch (err) {
88
+ disposeHandle();
89
+ throw err;
90
+ }
91
+ const transport = createMessagePortTransport({
92
+ port: handshakeResult.port,
93
+ onClose: () => {
94
+ if (!entries.has(windowId))
95
+ return;
96
+ lifecycle.markCrashed(windowId);
97
+ void close(windowId, { force: true }).catch(() => { });
98
+ },
99
+ });
100
+ childrenChannel.attach(windowId, transport);
101
+ windows.set(windowId, {
102
+ windowId,
103
+ functionId: params.functionId,
104
+ targetNodeId: params.targetNodeId,
105
+ isolation: 'sandboxed',
106
+ state: 'active',
107
+ delegationToken: credential,
108
+ capabilities: params.capabilities,
109
+ });
110
+ lifecycle.register(windowId, 'active');
111
+ const refresher = wireTokenRefresher({
112
+ windowId,
113
+ functionId: params.functionId,
114
+ initial: { credential, expiresAt },
115
+ refreshMint,
116
+ windows,
117
+ children: childrenChannel,
118
+ onFailure: () => {
119
+ void close(windowId, { force: false }).catch(() => { });
120
+ },
121
+ });
122
+ refresher.start();
123
+ entries.set(windowId, { handle, dispose: disposeHandle, refresher });
124
+ return {
125
+ windowId,
126
+ get window() {
127
+ return windows.get(windowId);
128
+ },
129
+ handle: { element: handle.element },
130
+ close: (opts) => close(windowId, opts),
131
+ };
132
+ },
133
+ close,
134
+ async dispose() {
135
+ if (disposed)
136
+ return;
137
+ disposed = true;
138
+ const ids = [...entries.keys()];
139
+ await Promise.all(ids.map((id) => close(id, { force: true })));
140
+ },
141
+ };
142
+ return controller;
143
+ }
@@ -0,0 +1,73 @@
1
+ import type { SandboxProfile, WindowCapabilities } from '../capabilities';
2
+ import type { Window } from '../window';
3
+ /**
4
+ * A PRE-MINTED credential to hand the mounted iframe instead of the shell's
5
+ * own delegation mint — the app-principal path. The caller composes it from
6
+ * `app::open()`'s components: the USER mints a pre-attenuated delegation
7
+ * (`union(intersect(roles, self), intersect(self, app))` — only the user can
8
+ * sign it, so the app never holds the raw user token), then the final
9
+ * envelope is minted AS the app (`session.as(appCredential)`). The iframe
10
+ * then acts as the application with exactly that authority.
11
+ */
12
+ export type MountCredential = {
13
+ /** The token the iframe authenticates with. */
14
+ readonly token: string;
15
+ /** Epoch-ms expiry — drives the refresh schedule. */
16
+ readonly expiresAt: number;
17
+ /**
18
+ * Re-mint before expiry (typically re-calling `app::open` and returning its
19
+ * fresh `credential`/`expiresAt`). Omit to disable refresh — the window then
20
+ * lives at most until `expiresAt`.
21
+ */
22
+ readonly refresh?: () => Promise<{
23
+ token: string;
24
+ expiresAt: number;
25
+ }>;
26
+ };
27
+ export type MountParams = {
28
+ readonly host: HTMLElement;
29
+ readonly url: string;
30
+ readonly functionId: string;
31
+ readonly capabilities: WindowCapabilities;
32
+ readonly targetNodeId?: string;
33
+ /** See `WindowConfig.sandbox` — `null` opts the iframe out of the attribute. */
34
+ readonly sandbox?: SandboxProfile | null;
35
+ readonly handshakeTimeoutMs?: number;
36
+ readonly delegationTtlSeconds?: number;
37
+ /**
38
+ * Mount with THIS credential instead of self-minting one (see
39
+ * {@link MountCredential}). When present, `delegationTtlSeconds` is inert —
40
+ * the provided `expiresAt` (and `refresh`) govern the token's lifetime.
41
+ */
42
+ readonly credential?: MountCredential;
43
+ /**
44
+ * `'shell'` (default) runs the parent-side handshake, opening a MessagePort
45
+ * transport with delegation tokens and an intent channel. `'none'` mounts a
46
+ * plain iframe with no transport — used for hosted views that don't speak
47
+ * the shell protocol (`BlaxelComputer.deployFunction(kind:'view')`), so they
48
+ * render immediately instead of paying the 5 s handshake timeout. In `'none'`
49
+ * mode `capabilities` / `targetNodeId` / `delegationTtlSeconds` are inert
50
+ * (no channel to deliver them).
51
+ */
52
+ readonly handshake?: 'shell' | 'none';
53
+ };
54
+ export type CloseResult = {
55
+ kind: 'closed';
56
+ } | {
57
+ kind: 'refused';
58
+ reason?: string;
59
+ } | {
60
+ kind: 'noop';
61
+ };
62
+ export type MountedWindow = {
63
+ readonly windowId: string;
64
+ readonly window: Window;
65
+ readonly handle: {
66
+ element: HTMLIFrameElement | HTMLElement;
67
+ };
68
+ close(opts?: {
69
+ force?: boolean;
70
+ timeoutMs?: number;
71
+ }): Promise<CloseResult>;
72
+ };
73
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../../../src/window/mount/params.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACvE,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IACxC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAA;IACrC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpB,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,iBAAiB,GAAG,WAAW,CAAA;KAAE,CAAA;IAC7D,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAC5E,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import type { ShellAdapter } from '../embed/adapter';
2
+ import type { LifecycleController } from '../lifecycle/states';
3
+ import type { Window } from '../window';
4
+ import type { Closer, MountEntry } from './close';
5
+ import type { MountedWindow, MountParams } from './params';
6
+ export type PlainMountDeps = {
7
+ adapter: ShellAdapter;
8
+ windows: Map<string, Window>;
9
+ lifecycle: LifecycleController;
10
+ entries: Map<string, MountEntry>;
11
+ close: Closer;
12
+ };
13
+ /**
14
+ * Plain-iframe mount path — for hosted views that don't speak the shell
15
+ * protocol. No handshake, no transport, no delegation token, no refresher;
16
+ * the iframe just renders. `close()` skips the ack request (no channel) and
17
+ * falls straight to dispose.
18
+ */
19
+ export declare function mountPlain(deps: PlainMountDeps, windowId: string, params: MountParams): Promise<MountedWindow>;
20
+ //# sourceMappingURL=plain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain.d.ts","sourceRoot":"","sources":["../../../src/window/mount/plain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAI1D,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,EAAE,mBAAmB,CAAA;IAC9B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,aAAa,CAAC,CAyCxB"}
@@ -0,0 +1,46 @@
1
+ import { NOOP_REFRESHER } from './credential';
2
+ /**
3
+ * Plain-iframe mount path — for hosted views that don't speak the shell
4
+ * protocol. No handshake, no transport, no delegation token, no refresher;
5
+ * the iframe just renders. `close()` skips the ack request (no channel) and
6
+ * falls straight to dispose.
7
+ */
8
+ export async function mountPlain(deps, windowId, params) {
9
+ const { adapter, windows, lifecycle, entries, close } = deps;
10
+ const { handle, dispose: disposeHandle } = adapter.mount({
11
+ functionId: params.functionId,
12
+ isolation: 'sandboxed',
13
+ capabilities: params.capabilities,
14
+ sandbox: params.sandbox,
15
+ });
16
+ if (!(handle.element instanceof HTMLIFrameElement)) {
17
+ disposeHandle();
18
+ throw new Error('Shell adapter did not produce an HTMLIFrameElement');
19
+ }
20
+ params.host.appendChild(handle.element);
21
+ handle.element.src = params.url;
22
+ windows.set(windowId, {
23
+ windowId,
24
+ functionId: params.functionId,
25
+ targetNodeId: params.targetNodeId,
26
+ isolation: 'sandboxed',
27
+ state: 'active',
28
+ delegationToken: null,
29
+ capabilities: params.capabilities,
30
+ });
31
+ lifecycle.register(windowId, 'active');
32
+ entries.set(windowId, {
33
+ handle,
34
+ dispose: disposeHandle,
35
+ refresher: NOOP_REFRESHER,
36
+ plain: true,
37
+ });
38
+ return {
39
+ windowId,
40
+ get window() {
41
+ return windows.get(windowId);
42
+ },
43
+ handle: { element: handle.element },
44
+ close: (opts) => close(windowId, opts),
45
+ };
46
+ }
@@ -1,4 +1,4 @@
1
- import type { WindowCapabilities } from './config';
1
+ import type { WindowCapabilities } from './capabilities';
2
2
  export type WindowState = 'active' | 'hidden' | 'closing' | 'closed';
3
3
  export interface Window {
4
4
  readonly windowId: string;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../src/window/window.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAExD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEpE,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,QAAQ,CAAA;IAC1C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;CAC1C"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@astrale-os/shell",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Astrale Shell SDK — parent/child/kernel communication",
5
+ "license": "Apache-2.0",
5
6
  "repository": {
6
7
  "type": "git",
7
8
  "url": "https://github.com/astrale-os/shell.git",
@@ -21,21 +22,25 @@
21
22
  }
22
23
  },
23
24
  "publishConfig": {
24
- "access": "restricted",
25
- "registry": "https://npm.pkg.github.com"
25
+ "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@astrale-os/kernel-client": ">=0.1.0 <1.0.0",
28
+ "@astrale-domains/workspace-schema": ">=0.2.0 <1.0.0",
29
+ "@astrale-os/kernel-client": ">=0.3.0 <1.0.0",
29
30
  "@astrale-os/kernel-core": ">=0.4.1 <1.0.0"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@typescript/native-preview": "latest",
33
- "typescript": "~6.0.1-rc"
34
+ "happy-dom": "^20.0.0",
35
+ "typescript": "~6.0.1-rc",
36
+ "vitest": "^3.2.4"
34
37
  },
35
38
  "scripts": {
36
39
  "build": "tsgo",
37
40
  "clean": "rm -rf dist *.tsbuildinfo",
38
- "typecheck": "tsgo --noEmit",
41
+ "typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.test.json",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
39
44
  "preinstall": "node .check-workspace.cjs"
40
45
  }
41
46
  }