@astrale-os/shell-react 0.1.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 (378) hide show
  1. package/LICENSE +202 -0
  2. package/dist/application/apps/app-host.d.ts +31 -0
  3. package/dist/application/apps/app-host.d.ts.map +1 -0
  4. package/dist/application/apps/app-host.js +59 -0
  5. package/dist/application/apps/app-logo.d.ts +19 -0
  6. package/dist/application/apps/app-logo.d.ts.map +1 -0
  7. package/dist/application/apps/app-logo.js +31 -0
  8. package/dist/application/apps/apps.hook.d.ts +12 -0
  9. package/dist/application/apps/apps.hook.d.ts.map +1 -0
  10. package/dist/application/apps/apps.hook.js +51 -0
  11. package/dist/application/apps/open-app.hook.d.ts +10 -0
  12. package/dist/application/apps/open-app.hook.d.ts.map +1 -0
  13. package/dist/application/apps/open-app.hook.js +7 -0
  14. package/dist/application/index.d.ts +18 -0
  15. package/dist/application/index.d.ts.map +1 -0
  16. package/dist/application/index.js +12 -0
  17. package/dist/application/roles.hook.d.ts +7 -0
  18. package/dist/application/roles.hook.d.ts.map +1 -0
  19. package/dist/application/roles.hook.js +23 -0
  20. package/dist/application/spaces/current-space.hook.d.ts +7 -0
  21. package/dist/application/spaces/current-space.hook.d.ts.map +1 -0
  22. package/dist/application/spaces/current-space.hook.js +27 -0
  23. package/dist/application/spaces/current-space.store.d.ts +32 -0
  24. package/dist/application/spaces/current-space.store.d.ts.map +1 -0
  25. package/dist/application/spaces/current-space.store.js +79 -0
  26. package/dist/application/spaces/space.hook.d.ts +5 -0
  27. package/dist/application/spaces/space.hook.d.ts.map +1 -0
  28. package/dist/application/spaces/space.hook.js +25 -0
  29. package/dist/application/spaces/spaces.hook.d.ts +14 -0
  30. package/dist/application/spaces/spaces.hook.d.ts.map +1 -0
  31. package/dist/application/spaces/spaces.hook.js +82 -0
  32. package/dist/application/user/user.hook.d.ts +10 -0
  33. package/dist/application/user/user.hook.d.ts.map +1 -0
  34. package/dist/application/user/user.hook.js +19 -0
  35. package/dist/application/user/user.store.d.ts +35 -0
  36. package/dist/application/user/user.store.d.ts.map +1 -0
  37. package/dist/application/user/user.store.js +51 -0
  38. package/dist/auth/act-as.context.d.ts +40 -0
  39. package/dist/auth/act-as.context.d.ts.map +1 -0
  40. package/dist/auth/act-as.context.js +84 -0
  41. package/dist/auth/auth.hook.d.ts +3 -0
  42. package/dist/auth/auth.hook.d.ts.map +1 -0
  43. package/dist/auth/auth.hook.js +25 -0
  44. package/dist/auth/can.hook.d.ts +40 -0
  45. package/dist/auth/can.hook.d.ts.map +1 -0
  46. package/dist/auth/can.hook.js +119 -0
  47. package/dist/auth/index.d.ts +6 -0
  48. package/dist/auth/index.d.ts.map +1 -0
  49. package/dist/auth/index.js +5 -0
  50. package/dist/auth/self.hook.d.ts +3 -0
  51. package/dist/auth/self.hook.d.ts.map +1 -0
  52. package/dist/auth/self.hook.js +11 -0
  53. package/dist/auth/session-kernel.d.ts +4 -0
  54. package/dist/auth/session-kernel.d.ts.map +1 -0
  55. package/dist/auth/session-kernel.js +21 -0
  56. package/dist/graph/action.d.ts +22 -0
  57. package/dist/graph/action.d.ts.map +1 -0
  58. package/dist/graph/action.js +85 -0
  59. package/dist/graph/async.d.ts +11 -0
  60. package/dist/graph/async.d.ts.map +1 -0
  61. package/dist/graph/async.js +60 -0
  62. package/dist/graph/call/call.hook.d.ts +21 -0
  63. package/dist/graph/call/call.hook.d.ts.map +1 -0
  64. package/dist/graph/call/call.hook.js +23 -0
  65. package/dist/graph/call/stream.hook.d.ts +28 -0
  66. package/dist/graph/call/stream.hook.d.ts.map +1 -0
  67. package/dist/graph/call/stream.hook.js +137 -0
  68. package/dist/graph/graph.context.d.ts +4 -0
  69. package/dist/graph/graph.context.d.ts.map +1 -0
  70. package/dist/graph/graph.context.js +10 -0
  71. package/dist/graph/guards.error.d.ts +9 -0
  72. package/dist/graph/guards.error.d.ts.map +1 -0
  73. package/dist/graph/guards.error.js +7 -0
  74. package/dist/graph/index.d.ts +20 -0
  75. package/dist/graph/index.d.ts.map +1 -0
  76. package/dist/graph/index.js +9 -0
  77. package/dist/graph/memory/base.d.ts +21 -0
  78. package/dist/graph/memory/base.d.ts.map +1 -0
  79. package/dist/graph/memory/base.js +33 -0
  80. package/dist/graph/memory/read/envelope.d.ts +71 -0
  81. package/dist/graph/memory/read/envelope.d.ts.map +1 -0
  82. package/dist/graph/memory/read/envelope.js +29 -0
  83. package/dist/graph/memory/read/idle.d.ts +18 -0
  84. package/dist/graph/memory/read/idle.d.ts.map +1 -0
  85. package/dist/graph/memory/read/idle.js +16 -0
  86. package/dist/graph/memory/read/read.d.ts +9 -0
  87. package/dist/graph/memory/read/read.d.ts.map +1 -0
  88. package/dist/graph/memory/read/read.js +87 -0
  89. package/dist/graph/memory/scope.d.ts +7 -0
  90. package/dist/graph/memory/scope.d.ts.map +1 -0
  91. package/dist/graph/memory/scope.js +25 -0
  92. package/dist/graph/mutate/move.hook.d.ts +7 -0
  93. package/dist/graph/mutate/move.hook.d.ts.map +1 -0
  94. package/dist/graph/mutate/move.hook.js +49 -0
  95. package/dist/graph/mutate/mutate.hook.d.ts +17 -0
  96. package/dist/graph/mutate/mutate.hook.d.ts.map +1 -0
  97. package/dist/graph/mutate/mutate.hook.js +45 -0
  98. package/dist/graph/query/children.hook.d.ts +29 -0
  99. package/dist/graph/query/children.hook.d.ts.map +1 -0
  100. package/dist/graph/query/children.hook.js +64 -0
  101. package/dist/graph/query/graph.hook.d.ts +9 -0
  102. package/dist/graph/query/graph.hook.d.ts.map +1 -0
  103. package/dist/graph/query/graph.hook.js +5 -0
  104. package/dist/graph/query/neighbors.hook.d.ts +7 -0
  105. package/dist/graph/query/neighbors.hook.d.ts.map +1 -0
  106. package/dist/graph/query/neighbors.hook.js +79 -0
  107. package/dist/graph/query/node.hook.d.ts +14 -0
  108. package/dist/graph/query/node.hook.d.ts.map +1 -0
  109. package/dist/graph/query/node.hook.js +43 -0
  110. package/dist/graph/query/query.hook.d.ts +14 -0
  111. package/dist/graph/query/query.hook.d.ts.map +1 -0
  112. package/dist/graph/query/query.hook.js +23 -0
  113. package/dist/index.d.ts +43 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +34 -0
  116. package/dist/intents/envelope.d.ts +33 -0
  117. package/dist/intents/envelope.d.ts.map +1 -0
  118. package/dist/intents/envelope.js +19 -0
  119. package/dist/intents/index.d.ts +6 -0
  120. package/dist/intents/index.d.ts.map +1 -0
  121. package/dist/intents/index.js +3 -0
  122. package/dist/intents/listen-intent.hook.d.ts +16 -0
  123. package/dist/intents/listen-intent.hook.d.ts.map +1 -0
  124. package/dist/intents/listen-intent.hook.js +75 -0
  125. package/dist/intents/reply.d.ts +9 -0
  126. package/dist/intents/reply.d.ts.map +1 -0
  127. package/dist/intents/reply.js +14 -0
  128. package/dist/intents/send-intent.hook.d.ts +12 -0
  129. package/dist/intents/send-intent.hook.d.ts.map +1 -0
  130. package/dist/intents/send-intent.hook.js +86 -0
  131. package/dist/navigation/adapters/adapter.d.ts +15 -0
  132. package/dist/navigation/adapters/adapter.d.ts.map +1 -0
  133. package/dist/navigation/adapters/adapter.js +1 -0
  134. package/dist/navigation/adapters/memory.adapter.d.ts +9 -0
  135. package/dist/navigation/adapters/memory.adapter.d.ts.map +1 -0
  136. package/dist/navigation/adapters/memory.adapter.js +15 -0
  137. package/dist/navigation/adapters/url.adapter.d.ts +6 -0
  138. package/dist/navigation/adapters/url.adapter.d.ts.map +1 -0
  139. package/dist/navigation/adapters/url.adapter.js +59 -0
  140. package/dist/navigation/history.d.ts +63 -0
  141. package/dist/navigation/history.d.ts.map +1 -0
  142. package/dist/navigation/history.js +144 -0
  143. package/dist/navigation/hooks/ancestors.hook.d.ts +13 -0
  144. package/dist/navigation/hooks/ancestors.hook.d.ts.map +1 -0
  145. package/dist/navigation/hooks/ancestors.hook.js +56 -0
  146. package/dist/navigation/hooks/location.hook.d.ts +4 -0
  147. package/dist/navigation/hooks/location.hook.d.ts.map +1 -0
  148. package/dist/navigation/hooks/location.hook.js +17 -0
  149. package/dist/navigation/hooks/navigate.hook.d.ts +12 -0
  150. package/dist/navigation/hooks/navigate.hook.d.ts.map +1 -0
  151. package/dist/navigation/hooks/navigate.hook.js +33 -0
  152. package/dist/navigation/hooks/scope.hook.d.ts +12 -0
  153. package/dist/navigation/hooks/scope.hook.d.ts.map +1 -0
  154. package/dist/navigation/hooks/scope.hook.js +21 -0
  155. package/dist/navigation/index.d.ts +16 -0
  156. package/dist/navigation/index.d.ts.map +1 -0
  157. package/dist/navigation/index.js +10 -0
  158. package/dist/navigation/link.d.ts +11 -0
  159. package/dist/navigation/link.d.ts.map +1 -0
  160. package/dist/navigation/link.js +62 -0
  161. package/dist/navigation/location.d.ts +48 -0
  162. package/dist/navigation/location.d.ts.map +1 -0
  163. package/dist/navigation/location.js +88 -0
  164. package/dist/navigation/scope.context.d.ts +64 -0
  165. package/dist/navigation/scope.context.d.ts.map +1 -0
  166. package/dist/navigation/scope.context.js +178 -0
  167. package/dist/navigation/snapshot.d.ts +22 -0
  168. package/dist/navigation/snapshot.d.ts.map +1 -0
  169. package/dist/navigation/snapshot.js +65 -0
  170. package/dist/schema/bind-read.d.ts +37 -0
  171. package/dist/schema/bind-read.d.ts.map +1 -0
  172. package/dist/schema/bind-read.js +205 -0
  173. package/dist/schema/binding.d.ts +21 -0
  174. package/dist/schema/binding.d.ts.map +1 -0
  175. package/dist/schema/binding.js +55 -0
  176. package/dist/schema/domain.hook.d.ts +10 -0
  177. package/dist/schema/domain.hook.d.ts.map +1 -0
  178. package/dist/schema/domain.hook.js +7 -0
  179. package/dist/schema/function.hook.d.ts +9 -0
  180. package/dist/schema/function.hook.d.ts.map +1 -0
  181. package/dist/schema/function.hook.js +40 -0
  182. package/dist/schema/index.d.ts +10 -0
  183. package/dist/schema/index.d.ts.map +1 -0
  184. package/dist/schema/index.js +8 -0
  185. package/dist/schema/kernel.hook.d.ts +10 -0
  186. package/dist/schema/kernel.hook.d.ts.map +1 -0
  187. package/dist/schema/kernel.hook.js +8 -0
  188. package/dist/schema/method.hook.d.ts +14 -0
  189. package/dist/schema/method.hook.d.ts.map +1 -0
  190. package/dist/schema/method.hook.js +49 -0
  191. package/dist/schema/tag.d.ts +11 -0
  192. package/dist/schema/tag.d.ts.map +1 -0
  193. package/dist/schema/tag.js +11 -0
  194. package/dist/schema/types.d.ts +73 -0
  195. package/dist/schema/types.d.ts.map +1 -0
  196. package/dist/schema/types.js +1 -0
  197. package/dist/schema/write.d.ts +15 -0
  198. package/dist/schema/write.d.ts.map +1 -0
  199. package/dist/schema/write.js +45 -0
  200. package/dist/selection/index.d.ts +4 -0
  201. package/dist/selection/index.d.ts.map +1 -0
  202. package/dist/selection/index.js +4 -0
  203. package/dist/selection/live.d.ts +21 -0
  204. package/dist/selection/live.d.ts.map +1 -0
  205. package/dist/selection/live.js +116 -0
  206. package/dist/selection/scope.context.d.ts +26 -0
  207. package/dist/selection/scope.context.d.ts.map +1 -0
  208. package/dist/selection/scope.context.js +100 -0
  209. package/dist/selection/selection.hook.d.ts +4 -0
  210. package/dist/selection/selection.hook.d.ts.map +1 -0
  211. package/dist/selection/selection.hook.js +14 -0
  212. package/dist/session/boot/boot.d.ts +19 -0
  213. package/dist/session/boot/boot.d.ts.map +1 -0
  214. package/dist/session/boot/boot.js +45 -0
  215. package/dist/session/boot/ownership.d.ts +18 -0
  216. package/dist/session/boot/ownership.d.ts.map +1 -0
  217. package/dist/session/boot/ownership.js +39 -0
  218. package/dist/session/boot/status.d.ts +24 -0
  219. package/dist/session/boot/status.d.ts.map +1 -0
  220. package/dist/session/boot/status.js +11 -0
  221. package/dist/session/connection/connection.hook.d.ts +3 -0
  222. package/dist/session/connection/connection.hook.d.ts.map +1 -0
  223. package/dist/session/connection/connection.hook.js +12 -0
  224. package/dist/session/connection/connection.store.d.ts +24 -0
  225. package/dist/session/connection/connection.store.d.ts.map +1 -0
  226. package/dist/session/connection/connection.store.js +31 -0
  227. package/dist/session/index.d.ts +8 -0
  228. package/dist/session/index.d.ts.map +1 -0
  229. package/dist/session/index.js +6 -0
  230. package/dist/session/kernel-client.hook.d.ts +9 -0
  231. package/dist/session/kernel-client.hook.d.ts.map +1 -0
  232. package/dist/session/kernel-client.hook.js +5 -0
  233. package/dist/session/kernel-instance.hook.d.ts +9 -0
  234. package/dist/session/kernel-instance.hook.d.ts.map +1 -0
  235. package/dist/session/kernel-instance.hook.js +10 -0
  236. package/dist/session/provider.d.ts +31 -0
  237. package/dist/session/provider.d.ts.map +1 -0
  238. package/dist/session/provider.js +165 -0
  239. package/dist/session/session.context.d.ts +18 -0
  240. package/dist/session/session.context.d.ts.map +1 -0
  241. package/dist/session/session.context.js +8 -0
  242. package/dist/session/shell.hook.d.ts +4 -0
  243. package/dist/session/shell.hook.d.ts.map +1 -0
  244. package/dist/session/shell.hook.js +15 -0
  245. package/dist/window/adopt.d.ts +27 -0
  246. package/dist/window/adopt.d.ts.map +1 -0
  247. package/dist/window/adopt.js +27 -0
  248. package/dist/window/hooks/close-guard.hook.d.ts +6 -0
  249. package/dist/window/hooks/close-guard.hook.d.ts.map +1 -0
  250. package/dist/window/hooks/close-guard.hook.js +90 -0
  251. package/dist/window/hooks/target-node.hook.d.ts +7 -0
  252. package/dist/window/hooks/target-node.hook.d.ts.map +1 -0
  253. package/dist/window/hooks/target-node.hook.js +77 -0
  254. package/dist/window/hooks/views-for.hook.d.ts +8 -0
  255. package/dist/window/hooks/views-for.hook.d.ts.map +1 -0
  256. package/dist/window/hooks/views-for.hook.js +51 -0
  257. package/dist/window/hooks/window.hook.d.ts +4 -0
  258. package/dist/window/hooks/window.hook.d.ts.map +1 -0
  259. package/dist/window/hooks/window.hook.js +14 -0
  260. package/dist/window/hooks/windows.hook.d.ts +15 -0
  261. package/dist/window/hooks/windows.hook.d.ts.map +1 -0
  262. package/dist/window/hooks/windows.hook.js +25 -0
  263. package/dist/window/host.d.ts +31 -0
  264. package/dist/window/host.d.ts.map +1 -0
  265. package/dist/window/host.js +270 -0
  266. package/dist/window/hub.d.ts +41 -0
  267. package/dist/window/hub.d.ts.map +1 -0
  268. package/dist/window/hub.js +100 -0
  269. package/dist/window/index.d.ts +15 -0
  270. package/dist/window/index.d.ts.map +1 -0
  271. package/dist/window/index.js +10 -0
  272. package/dist/window/lifecycle/mount.d.ts +47 -0
  273. package/dist/window/lifecycle/mount.d.ts.map +1 -0
  274. package/dist/window/lifecycle/mount.js +92 -0
  275. package/dist/window/lifecycle/states.d.ts +33 -0
  276. package/dist/window/lifecycle/states.d.ts.map +1 -0
  277. package/dist/window/lifecycle/states.js +7 -0
  278. package/dist/window/placement.d.ts +33 -0
  279. package/dist/window/placement.d.ts.map +1 -0
  280. package/dist/window/placement.js +57 -0
  281. package/dist/window/stage.d.ts +3 -0
  282. package/dist/window/stage.d.ts.map +1 -0
  283. package/dist/window/stage.js +41 -0
  284. package/package.json +59 -0
  285. package/src/application/apps/app-host.tsx +113 -0
  286. package/src/application/apps/app-logo.tsx +56 -0
  287. package/src/application/apps/apps.hook.ts +81 -0
  288. package/src/application/apps/open-app.hook.ts +18 -0
  289. package/src/application/index.ts +22 -0
  290. package/src/application/roles.hook.ts +41 -0
  291. package/src/application/spaces/current-space.hook.ts +43 -0
  292. package/src/application/spaces/current-space.store.ts +110 -0
  293. package/src/application/spaces/space.hook.ts +41 -0
  294. package/src/application/spaces/spaces.hook.ts +113 -0
  295. package/src/application/user/user.hook.ts +27 -0
  296. package/src/application/user/user.store.ts +85 -0
  297. package/src/auth/act-as.context.tsx +124 -0
  298. package/src/auth/auth.hook.ts +36 -0
  299. package/src/auth/can.hook.ts +149 -0
  300. package/src/auth/index.ts +6 -0
  301. package/src/auth/self.hook.ts +14 -0
  302. package/src/auth/session-kernel.ts +24 -0
  303. package/src/graph/action.ts +127 -0
  304. package/src/graph/async.ts +87 -0
  305. package/src/graph/call/call.hook.ts +42 -0
  306. package/src/graph/call/stream.hook.ts +200 -0
  307. package/src/graph/graph.context.ts +14 -0
  308. package/src/graph/guards.error.ts +58 -0
  309. package/src/graph/index.ts +20 -0
  310. package/src/graph/memory/base.ts +50 -0
  311. package/src/graph/memory/read/envelope.ts +98 -0
  312. package/src/graph/memory/read/idle.ts +24 -0
  313. package/src/graph/memory/read/read.ts +103 -0
  314. package/src/graph/memory/scope.ts +29 -0
  315. package/src/graph/mutate/move.hook.ts +70 -0
  316. package/src/graph/mutate/mutate.hook.ts +69 -0
  317. package/src/graph/query/children.hook.ts +136 -0
  318. package/src/graph/query/graph.hook.ts +13 -0
  319. package/src/graph/query/neighbors.hook.ts +124 -0
  320. package/src/graph/query/node.hook.ts +83 -0
  321. package/src/graph/query/query.hook.ts +42 -0
  322. package/src/index.ts +202 -0
  323. package/src/intents/envelope.ts +52 -0
  324. package/src/intents/index.ts +6 -0
  325. package/src/intents/listen-intent.hook.ts +92 -0
  326. package/src/intents/reply.ts +24 -0
  327. package/src/intents/send-intent.hook.ts +111 -0
  328. package/src/navigation/adapters/adapter.ts +13 -0
  329. package/src/navigation/adapters/memory.adapter.ts +22 -0
  330. package/src/navigation/adapters/url.adapter.ts +61 -0
  331. package/src/navigation/history.ts +170 -0
  332. package/src/navigation/hooks/ancestors.hook.ts +71 -0
  333. package/src/navigation/hooks/location.hook.ts +21 -0
  334. package/src/navigation/hooks/navigate.hook.ts +43 -0
  335. package/src/navigation/hooks/scope.hook.ts +34 -0
  336. package/src/navigation/index.ts +17 -0
  337. package/src/navigation/link.tsx +110 -0
  338. package/src/navigation/location.ts +122 -0
  339. package/src/navigation/scope.context.tsx +267 -0
  340. package/src/navigation/snapshot.ts +79 -0
  341. package/src/schema/bind-read.ts +319 -0
  342. package/src/schema/binding.ts +91 -0
  343. package/src/schema/domain.hook.ts +16 -0
  344. package/src/schema/function.hook.ts +68 -0
  345. package/src/schema/index.ts +25 -0
  346. package/src/schema/kernel.hook.ts +17 -0
  347. package/src/schema/method.hook.ts +103 -0
  348. package/src/schema/tag.ts +19 -0
  349. package/src/schema/types.ts +150 -0
  350. package/src/schema/write.ts +80 -0
  351. package/src/selection/index.ts +5 -0
  352. package/src/selection/live.ts +142 -0
  353. package/src/selection/scope.context.tsx +151 -0
  354. package/src/selection/selection.hook.ts +18 -0
  355. package/src/session/boot/boot.ts +57 -0
  356. package/src/session/boot/ownership.ts +54 -0
  357. package/src/session/boot/status.ts +32 -0
  358. package/src/session/connection/connection.hook.ts +16 -0
  359. package/src/session/connection/connection.store.ts +58 -0
  360. package/src/session/index.ts +8 -0
  361. package/src/session/kernel-client.hook.ts +13 -0
  362. package/src/session/kernel-instance.hook.ts +22 -0
  363. package/src/session/provider.tsx +205 -0
  364. package/src/session/session.context.ts +27 -0
  365. package/src/session/shell.hook.ts +19 -0
  366. package/src/window/adopt.ts +57 -0
  367. package/src/window/hooks/close-guard.hook.ts +100 -0
  368. package/src/window/hooks/target-node.hook.ts +94 -0
  369. package/src/window/hooks/views-for.hook.ts +82 -0
  370. package/src/window/hooks/window.hook.ts +18 -0
  371. package/src/window/hooks/windows.hook.ts +40 -0
  372. package/src/window/host.tsx +347 -0
  373. package/src/window/hub.ts +156 -0
  374. package/src/window/index.ts +17 -0
  375. package/src/window/lifecycle/mount.ts +134 -0
  376. package/src/window/lifecycle/states.ts +23 -0
  377. package/src/window/placement.ts +84 -0
  378. package/src/window/stage.tsx +46 -0
