@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,247 @@
1
+ import { createNode } from '#core';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, rm, stat } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { afterEach, describe, it } from 'node:test';
7
+ import { createFsTree } from './fs';
8
+ import { mapSiftQuery } from './query';
9
+
10
+ describe('FsStore', () => {
11
+ let dir: string;
12
+
13
+ afterEach(async () => {
14
+ if (dir) await rm(dir, { recursive: true, force: true });
15
+ });
16
+
17
+ async function setup() {
18
+ dir = await mkdtemp(join(tmpdir(), 'treenity-fs-test-'));
19
+ return createFsTree(dir);
20
+ }
21
+
22
+ function exists(path: string) {
23
+ return stat(join(dir, path)).then(() => true, () => false);
24
+ }
25
+
26
+ it('set and get', async () => {
27
+ const store = await setup();
28
+ const node = createNode('/users/alice', 'user');
29
+ await store.set(node);
30
+ assert.deepEqual(await store.get('/users/alice'), node);
31
+ });
32
+
33
+ it('get returns undefined for missing', async () => {
34
+ const store = await setup();
35
+ assert.equal(await store.get('/nope'), undefined);
36
+ });
37
+
38
+ it('getChildren direct', async () => {
39
+ const store = await setup();
40
+ await store.set(createNode('/app', 'root'));
41
+ await store.set(createNode('/app/a', 'item'));
42
+ await store.set(createNode('/app/b', 'item'));
43
+ await store.set(createNode('/app/a/deep', 'item'));
44
+
45
+ const children = await store.getChildren('/app');
46
+ assert.equal(children.items.length, 2);
47
+ assert.deepEqual(children.items.map((n) => n.$path).sort(), ['/app/a', '/app/b']);
48
+ });
49
+
50
+ it('getChildren with depth', async () => {
51
+ const store = await setup();
52
+ await store.set(createNode('/x', 'root'));
53
+ await store.set(createNode('/x/a', 'item'));
54
+ await store.set(createNode('/x/a/b', 'item'));
55
+ await store.set(createNode('/x/a/b/c', 'item'));
56
+
57
+ const d2 = await store.getChildren('/x', { depth: 2 });
58
+ assert.equal(d2.items.length, 2);
59
+ assert.deepEqual(d2.items.map((n) => n.$path).sort(), ['/x/a', '/x/a/b']);
60
+
61
+ const all = await store.getChildren('/x', { depth: Infinity });
62
+ assert.equal(all.items.length, 3);
63
+ });
64
+
65
+ it('remove', async () => {
66
+ const store = await setup();
67
+ await store.set(createNode('/z', 'item'));
68
+ assert.equal(await store.remove('/z'), true);
69
+ assert.equal(await store.get('/z'), undefined);
70
+ assert.equal(await store.remove('/z'), false);
71
+ });
72
+
73
+ it('handles nested paths', async () => {
74
+ const store = await setup();
75
+ const node = createNode('/a/b/c/d/e', 'deep');
76
+ await store.set(node);
77
+ assert.deepEqual(await store.get('/a/b/c/d/e'), node);
78
+ });
79
+
80
+ // ── Storage format tests ──
81
+
82
+ it('leaf node stored as name.json', async () => {
83
+ const store = await setup();
84
+ await store.set(createNode('/leaf', 'item'));
85
+
86
+ assert.ok(await exists('leaf.json'), 'leaf.json should exist');
87
+ assert.ok(!(await exists('leaf/$.json')), 'leaf/$.json should NOT exist');
88
+ });
89
+
90
+ it('root always stored as $/$.json (dir form)', async () => {
91
+ const store = await setup();
92
+ await store.set(createNode('/', 'root'));
93
+
94
+ assert.ok(await exists('$.json'), 'root $.json should exist');
95
+ });
96
+
97
+ it('auto-promotes leaf to dir when child is created', async () => {
98
+ const store = await setup();
99
+ await store.set(createNode('/parent', 'dir'));
100
+
101
+ // Initially leaf form
102
+ assert.ok(await exists('parent.json'), 'should be leaf form');
103
+ assert.ok(!(await exists('parent/$.json')), 'should NOT be dir form yet');
104
+
105
+ // Add child — parent must be promoted to dir form
106
+ await store.set(createNode('/parent/child', 'item'));
107
+
108
+ assert.ok(await exists('parent/$.json'), 'parent should now be dir form');
109
+ assert.ok(!(await exists('parent.json')), 'parent.json should be gone');
110
+ assert.ok(await exists('parent/child.json'), 'child should be leaf form');
111
+
112
+ // Both nodes still readable
113
+ const parent = await store.get('/parent');
114
+ assert.equal(parent?.$type, 't.dir');
115
+ const child = await store.get('/parent/child');
116
+ assert.equal(child?.$type, 't.item');
117
+ });
118
+
119
+ it('auto-demotes dir to leaf when last child is removed', async () => {
120
+ const store = await setup();
121
+ await store.set(createNode('/parent', 'dir'));
122
+ await store.set(createNode('/parent/child', 'item'));
123
+
124
+ // Parent in dir form
125
+ assert.ok(await exists('parent/$.json'));
126
+
127
+ // Remove the child
128
+ await store.remove('/parent/child');
129
+
130
+ // Parent should be demoted to leaf form
131
+ assert.ok(await exists('parent.json'), 'parent should be demoted to leaf form');
132
+ assert.ok(!(await exists('parent/$.json')), 'parent dir form should be gone');
133
+
134
+ // Parent still readable
135
+ const parent = await store.get('/parent');
136
+ assert.equal(parent?.$type, 't.dir');
137
+ });
138
+
139
+ it('node with children stays in dir form on re-save', async () => {
140
+ const store = await setup();
141
+ await store.set(createNode('/p', 'dir'));
142
+ await store.set(createNode('/p/a', 'item'));
143
+
144
+ // Re-save parent — should stay dir form because it has children
145
+ const p = await store.get('/p');
146
+ await store.set({ ...p!, $rev: p!.$rev });
147
+
148
+ assert.ok(await exists('p/$.json'), 'should remain dir form');
149
+ assert.ok(!(await exists('p.json')), 'should not have leaf form');
150
+ });
151
+
152
+ it('deep promotion chain', async () => {
153
+ const store = await setup();
154
+ await store.set(createNode('/a', 'n'));
155
+ await store.set(createNode('/a/b', 'n'));
156
+
157
+ // Both start as leaves where possible
158
+ assert.ok(await exists('a/$.json'), 'a has child b → dir form');
159
+ assert.ok(await exists('a/b.json'), 'b is leaf');
160
+
161
+ // Create deep child — b must promote
162
+ await store.set(createNode('/a/b/c', 'n'));
163
+
164
+ assert.ok(await exists('a/b/$.json'), 'b promoted to dir form');
165
+ assert.ok(!(await exists('a/b.json')), 'b.json gone');
166
+ assert.ok(await exists('a/b/c.json'), 'c is leaf');
167
+ });
168
+
169
+ it('deep demotion chain', async () => {
170
+ const store = await setup();
171
+ await store.set(createNode('/a', 'n'));
172
+ await store.set(createNode('/a/b', 'n'));
173
+ await store.set(createNode('/a/b/c', 'n'));
174
+
175
+ // Remove c — b should demote, a should demote too (b.json is still a child of a)
176
+ await store.remove('/a/b/c');
177
+
178
+ assert.ok(await exists('a/b.json'), 'b demoted to leaf');
179
+ assert.ok(await exists('a/$.json'), 'a still dir form (has child b.json)');
180
+
181
+ // Remove b — a should demote
182
+ await store.remove('/a/b');
183
+
184
+ assert.ok(await exists('a.json'), 'a demoted to leaf');
185
+ assert.ok(!(await exists('a/$.json')), 'a dir form gone');
186
+ });
187
+
188
+ it('data integrity through promotion/demotion', async () => {
189
+ const store = await setup();
190
+ const parent = createNode('/p', 'dir', { label: 'parent' });
191
+ await store.set(parent);
192
+
193
+ const child = createNode('/p/c', 'item', { label: 'child' });
194
+ await store.set(child);
195
+
196
+ // Verify data survived promotion
197
+ const p = await store.get('/p');
198
+ assert.equal((p as any).label, 'parent');
199
+
200
+ await store.remove('/p/c');
201
+
202
+ // Verify data survived demotion
203
+ const p2 = await store.get('/p');
204
+ assert.equal((p2 as any).label, 'parent');
205
+ });
206
+
207
+ it('OCC still works across storage forms', async () => {
208
+ const store = await setup();
209
+ await store.set(createNode('/n', 'item'));
210
+ const n = await store.get('/n');
211
+
212
+ // Promote by adding child
213
+ await store.set(createNode('/n/child', 'item'));
214
+
215
+ // OCC should still work on promoted node
216
+ await assert.rejects(() => store.set({ ...n!, $rev: 999 }));
217
+ });
218
+
219
+ it('getChildren applies sift query filter', async () => {
220
+ const store = await setup();
221
+ await store.set(createNode('/tasks', 'dir'));
222
+ await store.set(createNode('/tasks/t1', 'task', { status: 'pending' }));
223
+ await store.set(createNode('/tasks/t2', 'task', { status: 'done' }));
224
+ await store.set(createNode('/tasks/t3', 'task', { status: 'pending' }));
225
+
226
+ const pending = await store.getChildren('/tasks', {
227
+ query: mapSiftQuery({ $type: 't.task', status: 'pending' }) as Record<string, unknown>,
228
+ });
229
+ assert.equal(pending.items.length, 2);
230
+ assert.deepEqual(pending.items.map(n => n.$path).sort(), ['/tasks/t1', '/tasks/t3']);
231
+ });
232
+
233
+ it('getChildren sift filter excludes non-matching types', async () => {
234
+ const store = await setup();
235
+ await store.set(createNode('/parent', 'config'));
236
+ await store.set(createNode('/parent/mount-a', 'mount-point'));
237
+ await store.set(createNode('/parent/mount-b', 'mount-point'));
238
+ await store.set(createNode('/parent/t1', 'task', { status: 'pending' }));
239
+ await store.set(createNode('/parent/t2', 'task', { status: 'done' }));
240
+
241
+ const tasks = await store.getChildren('/parent', {
242
+ query: mapSiftQuery({ $type: 't.task' }) as Record<string, unknown>,
243
+ });
244
+ assert.equal(tasks.items.length, 2);
245
+ assert.ok(tasks.items.every(n => n.$type === 't.task'));
246
+ });
247
+ });
package/src/tree/fs.ts ADDED
@@ -0,0 +1,251 @@
1
+ // Treenity FS Tree — Layer 1
2
+ // Stores nodes as JSON files on disk.
3
+ // Leaf nodes → name.json, directory nodes (with children) → name/$.json
4
+ // Auto-promotes leaf→dir when children appear, demotes dir→leaf when last child removed.
5
+
6
+ import type { NodeData } from '#core';
7
+ import { dirname as treeDirname, isChildPath } from '#core/path';
8
+ import { mkdir, readdir, readFile, rmdir, unlink, writeFile } from 'node:fs/promises';
9
+ import { dirname, join, resolve } from 'node:path';
10
+ import sift from 'sift';
11
+ import type { Tree } from './index';
12
+ import { paginate } from './index';
13
+ import { defaultPatch } from './patch';
14
+ import { mapNodeForSift } from './query';
15
+
16
+ function securityCheck(root: string, file: string) {
17
+ if (!file.startsWith(resolve(root))) throw new Error(`Path traversal blocked`);
18
+ }
19
+
20
+ export async function createFsTree(rootDir: string): Promise<Tree> {
21
+ rootDir = resolve(rootDir);
22
+ await mkdir(rootDir, { recursive: true });
23
+
24
+ // Per-path write queue — serializes concurrent writes to the same file
25
+ const writeQueues = new Map<string, Promise<void>>();
26
+ function enqueue(path: string, fn: () => Promise<void>): Promise<void> {
27
+ const prev = writeQueues.get(path) ?? Promise.resolve();
28
+ const next = prev.then(fn, fn);
29
+ const cleanup = () => { if (writeQueues.get(path) === next) writeQueues.delete(path); };
30
+ next.then(cleanup, cleanup);
31
+ writeQueues.set(path, next);
32
+ return next;
33
+ }
34
+
35
+ // Read node from whichever form exists: dir (path/$.json) or leaf (path.json)
36
+ async function readNode(path: string): Promise<NodeData | undefined> {
37
+ const dirFile = resolve(join(rootDir, path, '$.json'));
38
+ securityCheck(rootDir, dirFile);
39
+ try {
40
+ return JSON.parse(await readFile(dirFile, 'utf-8'));
41
+ } catch (e: any) {
42
+ if (e.code === 'ENOENT') { /* fall through to leaf form */ }
43
+ else throw e; // SyntaxError = corrupted JSON, propagate loudly
44
+ }
45
+
46
+ if (path !== '/') {
47
+ const leafFile = resolve(join(rootDir, path + '.json'));
48
+ securityCheck(rootDir, leafFile);
49
+ try {
50
+ return JSON.parse(await readFile(leafFile, 'utf-8'));
51
+ } catch (e: any) {
52
+ if (e.code === 'ENOENT') { /* not found */ }
53
+ else throw e;
54
+ }
55
+ }
56
+
57
+ return undefined;
58
+ }
59
+
60
+ // Promote a node from leaf form (path.json) to dir form (path/$.json)
61
+ async function promoteIfNeeded(path: string): Promise<void> {
62
+ if (path === '/') return;
63
+ const leafFile = resolve(join(rootDir, path + '.json'));
64
+ try {
65
+ const data = await readFile(leafFile, 'utf-8');
66
+ const dir = resolve(join(rootDir, path));
67
+ await mkdir(dir, { recursive: true });
68
+ await writeFile(join(dir, '$.json'), data);
69
+ await unlink(leafFile);
70
+ } catch (e: any) {
71
+ if (e.code !== 'ENOENT') throw e;
72
+ }
73
+ }
74
+
75
+ // Promote all ancestors that might be in leaf form
76
+ async function promoteAncestors(path: string): Promise<void> {
77
+ const parts = path === '/' ? [] : path.slice(1).split('/');
78
+ for (let i = 0; i < parts.length - 1; i++) {
79
+ await promoteIfNeeded('/' + parts.slice(0, i + 1).join('/'));
80
+ }
81
+ }
82
+
83
+ // Check if a node's directory has children (entries beyond $.json)
84
+ async function hasChildren(path: string): Promise<boolean> {
85
+ const dir = resolve(join(rootDir, path));
86
+ try {
87
+ const entries = await readdir(dir);
88
+ return entries.some(e => e !== '$.json');
89
+ } catch (e: any) {
90
+ if (e.code === 'ENOENT') return false;
91
+ throw e;
92
+ }
93
+ }
94
+
95
+ // After removing a node, clean up empty dirs and demote childless parents
96
+ async function cleanupAfterRemove(removedPath: string): Promise<void> {
97
+ // Remove the node's now-empty directory if it exists
98
+ try {
99
+ const nodeDir = resolve(join(rootDir, removedPath));
100
+ const entries = await readdir(nodeDir);
101
+ if (entries.length === 0) await rmdir(nodeDir);
102
+ } catch (e: any) {
103
+ if (e.code !== 'ENOENT') console.error(`[fs] cleanup error at ${removedPath}:`, e);
104
+ }
105
+
106
+ // Walk up and demote parents that lost their last child
107
+ let current = treeDirname(removedPath);
108
+ while (current && current !== '/') {
109
+ const dir = resolve(join(rootDir, current));
110
+ try {
111
+ const entries = await readdir(dir);
112
+ if (entries.length === 1 && entries[0] === '$.json') {
113
+ // Only $.json remains — demote to leaf form
114
+ const data = await readFile(join(dir, '$.json'), 'utf-8');
115
+ await unlink(join(dir, '$.json'));
116
+ await rmdir(dir);
117
+ await writeFile(resolve(join(rootDir, current + '.json')), data);
118
+ } else if (entries.length === 0) {
119
+ await rmdir(dir);
120
+ } else {
121
+ break; // still has children
122
+ }
123
+ } catch (e: any) {
124
+ if (e.code !== 'ENOENT') console.error(`[fs] demotion walk error at ${current}:`, e);
125
+ break;
126
+ }
127
+ current = treeDirname(current);
128
+ }
129
+ }
130
+
131
+ // Walk all nodes in both forms
132
+ async function allNodes(): Promise<NodeData[]> {
133
+ const results: NodeData[] = [];
134
+ async function walk(dir: string) {
135
+ let entries;
136
+ try { entries = await readdir(dir, { withFileTypes: true }); } catch (e: any) {
137
+ if (e.code === 'ENOENT') return;
138
+ throw e;
139
+ }
140
+
141
+ for (const e of entries) {
142
+ const full = join(dir, e.name);
143
+ if (e.isDirectory()) { await walk(full); continue; }
144
+ if (e.name.endsWith('.json')) {
145
+ results.push(JSON.parse(await readFile(full, 'utf-8')));
146
+ }
147
+ }
148
+ }
149
+ await walk(rootDir);
150
+ return results;
151
+ }
152
+
153
+ const tree: Tree = {
154
+ async get(path) {
155
+ return readNode(path);
156
+ },
157
+
158
+ async getChildren(parent, opts) {
159
+ const depth = opts?.depth ?? 1;
160
+ const all = await allNodes();
161
+ let filtered = all.filter((n) => {
162
+ if (!isChildPath(parent, n.$path, false)) return false;
163
+ if (depth === Infinity) return true;
164
+ const rest = parent === '/' ? n.$path.slice(1) : n.$path.slice(parent.length + 1);
165
+ return rest.split('/').length <= depth;
166
+ });
167
+ if (opts?.query) {
168
+ const test = sift(opts.query);
169
+ filtered = filtered.filter(n => test(mapNodeForSift(n)));
170
+ }
171
+ return paginate(filtered, opts);
172
+ },
173
+
174
+ async set(node) {
175
+ return enqueue(node.$path, async () => {
176
+ const path = node.$path;
177
+
178
+ await promoteAncestors(path);
179
+
180
+ // OCC check
181
+ if (node.$rev != null) {
182
+ const existing = await readNode(path);
183
+ if (!existing) {
184
+ throw new Error(`OptimisticConcurrencyError: node ${path} does not exist but $rev was provided`);
185
+ }
186
+ if (existing.$rev !== node.$rev) {
187
+ throw new Error(`OptimisticConcurrencyError: node ${path} modified by another transaction. Expected $rev ${existing.$rev}, got ${node.$rev}`);
188
+ }
189
+ }
190
+
191
+ node.$rev = (node.$rev ?? 0) + 1;
192
+ const data = JSON.stringify(node, null, 2) + '\n';
193
+
194
+ if (path === '/' || await hasChildren(path)) {
195
+ // Dir form: has children
196
+ const dirFile = resolve(join(rootDir, path, '$.json'));
197
+ securityCheck(rootDir, dirFile);
198
+ await mkdir(resolve(join(rootDir, path)), { recursive: true });
199
+ await writeFile(dirFile, data);
200
+ // Clean up stale leaf form
201
+ if (path !== '/') {
202
+ try { await unlink(resolve(join(rootDir, path + '.json'))); } catch (e: any) { if (e.code !== 'ENOENT') throw e; }
203
+ }
204
+ } else {
205
+ // Leaf form: no children
206
+ const leafFile = resolve(join(rootDir, path + '.json'));
207
+ securityCheck(rootDir, leafFile);
208
+ await mkdir(dirname(leafFile), { recursive: true });
209
+ await writeFile(leafFile, data);
210
+ // Clean up stale dir form + empty dir
211
+ try { await unlink(resolve(join(rootDir, path, '$.json'))); } catch (e: any) { if (e.code !== 'ENOENT') throw e; }
212
+ try { await rmdir(resolve(join(rootDir, path))); } catch (e: any) { if (e.code !== 'ENOENT' && e.code !== 'ENOTEMPTY') throw e; }
213
+ }
214
+ });
215
+ },
216
+
217
+ async remove(path) {
218
+ // Try dir form first
219
+ const dirFile = resolve(join(rootDir, path, '$.json'));
220
+ securityCheck(rootDir, dirFile);
221
+ try {
222
+ await unlink(dirFile);
223
+ await cleanupAfterRemove(path);
224
+ return true;
225
+ } catch (e: any) {
226
+ if (e.code !== 'ENOENT') throw e;
227
+ }
228
+
229
+ // Try leaf form
230
+ if (path !== '/') {
231
+ const leafFile = resolve(join(rootDir, path + '.json'));
232
+ securityCheck(rootDir, leafFile);
233
+ try {
234
+ await unlink(leafFile);
235
+ await cleanupAfterRemove(path);
236
+ return true;
237
+ } catch (e: any) {
238
+ if (e.code !== 'ENOENT') throw e;
239
+ }
240
+ }
241
+
242
+ return false;
243
+ },
244
+
245
+ async patch(path, ops, ctx) {
246
+ return defaultPatch(readNode, (n) => tree.set(n, ctx), path, ops, ctx);
247
+ },
248
+ };
249
+
250
+ return tree;
251
+ }
@@ -0,0 +1,164 @@
1
+ import { createNode } from '#core';
2
+ import assert from 'node:assert/strict';
3
+ import { describe, it } from 'node:test';
4
+ import { createMemoryTree, createOverlayTree } from './index';
5
+
6
+ describe('MemoryStore', () => {
7
+ it('set and get', async () => {
8
+ const store = createMemoryTree();
9
+ const node = createNode('/tasks/1', 'task');
10
+ await store.set(node);
11
+ assert.deepEqual(await store.get('/tasks/1'), node);
12
+ });
13
+
14
+ it('get returns undefined for missing', async () => {
15
+ const store = createMemoryTree();
16
+ assert.equal(await store.get('/nope'), undefined);
17
+ });
18
+
19
+ it('getChildren returns direct children only', async () => {
20
+ const store = createMemoryTree();
21
+ await store.set(createNode('/bot', 'bot'));
22
+ await store.set(createNode('/bot/commands', 'dir'));
23
+ await store.set(createNode('/bot/commands/start', 'page'));
24
+ await store.set(createNode('/bot/commands/help', 'page'));
25
+ await store.set(createNode('/bot/config', 'config'));
26
+
27
+ const { items: botChildren } = await store.getChildren('/bot');
28
+ assert.equal(botChildren.length, 2);
29
+ assert.deepEqual(botChildren.map((n) => n.$path).sort(), ['/bot/commands', '/bot/config']);
30
+
31
+ const { items: commands } = await store.getChildren('/bot/commands');
32
+ assert.equal(commands.length, 2);
33
+ assert.deepEqual(commands.map((n) => n.$path).sort(), [
34
+ '/bot/commands/help',
35
+ '/bot/commands/start',
36
+ ]);
37
+ });
38
+
39
+ it('getChildren with depth=2 returns two levels', async () => {
40
+ const store = createMemoryTree();
41
+ await store.set(createNode('/a', 'dir'));
42
+ await store.set(createNode('/a/b', 'dir'));
43
+ await store.set(createNode('/a/b/c', 'item'));
44
+ await store.set(createNode('/a/b/c/d', 'item'));
45
+ await store.set(createNode('/a/x', 'item'));
46
+
47
+ const { items } = await store.getChildren('/a', { depth: 2 });
48
+ assert.equal(items.length, 3);
49
+ assert.deepEqual(items.map((n) => n.$path).sort(), ['/a/b', '/a/b/c', '/a/x']);
50
+ });
51
+
52
+ it('getChildren with depth=Infinity returns all descendants', async () => {
53
+ const store = createMemoryTree();
54
+ await store.set(createNode('/r', 'root'));
55
+ await store.set(createNode('/r/a', 'dir'));
56
+ await store.set(createNode('/r/a/b', 'dir'));
57
+ await store.set(createNode('/r/a/b/c', 'item'));
58
+
59
+ const result = await store.getChildren('/r', { depth: Infinity });
60
+ assert.equal(result.items.length, 3);
61
+ assert.deepEqual(result.items.map((n) => n.$path).sort(), ['/r/a', '/r/a/b', '/r/a/b/c']);
62
+ });
63
+
64
+ it('remove', async () => {
65
+ const store = createMemoryTree();
66
+ await store.set(createNode('/x', 'x'));
67
+ assert.equal(await store.remove('/x'), true);
68
+ assert.equal(await store.get('/x'), undefined);
69
+ assert.equal(await store.remove('/x'), false);
70
+ });
71
+
72
+ it('getChildren with limit', async () => {
73
+ const store = createMemoryTree();
74
+ await store.set(createNode('/p', 'dir'));
75
+ await store.set(createNode('/p/a', 'item'));
76
+ await store.set(createNode('/p/b', 'item'));
77
+ await store.set(createNode('/p/c', 'item'));
78
+
79
+ const result = await store.getChildren('/p', { limit: 2 });
80
+ assert.equal(result.items.length, 2);
81
+ assert.equal(result.total, 3);
82
+ });
83
+
84
+ it('remove does not cascade to children', async () => {
85
+ const store = createMemoryTree();
86
+ await store.set(createNode('/a', 'dir'));
87
+ await store.set(createNode('/a/b', 'item'));
88
+ await store.set(createNode('/a/b/c', 'item'));
89
+ assert.equal(await store.remove('/a'), true);
90
+ assert.equal(await store.get('/a'), undefined);
91
+ assert.equal((await store.get('/a/b'))?.$type, 't.item');
92
+ assert.equal((await store.get('/a/b/c'))?.$type, 't.item');
93
+ });
94
+
95
+ it('getChildren with limit and offset', async () => {
96
+ const store = createMemoryTree();
97
+ await store.set(createNode('/p', 'dir'));
98
+ await store.set(createNode('/p/a', 'item'));
99
+ await store.set(createNode('/p/b', 'item'));
100
+ await store.set(createNode('/p/c', 'item'));
101
+
102
+ const all = await store.getChildren('/p');
103
+ const result = await store.getChildren('/p', { limit: 2, offset: 1 });
104
+ assert.equal(result.items.length, 2);
105
+ assert.equal(result.total, 3);
106
+ assert.deepEqual(result.items, all.items.slice(1, 3));
107
+ });
108
+
109
+ });
110
+
111
+ describe('OverlayStore', () => {
112
+ it('reads from upper first', async () => {
113
+ const upper = createMemoryTree();
114
+ const lower = createMemoryTree();
115
+ await upper.set(createNode('/a', 'upper'));
116
+ await lower.set(createNode('/a', 'lower'));
117
+ const store = createOverlayTree(upper, lower);
118
+ const node = await store.get('/a');
119
+ assert.equal(node?.$type, 't.upper');
120
+ });
121
+
122
+ it('falls back to lower on miss', async () => {
123
+ const upper = createMemoryTree();
124
+ const lower = createMemoryTree();
125
+ await lower.set(createNode('/b', 'lower'));
126
+ const store = createOverlayTree(upper, lower);
127
+ assert.equal((await store.get('/b'))?.$type, 't.lower');
128
+ });
129
+
130
+ it('writes go to upper only', async () => {
131
+ const upper = createMemoryTree();
132
+ const lower = createMemoryTree();
133
+ const store = createOverlayTree(upper, lower);
134
+ await store.set(createNode('/c', 'new'));
135
+ assert.equal((await upper.get('/c'))?.$type, 't.new');
136
+ assert.equal(await lower.get('/c'), undefined);
137
+ });
138
+
139
+ it('getChildren merges, upper wins', async () => {
140
+ const upper = createMemoryTree();
141
+ const lower = createMemoryTree();
142
+ await lower.set(createNode('/p/a', 'lower'));
143
+ await lower.set(createNode('/p/b', 'lower'));
144
+ await upper.set(createNode('/p/b', 'upper'));
145
+ await upper.set(createNode('/p/c', 'upper'));
146
+ const store = createOverlayTree(upper, lower);
147
+ const { items } = await store.getChildren('/p');
148
+ assert.equal(items.length, 3);
149
+ const map = Object.fromEntries(items.map((n) => [n.$path, n.$type]));
150
+ assert.equal(map['/p/a'], 't.lower');
151
+ assert.equal(map['/p/b'], 't.upper');
152
+ assert.equal(map['/p/c'], 't.upper');
153
+ });
154
+
155
+ it('remove only affects upper', async () => {
156
+ const upper = createMemoryTree();
157
+ const lower = createMemoryTree();
158
+ await upper.set(createNode('/x', 'upper'));
159
+ await lower.set(createNode('/y', 'lower'));
160
+ const store = createOverlayTree(upper, lower);
161
+ assert.equal(await store.remove('/x'), true);
162
+ assert.equal(await store.remove('/y'), false);
163
+ });
164
+ });