@treenity/core 1.0.48 → 3.0.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 (696) hide show
  1. package/dist/chain.d.ts +20 -0
  2. package/dist/chain.d.ts.map +1 -0
  3. package/dist/chain.js +59 -0
  4. package/dist/chain.js.map +1 -0
  5. package/dist/client/handle.d.ts +13 -0
  6. package/dist/client/handle.d.ts.map +1 -0
  7. package/dist/client/handle.js +46 -0
  8. package/dist/client/handle.js.map +1 -0
  9. package/dist/client/index.d.ts +21 -0
  10. package/dist/client/index.d.ts.map +1 -0
  11. package/dist/client/index.js +5 -0
  12. package/dist/client/index.js.map +1 -0
  13. package/dist/client/trpc.d.ts +13 -0
  14. package/dist/client/trpc.d.ts.map +1 -0
  15. package/dist/client/trpc.js +78 -0
  16. package/dist/client/trpc.js.map +1 -0
  17. package/dist/client.d.ts +2 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +2 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/comp/handle.d.ts +14 -0
  22. package/dist/comp/handle.d.ts.map +1 -0
  23. package/dist/comp/handle.js +21 -0
  24. package/dist/comp/handle.js.map +1 -0
  25. package/dist/comp/index.d.ts +42 -0
  26. package/dist/comp/index.d.ts.map +1 -0
  27. package/dist/comp/index.js +126 -0
  28. package/dist/comp/index.js.map +1 -0
  29. package/dist/comp/needs.d.ts +26 -0
  30. package/dist/comp/needs.d.ts.map +1 -0
  31. package/dist/comp/needs.js +101 -0
  32. package/dist/comp/needs.js.map +1 -0
  33. package/dist/comp/validate.d.ts +13 -0
  34. package/dist/comp/validate.d.ts.map +1 -0
  35. package/dist/comp/validate.js +117 -0
  36. package/dist/comp/validate.js.map +1 -0
  37. package/dist/comp.d.ts +2 -0
  38. package/dist/comp.d.ts.map +1 -0
  39. package/dist/comp.js +2 -0
  40. package/dist/comp.js.map +1 -0
  41. package/dist/contexts/schema/index.d.ts +7 -0
  42. package/dist/contexts/schema/index.d.ts.map +1 -0
  43. package/dist/contexts/schema/index.js +2 -0
  44. package/dist/contexts/schema/index.js.map +1 -0
  45. package/dist/contexts/schema.d.ts +2 -0
  46. package/dist/contexts/schema.d.ts.map +1 -0
  47. package/dist/contexts/schema.js +2 -0
  48. package/dist/contexts/schema.js.map +1 -0
  49. package/dist/contexts/service/index.d.ts +31 -0
  50. package/dist/contexts/service/index.d.ts.map +1 -0
  51. package/dist/contexts/service/index.js +16 -0
  52. package/dist/contexts/service/index.js.map +1 -0
  53. package/dist/contexts/service.d.ts +1 -14
  54. package/dist/contexts/service.d.ts.map +1 -1
  55. package/dist/contexts/service.js +2 -0
  56. package/dist/contexts/service.js.map +1 -0
  57. package/dist/contexts/telegram/index.d.ts +19 -0
  58. package/dist/contexts/telegram/index.d.ts.map +1 -0
  59. package/dist/contexts/telegram/index.js +89 -0
  60. package/dist/contexts/telegram/index.js.map +1 -0
  61. package/dist/contexts/text/index.d.ts +7 -0
  62. package/dist/contexts/text/index.d.ts.map +1 -0
  63. package/dist/contexts/text/index.js +9 -0
  64. package/dist/contexts/text/index.js.map +1 -0
  65. package/dist/contexts/text.d.ts +2 -0
  66. package/dist/contexts/text.d.ts.map +1 -0
  67. package/dist/contexts/text.js +2 -0
  68. package/dist/contexts/text.js.map +1 -0
  69. package/dist/core/component.d.ts +41 -0
  70. package/dist/core/component.d.ts.map +1 -0
  71. package/dist/core/component.js +105 -0
  72. package/dist/core/component.js.map +1 -0
  73. package/dist/core/context.d.ts +5 -0
  74. package/dist/core/context.d.ts.map +1 -0
  75. package/dist/core/context.js +3 -0
  76. package/dist/core/context.js.map +1 -0
  77. package/dist/core/index.d.ts +5 -0
  78. package/dist/core/index.d.ts.map +1 -0
  79. package/dist/core/index.js +8 -0
  80. package/dist/core/index.js.map +1 -0
  81. package/dist/core/path.d.ts +7 -0
  82. package/dist/core/path.d.ts.map +1 -0
  83. package/dist/core/path.js +37 -0
  84. package/dist/core/path.js.map +1 -0
  85. package/dist/core/registry.d.ts +16 -0
  86. package/dist/core/registry.d.ts.map +1 -0
  87. package/dist/core/registry.js +100 -0
  88. package/dist/core/registry.js.map +1 -0
  89. package/dist/core.d.ts +2 -0
  90. package/dist/core.d.ts.map +1 -0
  91. package/dist/core.js +2 -0
  92. package/dist/core.js.map +1 -0
  93. package/dist/log.d.ts +8 -0
  94. package/dist/log.d.ts.map +1 -0
  95. package/dist/log.js +31 -0
  96. package/dist/log.js.map +1 -0
  97. package/dist/mod/discover.d.ts +4 -0
  98. package/dist/mod/discover.d.ts.map +1 -0
  99. package/dist/mod/discover.js +103 -0
  100. package/dist/mod/discover.js.map +1 -0
  101. package/dist/mod/examples/ticker/seed.d.ts +3 -0
  102. package/dist/mod/examples/ticker/seed.d.ts.map +1 -0
  103. package/dist/mod/examples/ticker/seed.js +16 -0
  104. package/dist/mod/examples/ticker/seed.js.map +1 -0
  105. package/dist/mod/examples/ticker/service.d.ts +2 -0
  106. package/dist/mod/examples/ticker/service.d.ts.map +1 -0
  107. package/dist/mod/examples/ticker/service.js +18 -0
  108. package/dist/mod/examples/ticker/service.js.map +1 -0
  109. package/dist/mod/examples/ticker/types.d.ts +16 -0
  110. package/dist/mod/examples/ticker/types.d.ts.map +1 -0
  111. package/dist/mod/examples/ticker/types.js +20 -0
  112. package/dist/mod/examples/ticker/types.js.map +1 -0
  113. package/dist/mod/examples/ticker/view.d.ts +2 -0
  114. package/dist/mod/examples/ticker/view.d.ts.map +1 -0
  115. package/dist/mod/examples/ticker/view.js +10 -0
  116. package/dist/mod/examples/ticker/view.js.map +1 -0
  117. package/dist/mod/index.d.ts +11 -0
  118. package/dist/mod/index.d.ts.map +1 -0
  119. package/dist/mod/index.js +8 -0
  120. package/dist/mod/index.js.map +1 -0
  121. package/dist/mod/loader.d.ts +18 -0
  122. package/dist/mod/loader.d.ts.map +1 -0
  123. package/dist/mod/loader.js +154 -0
  124. package/dist/mod/loader.js.map +1 -0
  125. package/dist/mod/optimistic.d.ts +34 -0
  126. package/dist/mod/optimistic.d.ts.map +1 -0
  127. package/dist/mod/optimistic.js +176 -0
  128. package/dist/mod/optimistic.js.map +1 -0
  129. package/dist/mod/prefab.d.ts +18 -0
  130. package/dist/mod/prefab.d.ts.map +1 -0
  131. package/dist/mod/prefab.js +42 -0
  132. package/dist/mod/prefab.js.map +1 -0
  133. package/dist/mod/tracking.d.ts +8 -0
  134. package/dist/mod/tracking.d.ts.map +1 -0
  135. package/dist/mod/tracking.js +44 -0
  136. package/dist/mod/tracking.js.map +1 -0
  137. package/dist/mod/types.d.ts +31 -0
  138. package/dist/mod/types.d.ts.map +1 -0
  139. package/dist/mod/types.js +5 -0
  140. package/dist/mod/types.js.map +1 -0
  141. package/dist/mod.d.ts +2 -0
  142. package/dist/mod.d.ts.map +1 -0
  143. package/dist/mod.js +2 -0
  144. package/dist/mod.js.map +1 -0
  145. package/dist/mods/autostart/server.d.ts +2 -0
  146. package/dist/mods/autostart/server.d.ts.map +1 -0
  147. package/dist/mods/autostart/server.js +2 -0
  148. package/dist/mods/autostart/server.js.map +1 -0
  149. package/dist/mods/autostart/service.d.ts +14 -0
  150. package/dist/mods/autostart/service.d.ts.map +1 -0
  151. package/dist/mods/autostart/service.js +88 -0
  152. package/dist/mods/autostart/service.js.map +1 -0
  153. package/dist/mods/llm/index.d.ts +13 -0
  154. package/dist/mods/llm/index.d.ts.map +1 -0
  155. package/dist/mods/llm/index.js +43 -0
  156. package/dist/mods/llm/index.js.map +1 -0
  157. package/dist/mods/llm/server.d.ts +2 -0
  158. package/dist/mods/llm/server.d.ts.map +1 -0
  159. package/dist/mods/llm/server.js +2 -0
  160. package/dist/mods/llm/server.js.map +1 -0
  161. package/dist/mods/mcp/server.d.ts +3 -0
  162. package/dist/mods/mcp/server.d.ts.map +1 -0
  163. package/dist/mods/mcp/server.js +3 -0
  164. package/dist/mods/mcp/server.js.map +1 -0
  165. package/dist/mods/mcp/service.d.ts +2 -0
  166. package/dist/mods/mcp/service.d.ts.map +1 -0
  167. package/dist/mods/mcp/service.js +17 -0
  168. package/dist/mods/mcp/service.js.map +1 -0
  169. package/dist/mods/mcp/types.d.ts +5 -0
  170. package/dist/mods/mcp/types.d.ts.map +1 -0
  171. package/dist/mods/mcp/types.js +7 -0
  172. package/dist/mods/mcp/types.js.map +1 -0
  173. package/dist/mods/treenity/agent-port.d.ts +2 -0
  174. package/dist/mods/treenity/agent-port.d.ts.map +1 -0
  175. package/dist/mods/treenity/agent-port.js +76 -0
  176. package/dist/mods/treenity/agent-port.js.map +1 -0
  177. package/dist/mods/treenity/groups.d.ts +2 -0
  178. package/dist/mods/treenity/groups.d.ts.map +1 -0
  179. package/dist/mods/treenity/groups.js +18 -0
  180. package/dist/mods/treenity/groups.js.map +1 -0
  181. package/dist/mods/treenity/mod-type.d.ts +2 -0
  182. package/dist/mods/treenity/mod-type.d.ts.map +1 -0
  183. package/dist/mods/treenity/mod-type.js +9 -0
  184. package/dist/mods/treenity/mod-type.js.map +1 -0
  185. package/dist/mods/treenity/seed.d.ts +2 -0
  186. package/dist/mods/treenity/seed.d.ts.map +1 -0
  187. package/dist/mods/treenity/seed.js +53 -0
  188. package/dist/mods/treenity/seed.js.map +1 -0
  189. package/dist/mods/treenity/server.d.ts +5 -0
  190. package/dist/mods/treenity/server.d.ts.map +1 -0
  191. package/dist/mods/treenity/server.js +5 -0
  192. package/dist/mods/treenity/server.js.map +1 -0
  193. package/dist/mods/treenity/system.d.ts +36 -0
  194. package/dist/mods/treenity/system.d.ts.map +1 -0
  195. package/dist/mods/treenity/system.js +61 -0
  196. package/dist/mods/treenity/system.js.map +1 -0
  197. package/dist/mods/uix/client.d.ts +3 -0
  198. package/dist/mods/uix/client.d.ts.map +1 -0
  199. package/dist/mods/uix/client.js +96 -0
  200. package/dist/mods/uix/client.js.map +1 -0
  201. package/dist/mods/uix/compile.d.ts +7 -0
  202. package/dist/mods/uix/compile.d.ts.map +1 -0
  203. package/dist/mods/uix/compile.js +94 -0
  204. package/dist/mods/uix/compile.js.map +1 -0
  205. package/dist/mods/uix/jsx-parser.d.ts +2 -0
  206. package/dist/mods/uix/jsx-parser.d.ts.map +1 -0
  207. package/dist/mods/uix/jsx-parser.js +565 -0
  208. package/dist/mods/uix/jsx-parser.js.map +1 -0
  209. package/dist/mods/uix/verify.d.ts +7 -0
  210. package/dist/mods/uix/verify.d.ts.map +1 -0
  211. package/dist/mods/uix/verify.js +59 -0
  212. package/dist/mods/uix/verify.js.map +1 -0
  213. package/dist/schema/catalog.d.ts +23 -0
  214. package/dist/schema/catalog.d.ts.map +1 -0
  215. package/dist/schema/catalog.js +82 -0
  216. package/dist/schema/catalog.js.map +1 -0
  217. package/dist/schema/extract-schemas.d.ts +5 -0
  218. package/dist/schema/extract-schemas.d.ts.map +1 -0
  219. package/dist/schema/extract-schemas.js +444 -0
  220. package/dist/schema/extract-schemas.js.map +1 -0
  221. package/dist/schema/load.d.ts +2 -0
  222. package/dist/schema/load.d.ts.map +1 -0
  223. package/dist/schema/load.js +23 -0
  224. package/dist/schema/load.js.map +1 -0
  225. package/dist/schema/test-fixture.types.d.ts +2 -0
  226. package/dist/schema/test-fixture.types.d.ts.map +1 -0
  227. package/dist/schema/test-fixture.types.js +19 -0
  228. package/dist/schema/test-fixture.types.js.map +1 -0
  229. package/dist/schema/test-opaque.d.ts +3 -0
  230. package/dist/schema/test-opaque.d.ts.map +1 -0
  231. package/dist/schema/test-opaque.js +43 -0
  232. package/dist/schema/test-opaque.js.map +1 -0
  233. package/dist/schema/types.d.ts +37 -0
  234. package/dist/schema/types.d.ts.map +1 -0
  235. package/dist/schema/types.js +3 -0
  236. package/dist/schema/types.js.map +1 -0
  237. package/dist/server/actions.d.ts +35 -0
  238. package/dist/server/actions.d.ts.map +1 -0
  239. package/dist/server/actions.js +170 -0
  240. package/dist/server/actions.js.map +1 -0
  241. package/dist/server/agent.d.ts +6 -0
  242. package/dist/server/agent.d.ts.map +1 -0
  243. package/dist/server/agent.js +15 -0
  244. package/dist/server/agent.js.map +1 -0
  245. package/dist/server/auth.d.ts +39 -0
  246. package/dist/server/auth.d.ts.map +1 -0
  247. package/dist/server/auth.js +335 -0
  248. package/dist/server/auth.js.map +1 -0
  249. package/dist/server/client.d.ts +183 -0
  250. package/dist/server/client.d.ts.map +1 -0
  251. package/dist/server/client.js +22 -0
  252. package/dist/server/client.js.map +1 -0
  253. package/dist/server/cookies.d.ts +5 -0
  254. package/dist/server/cookies.d.ts.map +1 -0
  255. package/dist/server/cookies.js +24 -0
  256. package/dist/server/cookies.js.map +1 -0
  257. package/dist/server/doc-index.d.ts +38 -0
  258. package/dist/server/doc-index.d.ts.map +1 -0
  259. package/dist/server/doc-index.js +244 -0
  260. package/dist/server/doc-index.js.map +1 -0
  261. package/dist/server/errors.d.ts +7 -0
  262. package/dist/server/errors.d.ts.map +1 -0
  263. package/dist/server/errors.js +11 -0
  264. package/dist/server/errors.js.map +1 -0
  265. package/dist/server/factory.d.ts +23 -0
  266. package/dist/server/factory.d.ts.map +1 -0
  267. package/dist/server/factory.js +70 -0
  268. package/dist/server/factory.js.map +1 -0
  269. package/dist/server/main.d.ts +6 -0
  270. package/dist/server/main.d.ts.map +1 -0
  271. package/dist/server/main.js +45 -0
  272. package/dist/server/main.js.map +1 -0
  273. package/dist/server/mcp.d.ts +5 -0
  274. package/dist/server/mcp.d.ts.map +1 -0
  275. package/dist/server/mcp.js +280 -0
  276. package/dist/server/mcp.js.map +1 -0
  277. package/dist/server/migrate.d.ts +3 -0
  278. package/dist/server/migrate.d.ts.map +1 -0
  279. package/dist/server/migrate.js +56 -0
  280. package/dist/server/migrate.js.map +1 -0
  281. package/dist/server/mod-catalog.d.ts +14 -0
  282. package/dist/server/mod-catalog.d.ts.map +1 -0
  283. package/dist/server/mod-catalog.js +40 -0
  284. package/dist/server/mod-catalog.js.map +1 -0
  285. package/dist/server/mods-mount.d.ts +4 -0
  286. package/dist/server/mods-mount.d.ts.map +1 -0
  287. package/dist/server/mods-mount.js +169 -0
  288. package/dist/server/mods-mount.js.map +1 -0
  289. package/dist/server/mount-adapters.d.ts +9 -0
  290. package/dist/server/mount-adapters.d.ts.map +1 -0
  291. package/dist/server/mount-adapters.js +75 -0
  292. package/dist/server/mount-adapters.js.map +1 -0
  293. package/dist/server/mount.d.ts +3 -0
  294. package/dist/server/mount.d.ts.map +1 -0
  295. package/dist/server/mount.js +195 -0
  296. package/dist/server/mount.js.map +1 -0
  297. package/dist/server/prefab.d.ts +18 -0
  298. package/dist/server/prefab.d.ts.map +1 -0
  299. package/dist/server/prefab.js +70 -0
  300. package/dist/server/prefab.js.map +1 -0
  301. package/dist/server/seed/index.d.ts +6 -0
  302. package/dist/server/seed/index.d.ts.map +1 -0
  303. package/dist/server/seed/index.js +16 -0
  304. package/dist/server/seed/index.js.map +1 -0
  305. package/dist/server/server.d.ts +28 -0
  306. package/dist/server/server.d.ts.map +1 -0
  307. package/dist/server/server.js +117 -0
  308. package/dist/server/server.js.map +1 -0
  309. package/dist/server/sub.d.ts +34 -0
  310. package/dist/server/sub.d.ts.map +1 -0
  311. package/dist/server/sub.js +174 -0
  312. package/dist/server/sub.js.map +1 -0
  313. package/dist/server/trpc.d.ts +192 -0
  314. package/dist/server/trpc.d.ts.map +1 -0
  315. package/dist/server/trpc.js +319 -0
  316. package/dist/server/trpc.js.map +1 -0
  317. package/dist/server/types-mount.d.ts +3 -0
  318. package/dist/server/types-mount.d.ts.map +1 -0
  319. package/dist/server/types-mount.js +144 -0
  320. package/dist/server/types-mount.js.map +1 -0
  321. package/dist/server/validate.d.ts +3 -0
  322. package/dist/server/validate.d.ts.map +1 -0
  323. package/dist/server/validate.js +20 -0
  324. package/dist/server/validate.js.map +1 -0
  325. package/dist/server/volatile.d.ts +11 -0
  326. package/dist/server/volatile.d.ts.map +1 -0
  327. package/dist/server/volatile.js +26 -0
  328. package/dist/server/volatile.js.map +1 -0
  329. package/dist/server/watch.d.ts +23 -0
  330. package/dist/server/watch.d.ts.map +1 -0
  331. package/dist/server/watch.js +178 -0
  332. package/dist/server/watch.js.map +1 -0
  333. package/dist/tree/cache.d.ts +3 -0
  334. package/dist/tree/cache.d.ts.map +1 -0
  335. package/dist/tree/cache.js +48 -0
  336. package/dist/tree/cache.js.map +1 -0
  337. package/dist/tree/fs.d.ts +3 -0
  338. package/dist/tree/fs.d.ts.map +1 -0
  339. package/dist/tree/fs.js +274 -0
  340. package/dist/tree/fs.js.map +1 -0
  341. package/dist/tree/index.d.ts +38 -0
  342. package/dist/tree/index.d.ts.map +1 -0
  343. package/dist/tree/index.js +182 -0
  344. package/dist/tree/index.js.map +1 -0
  345. package/dist/tree/inflight.d.ts +2 -0
  346. package/dist/tree/inflight.d.ts.map +1 -0
  347. package/dist/tree/inflight.js +15 -0
  348. package/dist/tree/inflight.js.map +1 -0
  349. package/dist/tree/json-codec.d.ts +2 -0
  350. package/dist/tree/json-codec.d.ts.map +1 -0
  351. package/dist/tree/json-codec.js +13 -0
  352. package/dist/tree/json-codec.js.map +1 -0
  353. package/dist/tree/mimefs.d.ts +13 -0
  354. package/dist/tree/mimefs.d.ts.map +1 -0
  355. package/dist/tree/mimefs.js +124 -0
  356. package/dist/tree/mimefs.js.map +1 -0
  357. package/dist/tree/mongo.d.ts +5 -0
  358. package/dist/tree/mongo.d.ts.map +1 -0
  359. package/dist/tree/mongo.js +110 -0
  360. package/dist/tree/mongo.js.map +1 -0
  361. package/dist/tree/patch.d.ts +30 -0
  362. package/dist/tree/patch.d.ts.map +1 -0
  363. package/dist/tree/patch.js +112 -0
  364. package/dist/tree/patch.js.map +1 -0
  365. package/dist/tree/query.d.ts +12 -0
  366. package/dist/tree/query.d.ts.map +1 -0
  367. package/dist/tree/query.js +61 -0
  368. package/dist/tree/query.js.map +1 -0
  369. package/dist/tree/repath.d.ts +3 -0
  370. package/dist/tree/repath.d.ts.map +1 -0
  371. package/dist/tree/repath.js +38 -0
  372. package/dist/tree/repath.js.map +1 -0
  373. package/dist/tree-chain.d.ts +18 -0
  374. package/dist/tree-chain.d.ts.map +1 -0
  375. package/dist/tree-chain.js +109 -0
  376. package/dist/tree-chain.js.map +1 -0
  377. package/dist/tree.d.ts +2 -0
  378. package/dist/tree.d.ts.map +1 -0
  379. package/dist/tree.js +2 -0
  380. package/dist/tree.js.map +1 -0
  381. package/dist/uri.d.ts +11 -0
  382. package/dist/uri.d.ts.map +1 -0
  383. package/dist/uri.js +79 -0
  384. package/dist/uri.js.map +1 -0
  385. package/package.json +39 -40
  386. package/src/chain.test.ts +190 -0
  387. package/src/chain.ts +82 -0
  388. package/src/client/client.test.ts +192 -0
  389. package/src/client/handle.ts +53 -0
  390. package/src/client/index.ts +18 -0
  391. package/src/client/trpc.ts +91 -0
  392. package/src/client.ts +1 -0
  393. package/src/comp/CLAUDE.md +14 -0
  394. package/src/comp/handle.ts +36 -0
  395. package/src/comp/index.test.ts +129 -0
  396. package/src/comp/index.ts +175 -0
  397. package/src/comp/needs.test.ts +499 -0
  398. package/src/comp/needs.ts +113 -0
  399. package/src/comp/validate.test.ts +304 -0
  400. package/src/comp/validate.ts +125 -0
  401. package/src/comp.ts +1 -0
  402. package/src/contexts/schema/index.ts +7 -0
  403. package/src/contexts/schema.ts +1 -0
  404. package/src/contexts/service/index.test.ts +323 -0
  405. package/src/contexts/service/index.ts +43 -0
  406. package/src/contexts/service.ts +1 -0
  407. package/src/contexts/telegram/index.ts +115 -0
  408. package/src/contexts/text/index.test.ts +31 -0
  409. package/src/contexts/text/index.ts +18 -0
  410. package/src/contexts/text.ts +1 -0
  411. package/src/core/component.ts +151 -0
  412. package/src/core/context.ts +14 -0
  413. package/src/core/index.test.ts +203 -0
  414. package/src/core/index.ts +9 -0
  415. package/src/core/path.ts +35 -0
  416. package/src/core/registry.ts +115 -0
  417. package/src/core.ts +1 -0
  418. package/src/log.test.ts +70 -0
  419. package/src/log.ts +28 -0
  420. package/src/mod/discover.test.ts +133 -0
  421. package/src/mod/discover.ts +100 -0
  422. package/src/mod/docs/00-index.md +19 -0
  423. package/src/mod/docs/01-primitives.md +38 -0
  424. package/src/mod/docs/02-core-api.md +68 -0
  425. package/src/mod/docs/03-registry.md +30 -0
  426. package/src/mod/docs/04-store.md +62 -0
  427. package/src/mod/docs/05-comp.md +111 -0
  428. package/src/mod/docs/06-actions.md +193 -0
  429. package/src/mod/docs/07-realtime.md +100 -0
  430. package/src/mod/docs/08-services.md +33 -0
  431. package/src/mod/docs/09-mounts.md +43 -0
  432. package/src/mod/docs/10-acl.md +60 -0
  433. package/src/mod/docs/11-server.md +62 -0
  434. package/src/mod/docs/12-conventions.md +65 -0
  435. package/src/mod/docs/13-example.md +132 -0
  436. package/src/mod/docs/14-mod-format.md +304 -0
  437. package/src/mod/docs/15-documenting-types.md +156 -0
  438. package/src/mod/examples/ticker/seed.ts +19 -0
  439. package/src/mod/examples/ticker/service.ts +20 -0
  440. package/src/mod/examples/ticker/ticker.test.ts +18 -0
  441. package/src/mod/examples/ticker/types.ts +22 -0
  442. package/src/mod/examples/ticker/view.tsx +19 -0
  443. package/src/mod/index.ts +12 -0
  444. package/src/mod/loader.test.ts +168 -0
  445. package/src/mod/loader.ts +174 -0
  446. package/src/mod/optimistic.test.ts +446 -0
  447. package/src/mod/optimistic.ts +210 -0
  448. package/src/mod/prefab.ts +62 -0
  449. package/src/mod/tracking.test.ts +59 -0
  450. package/src/mod/tracking.ts +51 -0
  451. package/src/mod/types.ts +40 -0
  452. package/src/mod.ts +1 -0
  453. package/src/mods/autostart/CLAUDE.md +6 -0
  454. package/src/mods/autostart/autostart.test.ts +85 -0
  455. package/src/mods/autostart/server.ts +1 -0
  456. package/src/mods/autostart/service.ts +98 -0
  457. package/src/mods/llm/CLAUDE.md +6 -0
  458. package/src/mods/llm/index.ts +57 -0
  459. package/src/mods/llm/llm.test.ts +109 -0
  460. package/src/mods/llm/server.ts +1 -0
  461. package/src/mods/mcp/CLAUDE.md +6 -0
  462. package/src/mods/mcp/server.ts +2 -0
  463. package/src/mods/mcp/service.ts +19 -0
  464. package/src/mods/mcp/types.ts +7 -0
  465. package/src/mods/treenity/agent-port.ts +93 -0
  466. package/src/mods/treenity/groups.ts +19 -0
  467. package/src/mods/treenity/mod-type.ts +10 -0
  468. package/src/mods/treenity/seed.ts +56 -0
  469. package/src/mods/treenity/server.ts +4 -0
  470. package/src/mods/treenity/system.ts +70 -0
  471. package/src/mods/uix/CLAUDE.md +7 -0
  472. package/src/mods/uix/client.ts +117 -0
  473. package/src/mods/uix/compile.test.ts +228 -0
  474. package/src/mods/uix/compile.ts +110 -0
  475. package/src/mods/uix/jsx-parser.test.ts +554 -0
  476. package/src/mods/uix/jsx-parser.ts +489 -0
  477. package/src/mods/uix/lazy-load.test.ts +261 -0
  478. package/src/mods/uix/uix-repomix.md +3509 -0
  479. package/src/mods/uix/verify.ts +68 -0
  480. package/src/schema/CLAUDE.md +13 -0
  481. package/src/schema/catalog.ts +101 -0
  482. package/src/schema/extract-schemas.test.ts +84 -0
  483. package/src/schema/extract-schemas.ts +462 -0
  484. package/src/schema/generated/autostart.json +44 -0
  485. package/src/schema/generated/board.column.json +25 -0
  486. package/src/schema/generated/board.task.json +147 -0
  487. package/src/schema/generated/brahman.action.back.json +12 -0
  488. package/src/schema/generated/brahman.action.broadcast.json +31 -0
  489. package/src/schema/generated/brahman.action.call.json +57 -0
  490. package/src/schema/generated/brahman.action.emittext.json +23 -0
  491. package/src/schema/generated/brahman.action.eval.json +23 -0
  492. package/src/schema/generated/brahman.action.file.json +28 -0
  493. package/src/schema/generated/brahman.action.forward.json +29 -0
  494. package/src/schema/generated/brahman.action.getvalue.json +28 -0
  495. package/src/schema/generated/brahman.action.ifelse.json +42 -0
  496. package/src/schema/generated/brahman.action.keywordselect.json +46 -0
  497. package/src/schema/generated/brahman.action.message.json +127 -0
  498. package/src/schema/generated/brahman.action.onerror.json +29 -0
  499. package/src/schema/generated/brahman.action.page.json +22 -0
  500. package/src/schema/generated/brahman.action.params.json +36 -0
  501. package/src/schema/generated/brahman.action.question.json +43 -0
  502. package/src/schema/generated/brahman.action.remove.json +12 -0
  503. package/src/schema/generated/brahman.action.resethistory.json +12 -0
  504. package/src/schema/generated/brahman.action.resetsession.json +12 -0
  505. package/src/schema/generated/brahman.action.selectlang.json +20 -0
  506. package/src/schema/generated/brahman.action.setvalue.json +27 -0
  507. package/src/schema/generated/brahman.action.tag.json +27 -0
  508. package/src/schema/generated/brahman.bot.json +68 -0
  509. package/src/schema/generated/brahman.page.json +25 -0
  510. package/src/schema/generated/brahman.session.json +29 -0
  511. package/src/schema/generated/brahman.user.json +58 -0
  512. package/src/schema/generated/cafe.contact.json +56 -0
  513. package/src/schema/generated/cafe.mail.json +29 -0
  514. package/src/schema/generated/default.json +15 -0
  515. package/src/schema/generated/doc.page.json +23 -0
  516. package/src/schema/generated/examples.demo.generator.json +16 -0
  517. package/src/schema/generated/examples.demo.sensor.json +35 -0
  518. package/src/schema/generated/groups.json +20 -0
  519. package/src/schema/generated/launcher.json +91 -0
  520. package/src/schema/generated/mcp.server.json +15 -0
  521. package/src/schema/generated/mindmap.map.json +22 -0
  522. package/src/schema/generated/sim.agent.json +24 -0
  523. package/src/schema/generated/sim.ai.json +24 -0
  524. package/src/schema/generated/sim.config.json +38 -0
  525. package/src/schema/generated/sim.descriptive.json +26 -0
  526. package/src/schema/generated/sim.events.json +47 -0
  527. package/src/schema/generated/sim.item.json +20 -0
  528. package/src/schema/generated/sim.memory.json +17 -0
  529. package/src/schema/generated/sim.nearby.json +17 -0
  530. package/src/schema/generated/sim.position.json +25 -0
  531. package/src/schema/generated/sim.round.json +64 -0
  532. package/src/schema/generated/sim.world.json +32 -0
  533. package/src/schema/generated/t.agent.port.json +74 -0
  534. package/src/schema/generated/t.llm.json +20 -0
  535. package/src/schema/generated/t.mod.json +27 -0
  536. package/src/schema/generated/t3d.animator.json +46 -0
  537. package/src/schema/generated/t3d.audio.json +58 -0
  538. package/src/schema/generated/t3d.camera.json +50 -0
  539. package/src/schema/generated/t3d.collider.json +84 -0
  540. package/src/schema/generated/t3d.light.json +90 -0
  541. package/src/schema/generated/t3d.line.json +47 -0
  542. package/src/schema/generated/t3d.lod.json +28 -0
  543. package/src/schema/generated/t3d.material.json +131 -0
  544. package/src/schema/generated/t3d.mesh.json +65 -0
  545. package/src/schema/generated/t3d.object.json +64 -0
  546. package/src/schema/generated/t3d.particles.json +109 -0
  547. package/src/schema/generated/t3d.rigidbody.json +81 -0
  548. package/src/schema/generated/t3d.scene.json +7 -0
  549. package/src/schema/generated/t3d.script.json +23 -0
  550. package/src/schema/generated/t3d.text.json +86 -0
  551. package/src/schema/generated/t3d.trail.json +45 -0
  552. package/src/schema/generated/task.json +96 -0
  553. package/src/schema/generated/test.fixture.json +43 -0
  554. package/src/schema/generated/ticker.config.json +43 -0
  555. package/src/schema/generated/ticker.price.json +20 -0
  556. package/src/schema/generated/todo.item.json +25 -0
  557. package/src/schema/generated/todo.list.json +33 -0
  558. package/src/schema/generated/treenity.system.json +259 -0
  559. package/src/schema/generated/whisper.audio.json +25 -0
  560. package/src/schema/generated/whisper.checklist.json +17 -0
  561. package/src/schema/generated/whisper.config.json +30 -0
  562. package/src/schema/generated/whisper.inbox.json +24 -0
  563. package/src/schema/generated/whisper.meta.json +35 -0
  564. package/src/schema/generated/whisper.text.json +16 -0
  565. package/src/schema/load.ts +24 -0
  566. package/src/schema/schema.test.ts +86 -0
  567. package/src/schema/test-fixture.types.ts +21 -0
  568. package/src/schema/test-opaque.ts +42 -0
  569. package/src/schema/types.ts +33 -0
  570. package/src/server/CLAUDE.md +26 -0
  571. package/src/server/actions.test.ts +272 -0
  572. package/src/server/actions.ts +274 -0
  573. package/src/server/agent-sub.test.ts +90 -0
  574. package/src/server/agent.test.ts +305 -0
  575. package/src/server/agent.ts +17 -0
  576. package/src/server/api.test.ts +454 -0
  577. package/src/server/auth.test.ts +441 -0
  578. package/src/server/auth.ts +386 -0
  579. package/src/server/client.ts +24 -0
  580. package/src/server/conditions.test.ts +128 -0
  581. package/src/server/cookies.ts +25 -0
  582. package/src/server/coverage.test.ts +827 -0
  583. package/src/server/doc-index.ts +286 -0
  584. package/src/server/e2e.test.ts +966 -0
  585. package/src/server/errors.ts +11 -0
  586. package/src/server/factory.ts +99 -0
  587. package/src/server/main.ts +56 -0
  588. package/src/server/mcp.ts +326 -0
  589. package/src/server/migrate.test.ts +123 -0
  590. package/src/server/migrate.ts +62 -0
  591. package/src/server/mod-catalog.ts +59 -0
  592. package/src/server/mods-mount.ts +179 -0
  593. package/src/server/mount-adapters.ts +82 -0
  594. package/src/server/mount.parametrized.test.ts +52 -0
  595. package/src/server/mount.query.test.ts +127 -0
  596. package/src/server/mount.test.ts +497 -0
  597. package/src/server/mount.ts +208 -0
  598. package/src/server/prefab.test.ts +415 -0
  599. package/src/server/prefab.ts +104 -0
  600. package/src/server/seed/index.ts +24 -0
  601. package/src/server/server.ts +153 -0
  602. package/src/server/stress.test.ts +844 -0
  603. package/src/server/sub.test.ts +55 -0
  604. package/src/server/sub.ts +224 -0
  605. package/src/server/trpc.ts +369 -0
  606. package/src/server/types-mount.ts +142 -0
  607. package/src/server/validate.test.ts +91 -0
  608. package/src/server/validate.ts +22 -0
  609. package/src/server/volatile.test.ts +140 -0
  610. package/src/server/volatile.ts +32 -0
  611. package/src/server/watch.test.ts +594 -0
  612. package/src/server/watch.ts +202 -0
  613. package/src/server/workflow.test.ts +82 -0
  614. package/src/tree/CLAUDE.md +13 -0
  615. package/src/tree/cache.test.ts +213 -0
  616. package/src/tree/cache.ts +51 -0
  617. package/src/tree/fs.test.ts +247 -0
  618. package/src/tree/fs.ts +251 -0
  619. package/src/tree/index.test.ts +164 -0
  620. package/src/tree/index.ts +216 -0
  621. package/src/tree/inflight.ts +15 -0
  622. package/src/tree/json-codec.ts +16 -0
  623. package/src/tree/mimefs.test.ts +289 -0
  624. package/src/tree/mimefs.ts +142 -0
  625. package/src/tree/mongo.ts +125 -0
  626. package/src/tree/patch.test.ts +192 -0
  627. package/src/tree/patch.ts +133 -0
  628. package/src/tree/query.test.ts +110 -0
  629. package/src/tree/query.ts +70 -0
  630. package/src/tree/repath.test.ts +86 -0
  631. package/src/tree/repath.ts +53 -0
  632. package/src/tree-chain.test.ts +723 -0
  633. package/src/tree-chain.ts +144 -0
  634. package/src/tree.ts +1 -0
  635. package/src/uri.test.ts +86 -0
  636. package/src/uri.ts +82 -0
  637. package/CHANGELOG.md +0 -314
  638. package/README.md +0 -18
  639. package/dist/context.d.ts +0 -41
  640. package/dist/context.d.ts.map +0 -1
  641. package/dist/context.mjs +0 -81
  642. package/dist/context.mjs.map +0 -1
  643. package/dist/contexts/node-engine.d.ts +0 -12
  644. package/dist/contexts/node-engine.d.ts.map +0 -1
  645. package/dist/contexts/node-engine.mjs +0 -7
  646. package/dist/contexts/node-engine.mjs.map +0 -1
  647. package/dist/contexts/noflo/types.d.ts +0 -20
  648. package/dist/contexts/noflo/types.d.ts.map +0 -1
  649. package/dist/contexts/object.d.ts +0 -11
  650. package/dist/contexts/object.d.ts.map +0 -1
  651. package/dist/contexts/object.mjs +0 -15
  652. package/dist/contexts/object.mjs.map +0 -1
  653. package/dist/contexts/proto.d.ts +0 -11
  654. package/dist/contexts/proto.d.ts.map +0 -1
  655. package/dist/contexts/proto.mjs +0 -7
  656. package/dist/contexts/proto.mjs.map +0 -1
  657. package/dist/contexts/react-context.d.ts +0 -21
  658. package/dist/contexts/react-context.d.ts.map +0 -1
  659. package/dist/contexts/react-context.mjs +0 -24
  660. package/dist/contexts/react-context.mjs.map +0 -1
  661. package/dist/contexts/service.mjs +0 -7
  662. package/dist/contexts/service.mjs.map +0 -1
  663. package/dist/get-type-cache.d.ts +0 -2
  664. package/dist/get-type-cache.d.ts.map +0 -1
  665. package/dist/get-type-cache.mjs +0 -7
  666. package/dist/get-type-cache.mjs.map +0 -1
  667. package/dist/index.d.ts +0 -13
  668. package/dist/index.d.ts.map +0 -1
  669. package/dist/index.mjs +0 -10
  670. package/dist/index.mjs.map +0 -1
  671. package/dist/link/link.d.ts +0 -25
  672. package/dist/link/link.d.ts.map +0 -1
  673. package/dist/link/link.mjs +0 -72
  674. package/dist/link/link.mjs.map +0 -1
  675. package/dist/link/link.test.d.ts +0 -2
  676. package/dist/link/link.test.d.ts.map +0 -1
  677. package/dist/loading.d.ts +0 -9
  678. package/dist/loading.d.ts.map +0 -1
  679. package/dist/meta-type.d.ts +0 -58
  680. package/dist/meta-type.d.ts.map +0 -1
  681. package/dist/meta-type.mjs +0 -104
  682. package/dist/meta-type.mjs.map +0 -1
  683. package/dist/meta.d.ts +0 -20
  684. package/dist/meta.d.ts.map +0 -1
  685. package/dist/meta.mjs +0 -16
  686. package/dist/meta.mjs.map +0 -1
  687. package/dist/node/index.d.ts +0 -2
  688. package/dist/node/index.d.ts.map +0 -1
  689. package/dist/node/types.d.ts +0 -37
  690. package/dist/node/types.d.ts.map +0 -1
  691. package/dist/test/context.test.d.ts +0 -2
  692. package/dist/test/context.test.d.ts.map +0 -1
  693. package/dist/types.d.ts +0 -14
  694. package/dist/types.d.ts.map +0 -1
  695. package/dist/types.mjs +0 -16
  696. package/dist/types.mjs.map +0 -1