@@ -0,0 +1,319 @@
1
+ import type { GatherBuilder, OrderInput, QuerySource } from '@astrale-os/kernel-client/graph'
2
+ import type {
3
+ LiveResult,
4
+ ReadOptions as StoreReadOptions,
5
+ ShapeInput,
6
+ } from '@astrale-os/kernel-client/store'
7
+ import type { Node } from '@astrale-os/kernel-core'
8
+ import type { Schema } from '@astrale-os/kernel-dsl'
9
+
10
+ import {
11
+ BindTypeMismatchError,
12
+ normalizeRef,
13
+ type AnyBoundNode,
14
+ type BoundNode,
15
+ type DefOf,
16
+ type NodeRefInput,
17
+ type SchemaBoundView,
18
+ type TypeNames,
19
+ } from '@astrale-os/kernel-client'
20
+ import { shapeInputToAST, stableStringify, stripCursors } from '@astrale-os/kernel-client/store'
21
+ import { compileDomain } from '@astrale-os/kernel-core/domain'
22
+ /**
23
+ * Binder-over-memory composition (API §6, §1.7) — consumed by the `graph/query/`
24
+ * overloads. Binds wire `Node`s from the shared memory to stable `BoundNode`s
25
+ * (an unchanged wire node re-binds to the SAME instance), and composes those
26
+ * binders with a `useSyncExternalStore` read substrate (`useShape`) into the typed
27
+ * `useNode`/`useChildren` reads. A typed read and its untyped twin ride the SAME
28
+ * memory shape, so they are one cache entry, one liveness, one overlay.
29
+ */
30
+ import { useCallback, useMemo, useRef, useSyncExternalStore } from 'react'
31
+
32
+ import type { ReadState, ReadOptions } from '../graph/memory/read/envelope'
33
+ import type { TypedChildrenResult, TypedNodeResult } from './types'
34
+
35
+ import { useSessionKernel } from '../auth/session-kernel'
36
+ import { idleReadState, isIdleInput } from '../graph/memory/read/idle'
37
+ import { useMemory } from '../graph/memory/scope'
38
+ import { bindingFor } from './binding'
39
+ import { schemaOf, tagSchema } from './tag'
40
+
41
+ // ── binder hooks ────────────────────────────────────────────────────────────────
42
+
43
+ interface OneCache<S extends Schema> {
44
+ wire: Node | null
45
+ binding: SchemaBoundView<S>
46
+ type: string
47
+ bound: BoundNode<unknown, S> | null
48
+ }
49
+
50
+ /** Bind one wire node as `type`; stable while the wire node, binding, and type hold. */
51
+ export function useBoundNode<S extends Schema, T extends TypeNames<S>>(
52
+ binding: SchemaBoundView<S>,
53
+ type: T,
54
+ wire: Node | null | undefined,
55
+ ): BoundNode<DefOf<S, T>, S> | null {
56
+ const ref = useRef<OneCache<S> | null>(null)
57
+ const node = wire ?? null
58
+ let entry = ref.current
59
+ if (entry === null || entry.wire !== node || entry.binding !== binding || entry.type !== type) {
60
+ entry = {
61
+ wire: node,
62
+ binding,
63
+ type,
64
+ bound: node === null ? null : (binding.bind(type, node) as BoundNode<unknown, S>),
65
+ }
66
+ ref.current = entry
67
+ }
68
+ return entry.bound as BoundNode<DefOf<S, T>, S> | null
69
+ }
70
+
71
+ interface ManyCache<S extends Schema> {
72
+ binding: SchemaBoundView<S>
73
+ type: string
74
+ perNode: WeakMap<Node, BoundNode<unknown, S>>
75
+ last: readonly BoundNode<unknown, S>[]
76
+ }
77
+
78
+ /**
79
+ * Bind many wire nodes as `type`; per-node identity + a stable array on no
80
+ * change. `skipMismatch` drops nodes whose class is not `type` (a mixed-class
81
+ * child set) instead of throwing.
82
+ */
83
+ export function useBoundNodes<S extends Schema, T extends TypeNames<S>>(
84
+ binding: SchemaBoundView<S>,
85
+ type: T,
86
+ wires: readonly Node[],
87
+ skipMismatch = false,
88
+ ): readonly BoundNode<DefOf<S, T>, S>[] {
89
+ const ref = useRef<ManyCache<S> | null>(null)
90
+ if (ref.current === null || ref.current.binding !== binding || ref.current.type !== type) {
91
+ ref.current = { binding, type, perNode: new WeakMap(), last: [] }
92
+ }
93
+ const cache = ref.current
94
+ const next: BoundNode<unknown, S>[] = []
95
+ for (const w of wires) {
96
+ let b = cache.perNode.get(w)
97
+ if (b === undefined) {
98
+ try {
99
+ b = binding.bind(type, w) as BoundNode<unknown, S>
100
+ } catch (error) {
101
+ if (skipMismatch && error instanceof BindTypeMismatchError) continue
102
+ throw error
103
+ }
104
+ cache.perNode.set(w, b)
105
+ }
106
+ next.push(b)
107
+ }
108
+ if (sameArray(cache.last, next)) return cache.last as readonly BoundNode<DefOf<S, T>, S>[]
109
+ cache.last = next
110
+ return next as readonly BoundNode<DefOf<S, T>, S>[]
111
+ }
112
+
113
+ interface EndpointCache<S extends Schema> {
114
+ binding: SchemaBoundView<S>
115
+ perNode: WeakMap<Node, AnyBoundNode<S>>
116
+ last: readonly AnyBoundNode<S>[]
117
+ }
118
+
119
+ /** Bind many wire nodes classlessly (traversal endpoints); per-node + stable array. */
120
+ export function useBoundEndpoints<S extends Schema>(
121
+ binding: SchemaBoundView<S>,
122
+ wires: readonly Node[],
123
+ ): readonly AnyBoundNode<S>[] {
124
+ const ref = useRef<EndpointCache<S> | null>(null)
125
+ if (ref.current === null || ref.current.binding !== binding) {
126
+ ref.current = { binding, perNode: new WeakMap(), last: [] }
127
+ }
128
+ const cache = ref.current
129
+ const next = wires.map((w) => {
130
+ let b = cache.perNode.get(w)
131
+ if (b === undefined) {
132
+ b = binding.bindAny(w)
133
+ cache.perNode.set(w, b)
134
+ }
135
+ return b
136
+ })
137
+ if (sameArray(cache.last, next)) return cache.last
138
+ cache.last = next
139
+ return next
140
+ }
141
+
142
+ function sameArray<A>(a: readonly A[], b: readonly A[]): boolean {
143
+ return a.length === b.length && a.every((x, i) => x === b[i])
144
+ }
145
+
146
+ // ── read substrate ────────────────────────────────────────────────────────────
147
+
148
+ /** Base read options plus the window growth mode (a `useChildren`-specific extra). */
149
+ export type ShapeOptions = ReadOptions & { window?: 'append' | 'paged' }
150
+
151
+ /** Store-facing options — render-time concerns (`suspend`/`keepPrevious`) stay here. */
152
+ function toStoreOptions(opts?: ShapeOptions): StoreReadOptions | undefined {
153
+ if (opts === undefined) return undefined
154
+ const out: StoreReadOptions = {}
155
+ if (opts.live !== undefined) out.live = opts.live
156
+ if (opts.window !== undefined) out.window = opts.window
157
+ if (opts.with !== undefined) out.with = opts.with as unknown as StoreReadOptions['with']
158
+ return out
159
+ }
160
+
161
+ /** A stable string identity for a (shape, options) pair — the subscription key. */
162
+ function keyOf(shape: ShapeInput, opts?: ShapeOptions): string {
163
+ const ast = stripCursors(shapeInputToAST(shape))
164
+ return `${stableStringify(ast)}|${opts?.live === false ? '0' : '1'}|${opts?.window ?? ''}|${opts?.with ?? ''}`
165
+ }
166
+
167
+ /**
168
+ * Subscribe to a memory shape. `null` shape → idle (`null`, no I/O). A cold
169
+ * shape suspends unless `suspend: false`. The returned `LiveResult` is the
170
+ * unified read envelope — callers project `node`/`children`/`nodes`.
171
+ */
172
+ export function useShape(
173
+ shape: ShapeInput | null | undefined,
174
+ opts?: ShapeOptions,
175
+ ): LiveResult | null {
176
+ const memory = useMemory()
177
+ const storeOpts = toStoreOptions(opts)
178
+ const key = isIdleInput(shape) ? null : keyOf(shape, opts)
179
+
180
+ const subscribe = useCallback(
181
+ (onChange: () => void): (() => void) => {
182
+ if (isIdleInput(shape)) return () => {}
183
+ return memory.subscribe(shape, onChange, storeOpts)
184
+ },
185
+ // The shape's canonical identity is `key`; the captured `shape` is equivalent.
186
+ // eslint-disable-next-line react-hooks/exhaustive-deps
187
+ [memory, key],
188
+ )
189
+ const getSnapshot = useCallback(
190
+ (): LiveResult | null => (isIdleInput(shape) ? null : memory.getSnapshot(shape, storeOpts)),
191
+ // eslint-disable-next-line react-hooks/exhaustive-deps
192
+ [memory, key],
193
+ )
194
+
195
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
196
+
197
+ if (!isIdleInput(shape) && opts?.suspend !== false && memory.peek(shape, storeOpts) === null) {
198
+ // Cold FAILURE (§1.4): a rejected fetch with no data throws to the boundary;
199
+ // re-throwing the promise would re-arm a fetch each retry (infinite loop).
200
+ if (snapshot !== null && snapshot.error !== undefined) throw snapshot.error
201
+ throw memory.promise(shape, storeOpts)
202
+ }
203
+ return snapshot
204
+ }
205
+
206
+ // ── typed reads (schema-first useNode, BoundNode-first useChildren) ──────────────
207
+
208
+ export function useNode<S extends Schema, T extends TypeNames<S>>(
209
+ schema: S,
210
+ type: T,
211
+ ref: NodeRefInput | null | undefined,
212
+ opts?: ReadOptions,
213
+ ): TypedNodeResult<S, T> {
214
+ const binding = bindingFor(useSessionKernel(), schema, useMemory())
215
+ const shape = isIdleInput(ref) ? null : normalizeRef(ref)
216
+ const snap = useShape(shape, opts)
217
+ const wire = snap?.node ?? null
218
+ const bound = useBoundNode(binding, type, wire)
219
+ if (bound !== null) tagSchema(bound, schema)
220
+ const live = snap === null ? idleReadState() : readStateOf(snap)
221
+ return useMemo(
222
+ () => ({ ...live, node: bound }),
223
+ [bound, live.pending, live.error, live.live, live.seq],
224
+ )
225
+ }
226
+
227
+ export function useChildren<S extends Schema, T extends TypeNames<S>>(
228
+ parent: AnyBoundNode<S> | null | undefined,
229
+ opts: ReadOptions & {
230
+ type: T
231
+ order?: OrderInput
232
+ limit?: number
233
+ window?: 'append' | 'paged'
234
+ },
235
+ ): TypedChildrenResult<S, T> {
236
+ const kernel = useSessionKernel()
237
+ const memory = useMemory()
238
+ const schema = isIdleInput(parent) ? null : requireSchema(parent)
239
+ const binding = schema === null ? null : bindingFor(kernel, schema, memory)
240
+
241
+ const parentRef = isIdleInput(parent) ? null : `@${parent.id}`
242
+ const classRef = schema === null ? undefined : classRefFor(schema, opts.type)
243
+ const shape =
244
+ parentRef === null
245
+ ? null
246
+ : (q: QuerySource): GatherBuilder =>
247
+ q.from(parentRef).children({
248
+ ...(classRef !== undefined ? { classes: [classRef] } : {}),
249
+ ...(opts.order !== undefined ? { order: opts.order } : {}),
250
+ ...(opts.limit !== undefined ? { limit: opts.limit } : {}),
251
+ })
252
+
253
+ const snap = useShape(shape, opts)
254
+ const wires = snap?.children ?? EMPTY
255
+ const bound = useBoundNodes(
256
+ (binding ?? UNBOUND) as unknown as SchemaBoundView<S>,
257
+ opts.type,
258
+ binding === null ? EMPTY : wires,
259
+ true,
260
+ )
261
+ if (schema !== null) for (const b of bound) tagSchema(b, schema)
262
+
263
+ const live = snap === null ? idleReadState() : readStateOf(snap)
264
+ const more = snap?.more ?? IDLE_MORE
265
+ return useMemo(
266
+ () => ({ ...live, more, children: bound }),
267
+ [bound, more, live.pending, live.error, live.live, live.seq],
268
+ )
269
+ }
270
+
271
+ // ── helpers ──────────────────────────────────────────────────────────────────
272
+
273
+ const EMPTY: readonly never[] = Object.freeze([])
274
+ const IDLE_MORE = { has: false, load: () => Promise.resolve(), pending: false } as const
275
+ const UNBOUND = {} as never
276
+
277
+ function readStateOf(snap: {
278
+ pending: boolean
279
+ error: unknown
280
+ live: boolean
281
+ seq: number
282
+ refetch(): Promise<void>
283
+ }): ReadState {
284
+ return {
285
+ pending: snap.pending,
286
+ error: snap.error,
287
+ live: snap.live,
288
+ seq: snap.seq,
289
+ refetch: snap.refetch,
290
+ }
291
+ }
292
+
293
+ /** Recover the schema a BoundNode was bound under; a clear error if it is untagged. */
294
+ export function requireSchema(node: object): Schema {
295
+ const schema = schemaOf(node)
296
+ if (schema === undefined) {
297
+ throw new Error(
298
+ 'useChildren: the parent was not produced by a shell-react typed binding — ' +
299
+ 'obtain it from useNode / useDomain(schema).bind() so its schema is known.',
300
+ )
301
+ }
302
+ return schema
303
+ }
304
+
305
+ const compiledCache = new WeakMap<Schema, ReturnType<typeof compileDomain>>()
306
+
307
+ /** The descent class ref for a type name (class or interface), or undefined. */
308
+ function classRefFor(schema: Schema, type: string): string | undefined {
309
+ let compiled = compiledCache.get(schema)
310
+ if (compiled === undefined) {
311
+ compiled = compileDomain(schema)
312
+ compiledCache.set(schema, compiled)
313
+ }
314
+ const accessor = compiled.$ as unknown as {
315
+ c(name: string): { path?: { class?: { raw: string } } } | undefined
316
+ i(name: string): { path?: { class?: { raw: string } } } | undefined
317
+ }
318
+ return accessor.c(type)?.path?.class?.raw ?? accessor.i(type)?.path?.class?.raw
319
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * bindingFor(kernel, schema) — memoized `withSchema` handles (API §6). The
3
+ * `withSchema` handle is a *binding*, never a "view". Memoizes
4
+ * `createSchemaBoundView` per (session, schema) so a binding — and the BoundNodes
5
+ * it mints — keep referential identity across renders. Each binding is wrapped so
6
+ * every BoundNode it hands back is schema-tagged (the traversal hooks read it
7
+ * back) and its `id → tree path` recorded (the write capability's index).
8
+ */
9
+ import type { FnMap, NodeRefInput, SchemaBoundView } from '@astrale-os/kernel-client'
10
+ import type { GraphMemory } from '@astrale-os/kernel-client/store'
11
+ import type { Node } from '@astrale-os/kernel-core'
12
+ import type { Schema } from '@astrale-os/kernel-dsl'
13
+
14
+ import { createSchemaBoundView } from '@astrale-os/kernel-client'
15
+
16
+ import type { SessionView } from '../auth/act-as.context'
17
+
18
+ import { tagSchema } from './tag'
19
+ import { recordPath, writableFor } from './write'
20
+
21
+ type AnyBinding = SchemaBoundView<Schema, FnMap>
22
+
23
+ /** One tagged binding per (kernel, schema) — shared across the whole tree. */
24
+ const bindingCache = new WeakMap<object, WeakMap<Schema, AnyBinding>>()
25
+
26
+ /**
27
+ * Resolve (and memoize) the tagged schema-bound handle for `schema` on `kernel`.
28
+ * `kernel` is the EFFECTIVE dispatch kernel (root or `<ActAs>` — §4); the cache is
29
+ * keyed on it, so an acted-as subtree gets its own memoized bindings for free.
30
+ * `memory` is the SAME scoped memory the read hooks use (1:1 with `kernel`).
31
+ */
32
+ export function bindingFor<S extends Schema>(
33
+ kernel: SessionView,
34
+ schema: S,
35
+ memory: GraphMemory,
36
+ ): SchemaBoundView<S, FnMap> {
37
+ let bySchema = bindingCache.get(kernel)
38
+ if (bySchema === undefined) {
39
+ bySchema = new WeakMap()
40
+ bindingCache.set(kernel, bySchema)
41
+ }
42
+ let binding = bySchema.get(schema)
43
+ if (binding === undefined) {
44
+ const { writable, pathIndex } = writableFor(kernel, memory)
45
+ binding = tagBinding(createSchemaBoundView(writable, schema) as AnyBinding, schema, pathIndex)
46
+ bySchema.set(schema, binding)
47
+ }
48
+ return binding as SchemaBoundView<S, FnMap>
49
+ }
50
+
51
+ /**
52
+ * Wrap a binding so `bind`/`bindAny`/`as` outputs carry a schema tag, and every
53
+ * bound wire node's `id → tree path` is recorded (the write cap's index); the
54
+ * bare-call dispatch and every other member pass straight through.
55
+ */
56
+ function tagBinding(
57
+ binding: AnyBinding,
58
+ schema: Schema,
59
+ pathIndex: Map<string, string>,
60
+ ): AnyBinding {
61
+ const handler: ProxyHandler<AnyBinding> = {
62
+ apply: (target, _thisArg, args) =>
63
+ (target as unknown as (...a: unknown[]) => unknown)(...(args as unknown[])),
64
+ get: (target, prop, receiver) => {
65
+ const value = Reflect.get(target, prop, receiver) as unknown
66
+ if (prop === 'bind') {
67
+ return (type: string, node: Node, opts?: unknown) => {
68
+ recordPath(pathIndex, node)
69
+ return tagSchema(
70
+ (value as (t: string, n: Node, o?: unknown) => unknown)(type, node, opts),
71
+ schema,
72
+ )
73
+ }
74
+ }
75
+ if (prop === 'bindAny') {
76
+ return (node: Node) => {
77
+ recordPath(pathIndex, node)
78
+ return tagSchema((value as (n: Node) => unknown)(node), schema)
79
+ }
80
+ }
81
+ if (prop === 'as') {
82
+ return (credential: unknown) =>
83
+ tagBinding((value as (c: unknown) => AnyBinding)(credential), schema, pathIndex)
84
+ }
85
+ return value
86
+ },
87
+ }
88
+ return new Proxy(binding, handler)
89
+ }
90
+
91
+ export type { NodeRefInput }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * useDomain(schema) (API §6) — the typed domain binding, `kernel.withSchema(schema)`,
3
+ * memoized per (effective session, schema). The BoundNodes it mints ride the
4
+ * shared scoped memory, so typed writes are optimistic and revalidate (§6).
5
+ */
6
+ import type { FnMap, SchemaBoundView } from '@astrale-os/kernel-client'
7
+ import type { Schema } from '@astrale-os/kernel-dsl'
8
+
9
+ import { useSessionKernel } from '../auth/session-kernel'
10
+ import { useMemory } from '../graph/memory/scope'
11
+ import { bindingFor } from './binding'
12
+
13
+ /** The typed domain binding — `kernel.withSchema(schema)`, memoized per (session, schema). */
14
+ export function useDomain<S extends Schema>(schema: S): SchemaBoundView<S, FnMap> {
15
+ return bindingFor(useSessionKernel(), schema, useMemory())
16
+ }
@@ -0,0 +1,68 @@
1
+ import type { Schema } from '@astrale-os/kernel-dsl'
2
+
3
+ import { compileDomain } from '@astrale-os/kernel-core/domain'
4
+ /**
5
+ * `useFunction` (API §6) — `useMethod`'s sibling for a domain's top-level
6
+ * standalone functions (the `functions` map of `defineSchema`). Dispatched by the
7
+ * compiled FunctionPath from the compiled-domain accessor, never string-built.
8
+ * Dispatch is correct but not compile-time typed (see the note in schema/types.ts).
9
+ */
10
+ import { useCallback, useMemo, useRef } from 'react'
11
+
12
+ import type { Action } from '../graph/action'
13
+ import type { FunctionNames, FunctionParams, FunctionResult } from './types'
14
+
15
+ import { useAction } from '../graph/action'
16
+ import { useDomain } from './domain.hook'
17
+
18
+ export interface FunctionOptions {
19
+ /** Extra failure channel; the action still rejects and records `.error`. */
20
+ onError?: (error: unknown) => void
21
+ }
22
+
23
+ const compiledCache = new WeakMap<Schema, ReturnType<typeof compileDomain>>()
24
+
25
+ /** The dispatch path for a standalone function, from the compiled accessor. */
26
+ function functionPathOf(schema: Schema, name: string): string {
27
+ let compiled = compiledCache.get(schema)
28
+ if (compiled === undefined) {
29
+ compiled = compileDomain(schema)
30
+ compiledCache.set(schema, compiled)
31
+ }
32
+ const entry = compiled.$.f(name)
33
+ if (entry === undefined) {
34
+ throw new Error(`Unknown function '${name}' in schema '${schema.domain}'`)
35
+ }
36
+ return entry.path.domain.raw
37
+ }
38
+
39
+ type RawCall = (method: string, params: unknown, opts?: unknown) => Promise<unknown>
40
+
41
+ export function useFunction<S extends Schema, F extends FunctionNames<S>>(
42
+ schema: S,
43
+ fn: F,
44
+ opts?: FunctionOptions,
45
+ ): Action<[FunctionParams<S, F>], FunctionResult<S, F>> {
46
+ const binding = useDomain(schema)
47
+ const path = useMemo(() => functionPathOf(schema, String(fn)), [schema, fn])
48
+
49
+ const onErrorRef = useRef(opts?.onError)
50
+ onErrorRef.current = opts?.onError
51
+
52
+ const call = useCallback(
53
+ async (params: FunctionParams<S, F>): Promise<FunctionResult<S, F>> => {
54
+ try {
55
+ return (await (binding.raw.call as unknown as RawCall)(path, params)) as FunctionResult<
56
+ S,
57
+ F
58
+ >
59
+ } catch (error) {
60
+ onErrorRef.current?.(error)
61
+ throw error
62
+ }
63
+ },
64
+ [binding, path],
65
+ )
66
+
67
+ return useAction(call)
68
+ }
@@ -0,0 +1,25 @@
1
+ // schema — the typing substrate (API §6). Typed kernel/domain bindings, typed
2
+ // reads, method/function dispatch. See SHELL_REACT_API.md.
3
+ export { useKernel } from './kernel.hook'
4
+ export { useDomain } from './domain.hook'
5
+ export { useNode, useChildren, useBoundNode, useBoundNodes, useBoundEndpoints } from './bind-read'
6
+ export { useOut, useIn } from '../graph/query/neighbors.hook'
7
+ export { useMethod } from './method.hook'
8
+ export type { MethodOptions } from './method.hook'
9
+ export { useFunction } from './function.hook'
10
+ export type { FunctionOptions } from './function.hook'
11
+ export type {
12
+ AutoMethodAction,
13
+ BoundMethodAction,
14
+ EndpointResult,
15
+ FunctionNames,
16
+ FunctionParams,
17
+ FunctionResult,
18
+ MethodIsStatic,
19
+ MethodNames,
20
+ MethodParams,
21
+ MethodResult,
22
+ TargetedMethodAction,
23
+ TypedChildrenResult,
24
+ TypedNodeResult,
25
+ } from './types'
@@ -0,0 +1,17 @@
1
+ /**
2
+ * useKernel (API §6) — the typed kernel, `kernel.withSchema(KernelSchema)`,
3
+ * memoized per (effective) session. The kernel's own schema baked in; dispatch
4
+ * and reads run as the subtree's principal over its scoped memory (§4).
5
+ */
6
+ import type { FnMap, SchemaBoundView } from '@astrale-os/kernel-client'
7
+
8
+ import { KernelSchema } from '@astrale-os/kernel-core'
9
+
10
+ import { useSessionKernel } from '../auth/session-kernel'
11
+ import { useMemory } from '../graph/memory/scope'
12
+ import { bindingFor } from './binding'
13
+
14
+ /** The typed kernel — `kernel.withSchema(KernelSchema)`, memoized (§4-scoped). */
15
+ export function useKernel(): SchemaBoundView<typeof KernelSchema, FnMap> {
16
+ return bindingFor(useSessionKernel(), KernelSchema, useMemory())
17
+ }
@@ -0,0 +1,103 @@
1
+ import type { NodeRefInput, TypeNames } from '@astrale-os/kernel-client'
2
+ import type { Schema } from '@astrale-os/kernel-dsl'
3
+
4
+ import { resolveAllMethods } from '@astrale-os/kernel-dsl'
5
+ /**
6
+ * `useMethod` (API §6) — typed method dispatch with call state. Three forms,
7
+ * selected by the method's static-ness and whether `on` is given:
8
+ * static → action(params)
9
+ * instance + on → action(params) (bound to that node)
10
+ * instance, no on → action(ref, params)
11
+ * Params/result come from the method's Zod contracts.
12
+ */
13
+ import { useCallback, useMemo, useRef } from 'react'
14
+
15
+ import type { Action } from '../graph/action'
16
+ import type { AutoMethodAction, BoundMethodAction, MethodNames } from './types'
17
+
18
+ import { useAction } from '../graph/action'
19
+ import { useDomain } from './domain.hook'
20
+
21
+ export interface MethodOptions {
22
+ /** Bind the action to this node — dispatch as `action(params)`. */
23
+ on?: NodeRefInput
24
+ /** Extra failure channel; the action still rejects and records `.error`. */
25
+ onError?: (error: unknown) => void
26
+ }
27
+
28
+ type Dispatch = Record<string, (params: unknown) => Promise<unknown>>
29
+
30
+ export function useMethod<S extends Schema, T extends TypeNames<S>, M extends MethodNames<S, T>>(
31
+ schema: S,
32
+ type: T,
33
+ method: M,
34
+ opts: MethodOptions & { on: NodeRefInput },
35
+ ): BoundMethodAction<S, T, M>
36
+ export function useMethod<S extends Schema, T extends TypeNames<S>, M extends MethodNames<S, T>>(
37
+ schema: S,
38
+ type: T,
39
+ method: M,
40
+ opts?: MethodOptions,
41
+ ): AutoMethodAction<S, T, M>
42
+ export function useMethod<S extends Schema, T extends TypeNames<S>, M extends MethodNames<S, T>>(
43
+ schema: S,
44
+ type: T,
45
+ method: M,
46
+ opts?: MethodOptions,
47
+ ): Action<never[], unknown> {
48
+ const binding = useDomain(schema) as unknown as Dispatcher
49
+ const on = opts?.on
50
+ const isStatic = useMemo(() => detectStatic(schema, type, String(method)), [schema, type, method])
51
+
52
+ const onErrorRef = useRef(opts?.onError)
53
+ onErrorRef.current = opts?.onError
54
+
55
+ const call = useCallback(
56
+ async (...args: unknown[]): Promise<unknown> => {
57
+ const name = String(method)
58
+ try {
59
+ if (on !== undefined) return await instance(binding, type as string, on, name, args[0])
60
+ if (isStatic) return await staticCall(binding, type as string, name, args[0])
61
+ return await instance(binding, type as string, args[0] as NodeRefInput, name, args[1])
62
+ } catch (error) {
63
+ onErrorRef.current?.(error)
64
+ throw error
65
+ }
66
+ },
67
+ [binding, type, method, on, isStatic],
68
+ )
69
+
70
+ return useAction(call)
71
+ }
72
+
73
+ /** The bare-call dispatch signature of a `SchemaBoundView`, address-only. */
74
+ type Dispatcher = (type: string, ref?: NodeRefInput) => unknown
75
+
76
+ function staticCall(
77
+ binding: Dispatcher,
78
+ type: string,
79
+ method: string,
80
+ params: unknown,
81
+ ): Promise<unknown> {
82
+ return (binding(type) as unknown as Dispatch)[method]!(params)
83
+ }
84
+
85
+ function instance(
86
+ binding: Dispatcher,
87
+ type: string,
88
+ ref: NodeRefInput,
89
+ method: string,
90
+ params: unknown,
91
+ ): Promise<unknown> {
92
+ return (binding(type, ref) as unknown as Dispatch)[method]!(params)
93
+ }
94
+
95
+ /** Read the method's `static` flag from the schema (own + inherited). */
96
+ function detectStatic(schema: Schema, type: string, method: string): boolean {
97
+ const def =
98
+ (schema.classes as Record<string, unknown>)[type] ??
99
+ (schema.interfaces as Record<string, unknown>)[type]
100
+ if (def === undefined) return false
101
+ const methods = resolveAllMethods(def as Parameters<typeof resolveAllMethods>[0])
102
+ return methods[method]?.config.static === true
103
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Schema-tagging of raw nodes (API §6, the bindAny bridge). A module-level tag
3
+ * records the schema each BoundNode was bound under, so the traversal hooks can
4
+ * recover it from the node alone (`useChildren`/`useOut`/`useIn` re-bind endpoints).
5
+ */
6
+ import type { Schema } from '@astrale-os/kernel-dsl'
7
+
8
+ const schemaOfNode = new WeakMap<object, Schema>()
9
+
10
+ /** Record the schema a BoundNode was bound under; returns the node unchanged. */
11
+ export function tagSchema<N>(node: N, schema: Schema): N {
12
+ if (node !== null && typeof node === 'object') schemaOfNode.set(node, schema)
13
+ return node
14
+ }
15
+
16
+ /** The schema a BoundNode was bound under, if it came from a shell-react binding. */
17
+ export function schemaOf(node: object): Schema | undefined {
18
+ return schemaOfNode.get(node)
19
+ }