@@ -0,0 +1,386 @@
1
+ // Treenity Auth — ACL on nodes
2
+ // Group-based permissions, tree inheritance, deny-is-sticky.
3
+ // Tree wrapper: resolves ACL per path, strips forbidden components.
4
+
5
+ import {
6
+ A,
7
+ type ComponentData,
8
+ type GroupPerm,
9
+ isComponent,
10
+ type NodeData,
11
+ R,
12
+ resolve as resolveHandler,
13
+ W,
14
+ } from '#core';
15
+ import { paginate, type Tree } from '#tree';
16
+ import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';
17
+
18
+ export type AclHandler = () => GroupPerm[];
19
+
20
+ declare module '#core/context' {
21
+ interface ContextHandlers {
22
+ acl: AclHandler;
23
+ }
24
+ }
25
+
26
+ // ── Types ──
27
+
28
+ export type Session = { userId: string; claims?: string[] };
29
+
30
+ // Session nodes are stored as regular nodes with extra fields
31
+ type SessionNode = NodeData & { userId: string; createdAt: number; expiresAt: number; claims?: string[] };
32
+
33
+ // ── Sessions (store-backed, /auth/sessions/{token}) ──
34
+
35
+ const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
36
+
37
+ export async function createSession(
38
+ store: Tree,
39
+ userId: string,
40
+ opts?: { ttlMs?: number; claims?: string[] },
41
+ ): Promise<string> {
42
+ const token = randomBytes(32).toString('hex');
43
+ const now = Date.now();
44
+ const sessionNode: SessionNode = {
45
+ $path: `/auth/sessions/${token}`, $type: 'session',
46
+ userId, createdAt: now, expiresAt: now + (opts?.ttlMs ?? SESSION_TTL_MS),
47
+ ...(opts?.claims && { claims: opts.claims }),
48
+ };
49
+ await store.set(sessionNode);
50
+ return token;
51
+ }
52
+
53
+ export async function resolveToken(store: Tree, token: string): Promise<Session | null> {
54
+ const node = await store.get(`/auth/sessions/${token}`) as SessionNode | undefined;
55
+ if (!node) return null;
56
+ if (!node.userId || !node.expiresAt) {
57
+ console.error(`[auth] corrupt session: ${token} (missing userId or expiresAt)`);
58
+ await store.remove(`/auth/sessions/${token}`);
59
+ return null;
60
+ }
61
+ if (Date.now() > node.expiresAt) {
62
+ await store.remove(`/auth/sessions/${token}`);
63
+ return null;
64
+ }
65
+ return { userId: node.userId, ...(node.claims && { claims: node.claims }) };
66
+ }
67
+
68
+ export async function revokeSession(store: Tree, token: string): Promise<boolean> {
69
+ return store.remove(`/auth/sessions/${token}`);
70
+ }
71
+
72
+ // ── Password hashing ──
73
+
74
+ export async function hashPassword(password: string): Promise<string> {
75
+ const salt = randomBytes(16);
76
+ const key = await new Promise<Buffer>((resolve, reject) =>
77
+ scrypt(password, salt, 64, (err, key) => (err ? reject(err) : resolve(key))),
78
+ );
79
+ return salt.toString('hex') + ':' + key.toString('hex');
80
+ }
81
+
82
+ // Pre-computed dummy hash for constant-time login (prevents timing-based user enumeration)
83
+ export const DUMMY_HASH = randomBytes(16).toString('hex') + ':' + randomBytes(64).toString('hex');
84
+
85
+ export async function verifyPassword(password: string, hash: string): Promise<boolean> {
86
+ const [saltHex, keyHex] = hash.split(':');
87
+ if (!saltHex || !keyHex) throw new Error('Malformed password hash');
88
+ const salt = Buffer.from(saltHex, 'hex');
89
+ const stored = Buffer.from(keyHex, 'hex');
90
+ const key = await new Promise<Buffer>((resolve, reject) =>
91
+ scrypt(password, salt, 64, (err, key) => (err ? reject(err) : resolve(key))),
92
+ );
93
+ return timingSafeEqual(stored, key);
94
+ }
95
+
96
+ // ── Path utils ──
97
+
98
+ export function ancestorPaths(path: string): string[] {
99
+ if (path === '/') return ['/'];
100
+ const parts = path.split('/').filter(Boolean);
101
+ const result = ['/'];
102
+ let current = '';
103
+ for (const part of parts) {
104
+ current += '/' + part;
105
+ result.push(current);
106
+ }
107
+ return result;
108
+ }
109
+
110
+ // ── ACL resolution ──
111
+
112
+ // Accumulated ACL state at a given tree level — built from root downward.
113
+ // Cached per level so sibling paths skip ancestor re-processing entirely.
114
+ type AclState = {
115
+ groupPerms: Map<string, number>;
116
+ denied: Set<string>;
117
+ deniedBits: Map<string, number>;
118
+ owner: string | undefined;
119
+ };
120
+
121
+ function cloneAclState(s: AclState): AclState {
122
+ return {
123
+ groupPerms: new Map(s.groupPerms),
124
+ denied: new Set(s.denied),
125
+ deniedBits: new Map(s.deniedBits),
126
+ owner: s.owner,
127
+ };
128
+ }
129
+
130
+ // Walk ancestors, carry forward per-group.
131
+ // p=0: deny all (sticky), p<0: deny bits (sticky), p>0: allow bits.
132
+ // "owner" pseudo-group: matches if userId === $owner on node (or inherited).
133
+ //
134
+ // nodeCache: avoids re-fetching already-seen nodes (keyed by path, null = not found)
135
+ // stateCache: accumulated ACL state at each tree level — on sibling paths, start
136
+ // from the deepest cached ancestor instead of walking from root again.
137
+ export async function resolvePermission(
138
+ store: Tree,
139
+ path: string,
140
+ userId: string | null,
141
+ claims: string[],
142
+ cache?: Map<string, number>,
143
+ nodeCache?: Map<string, NodeData | null>,
144
+ stateCache?: Map<string, AclState>,
145
+ ): Promise<number> {
146
+ if (cache?.has(path)) return cache.get(path)!;
147
+
148
+ const ancestors = ancestorPaths(path);
149
+
150
+ // Start from deepest cached ancestor state (skip already-accumulated prefix)
151
+ let startIdx = 0;
152
+ let state: AclState = { groupPerms: new Map(), denied: new Set(), deniedBits: new Map(), owner: undefined };
153
+
154
+ if (stateCache) {
155
+ for (let i = ancestors.length - 1; i >= 0; i--) {
156
+ const cached = stateCache.get(ancestors[i]);
157
+ if (cached) {
158
+ state = cloneAclState(cached);
159
+ startIdx = i + 1;
160
+ break;
161
+ }
162
+ }
163
+ }
164
+
165
+ for (let i = startIdx; i < ancestors.length; i++) {
166
+ const p = ancestors[i];
167
+
168
+ let node: NodeData | null | undefined;
169
+ if (nodeCache?.has(p)) {
170
+ node = nodeCache.get(p);
171
+ } else {
172
+ const fetched = await store.get(p);
173
+ node = fetched ?? null;
174
+ nodeCache?.set(p, node);
175
+ }
176
+
177
+ if (node) {
178
+ if (node.$owner) state.owner = node.$owner;
179
+ if (node.$acl) {
180
+ for (const { g, p: perm } of node.$acl) {
181
+ const matches = g === 'owner' ? userId !== null && userId === state.owner : claims.includes(g);
182
+ if (!matches) continue;
183
+ if (state.denied.has(g)) continue;
184
+ if (perm < 0) {
185
+ // Sticky deny specific bits
186
+ const bits = -perm;
187
+ state.deniedBits.set(g, (state.deniedBits.get(g) || 0) | bits);
188
+ } else if (perm === 0) {
189
+ // Deny all (sticky)
190
+ state.denied.add(g);
191
+ state.groupPerms.set(g, 0);
192
+ } else {
193
+ // Allow bits, mask out denied
194
+ const allowed = perm & ~(state.deniedBits.get(g) || 0);
195
+ state.groupPerms.set(g, allowed);
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ // Cache accumulated state at this level — future sibling paths start here
202
+ stateCache?.set(p, cloneAclState(state));
203
+ }
204
+
205
+ let effective = 0;
206
+ for (const v of state.groupPerms.values()) {
207
+ if (v > effective) effective = v;
208
+ }
209
+ cache?.set(path, effective);
210
+ return effective;
211
+ }
212
+
213
+ // ── Component ACL ──
214
+
215
+ export function componentPerm(
216
+ comp: ComponentData,
217
+ userId: string | null,
218
+ claims: string[],
219
+ owner: string | undefined,
220
+ ): number {
221
+ const typeAcl = resolveHandler(comp.$type, 'acl');
222
+ const acls: GroupPerm[][] = [];
223
+ if (typeAcl) acls.push(typeAcl());
224
+ if (comp.$acl) acls.push(comp.$acl);
225
+ if (acls.length === 0) return R | W | A; // no ACL = full access
226
+
227
+ let effective = R | W | A;
228
+ for (const aclList of acls) {
229
+ const groupPerms = new Map<string, number>();
230
+ const deniedBits = new Map<string, number>();
231
+ for (const { g, p } of aclList) {
232
+ const matches = g === 'owner' ? userId !== null && userId === owner : claims.includes(g);
233
+ if (!matches) continue;
234
+ if (p < 0) {
235
+ // Sticky deny specific bits
236
+ const bits = -p;
237
+ deniedBits.set(g, (deniedBits.get(g) || 0) | bits);
238
+ } else if (p === 0) {
239
+ groupPerms.set(g, 0);
240
+ } else {
241
+ // Allow bits, mask out denied
242
+ const allowed = p & ~(deniedBits.get(g) || 0);
243
+ groupPerms.set(g, allowed);
244
+ }
245
+ }
246
+ let best = 0;
247
+ for (const p of groupPerms.values()) {
248
+ if (p > best) best = p;
249
+ }
250
+ effective &= best;
251
+ }
252
+ return effective;
253
+ }
254
+
255
+ export function stripComponents(node: NodeData, userId: string | null, claims: string[]): NodeData {
256
+ const out: NodeData = { $path: node.$path, $type: node.$type };
257
+ if (node.$acl) out.$acl = node.$acl;
258
+ if (node.$owner) out.$owner = node.$owner;
259
+ if (node.$rev !== undefined) out.$rev = node.$rev;
260
+ if ('$ref' in node) out['$ref'] = node['$ref'];
261
+ if ('$patches' in node) out['$patches'] = node['$patches'];
262
+ for (const [key, val] of Object.entries(node)) {
263
+ if (key.startsWith('$')) continue;
264
+ if (!isComponent(val)) { out[key] = val; continue; }
265
+ if (componentPerm(val, userId, claims, node.$owner) & R) out[key] = val;
266
+ }
267
+ return out;
268
+ }
269
+
270
+ // ── Build claims ──
271
+
272
+ export async function buildClaims(store: Tree, userId: string): Promise<string[]> {
273
+ const claims = [`u:${userId}`, 'authenticated'];
274
+ const userNode = await store.get(`/auth/users/${userId}`);
275
+ if (userNode) {
276
+ const gv = userNode['groups'];
277
+ const groups = isComponent(gv) ? gv as { $type: string; list?: string[] } : undefined;
278
+ if (groups?.list) claims.push(...groups.list);
279
+ }
280
+ return claims;
281
+ }
282
+
283
+ // ── Tree wrapper ──
284
+
285
+ export type AclStore = Tree & {
286
+ /** Cached after get/getChildren — O(1) for already-resolved paths */
287
+ getPerm(path: string): Promise<number>;
288
+ };
289
+
290
+ export function withAcl(rawStore: Tree, userId: string | null, claims: string[]): AclStore {
291
+ const cache = new Map<string, number>();
292
+ // stateCache: accumulated ACL state per tree level — avoids re-walking shared ancestors
293
+ // within a single request. nodeCache is handled by withCache in the store pipeline.
294
+ const stateCache = new Map<string, AclState>();
295
+
296
+ async function getPerm(path: string): Promise<number> {
297
+ return resolvePermission(rawStore, path, userId, claims, cache, undefined, stateCache);
298
+ }
299
+
300
+ return {
301
+ getPerm,
302
+ async get(path, ctx) {
303
+ const perm = await getPerm(path);
304
+ if (!(perm & R)) return undefined;
305
+ const node = await rawStore.get(path, ctx);
306
+ if (!node) return undefined;
307
+ const out = stripComponents(node, userId, claims);
308
+ if (!(perm & A)) {
309
+ delete out.$acl;
310
+ delete out.$owner;
311
+ }
312
+ return out;
313
+ },
314
+
315
+ async getChildren(path, opts, ctx) {
316
+ const MAX_ACL_SCAN = 10_000;
317
+ // Fetch up to limit from underlying — ACL filters first, then paginate
318
+ const raw = await rawStore.getChildren(path, { depth: opts?.depth, limit: MAX_ACL_SCAN }, ctx);
319
+ if (raw.items.length >= MAX_ACL_SCAN) {
320
+ console.warn(`[acl] getChildren(${path}): hit scan limit ${MAX_ACL_SCAN}, results may be incomplete`);
321
+ }
322
+ const filtered: NodeData[] = [];
323
+ for (const child of raw.items) {
324
+ const perm = await getPerm(child.$path);
325
+ if (!(perm & R)) continue;
326
+ const out = stripComponents(child, userId, claims);
327
+ if (!(perm & A)) {
328
+ delete out.$acl;
329
+ delete out.$owner;
330
+ }
331
+ filtered.push(out);
332
+ }
333
+ // Preserve queryMount for CDC Matrix (sub.ts active query registration)
334
+ const result = paginate(filtered, opts);
335
+ if (raw.queryMount) result.queryMount = raw.queryMount;
336
+ return result;
337
+ },
338
+
339
+ async set(node, ctx) {
340
+ const perm = await getPerm(node.$path);
341
+ if (!(perm & W)) throw new Error(`Access denied: ${node.$path}`);
342
+ const existing = await rawStore.get(node.$path, ctx);
343
+ const safe = { ...node };
344
+ // Non-admin: preserve existing $acl/$owner
345
+ if (!(perm & A)) {
346
+ if (existing?.$acl) safe.$acl = existing.$acl;
347
+ else delete safe.$acl;
348
+ if (existing?.$owner) safe.$owner = existing.$owner;
349
+ else delete safe.$owner;
350
+ }
351
+ // Protect components: if user lacks W on a component, keep old value
352
+ const owner = safe.$owner ?? existing?.$owner;
353
+ for (const [key, val] of Object.entries(safe)) {
354
+ if (key.startsWith('$')) continue;
355
+ if (!isComponent(val)) continue;
356
+ if (!(componentPerm(val, userId, claims, owner) & W)) {
357
+ // User can't write this component — restore old value or remove
358
+ if (existing && key in existing) safe[key] = existing[key];
359
+ else delete safe[key];
360
+ }
361
+ }
362
+ // Also restore protected components the user may have omitted
363
+ if (existing) {
364
+ for (const [key, val] of Object.entries(existing)) {
365
+ if (key.startsWith('$')) continue;
366
+ if (!isComponent(val)) continue;
367
+ if (!(componentPerm(val, userId, claims, owner) & W) && !(key in safe))
368
+ safe[key] = val;
369
+ }
370
+ }
371
+ return rawStore.set(safe, ctx);
372
+ },
373
+
374
+ async remove(path, ctx) {
375
+ const perm = await getPerm(path);
376
+ if (!(perm & W)) throw new Error(`Access denied: ${path}`);
377
+ return rawStore.remove(path, ctx);
378
+ },
379
+
380
+ async patch(path, ops, ctx) {
381
+ const perm = await getPerm(path);
382
+ if (!(perm & W)) throw new Error(`Access denied: ${path}`);
383
+ return rawStore.patch(path, ops, ctx);
384
+ },
385
+ };
386
+ }
@@ -0,0 +1,24 @@
1
+ // Treenity tRPC Client — Node.js
2
+ // Creates tRPC client for tests and scripts (not browser).
3
+ // Uses `eventsource` npm package for SSE subscriptions.
4
+
5
+ import { createTRPCClient, httpBatchLink, httpSubscriptionLink, splitLink } from '@trpc/client';
6
+ import { EventSource } from 'eventsource';
7
+ import type { TreeRouter } from './trpc';
8
+
9
+ export function createClient(url: string, token?: string) {
10
+ const headers = token ? { Authorization: `Bearer ${token}` } : {};
11
+ return createTRPCClient<TreeRouter>({
12
+ links: [
13
+ splitLink({
14
+ condition: (op) => op.type === 'subscription',
15
+ true: httpSubscriptionLink({
16
+ url,
17
+ EventSource: EventSource as any,
18
+ connectionParams: () => (token ? { token } : {}),
19
+ }),
20
+ false: httpBatchLink({ url, headers: () => headers }),
21
+ }),
22
+ ],
23
+ });
24
+ }
@@ -0,0 +1,128 @@
1
+ // Pre/post condition warnings (Design by Contract)
2
+ // @pre fields warn if empty before action; @post fields warn if unchanged after
3
+
4
+ import { registerType } from '#comp';
5
+ import { createNode, register } from '#core';
6
+ import { clearRegistry } from '#core/index.test';
7
+ import { createMemoryTree } from '#tree';
8
+ import assert from 'node:assert/strict';
9
+ import { afterEach, beforeEach, describe, it } from 'node:test';
10
+ import { executeAction } from './actions';
11
+
12
+ // Test component with pre/post conditions declared in schema
13
+ class Ticket {
14
+ status = '';
15
+ assignee = '';
16
+ resolvedAt = 0;
17
+
18
+ close() {
19
+ this.status = 'closed';
20
+ this.resolvedAt = Date.now();
21
+ }
22
+
23
+ noop() {
24
+ // intentionally does nothing — postcondition should warn
25
+ }
26
+ }
27
+
28
+ describe('pre/post action conditions', () => {
29
+ beforeEach(() => {
30
+ clearRegistry();
31
+ registerType('test.ticket', Ticket);
32
+ // Schema with pre/post arrays
33
+ register('test.ticket', 'schema', () => ({
34
+ $id: 'test.ticket',
35
+ type: 'object',
36
+ properties: {
37
+ status: { type: 'string' },
38
+ assignee: { type: 'string' },
39
+ resolvedAt: { type: 'number' },
40
+ },
41
+ methods: {
42
+ close: {
43
+ description: 'Close the ticket',
44
+ pre: ['status', 'assignee'],
45
+ post: ['status', 'resolvedAt'],
46
+ arguments: [],
47
+ },
48
+ noop: {
49
+ description: 'Does nothing',
50
+ post: ['status'],
51
+ arguments: [],
52
+ },
53
+ },
54
+ }));
55
+ });
56
+
57
+ afterEach(() => clearRegistry());
58
+
59
+ it('warns when @pre fields are empty', async () => {
60
+ const store = createMemoryTree();
61
+ await store.set({ ...createNode('/t/1', 'test.ticket'), status: '', assignee: '' });
62
+
63
+ const warnings: string[] = [];
64
+ const orig = console.warn;
65
+ console.warn = (...args: any[]) => warnings.push(args.join(' '));
66
+
67
+ try {
68
+ await executeAction(store, '/t/1', undefined, undefined, 'close');
69
+ } finally {
70
+ console.warn = orig;
71
+ }
72
+
73
+ assert.ok(warnings.some(w => w.includes('[pre]') && w.includes('status')), 'should warn about empty status');
74
+ assert.ok(warnings.some(w => w.includes('[pre]') && w.includes('assignee')), 'should warn about empty assignee');
75
+ });
76
+
77
+ it('no pre warning when fields are filled', async () => {
78
+ const store = createMemoryTree();
79
+ await store.set({ ...createNode('/t/2', 'test.ticket'), status: 'open', assignee: 'alice' });
80
+
81
+ const warnings: string[] = [];
82
+ const orig = console.warn;
83
+ console.warn = (...args: any[]) => warnings.push(args.join(' '));
84
+
85
+ try {
86
+ await executeAction(store, '/t/2', undefined, undefined, 'close');
87
+ } finally {
88
+ console.warn = orig;
89
+ }
90
+
91
+ assert.ok(!warnings.some(w => w.includes('[pre]')), `unexpected pre warnings: ${warnings}`);
92
+ });
93
+
94
+ it('warns when @post fields are unchanged', async () => {
95
+ const store = createMemoryTree();
96
+ await store.set({ ...createNode('/t/3', 'test.ticket'), status: 'open', assignee: 'bob' });
97
+
98
+ const warnings: string[] = [];
99
+ const orig = console.warn;
100
+ console.warn = (...args: any[]) => warnings.push(args.join(' '));
101
+
102
+ try {
103
+ await executeAction(store, '/t/3', undefined, undefined, 'noop');
104
+ } finally {
105
+ console.warn = orig;
106
+ }
107
+
108
+ assert.ok(warnings.some(w => w.includes('[post]') && w.includes('status')),
109
+ `should warn about unchanged status. Warnings: ${warnings}`);
110
+ });
111
+
112
+ it('no post warning when fields change', async () => {
113
+ const store = createMemoryTree();
114
+ await store.set({ ...createNode('/t/4', 'test.ticket'), status: 'open', assignee: 'alice' });
115
+
116
+ const warnings: string[] = [];
117
+ const orig = console.warn;
118
+ console.warn = (...args: any[]) => warnings.push(args.join(' '));
119
+
120
+ try {
121
+ await executeAction(store, '/t/4', undefined, undefined, 'close');
122
+ } finally {
123
+ console.warn = orig;
124
+ }
125
+
126
+ assert.ok(!warnings.some(w => w.includes('[post]')), `unexpected post warnings: ${warnings}`);
127
+ });
128
+ });
@@ -0,0 +1,25 @@
1
+ // Cookie helpers for HttpOnly session management
2
+
3
+ import type { IncomingMessage, ServerResponse } from 'node:http';
4
+
5
+ export function parseCookies(req: IncomingMessage): Record<string, string> {
6
+ const header = req.headers.cookie;
7
+ if (!header) return {};
8
+ const out: Record<string, string> = {};
9
+ for (const pair of header.split(';')) {
10
+ const eq = pair.indexOf('=');
11
+ if (eq < 0) continue;
12
+ out[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
13
+ }
14
+ return out;
15
+ }
16
+
17
+ export function setSessionCookie(res: ServerResponse, token: string): void {
18
+ const parts = [`session=${token}`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=86400'];
19
+ if (process.env.NODE_ENV === 'production') parts.push('Secure');
20
+ res.setHeader('Set-Cookie', parts.join('; '));
21
+ }
22
+
23
+ export function clearSessionCookie(res: ServerResponse): void {
24
+ res.setHeader('Set-Cookie', 'session=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0');
25
+ }