@ibgib/core-gib 0.0.4

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 (330) hide show
  1. package/.vscode/core-gib-snippets.code-snippets +189 -0
  2. package/.vscode/launch.json +24 -0
  3. package/.vscode/settings.json +56 -0
  4. package/.vscode/tasks.json +37 -0
  5. package/CHANGELOG.md +11 -0
  6. package/README.md +215 -0
  7. package/dist/common/aws-constants.d.mts +7 -0
  8. package/dist/common/aws-constants.d.mts.map +1 -0
  9. package/dist/common/aws-constants.mjs +7 -0
  10. package/dist/common/aws-constants.mjs.map +1 -0
  11. package/dist/common/bin/bin-types.d.mts +17 -0
  12. package/dist/common/bin/bin-types.d.mts.map +1 -0
  13. package/dist/common/bin/bin-types.mjs +3 -0
  14. package/dist/common/bin/bin-types.mjs.map +1 -0
  15. package/dist/common/cache/cache-types.d.mts +57 -0
  16. package/dist/common/cache/cache-types.d.mts.map +1 -0
  17. package/dist/common/cache/cache-types.mjs +2 -0
  18. package/dist/common/cache/cache-types.mjs.map +1 -0
  19. package/dist/common/comment/comment-constants.d.mts +11 -0
  20. package/dist/common/comment/comment-constants.d.mts.map +1 -0
  21. package/dist/common/comment/comment-constants.mjs +11 -0
  22. package/dist/common/comment/comment-constants.mjs.map +1 -0
  23. package/dist/common/comment/comment-helper.d.mts +59 -0
  24. package/dist/common/comment/comment-helper.d.mts.map +1 -0
  25. package/dist/common/comment/comment-helper.mjs +173 -0
  26. package/dist/common/comment/comment-helper.mjs.map +1 -0
  27. package/dist/common/comment/comment-types.d.mts +16 -0
  28. package/dist/common/comment/comment-types.d.mts.map +1 -0
  29. package/dist/common/comment/comment-types.mjs +2 -0
  30. package/dist/common/comment/comment-types.mjs.map +1 -0
  31. package/dist/common/display/display-helper.d.mts +19 -0
  32. package/dist/common/display/display-helper.d.mts.map +1 -0
  33. package/dist/common/display/display-helper.mjs +68 -0
  34. package/dist/common/display/display-helper.mjs.map +1 -0
  35. package/dist/common/display/display-types.d.mts +97 -0
  36. package/dist/common/display/display-types.d.mts.map +1 -0
  37. package/dist/common/display/display-types.mjs +37 -0
  38. package/dist/common/display/display-types.mjs.map +1 -0
  39. package/dist/common/encrypt/encrypt-constants.d.mts +27 -0
  40. package/dist/common/encrypt/encrypt-constants.d.mts.map +1 -0
  41. package/dist/common/encrypt/encrypt-constants.mjs +27 -0
  42. package/dist/common/encrypt/encrypt-constants.mjs.map +1 -0
  43. package/dist/common/encrypt/encrypt-types.d.mts +116 -0
  44. package/dist/common/encrypt/encrypt-types.d.mts.map +1 -0
  45. package/dist/common/encrypt/encrypt-types.mjs +9 -0
  46. package/dist/common/encrypt/encrypt-types.mjs.map +1 -0
  47. package/dist/common/error/error-constants.d.mts +9 -0
  48. package/dist/common/error/error-constants.d.mts.map +1 -0
  49. package/dist/common/error/error-constants.mjs +9 -0
  50. package/dist/common/error/error-constants.mjs.map +1 -0
  51. package/dist/common/error/error-helper.d.mts +43 -0
  52. package/dist/common/error/error-helper.d.mts.map +1 -0
  53. package/dist/common/error/error-helper.mjs +167 -0
  54. package/dist/common/error/error-helper.mjs.map +1 -0
  55. package/dist/common/error/error-types.d.mts +58 -0
  56. package/dist/common/error/error-types.d.mts.map +1 -0
  57. package/dist/common/error/error-types.mjs +2 -0
  58. package/dist/common/error/error-types.mjs.map +1 -0
  59. package/dist/common/form/form-helper.d.mts +97 -0
  60. package/dist/common/form/form-helper.d.mts.map +1 -0
  61. package/dist/common/form/form-helper.mjs +185 -0
  62. package/dist/common/form/form-helper.mjs.map +1 -0
  63. package/dist/common/form/form-items.d.mts +229 -0
  64. package/dist/common/form/form-items.d.mts.map +1 -0
  65. package/dist/common/form/form-items.mjs +63 -0
  66. package/dist/common/form/form-items.mjs.map +1 -0
  67. package/dist/common/import-export/import-export-types.d.mts +18 -0
  68. package/dist/common/import-export/import-export-types.d.mts.map +1 -0
  69. package/dist/common/import-export/import-export-types.mjs +2 -0
  70. package/dist/common/import-export/import-export-types.mjs.map +1 -0
  71. package/dist/common/legacy/legacy-types.d.mts +2 -0
  72. package/dist/common/legacy/legacy-types.d.mts.map +1 -0
  73. package/dist/common/legacy/legacy-types.mjs +2 -0
  74. package/dist/common/legacy/legacy-types.mjs.map +1 -0
  75. package/dist/common/link/link-constants.d.mts +2 -0
  76. package/dist/common/link/link-constants.d.mts.map +1 -0
  77. package/dist/common/link/link-constants.mjs +2 -0
  78. package/dist/common/link/link-constants.mjs.map +1 -0
  79. package/dist/common/link/link-helper.d.mts +37 -0
  80. package/dist/common/link/link-helper.d.mts.map +1 -0
  81. package/dist/common/link/link-helper.mjs +143 -0
  82. package/dist/common/link/link-helper.mjs.map +1 -0
  83. package/dist/common/link/link-types.d.mts +14 -0
  84. package/dist/common/link/link-types.d.mts.map +1 -0
  85. package/dist/common/link/link-types.mjs +2 -0
  86. package/dist/common/link/link-types.mjs.map +1 -0
  87. package/dist/common/other/graph-helper.d.mts +166 -0
  88. package/dist/common/other/graph-helper.d.mts.map +1 -0
  89. package/dist/common/other/graph-helper.mjs +710 -0
  90. package/dist/common/other/graph-helper.mjs.map +1 -0
  91. package/dist/common/other/ibgib-helper.d.mts +245 -0
  92. package/dist/common/other/ibgib-helper.d.mts.map +1 -0
  93. package/dist/common/other/ibgib-helper.mjs +641 -0
  94. package/dist/common/other/ibgib-helper.mjs.map +1 -0
  95. package/dist/common/other/other-constants.d.mts +53 -0
  96. package/dist/common/other/other-constants.d.mts.map +1 -0
  97. package/dist/common/other/other-constants.mjs +67 -0
  98. package/dist/common/other/other-constants.mjs.map +1 -0
  99. package/dist/common/other/other-types.d.mts +166 -0
  100. package/dist/common/other/other-types.d.mts.map +1 -0
  101. package/dist/common/other/other-types.mjs +30 -0
  102. package/dist/common/other/other-types.mjs.map +1 -0
  103. package/dist/common/other/svg-constants.d.mts +2 -0
  104. package/dist/common/other/svg-constants.d.mts.map +1 -0
  105. package/dist/common/other/svg-constants.mjs +2 -0
  106. package/dist/common/other/svg-constants.mjs.map +1 -0
  107. package/dist/common/other/svg-helper.d.mts +54 -0
  108. package/dist/common/other/svg-helper.d.mts.map +1 -0
  109. package/dist/common/other/svg-helper.mjs +170 -0
  110. package/dist/common/other/svg-helper.mjs.map +1 -0
  111. package/dist/common/pic/pic-constants.d.mts +13 -0
  112. package/dist/common/pic/pic-constants.d.mts.map +1 -0
  113. package/dist/common/pic/pic-constants.mjs +13 -0
  114. package/dist/common/pic/pic-constants.mjs.map +1 -0
  115. package/dist/common/pic/pic-helper.d.mts +70 -0
  116. package/dist/common/pic/pic-helper.d.mts.map +1 -0
  117. package/dist/common/pic/pic-helper.mjs +235 -0
  118. package/dist/common/pic/pic-helper.mjs.map +1 -0
  119. package/dist/common/pic/pic-types.d.mts +23 -0
  120. package/dist/common/pic/pic-types.d.mts.map +1 -0
  121. package/dist/common/pic/pic-types.mjs +2 -0
  122. package/dist/common/pic/pic-types.mjs.map +1 -0
  123. package/dist/common/root/root-constants.d.mts +40 -0
  124. package/dist/common/root/root-constants.d.mts.map +1 -0
  125. package/dist/common/root/root-constants.mjs +40 -0
  126. package/dist/common/root/root-constants.mjs.map +1 -0
  127. package/dist/common/root/root-types.d.mts +9 -0
  128. package/dist/common/root/root-types.d.mts.map +1 -0
  129. package/dist/common/root/root-types.mjs +2 -0
  130. package/dist/common/root/root-types.mjs.map +1 -0
  131. package/dist/common/tag/tag-constants.d.mts +21 -0
  132. package/dist/common/tag/tag-constants.d.mts.map +1 -0
  133. package/dist/common/tag/tag-constants.mjs +28 -0
  134. package/dist/common/tag/tag-constants.mjs.map +1 -0
  135. package/dist/common/tag/tag-types.d.mts +14 -0
  136. package/dist/common/tag/tag-types.d.mts.map +1 -0
  137. package/dist/common/tag/tag-types.mjs +2 -0
  138. package/dist/common/tag/tag-types.mjs.map +1 -0
  139. package/dist/core-constants.d.mts +89 -0
  140. package/dist/core-constants.d.mts.map +1 -0
  141. package/dist/core-constants.mjs +484 -0
  142. package/dist/core-constants.mjs.map +1 -0
  143. package/dist/core-helper.d.mts +6 -0
  144. package/dist/core-helper.d.mts.map +1 -0
  145. package/dist/core-helper.mjs +28 -0
  146. package/dist/core-helper.mjs.map +1 -0
  147. package/dist/core-types.d.mts +520 -0
  148. package/dist/core-types.d.mts.map +1 -0
  149. package/dist/core-types.mjs +2 -0
  150. package/dist/core-types.mjs.map +1 -0
  151. package/dist/index.d.mts +2 -0
  152. package/dist/index.d.mts.map +1 -0
  153. package/dist/index.mjs +3 -0
  154. package/dist/index.mjs.map +1 -0
  155. package/dist/witness/app/app-base-v1.d.mts +130 -0
  156. package/dist/witness/app/app-base-v1.d.mts.map +1 -0
  157. package/dist/witness/app/app-base-v1.mjs +462 -0
  158. package/dist/witness/app/app-base-v1.mjs.map +1 -0
  159. package/dist/witness/app/app-constants.d.mts +10 -0
  160. package/dist/witness/app/app-constants.d.mts.map +1 -0
  161. package/dist/witness/app/app-constants.mjs +13 -0
  162. package/dist/witness/app/app-constants.mjs.map +1 -0
  163. package/dist/witness/app/app-helper.d.mts +85 -0
  164. package/dist/witness/app/app-helper.d.mts.map +1 -0
  165. package/dist/witness/app/app-helper.mjs +258 -0
  166. package/dist/witness/app/app-helper.mjs.map +1 -0
  167. package/dist/witness/app/app-types.d.mts +211 -0
  168. package/dist/witness/app/app-types.d.mts.map +1 -0
  169. package/dist/witness/app/app-types.mjs +49 -0
  170. package/dist/witness/app/app-types.mjs.map +1 -0
  171. package/dist/witness/app/chat-app/chat-app-types.d.mts +18 -0
  172. package/dist/witness/app/chat-app/chat-app-types.d.mts.map +1 -0
  173. package/dist/witness/app/chat-app/chat-app-types.mjs +25 -0
  174. package/dist/witness/app/chat-app/chat-app-types.mjs.map +1 -0
  175. package/dist/witness/app/flash-app/flash-app-types.d.mts +16 -0
  176. package/dist/witness/app/flash-app/flash-app-types.d.mts.map +1 -0
  177. package/dist/witness/app/flash-app/flash-app-types.mjs +23 -0
  178. package/dist/witness/app/flash-app/flash-app-types.mjs.map +1 -0
  179. package/dist/witness/app/raw-app/raw-app-types.d.mts +18 -0
  180. package/dist/witness/app/raw-app/raw-app-types.d.mts.map +1 -0
  181. package/dist/witness/app/raw-app/raw-app-types.mjs +25 -0
  182. package/dist/witness/app/raw-app/raw-app-types.mjs.map +1 -0
  183. package/dist/witness/app/todo-app/todo-app-types.d.ts +33 -0
  184. package/dist/witness/app/todo-app/todo-app-types.d.ts.map +1 -0
  185. package/dist/witness/app/todo-app/todo-app-types.js +31 -0
  186. package/dist/witness/app/todo-app/todo-app-types.js.map +1 -0
  187. package/dist/witness/robbot/robbot-base-v1.d.ts +432 -0
  188. package/dist/witness/robbot/robbot-base-v1.d.ts.map +1 -0
  189. package/dist/witness/robbot/robbot-base-v1.js +1407 -0
  190. package/dist/witness/robbot/robbot-base-v1.js.map +1 -0
  191. package/dist/witness/robbot/robbot-constants.d.mts +24 -0
  192. package/dist/witness/robbot/robbot-constants.d.mts.map +1 -0
  193. package/dist/witness/robbot/robbot-constants.mjs +24 -0
  194. package/dist/witness/robbot/robbot-constants.mjs.map +1 -0
  195. package/dist/witness/robbot/robbot-helper.d.mts +152 -0
  196. package/dist/witness/robbot/robbot-helper.d.mts.map +1 -0
  197. package/dist/witness/robbot/robbot-helper.mjs +609 -0
  198. package/dist/witness/robbot/robbot-helper.mjs.map +1 -0
  199. package/dist/witness/robbot/robbot-types.d.mts +539 -0
  200. package/dist/witness/robbot/robbot-types.d.mts.map +1 -0
  201. package/dist/witness/robbot/robbot-types.mjs +294 -0
  202. package/dist/witness/robbot/robbot-types.mjs.map +1 -0
  203. package/dist/witness/space/bootstrap/bootstrap-constants.d.mts +14 -0
  204. package/dist/witness/space/bootstrap/bootstrap-constants.d.mts.map +1 -0
  205. package/dist/witness/space/bootstrap/bootstrap-constants.mjs +15 -0
  206. package/dist/witness/space/bootstrap/bootstrap-constants.mjs.map +1 -0
  207. package/dist/witness/space/inner-space-v1.d.ts +63 -0
  208. package/dist/witness/space/inner-space-v1.d.ts.map +1 -0
  209. package/dist/witness/space/inner-space-v1.js +356 -0
  210. package/dist/witness/space/inner-space-v1.js.map +1 -0
  211. package/dist/witness/space/outer-space/outer-space-constants.d.mts +2 -0
  212. package/dist/witness/space/outer-space/outer-space-constants.d.mts.map +1 -0
  213. package/dist/witness/space/outer-space/outer-space-constants.mjs +2 -0
  214. package/dist/witness/space/outer-space/outer-space-constants.mjs.map +1 -0
  215. package/dist/witness/space/outer-space/outer-space-helper.d.mts +28 -0
  216. package/dist/witness/space/outer-space/outer-space-helper.d.mts.map +1 -0
  217. package/dist/witness/space/outer-space/outer-space-helper.mjs +87 -0
  218. package/dist/witness/space/outer-space/outer-space-helper.mjs.map +1 -0
  219. package/dist/witness/space/outer-space/outer-space-types.d.mts +548 -0
  220. package/dist/witness/space/outer-space/outer-space-types.d.mts.map +1 -0
  221. package/dist/witness/space/outer-space/outer-space-types.mjs +118 -0
  222. package/dist/witness/space/outer-space/outer-space-types.mjs.map +1 -0
  223. package/dist/witness/space/space-base-v1.d.mts +147 -0
  224. package/dist/witness/space/space-base-v1.d.mts.map +1 -0
  225. package/dist/witness/space/space-base-v1.mjs +350 -0
  226. package/dist/witness/space/space-base-v1.mjs.map +1 -0
  227. package/dist/witness/space/space-constants.d.mts +181 -0
  228. package/dist/witness/space/space-constants.d.mts.map +1 -0
  229. package/dist/witness/space/space-constants.mjs +192 -0
  230. package/dist/witness/space/space-constants.mjs.map +1 -0
  231. package/dist/witness/space/space-helper.d.mts +666 -0
  232. package/dist/witness/space/space-helper.d.mts.map +1 -0
  233. package/dist/witness/space/space-helper.mjs +2830 -0
  234. package/dist/witness/space/space-helper.mjs.map +1 -0
  235. package/dist/witness/space/space-types.d.mts +422 -0
  236. package/dist/witness/space/space-types.d.mts.map +1 -0
  237. package/dist/witness/space/space-types.mjs +52 -0
  238. package/dist/witness/space/space-types.mjs.map +1 -0
  239. package/dist/witness/witness-base-v1.d.ts +144 -0
  240. package/dist/witness/witness-base-v1.d.ts.map +1 -0
  241. package/dist/witness/witness-base-v1.js +300 -0
  242. package/dist/witness/witness-base-v1.js.map +1 -0
  243. package/dist/witness/witness-constants.d.mts +3 -0
  244. package/dist/witness/witness-constants.d.mts.map +1 -0
  245. package/dist/witness/witness-constants.mjs +3 -0
  246. package/dist/witness/witness-constants.mjs.map +1 -0
  247. package/dist/witness/witness-form-builder.d.mts +45 -0
  248. package/dist/witness/witness-form-builder.d.mts.map +1 -0
  249. package/dist/witness/witness-form-builder.mjs +95 -0
  250. package/dist/witness/witness-form-builder.mjs.map +1 -0
  251. package/dist/witness/witness-helper.d.mts +89 -0
  252. package/dist/witness/witness-helper.d.mts.map +1 -0
  253. package/dist/witness/witness-helper.mjs +229 -0
  254. package/dist/witness/witness-helper.mjs.map +1 -0
  255. package/dist/witness/witness-types.d.mts +211 -0
  256. package/dist/witness/witness-types.d.mts.map +1 -0
  257. package/dist/witness/witness-types.mjs +2 -0
  258. package/dist/witness/witness-types.mjs.map +1 -0
  259. package/jasmine-browser.json +18 -0
  260. package/jasmine.json +6 -0
  261. package/package.json +67 -0
  262. package/src/assumptions.spec.mts +45 -0
  263. package/src/common/aws-constants.mts +5 -0
  264. package/src/common/bin/bin-types.mts +17 -0
  265. package/src/common/cache/cache-types.mts +53 -0
  266. package/src/common/comment/comment-constants.mts +10 -0
  267. package/src/common/comment/comment-helper.mts +211 -0
  268. package/src/common/comment/comment-types.mts +19 -0
  269. package/src/common/display/display-helper.mts +88 -0
  270. package/src/common/display/display-types.mts +108 -0
  271. package/src/common/encrypt/encrypt-constants.mts +28 -0
  272. package/src/common/encrypt/encrypt-types.mts +130 -0
  273. package/src/common/error/error-constants.mts +8 -0
  274. package/src/common/error/error-helper.mts +155 -0
  275. package/src/common/error/error-types.mts +62 -0
  276. package/src/common/form/form-helper.mts +253 -0
  277. package/src/common/form/form-items.mts +236 -0
  278. package/src/common/import-export/import-export-types.mts +18 -0
  279. package/src/common/legacy/about-legacy.md +0 -0
  280. package/src/common/legacy/legacy-types.mts +0 -0
  281. package/src/common/link/link-constants.mts +1 -0
  282. package/src/common/link/link-helper.mts +155 -0
  283. package/src/common/link/link-types.mts +16 -0
  284. package/src/common/other/graph-helper.mts +853 -0
  285. package/src/common/other/ibgib-helper.mts +671 -0
  286. package/src/common/other/other-constants.mts +76 -0
  287. package/src/common/other/other-types.mts +186 -0
  288. package/src/common/other/svg-constants.mts +1 -0
  289. package/src/common/other/svg-helper.mts +238 -0
  290. package/src/common/pic/pic-constants.mts +13 -0
  291. package/src/common/pic/pic-helper.mts +295 -0
  292. package/src/common/pic/pic-types.mts +29 -0
  293. package/src/common/root/root-constants.mts +41 -0
  294. package/src/common/root/root-types.mts +8 -0
  295. package/src/common/tag/tag-constants.mts +34 -0
  296. package/src/common/tag/tag-types.mts +19 -0
  297. package/src/core-constants.mts +506 -0
  298. package/src/core-helper.mts +33 -0
  299. package/src/core-types.mts +519 -0
  300. package/src/helper.spec.mts +64 -0
  301. package/src/index.mts +1 -0
  302. package/src/witness/app/app-base-v1.mts +584 -0
  303. package/src/witness/app/app-constants.mts +16 -0
  304. package/src/witness/app/app-helper.mts +322 -0
  305. package/src/witness/app/app-types.mts +252 -0
  306. package/src/witness/app/chat-app/chat-app-types.mts +40 -0
  307. package/src/witness/app/flash-app/flash-app-types.mts +38 -0
  308. package/src/witness/app/raw-app/raw-app-types.mts +40 -0
  309. package/src/witness/app/todo-app/todo-app-types.ts +59 -0
  310. package/src/witness/robbot/robbot-base-v1.ts +1531 -0
  311. package/src/witness/robbot/robbot-constants.mts +25 -0
  312. package/src/witness/robbot/robbot-helper.mts +676 -0
  313. package/src/witness/robbot/robbot-helper.spec.mts +135 -0
  314. package/src/witness/robbot/robbot-types.mts +797 -0
  315. package/src/witness/space/bootstrap/bootstrap-constants.mts +15 -0
  316. package/src/witness/space/inner-space-v1.ts +372 -0
  317. package/src/witness/space/outer-space/outer-space-constants.mts +1 -0
  318. package/src/witness/space/outer-space/outer-space-helper.mts +91 -0
  319. package/src/witness/space/outer-space/outer-space-types.mts +627 -0
  320. package/src/witness/space/space-base-v1.mts +414 -0
  321. package/src/witness/space/space-constants.mts +208 -0
  322. package/src/witness/space/space-helper.mts +3242 -0
  323. package/src/witness/space/space-types.mts +476 -0
  324. package/src/witness/witness-base-v1.ts +325 -0
  325. package/src/witness/witness-constants.mts +2 -0
  326. package/src/witness/witness-form-builder.mts +128 -0
  327. package/src/witness/witness-helper.mts +256 -0
  328. package/src/witness/witness-types.mts +249 -0
  329. package/tsconfig.json +15 -0
  330. package/tsconfig.test.json +10 -0
@@ -0,0 +1,3242 @@
1
+ import {
2
+ clone, delay, getExpirationUTCString, getTimestampInTicks, getUUID, isExpired, pretty
3
+ } from '@ibgib/helper-gib';
4
+ import {
5
+ Ib, IbGibAddr, TransformResult, V1, getIbAndGib, getIbGibAddr,
6
+ } from '@ibgib/ts-gib';
7
+ import { GIB, IbGib_V1, Rel8n, Factory_V1 as factory, IBGIB_DELIMITER, } from '@ibgib/ts-gib/dist/V1/index.mjs';
8
+ import { getGib, } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
9
+ import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
10
+
11
+ import { APP_REL8N_NAME, GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
12
+ import {
13
+ GetIbGibOpts, GetIbGibResult,
14
+ PutIbGibOpts, PutIbGibResult,
15
+ DeleteIbGibOpts, DeleteIbGibResult, SpecialIbGibType,
16
+ IbGibTimelineUpdateInfo,
17
+ } from '../../common/other/other-types.mjs';
18
+ import {
19
+ getRootIb,
20
+ getSpecialConfigKey, getSpecialIbGibIb, getSpecialTypeFromIb,
21
+ getTjpAddr, isSpecial, isTjp_Naive, tagTextToIb
22
+ } from '../../common/other/ibgib-helper.mjs';
23
+ import { TagData_V1, TagIbGib_V1 } from '../../common/tag/tag-types.mjs';
24
+ import { BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY, BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY, BOOTSTRAP_IBGIB_ADDR } from './bootstrap/bootstrap-constants.mjs';
25
+ import {
26
+ SpaceLockScope, IbGibSpaceLockIbGib, BootstrapIbGib, SpaceId,
27
+ IbGibSpaceLockOptions, BootstrapData, BootstrapRel8ns, TxId,
28
+ IbGibSpaceResultIbGib, IbGibSpaceResultData, IbGibSpaceResultRel8ns
29
+ } from './space-types.mjs';
30
+ import { getAppIb, } from '../app/app-helper.mjs';
31
+ import { AppIbGib_V1 } from '../app/app-types.mjs';
32
+ import { DEFAULT_CHAT_APP_DATA_V1, DEFAULT_CHAT_APP_REL8NS_V1 } from '../app/chat-app/chat-app-types.mjs';
33
+ import { DEFAULT_RAW_APP_DATA_V1, DEFAULT_RAW_APP_REL8NS_V1 } from '../app/raw-app/raw-app-types.mjs';
34
+ import { DEFAULT_TODO_APP_DATA_V1, DEFAULT_TODO_APP_REL8NS_V1 } from '../app/todo-app/todo-app-types.js';
35
+ import { IbGibSpaceAny } from './space-base-v1.mjs';
36
+ import { RootData } from '../../common/root/root-types.mjs';
37
+ import { IbGibCacheService } from '../../common/cache/cache-types.mjs';
38
+ import { DEFAULT_MAX_DELAY_MS_RETRY_LOCK_ACQUIRE, DEFAULT_MAX_DELAY_RETRY_LOCK_ACQUIRE_ATTEMPTS, DEFAULT_SECONDS_VALID_LOCAL, DEFAULT_TX_ID_LENGTH, IBGIB_SPACE_NAME_DEFAULT, SPACE_LOCK_IB_TERM, SYNC_SPACE_REL8N_NAME } from './space-constants.mjs';
39
+ import { DEFAULT_TAG_DESCRIPTION, DEFAULT_TAG_ICON, TAG_REL8N_NAME } from '../../common/tag/tag-constants.mjs';
40
+ import { DEFAULT_ROOT_DESCRIPTION, DEFAULT_ROOT_ICON, DEFAULT_ROOT_REL8N_NAME, DEFAULT_ROOT_TEXT, ROOT_REL8N_NAME } from '../../common/root/root-constants.mjs';
41
+ import { ARCHIVE_REL8N_NAME, TRASH_REL8N_NAME } from '../../common/other/other-constants.mjs';
42
+
43
+ const logalot = GLOBAL_LOG_A_LOT || false;
44
+
45
+
46
+ /**
47
+ * Two spaces can be equivalent if they point to the same area.
48
+ *
49
+ * @returns true if the "same" space
50
+ */
51
+ export function isSameSpace({
52
+ a,
53
+ b,
54
+ mustHaveSameData,
55
+ }: {
56
+ a: IbGibSpaceAny,
57
+ b: IbGibSpaceAny,
58
+ /**
59
+ * If true, then only same exact internal data will do.
60
+ * Be careful if they have last modified timestamps.
61
+ */
62
+ mustHaveSameData?: boolean,
63
+ }): boolean {
64
+ const lc = `[${isSameSpace.name}]`;
65
+
66
+ if (!a) { throw new Error(`${lc} a is falsy`) };
67
+ if (!b) { throw new Error(`${lc} b is falsy`) };
68
+
69
+ // try by data
70
+ if (a.data && JSON.stringify(a.data) === JSON.stringify(b.data)) {
71
+ return true;
72
+ } else if (mustHaveSameData) {
73
+ return false;
74
+ }
75
+
76
+ // try by uuid
77
+ if (a.data?.uuid && b.data?.uuid) { return a.data!.uuid === b.data!.uuid; }
78
+
79
+ // try by tjp
80
+ if (a.rel8ns?.tjp?.length === 1 && b.rel8ns?.tjp?.length === 1) {
81
+ return a.rel8ns.tjp[0] === b.rel8ns.tjp[0];
82
+ }
83
+
84
+ // try by gib (last resort), can't both be falsy or primitive (maybe overkill)
85
+ if (!a.gib && !b.gib) {
86
+ throw new Error(`${lc} Invalid spaces. both a.gib and b.gib are falsy, neither has uuid and neither has tjp.`);
87
+ }
88
+ if (a.gib === GIB && b.gib === GIB) { throw new Error(`${lc} both a and b are primitives`); }
89
+ return a.gib === b.gib;
90
+ }
91
+
92
+ /**
93
+ * wrapper for dealing with a space.
94
+ *
95
+ * @returns legacy `GetIbGibResult`
96
+ */
97
+ export async function getFromSpace({
98
+ addr,
99
+ addrs,
100
+ isMeta,
101
+ isDna,
102
+ space,
103
+ force,
104
+ }: GetIbGibOpts): Promise<GetIbGibResult> {
105
+ let lc = `[${getFromSpace.name}]`;
106
+ try {
107
+ if (logalot) { console.log(`${lc} starting...`); }
108
+ if (!space) { throw new Error(`space required. (E: 4d188d6c863246f28aa575753a052304)`); }
109
+ if (!addr && (addrs ?? []).length === 0) { throw new Error(`addr or addrs required. (E: 1a0b92564ba942f1ba91a089ac1a2125)`); }
110
+
111
+ if (addr && (addrs?.length ?? 0) > 0) {
112
+ console.warn(`${lc} both addr and addrs provided, but supposed to be used one or the other. (W: 87226c2ac50e4ea28211334a7b58782f)`);
113
+ if (!addrs!.includes(addr)) { addrs!.push(addr); }
114
+ }
115
+ addrs = (addrs ?? []).length > 0 ? addrs : [addr!];
116
+
117
+ const argGet = await space.argy({
118
+ ibMetadata: getSpaceArgMetadata({ space }),
119
+ argData: {
120
+ cmd: 'get',
121
+ ibGibAddrs: addrs,
122
+ isMeta,
123
+ isDna,
124
+ },
125
+ });
126
+ const result = await space.witness(argGet);
127
+ if (result?.data?.success) {
128
+ if (logalot) { console.log(`${lc} got.`) }
129
+ return {
130
+ success: true,
131
+ ibGibs: result.ibGibs,
132
+ rawResultIbGib: result,
133
+ }
134
+ } else {
135
+ if (logalot) { console.log(`${lc} didn't get.`) }
136
+ return {
137
+ success: false,
138
+ errorMsg: result.data?.errors?.join('|') || `${lc} something went wrong. addrs: ${addrs!.join('\n')} (E: b9e9d5ce0cde4122bfb74f7688db85e0)`,
139
+ rawResultIbGib: result,
140
+ }
141
+ }
142
+ } catch (error) {
143
+ console.error(`${lc} ${error.message}`);
144
+ return { errorMsg: error.message, }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Wrapper for saving ibgib in a given space.
150
+ *
151
+ * ## warnings
152
+ *
153
+ * The given space doesn't have to work this way.
154
+ * This is a convenience for me ATOW.
155
+ */
156
+ export async function putInSpace({
157
+ ibGib,
158
+ ibGibs,
159
+ isMeta,
160
+ isDna,
161
+ force,
162
+ space,
163
+ }: PutIbGibOpts): Promise<PutIbGibResult> {
164
+ const lc = `[${putInSpace.name}]`;
165
+ try {
166
+ if (!ibGib && (ibGibs ?? []).length === 0) { throw new Error(`ibGib or ibGibs required. (E: e59c4de3695f4dd28c8fe82dbb9c4e90)`); }
167
+ if (!space) { throw new Error(`space required. (E: dd0b7189c67c43c586b905a8ed6f51c9)`); }
168
+
169
+ if (ibGib && (ibGibs ?? []).length > 0) {
170
+ console.warn(`${lc} Both ibGib and ibGibs is assigned, whereas this is intended to be exclusive one or the other. (W: 4c797835b620445f88e4cba6b5aa3460)`)
171
+ if (!ibGibs!.some(x => x.gib === ibGib.gib)) {
172
+ ibGibs = ibGibs!.concat([ibGib]);
173
+ }
174
+ }
175
+ ibGibs = ibGibs ?? [ibGib!];
176
+
177
+ if (logalot) { console.log(`${lc} ibGibs.length: ${ibGibs.length}`) }
178
+ const argPutIbGibs = await space.argy({
179
+ ibMetadata: getSpaceArgMetadata({ space }),
180
+ argData: {
181
+ cmd: 'put', isMeta, force, isDna,
182
+ ibGibAddrs: ibGibs.map(x => getIbGibAddr({ ibGib: x })),
183
+ },
184
+ ibGibs: ibGibs.concat(),
185
+ });
186
+ const resPutIbGibs = await space.witness(argPutIbGibs);
187
+ if (resPutIbGibs.data?.success) {
188
+ if ((resPutIbGibs.data!.warnings ?? []).length > 0) {
189
+ resPutIbGibs.data!.warnings!.forEach((warning: string) => console.warn(`${lc} ${warning}`));
190
+ }
191
+ return { success: true }
192
+ } else {
193
+ const errorMsg = resPutIbGibs?.data?.errors?.length > 0 ?
194
+ `Error(s) putting in local space:\n${resPutIbGibs.data.errors.join('\n')}` :
195
+ '(UNEXPECTED) unknown error putting ibGibs (E: 3d7426d4527243b79c5e55eb25f3fa73)';
196
+ throw new Error(errorMsg);
197
+ }
198
+ } catch (error) {
199
+ console.error(`${lc} ${error.message}`);
200
+ return { errorMsg: error.message, }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Wrapper for removing ibgib from the a given space, else the current space.
206
+ */
207
+ export async function deleteFromSpace({
208
+ addr,
209
+ isMeta,
210
+ isDna,
211
+ space,
212
+ }: DeleteIbGibOpts): Promise<DeleteIbGibResult> {
213
+ let lc = `[${deleteFromSpace.name}]`;
214
+ try {
215
+ if (!space) { throw new Error(`space required. (E: 40ab3b51e91c4b5eb4f215baeefbcef0)`); }
216
+ if (!space.data) { throw new Error(`space.data required. (E: 0d02c8e85ee143b8bd6a1a1db0d9af1b)`); }
217
+ lc = `${lc}[${space.data.name || 'noname?'}][${space.data.uuid || 'nouuid?'}]`;
218
+
219
+ const argDel = await space.argy({
220
+ ibMetadata: getSpaceArgMetadata({ space }),
221
+ argData: {
222
+ cmd: 'delete',
223
+ ibGibAddrs: [addr],
224
+ isMeta,
225
+ isDna,
226
+ },
227
+ });
228
+ const result = await space.witness(argDel);
229
+ if (result.data?.success) {
230
+ return { success: true, }
231
+ } else {
232
+ if (result.data?.warnings?.length > 0) {
233
+ console.warn(`${lc} warnings with delete (${addr}): ${result.data!.warnings!.join('|')}`);
234
+ }
235
+ if (result.data?.addrs?.length > 0) {
236
+ console.warn(`${lc} partial addrs deleted: ${result.data!.addrs!.join('|')}`);
237
+ }
238
+ const errorMsg: string = result.data?.errors?.join('|') || `${lc} something went wrong (E: e397fd09b4a746a3ba3305d6ea0893cb)`;
239
+ if (errorMsg.includes('File does not exist')) {
240
+ if (logalot) { console.log(`${lc} tried to delete file that does not exist. (I: cb5d1348ccbc58bf0bfc95f3006f1e22)`); }
241
+ }
242
+ return { errorMsg }
243
+ }
244
+ } catch (error) {
245
+ console.error(`${lc} ${error.message}`);
246
+ return { errorMsg: error.message };
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Convenience function for persisting a transform result, which has
252
+ * a newIbGib and optionally intermediate ibGibs and/or dnas.
253
+ *
254
+ * it persists these ibgibs into the given space, else the current space.
255
+ */
256
+ export async function persistTransformResult({
257
+ resTransform,
258
+ space,
259
+ isMeta,
260
+ force,
261
+ }: {
262
+ resTransform: TransformResult<IbGib_V1>,
263
+ space: IbGibSpaceAny,
264
+ isMeta?: boolean,
265
+ force?: boolean,
266
+ }): Promise<void> {
267
+ const lc = `[${persistTransformResult.name}]`;
268
+ try {
269
+ if (!space) { throw new Error(`space required. (E: cf94f1d74f1c4561bb88025a2095965b)`); }
270
+
271
+ const { newIbGib, intermediateIbGibs, dnas } = resTransform;
272
+ const ibGibs = [newIbGib, ...(intermediateIbGibs || [])];
273
+ const argPutIbGibs = await space.argy({
274
+ ibMetadata: getSpaceArgMetadata({ space }),
275
+ argData: {
276
+ cmd: 'put', isMeta, force,
277
+ ibGibAddrs: ibGibs.map(x => getIbGibAddr({ ibGib: x })),
278
+ },
279
+ ibGibs: ibGibs.concat(),
280
+ });
281
+ const resPutIbGibs = await space.witness(argPutIbGibs);
282
+ if (resPutIbGibs.data?.success) {
283
+ if (resPutIbGibs.data!.warnings?.length > 0) {
284
+ resPutIbGibs.data!.warnings!.forEach((warning: string) => console.warn(`${lc} ${warning}`));
285
+ }
286
+ } else {
287
+ const errorMsg = resPutIbGibs?.data?.errors?.length > 0 ?
288
+ resPutIbGibs.data.errors.join('\n') :
289
+ 'unknown error putting ibGibs';
290
+ throw new Error(errorMsg);
291
+ }
292
+
293
+ if (dnas?.length ?? 0 > 0) {
294
+ const argPutDnas = await space.argy({
295
+ ibMetadata: getSpaceArgMetadata({ space }),
296
+ argData: {
297
+ cmd: 'put', isDna: true, force,
298
+ ibGibAddrs: dnas!.map(x => getIbGibAddr({ ibGib: x })),
299
+ },
300
+ ibGibs: dnas!.concat(),
301
+ });
302
+ const resPutDnas = await space.witness(argPutDnas);
303
+ if (resPutDnas.data?.success) {
304
+ if (resPutDnas.data!.warnings?.length > 0) {
305
+ resPutDnas.data!.warnings!.forEach((warning: string) => console.warn(`${lc} ${warning}`));
306
+ }
307
+ } else {
308
+ const errorMsg = resPutDnas?.data?.errors?.length > 0 ?
309
+ resPutDnas.data.errors.join('\n') :
310
+ 'unknown error putting dna ibGibs';
311
+ throw new Error(errorMsg);
312
+ }
313
+ }
314
+ } catch (error) {
315
+ console.error(`${lc} ${error.message}`);
316
+ throw error;
317
+ }
318
+ }
319
+
320
+ export async function getSyncSpaces({
321
+ space,
322
+ }: {
323
+ space: IbGibSpaceAny,
324
+ }): Promise<IbGibSpaceAny[]> {
325
+ const lc = `[${getSyncSpaces.name}]`;
326
+ try {
327
+ if (!space) { throw new Error(`space required. (E: c03f80eca6b045b9a73b0aafa44cdf26)`); }
328
+ let syncSpaces = await getSpecialRel8dIbGibs<IbGibSpaceAny>({
329
+ type: "outerspaces",
330
+ rel8nName: SYNC_SPACE_REL8N_NAME,
331
+ space,
332
+ });
333
+ return syncSpaces;
334
+ } catch (error) {
335
+ console.error(`${lc} ${error.message}`);
336
+ throw error;
337
+ }
338
+ }
339
+
340
+ export async function getSpecialRel8dIbGibs<TIbGib extends IbGib_V1 = IbGib_V1>({
341
+ type,
342
+ rel8nName,
343
+ space,
344
+ }: {
345
+ type: SpecialIbGibType,
346
+ rel8nName: string,
347
+ space: IbGibSpaceAny,
348
+ }): Promise<TIbGib[]> {
349
+ const lc = `[${getSpecialRel8dIbGibs.name}]`;
350
+ try {
351
+ if (!space) { throw new Error(`space required. (E: f73868a952ac4181a4f90ee6d86cacf3)`); }
352
+
353
+ let special = await getSpecialIbGib({ type, space });
354
+ if (!special) { throw new Error(`couldn't get special (${type})`) };
355
+ const rel8dAddrs = special.rel8ns ? special.rel8ns[rel8nName] || [] : [];
356
+ const rel8dIbgibs: TIbGib[] = [];
357
+ for (let i = 0; i < rel8dAddrs.length; i++) {
358
+ const addr = rel8dAddrs[i];
359
+ let resGet = await getFromSpace({ addr, space });
360
+ if (resGet.success && resGet.ibGibs?.length === 1) {
361
+ rel8dIbgibs.push(resGet.ibGibs[0] as TIbGib);
362
+ } else {
363
+ throw new Error(`couldn't get addr: ${addr}`);
364
+ }
365
+ }
366
+ return rel8dIbgibs;
367
+ } catch (error) {
368
+ console.error(`${lc} ${error.message}`);
369
+ throw error;
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Gets one of the app's special ibGibs, e.g., TagsIbGib.
375
+ *
376
+ * When initializing tags, this will generate some boilerplate tags.
377
+ * I'm going to be doing roots here also, and who knows what else, but each
378
+ * one will have its own initialize specifics.
379
+ *
380
+ * @param initialize initialize (i.e. create) ONLY IF IbGib not found. Used for initializing app (first run).
381
+ *
382
+ * @see {@link createSpecial}
383
+ * @see {@link createSpecial_Tags}
384
+ */
385
+ export async function getSpecialIbGib({
386
+ type,
387
+ initialize,
388
+ space,
389
+ zeroSpace,
390
+ fnUpdateBootstrap,
391
+ fnBroadcast,
392
+ fnGetInitializing,
393
+ fnSetInitializing,
394
+ }: {
395
+ type: SpecialIbGibType,
396
+ initialize?: boolean,
397
+ space: IbGibSpaceAny,
398
+ /**
399
+ * Only required if `initialize` is true.
400
+ */
401
+ zeroSpace?: IbGibSpaceAny,
402
+ /**
403
+ * Only required if `initialize` is true.
404
+ */
405
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
406
+ /**
407
+ * Only required if `initialize` is true.
408
+ */
409
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
410
+ /**
411
+ * Initialization lock getter function.
412
+ */
413
+ fnGetInitializing?: () => boolean,
414
+ /**
415
+ * Initialization lock setter function.
416
+ *
417
+ * Because we don't want to initialize while we're initializing.
418
+ */
419
+ fnSetInitializing?: (x: boolean) => void,
420
+ }): Promise<IbGib_V1 | null> {
421
+ const lc = `[${getSpecialIbGib.name}]`;
422
+ try {
423
+ if (!space) { throw new Error(`space required. (E: d454b31d58764a9bb9c4e47fb5ef38b5)`); }
424
+
425
+ let key = getSpecialConfigKey({ type });
426
+ let addr = await getConfigAddr({ key, space });
427
+
428
+ if (!addr) {
429
+ if (initialize && (!fnGetInitializing || !fnSetInitializing)) { throw new Error(`if initialize, you must provide fnGetInitializeLock & fnSetInitializeLock. (E: 8eb322625d0c4538be089800882487de)`); }
430
+ if (initialize && !fnGetInitializing!()) {
431
+ fnSetInitializing!(true);
432
+ try {
433
+ if (!zeroSpace) { throw new Error(`zeroSpace required when 'initialize' truthy (E: 8582c92c637f90c9b2dbe0ec8355b523)`); }
434
+ addr = await createSpecial({ type, space, zeroSpace: zeroSpace!, fnBroadcast, fnUpdateBootstrap }) ?? undefined;
435
+ } catch (error) {
436
+ console.error(`${lc} error initializing: ${error.message}`);
437
+ } finally {
438
+ fnSetInitializing!(false);
439
+ }
440
+ }
441
+ if (!addr) {
442
+ if (fnGetInitializing && fnGetInitializing()) {
443
+ console.warn(`${lc} couldn't get addr, but we're still initializing...`);
444
+ return null;
445
+ } else {
446
+ throw new Error(`Special address not in config and couldn't initialize it either.`);
447
+ }
448
+ }
449
+ }
450
+
451
+ if (logalot) { console.log(`${lc} getting addr: ${addr}`); }
452
+
453
+ let resSpecial = await getFromSpace({ addr, isMeta: true, space });
454
+
455
+ // I'm putting in this check in case we're getting a meta that wasn't
456
+ // saved into the meta folder.
457
+ if (!resSpecial.success) {
458
+ resSpecial = await getFromSpace({ addr, isMeta: false, space });
459
+ if (resSpecial.success) {
460
+ console.warn(`${lc} special ibgib was not stored in meta folder...putting in meta folder now for the future (W: e6f5571fd98c449bb2809359be5057cc)`);
461
+ const resPutInMeta = await putInSpace({ ibGib: resSpecial!.ibGibs![0], isMeta: true, space });
462
+ if (!resPutInMeta.success) {
463
+ console.warn(`${lc} (UNEXPECTED) tried to put special in meta but success was false? (W: b934b53571c24057af172c790e6a7240)`)
464
+ }
465
+ }
466
+ }
467
+ if (!resSpecial.success) { throw new Error(resSpecial.errorMsg); }
468
+ if (resSpecial.ibGibs?.length !== 1) { throw new Error(`no ibGib in result (E: 3a42abdddc3648e292d63dc45c560064)`); }
469
+ const specialIbGib = resSpecial.ibGibs[0];
470
+
471
+ // I'm putting this check in here because it's bad not to have the
472
+ // latest special associated with a space
473
+ if (type !== 'latest') {
474
+ let resLatest = await getLatestAddrs({ ibGibs: [specialIbGib], space });
475
+ if (resLatest?.data?.success && resLatest.data.addrs?.length === 1) {
476
+ let specialAddr = getIbGibAddr({ ibGib: specialIbGib });
477
+ let latestAddr = resLatest.data.addrs[0];
478
+ if (latestAddr !== specialAddr) {
479
+ console.warn(`${lc} latest addr is not the one associated with the local space.\nspecialAddr: ${specialAddr}\nlatestAddr: ${latestAddr} (W: 141b69dc3c414efc9645bb76fcf12df9)`)
480
+ }
481
+ }
482
+ }
483
+
484
+ return specialIbGib;
485
+ } catch (error) {
486
+ console.error(`${lc} ${error.message}`);
487
+ return null;
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Gets a config addr from the current space via the given key
493
+ * as the space's rel8n name.
494
+ *
495
+ * For example, for `key = tags`, a space may look like:
496
+ *
497
+ * ```json
498
+ * {
499
+ * ib: space xyz,
500
+ * gib: e89ff8a1c4954db28432007615a78952,
501
+ * rel8ns: {
502
+ * past: [space xyz^21cb29cc353f45a491d2b49ff2f130db],
503
+ * ancestor: [space^gib],
504
+ * tags: [tags^99b388355f8f4a979ca30ba284d3a686], // <<< rel8n with name specified by key
505
+ * }
506
+ * }
507
+ * ```
508
+ *
509
+ * @param key config key
510
+ * @returns addr in config if exists, else undefined
511
+ */
512
+ export async function getConfigAddr({
513
+ key,
514
+ space,
515
+ }: {
516
+ key: string,
517
+ space: IbGibSpaceAny,
518
+ }): Promise<string | undefined> {
519
+ const lc = `[${getConfigAddr.name}](${key})`;
520
+ try {
521
+ if (logalot) { console.log(`${lc} getting...`) }
522
+
523
+ if (!space) { throw new Error(`space required. (E: 4f135d4276e64054ba21aeb9c304ecec)`); }
524
+
525
+ if (!space.rel8ns) {
526
+ console.warn(`${lc} space.rel8ns falsy.`);
527
+ return undefined;
528
+ }
529
+ if (!space.rel8ns[key]) {
530
+ console.warn(`${lc} space.rel8ns[${key}] falsy.`);
531
+ return undefined;
532
+ }
533
+ if (space.rel8ns[key]!.length === 1) {
534
+ if (logalot) { console.log(`${lc} got`); }
535
+ return space.rel8ns![key]![0];
536
+ } else if (space.rel8ns[key]!.length > 1) {
537
+ console.warn(`${lc} more than one config addr with ${key} rel8n.`)
538
+ return space.rel8ns![key]![0];
539
+ } else {
540
+ if (logalot) { console.log(`${lc} didn't find`); }
541
+ // key not found or
542
+ return undefined;
543
+ }
544
+ } catch (error) {
545
+ console.error(`${lc} ${error.message}`);
546
+ return undefined;
547
+ }
548
+ }
549
+
550
+ export async function setConfigAddr({
551
+ key,
552
+ addr,
553
+ space,
554
+ zeroSpace,
555
+ fnUpdateBootstrap,
556
+ }: {
557
+ key: string,
558
+ addr: string,
559
+ space: IbGibSpaceAny,
560
+ zeroSpace: IbGibSpaceAny,
561
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
562
+ }): Promise<IbGibSpaceAny> {
563
+ const lc = `[${setConfigAddr.name}]`;
564
+ try {
565
+ if (!space) { throw new Error(`space required. (E: c28b663c991d44419aef1026cc689636)`); }
566
+ if (!zeroSpace) { throw new Error(`zeroSpace required. (E: d3707ae5265d464891ad216f64be6184)`); }
567
+
568
+ // rel8 the `addr` to the current space via rel8n named `key`
569
+ // note that since we are replacing the pointer, we include the key
570
+ // in `linkedRel8ns` which will keep only the most recent
571
+ const rel8nsToAddByAddr = { [key]: [addr] };
572
+ const resNewSpace = await V1.rel8({
573
+ src: space.toIbGibDto(),
574
+ dna: false,
575
+ linkedRel8ns: ["past", "ancestor", key], // we only want the most recent key address
576
+ rel8nsToAddByAddr,
577
+ nCounter: true,
578
+ });
579
+
580
+ if (!resNewSpace.newIbGib) { throw new Error(`create new space failed.`); }
581
+
582
+ // persist the new space in both default space and its own space
583
+ // (will actually have the space witness its future self interestingly
584
+ // enough...perhaps should have the new space witness itself instead
585
+
586
+ // witness in the default zero space
587
+ // in refactoring, may have to make this optional...hmm
588
+ await persistTransformResult({ isMeta: true, resTransform: resNewSpace, space: zeroSpace });
589
+
590
+ // witness in the given space
591
+ await persistTransformResult({ isMeta: true, resTransform: resNewSpace, space });
592
+
593
+ // going to update the bootstrap^gib with the new space address, but first...
594
+ const newSpace = (resNewSpace.newIbGib as IbGibSpaceAny);
595
+
596
+ // ...must update the original space reference any time we change it.
597
+ // messy atm...
598
+ await space.loadIbGibDto(newSpace);
599
+
600
+ // ...now update so the proper space (config) loads on next app start
601
+ if (fnUpdateBootstrap) {
602
+ await fnUpdateBootstrap(newSpace);
603
+ } else {
604
+ console.warn(`${lc} fnUpdateBootstrap is falsy. (W: 9fb874de2b19454dac18645e61ac463f)`);
605
+ }
606
+
607
+ return newSpace;
608
+ } catch (error) {
609
+ console.error(`${lc} ${error.message}`);
610
+ throw error;
611
+ }
612
+ }
613
+
614
+ export async function getCurrentRoot({
615
+ space,
616
+ }: {
617
+ space: IbGibSpaceAny,
618
+ }): Promise<IbGib_V1<RootData> | undefined> {
619
+ const lc = `[${getCurrentRoot.name}]`;
620
+
621
+ try {
622
+ if (!space) { throw new Error(`space required. (E: f0d546101fba4c169256158114ab3c56)`); }
623
+
624
+ const roots = await getSpecialIbGib({ type: "roots", space });
625
+ if (!roots) { throw new Error(`Roots not initialized. (E: 89b1ba12ed12416aac41cef9fdaf1fc2)`); }
626
+ if (!roots.rel8ns) { throw new Error(`Roots not initialized properly. No rel8ns. (E: 8513a07cf530484db9521a2a3a27b7f6)`); }
627
+ if (!roots.rel8ns.current) { throw new Error(`Roots not initialized properly. No current root. (E: 459c3a007a30486d96fb8d83f696e239)`); }
628
+ if (roots.rel8ns.current.length === 0) { throw new Error(`Invalid Roots: empty current root rel8n. (E: bede5864090440bca01ea7ab7fd107d6)`); }
629
+ if (roots.rel8ns.current.length > 1) { throw new Error(`Invalid Roots: multiple current roots selected. (E: 97561acbf63a48ecaa037697bd26555a)`); }
630
+
631
+ const currentRootAddr = roots.rel8ns.current[0]!;
632
+ const resCurrentRoot =
633
+ await getFromSpace({ addr: currentRootAddr, isMeta: true, space });
634
+ if (resCurrentRoot.ibGibs?.length === 1) {
635
+ return resCurrentRoot.ibGibs![0] as IbGib_V1<RootData>;
636
+ } else {
637
+ throw new Error(`could not get current root. addr: ${currentRootAddr}`);
638
+ }
639
+ } catch (error) {
640
+ console.error(`${lc} ${error.message}`);
641
+ return undefined;
642
+ }
643
+ }
644
+
645
+ export async function setCurrentRoot({
646
+ root,
647
+ space,
648
+ zeroSpace,
649
+ fnUpdateBootstrap,
650
+ fnBroadcast,
651
+ }: {
652
+ root: IbGib_V1<RootData>,
653
+ space: IbGibSpaceAny,
654
+ zeroSpace: IbGibSpaceAny,
655
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
656
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
657
+ }): Promise<void> {
658
+ const lc = `[${setCurrentRoot.name}]`;
659
+ try {
660
+ if (!root) { throw new Error(`root required.`); }
661
+
662
+ if (!space) { throw new Error(`space required. (E: 186af2731c5342a78b063a0a4346f3db)`); }
663
+
664
+ const rootAddr = getIbGibAddr({ ibGib: root });
665
+
666
+ // get the roots and update its "current" rel8n
667
+ const roots = await getSpecialIbGib({ type: "roots", space });
668
+ if (!roots) { throw new Error(`Roots not initialized.`); }
669
+
670
+ // we'll rel8 current with a linkedRel8n, thus ensuring a maximum of only
671
+ // one rel8d addr (the one we're adding here)
672
+ const rel8nsToAddByAddr = { current: [rootAddr] };
673
+ const resNewRoots = await V1.rel8({
674
+ src: roots,
675
+ dna: false,
676
+ linkedRel8ns: ["past", "ancestor", "current"], // current here ensures only 1 rel8n
677
+ rel8nsToAddByAddr,
678
+ nCounter: true,
679
+ });
680
+ await persistTransformResult({ isMeta: true, resTransform: resNewRoots, space });
681
+
682
+ const configKey = getSpecialConfigKey({ type: "roots" });
683
+ const newRoots = resNewRoots.newIbGib;
684
+ const newRootsAddr = getIbGibAddr({ ibGib: newRoots });
685
+ await setConfigAddr({ key: configKey, addr: newRootsAddr, space, zeroSpace, fnUpdateBootstrap });
686
+ await registerNewIbGib({ ibGib: newRoots, space, fnBroadcast, zeroSpace, fnUpdateBootstrap });
687
+
688
+ // how to let others know roots has changed?
689
+ } catch (error) {
690
+ console.error(`${lc} ${error.message}`);
691
+ throw error;
692
+ }
693
+ }
694
+
695
+ /**
696
+ * Every tjp should be related to one of the roots in a space.
697
+ *
698
+ * You should NOT relate every ibgib frame of a given ibGib.
699
+ */
700
+ export async function rel8ToCurrentRoot({
701
+ ibGib,
702
+ linked,
703
+ rel8nName,
704
+ space,
705
+ fnBroadcast,
706
+ zeroSpace,
707
+ fnUpdateBootstrap,
708
+ }: {
709
+ ibGib: IbGib_V1,
710
+ linked?: boolean,
711
+ rel8nName?: string,
712
+ space: IbGibSpaceAny,
713
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
714
+ zeroSpace: IbGibSpaceAny,
715
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
716
+ }): Promise<void> {
717
+ const lc = `[${rel8ToCurrentRoot.name}]`;
718
+
719
+ try {
720
+ if (!space) { throw new Error(`space required. (E: f2758eab3bb844d2b749515672d9e392)`); }
721
+ if (!ibGib) { throw new Error(`ibGib required (E: f1bfd67754a7271553f4af544d30bc22)`); }
722
+
723
+ let currentRoot = await getCurrentRoot({ space });
724
+ if (!currentRoot) { throw new Error('currentRoot undefined (E: 5c2d84dafc664808866008f6eb535750'); }
725
+
726
+ // only relate the tjp to roots and use the latest special index ibgib to
727
+ // have the reference to the latest ibgib in the space.
728
+ const tjpAddr = getTjpAddr({ ibGib, defaultIfNone: "incomingAddr" });
729
+ const ibGibAddr = getIbGibAddr({ ibGib });
730
+
731
+ // check to see if it's already rel8d. If so, we're done.
732
+ if (rel8nName && tjpAddr &&
733
+ currentRoot.rel8ns &&
734
+ currentRoot.rel8ns[rel8nName] &&
735
+ currentRoot.rel8ns[rel8nName]!.includes(tjpAddr)) {
736
+ // already rel8d
737
+ return;
738
+ }
739
+
740
+ rel8nName = rel8nName || DEFAULT_ROOT_REL8N_NAME;
741
+
742
+ // we only need to add the ibgib itself to the root, not the tjp
743
+ // and not any dependent ibgibs. ...wakka doodle.
744
+ const resNewRoot = await V1.rel8({
745
+ src: currentRoot,
746
+ dna: false,
747
+ linkedRel8ns: linked ? ["past", "ancestor", rel8nName] : ["past", "ancestor"],
748
+ rel8nsToAddByAddr: { [rel8nName]: [ibGibAddr] },
749
+ nCounter: true,
750
+ });
751
+ await persistTransformResult({ isMeta: true, resTransform: resNewRoot, space });
752
+ const newRoot = resNewRoot.newIbGib as IbGib_V1<RootData>;
753
+ const newRootAddr = getIbGibAddr({ ibGib: newRoot });
754
+ if (logalot) { console.log(`${lc} updating _currentRoot root. newRootAddr: ${newRootAddr}`); }
755
+ await registerNewIbGib({ ibGib: newRoot, space, fnBroadcast, zeroSpace, fnUpdateBootstrap });
756
+ await setCurrentRoot({ root: newRoot, space, zeroSpace, fnUpdateBootstrap, fnBroadcast });
757
+
758
+ } catch (error) {
759
+ console.error(`${lc} ${error.message}`);
760
+ return;
761
+ }
762
+ }
763
+
764
+ /**
765
+ * Used for tracking tjpAddr -> latest ibGibAddr.
766
+ *
767
+ * Call this when you create a new ibGib.
768
+ *
769
+ * Need to put this in another service at some point, but crunch crunch
770
+ * like pacman's lunch.
771
+ */
772
+ export async function registerNewIbGib({
773
+ ibGib,
774
+ space,
775
+ fnBroadcast,
776
+ zeroSpace,
777
+ fnUpdateBootstrap,
778
+ }: {
779
+ ibGib: IbGib_V1,
780
+ space: IbGibSpaceAny,
781
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
782
+ zeroSpace: IbGibSpaceAny,
783
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
784
+ }): Promise<void> {
785
+ let lc = `[${registerNewIbGib.name}]`;
786
+ try {
787
+ const ibGibAddr: IbGibAddr = getIbGibAddr({ ibGib });
788
+ lc = `${lc}[${ibGibAddr}]`;
789
+
790
+ if (!space) { throw new Error(`space required. (E: ea0c03256f8a4062b460aa4de11f1e3e)`); }
791
+
792
+ if (logalot) { console.log(`${lc} starting...`); }
793
+
794
+ // this is the latest index ibGib. It's just the mapping of tjp -> latestAddr.
795
+ // Other refs to "latest" in this function
796
+ // will refer to the actual/attempted latest of the ibGib arg.
797
+ let specialLatest = await getSpecialIbGib({ type: "latest", space });
798
+ if (!specialLatest) { throw new Error(`unable to get specialLatest (E: a24f785753e482085b49b74e367cff23)`); }
799
+ if (!specialLatest.rel8ns) { specialLatest.rel8ns = {}; }
800
+
801
+ // get the tjp for the rel8nName mapping, and also for some checking logic
802
+ let tjp = await getTjpIbGib({ ibGib, space });
803
+ if (!tjp) {
804
+ console.warn(`${lc} tjp not found for ${ibGibAddr}? Should at least just be the ibGib's address itself.`);
805
+ tjp = ibGib;
806
+ }
807
+ let tjpAddr = getIbGibAddr({ ibGib: tjp });
808
+
809
+ // either we're adding the given ibGib, replacing the existing with the ibGib,
810
+ // or doing nothing. We can do this with our current vars in a closure at this point.
811
+ const replaceLatest: () => Promise<void> = async () => {
812
+ if (logalot) { console.log(`${lc} adding/replacing latest. tjp: ${tjpAddr}`); }
813
+ await rel8ToSpecialIbGib({
814
+ type: "latest",
815
+ rel8nName: tjpAddr,
816
+ ibGibsToRel8: [ibGib],
817
+ linked: true, // this ensures only one latest ibGib mapped at a time
818
+ deletePreviousSpecialIbGib: true, // the latest mapping is ephemeral
819
+ severPast: true,
820
+ // skipRel8ToRoot: true,
821
+ space,
822
+ zeroSpace,
823
+ fnUpdateBootstrap,
824
+ fnBroadcast,
825
+ });
826
+ if (fnBroadcast) {
827
+ fnBroadcast({ tjpAddr, latestAddr: ibGibAddr, latestIbGib: ibGib });
828
+ }
829
+ }
830
+
831
+ let existingMapping = specialLatest.rel8ns[tjpAddr] || [];
832
+ if (existingMapping.length > 0) {
833
+ if (logalot) { console.log(`${lc} tjp mapping exists. Checking which is newer.`) }
834
+ let existingLatestAddr = existingMapping[0];
835
+
836
+ // check to see if ibgib is already the latest
837
+ if (existingLatestAddr === ibGibAddr) {
838
+ if (logalot) { console.log(`${lc} Neither is newer because ibGibAddr is already registered as latest, so returning without any further action. (I: 7f5bd5d3391be95919240f0e97976e22)`); }
839
+ return; /* <<<< returns early */
840
+ }
841
+
842
+ // not the latest or not registered, so get the full existing latest
843
+ // ibgib analyze to see if the incoming ibgib is indeed newer
844
+ let resExistingLatest = await getFromSpace({ addr: existingLatestAddr, space });
845
+ if (!resExistingLatest.success || resExistingLatest.ibGibs?.length !== 1) {
846
+ console.error(`Didn't find existing latest ibGib (${existingLatestAddr}). I haven't implemented more robust multi-node/distributed strategies for this scenario yet. User chose YES to replace.`);
847
+ await replaceLatest();
848
+ return;
849
+ }
850
+ const existingLatest = resExistingLatest.ibGibs![0];
851
+
852
+ // if there is an nCounter, then we can go by that. Otherwise, we'll
853
+ // try to brute force "latest" comparison
854
+ const ibGibHasNCounter =
855
+ ibGib.data?.n &&
856
+ typeof ibGib.data!.n! === 'number' &&
857
+ ibGib.data!.n! >= 0;
858
+ if (ibGibHasNCounter) {
859
+ // #region ibGib.data.n counter method
860
+ if (logalot) { console.log(`found ibGib.data.n (version counter), using this to determine latest ibGib: ${ibGib.data!.n!}`); }
861
+ const n_ibGib = (ibGib.data!.n! as number);
862
+
863
+ const existingLatestHasNCounter =
864
+ existingLatest.data?.n &&
865
+ typeof existingLatest.data!.n! === 'number' &&
866
+ existingLatest.data!.n! >= 0;
867
+
868
+ if (existingLatestHasNCounter) {
869
+ // both have counters, so compare by those.
870
+ const n_existingLatest = (existingLatest.data!.n! as number);
871
+ if (n_ibGib > n_existingLatest) {
872
+ // is newer
873
+ await replaceLatest();
874
+ } else {
875
+ // is not newer, so we don't need to do anything else.
876
+ return;
877
+ }
878
+ } else {
879
+ // only the new one has the counter, so that wins by default
880
+ await replaceLatest();
881
+ }
882
+ // #endregion
883
+
884
+ } else {
885
+ if (logalot) { console.log(`${lc} no nCounter found. Trying brute force method.`); }
886
+ // #region brute force latest
887
+ let latestAddr = await getLatestAddr_Brute({
888
+ ibGib, ibGibAddr,
889
+ existingLatest, existingLatestAddr,
890
+ tjpAddr,
891
+ space,
892
+ });
893
+ if (latestAddr === ibGibAddr) {
894
+ await replaceLatest();
895
+ } else {
896
+ return;
897
+ }
898
+ // #endregion
899
+ }
900
+ } else {
901
+ // no existing mapping, so go ahead and add.
902
+ if (logalot) { console.log(`${lc} no existing tjp mapping. ${tjpAddr} -> ${ibGibAddr}`); }
903
+ await replaceLatest();
904
+ }
905
+ } catch (error) {
906
+ console.error(`${lc} ${error.message}`);
907
+ throw error;
908
+ } finally {
909
+ if (logalot) { console.log(`${lc} complete.`); }
910
+ }
911
+ }
912
+
913
+ /**
914
+ * Performs a rel8 transform on the special ibgib corresponding to the incoming
915
+ * `type`.
916
+ *
917
+ * ## special ibgibs
918
+ *
919
+ * much metadata configuration is stored via "special" ibgibs. Most of these are
920
+ * tracked in a space's ibgib directly, and the space itself is tracked in the
921
+ * bootstrap. So when storing configuration data, I usually create a new special
922
+ * ibgib. this function performs the plumbing for the rel8 transform related to
923
+ * that special ibgib.
924
+ *
925
+ * ## notes
926
+ *
927
+ * * special ibgib must exist in the space, i.e. previously initialized
928
+ * * i'm using this atm for mainly the local ionic space, but sometimes
929
+ * might be good in sync space, i dunno at this point.
930
+ * * I've migrated a lot of local space behavior into this space-agnostic file.
931
+ *
932
+ * @returns new special ibgib addr
933
+ */
934
+ export async function rel8ToSpecialIbGib({
935
+ type,
936
+ rel8nName,
937
+ ibGibsToRel8,
938
+ ibGibsToUnRel8,
939
+ linked,
940
+ severPast,
941
+ deletePreviousSpecialIbGib,
942
+ space,
943
+ zeroSpace,
944
+ fnUpdateBootstrap,
945
+ fnBroadcast,
946
+ }: {
947
+ /**
948
+ * the "name" of the special ibgib.
949
+ *
950
+ * This will drive deterministically what the special ibgib's `ib` will be,
951
+ * among other things.
952
+ */
953
+ type: SpecialIbGibType,
954
+ /**
955
+ * The rel8nName by which to rel8 the target incoming `ibGibsToRel8`.
956
+ */
957
+ rel8nName: string,
958
+ /**
959
+ * multiple ibgibs to rel8
960
+ */
961
+ ibGibsToRel8?: IbGib_V1[],
962
+ /**
963
+ * multiple ibgibs to UNrel8
964
+ */
965
+ ibGibsToUnRel8?: IbGib_V1[],
966
+ /**
967
+ * If linked, then the rel8nName will only contain one address, i.e. the
968
+ * last rel8d ibgib's address.
969
+ *
970
+ * This depends on your use case.
971
+ */
972
+ linked?: boolean,
973
+ /**
974
+ * Clears out the special.rel8ns.past array to an empty array.
975
+ *
976
+ * {@see deletePreviousSpecialIbGib} for driving use case.
977
+ */
978
+ severPast?: boolean,
979
+ /**
980
+ * Deletes the previous special ibGib.
981
+ *
982
+ * ## driving use case
983
+ *
984
+ * the latest ibGib is one that is completely ephemeral. It doesn't get attached
985
+ * to the current root, and it only has the current instance. So we don't want to
986
+ * keep around past incarnations.
987
+ */
988
+ deletePreviousSpecialIbGib?: boolean,
989
+ /**
990
+ * The space in which the special ibgib resides.
991
+ */
992
+ space: IbGibSpaceAny,
993
+ /**
994
+ * The default zero space that contains metaspace information, i.e.
995
+ * bootstrap ibgib, space ibgibs, etc.
996
+ */
997
+ zeroSpace: IbGibSpaceAny,
998
+ /**
999
+ * The function by which to update the bootstrap ibgib.
1000
+ *
1001
+ * This is necessary, because when you update a special ibgib,
1002
+ * the address of that special ibgib must be updated in the given
1003
+ * `space`. This will require an update to the space's address, which
1004
+ * itself is tracked in the bootstrap ibgib.
1005
+ */
1006
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1007
+ /**
1008
+ * Use this if you want to broadcast the new space's address after this
1009
+ * function performs the rel8 transform.
1010
+ */
1011
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1012
+ }): Promise<IbGibAddr> {
1013
+ const lc = `[${rel8ToSpecialIbGib.name}](type:${type},rel8nName:${rel8nName})`;
1014
+ try {
1015
+ if (!space) { throw new Error(`space required. (E: 956192eea28047eba6dad81620bb96fb)`); }
1016
+ if ((ibGibsToRel8 ?? []).length === 0 && (ibGibsToUnRel8 ?? []).length === 0) {
1017
+ throw new Error(`either ibGibsToRel8 or ibGibsToUnRel8 required. (E: 5add49c8e46a54e2c6b057c22646a822)`);
1018
+ }
1019
+
1020
+ const addrsToRel8 = ibGibsToRel8?.map(ibGib => getIbGibAddr({ ibGib }));
1021
+ const addrsToUnRel8 = ibGibsToUnRel8?.map(ibGib => getIbGibAddr({ ibGib }));
1022
+
1023
+ // get the special ibgib
1024
+ const configKey = getSpecialConfigKey({ type });
1025
+ let specialAddr = await getConfigAddr({ key: configKey, space });
1026
+ if (!specialAddr) { throw new Error(`specialAddr not found`) };
1027
+ let resGetSpecial = await getFromSpace({ addr: specialAddr, isMeta: true, space });
1028
+ if (!resGetSpecial.success) { throw new Error(`couldn't get special`) }
1029
+ if (!resGetSpecial.ibGibs) { throw new Error(`resGetSpecial.ibGibs falsy`) }
1030
+ if (resGetSpecial.ibGibs!.length !== 1) { throw new Error(`resGetSpecial.ibGibs count is not 1 (${resGetSpecial.ibGibs!.length})`) }
1031
+
1032
+ // rel8 the new tag to the special ibgib.
1033
+ const resNewSpecial = await V1.rel8({
1034
+ src: resGetSpecial.ibGibs![0],
1035
+ rel8nsToAddByAddr: addrsToRel8 ? { [rel8nName]: addrsToRel8 } : undefined,
1036
+ rel8nsToRemoveByAddr: addrsToUnRel8 ? { [rel8nName]: addrsToUnRel8 } : undefined,
1037
+ dna: false,
1038
+ linkedRel8ns: linked ? [Rel8n.past, rel8nName] : [Rel8n.past],
1039
+ nCounter: true,
1040
+ });
1041
+ const newSpecialIbGib = resNewSpecial.newIbGib;
1042
+
1043
+ // sever
1044
+ if (severPast) {
1045
+ if (resNewSpecial.intermediateIbGibs) { throw new Error('new special creates intermediate ibgibs. so severing past is harder. (E: b580c0c56253494192e9c62212ee187d)'); }
1046
+ newSpecialIbGib.rel8ns!.past = [];
1047
+ newSpecialIbGib.gib = await getGib({ ibGib: newSpecialIbGib });
1048
+ }
1049
+
1050
+ // persist
1051
+ await persistTransformResult({ resTransform: resNewSpecial, isMeta: true, space });
1052
+
1053
+ // return the new special address (not the incoming new ibGib)
1054
+ const newSpecialAddr = getIbGibAddr({ ibGib: newSpecialIbGib });
1055
+ // const specialTjpAddrs = getTjpAddrs({ ibGibs: [newSpecialIbGib] });
1056
+ // const specialTjpAddr = specialTjpAddrs?.length > 0 ? specialTjpAddrs[0] : null;
1057
+ const specialTjpAddr = getTjpAddr({ ibGib: newSpecialIbGib });
1058
+
1059
+ // update the space ibgib which contains the special/config information
1060
+ await setConfigAddr({ key: configKey, addr: newSpecialAddr, space, zeroSpace, fnUpdateBootstrap });
1061
+
1062
+ // delete if required, only after updating config with the new special addr.
1063
+ if (deletePreviousSpecialIbGib) {
1064
+ await deleteFromSpace({ addr: specialAddr, isMeta: true, space });
1065
+ }
1066
+
1067
+ // I'm thinking we also want to register the special ibgib in the latest index...
1068
+ if (type === 'latest') {
1069
+ // we're relating somethign to the latest index special ibgib (most
1070
+ // likely via registerNewIbgib), and we have a new latest index.
1071
+ // we already have updated our reference to space(?) so I'm
1072
+ // not sure what the problem is right now...
1073
+ } else {
1074
+ // we're relating something to a special ibgib that is NOT the
1075
+ // latest index, so our new special ibgib needs to be registered
1076
+ // with that latest index.
1077
+ await registerNewIbGib({
1078
+ ibGib: newSpecialIbGib,
1079
+ zeroSpace,
1080
+ fnBroadcast,
1081
+ fnUpdateBootstrap,
1082
+ space,
1083
+ });
1084
+ }
1085
+
1086
+ if (fnBroadcast && specialTjpAddr) {
1087
+ fnBroadcast({
1088
+ tjpAddr: specialTjpAddr,
1089
+ latestIbGib: newSpecialIbGib,
1090
+ latestAddr: newSpecialAddr,
1091
+ });
1092
+ }
1093
+
1094
+ return newSpecialAddr;
1095
+ } catch (error) {
1096
+ console.error(`${lc} ${error.message}`);
1097
+ throw error;
1098
+ }
1099
+ }
1100
+
1101
+ export async function getTjpIbGib({
1102
+ ibGib,
1103
+ naive = true,
1104
+ space,
1105
+ }: {
1106
+ ibGib: IbGib_V1<any>,
1107
+ naive?: boolean,
1108
+ space: IbGibSpaceAny,
1109
+ }): Promise<IbGib_V1<any> | undefined> {
1110
+ const lc = `[${getTjpIbGib.name}]`;
1111
+
1112
+ try {
1113
+ if (!space) { throw new Error(`space required. (E: 941f973d50e84415b58724af173f52c2)`); }
1114
+ if (!ibGib) { throw new Error('ibGib required.'); }
1115
+
1116
+ let ibGibAddr = getIbGibAddr({ ibGib });
1117
+ const { gib } = getIbAndGib({ ibGibAddr });
1118
+ if (gib === GIB) { return ibGib; }
1119
+ let isTjp = await isTjp_Naive({ ibGib, naive });
1120
+ if (isTjp) { return ibGib; }
1121
+
1122
+ // the given ibGib arg isn't itself the tjp
1123
+
1124
+ // if no rel8ns, then there is no tjp ibgib, since this is not
1125
+ // intrinsically the tjp and there is no 'tjp' or 'past' rel8n to check
1126
+ if (!ibGib.rel8ns) {
1127
+ if (logalot) { console.log(`${lc} ibgib not tjp in data, and rel8ns is falsy. so tjp is undefined (I: acdadb76a7568807db7a68f6f866de22)`); }
1128
+ return undefined; // <<<< returns early
1129
+ }
1130
+
1131
+ // check explicitly listed tjp in rel8ns
1132
+ if (ibGib.rel8ns!.tjp && ibGib.rel8ns!.tjp.length > 0) {
1133
+ let firstTjpAddr = ibGib.rel8ns!.tjp[0];
1134
+ let resGetTjpIbGib = await getFromSpace({ addr: firstTjpAddr, space });
1135
+ if (resGetTjpIbGib.success && resGetTjpIbGib.ibGibs?.length === 1) {
1136
+ return resGetTjpIbGib.ibGibs[0]
1137
+ } else {
1138
+ const resErrorMsg = resGetTjpIbGib.errorMsg ?? '[unspecified error in get result]';
1139
+ throw new Error(`ibGib references tjp but could not retrieve from space. res error: ${resErrorMsg} (E: 94f0340706ad48c794c6a62c1b235a22)`);
1140
+ }
1141
+ }
1142
+
1143
+ // couldn't get the tjp from the rel8ns.tjp, so look for manually in past.
1144
+ // but we can't just get the earliest in the 'past', because the tjp
1145
+ // may be one of the intermediates!
1146
+ // So, check the immediate past ibGib recursively.
1147
+
1148
+ const past = ibGib.rel8ns!.past || [];
1149
+ if (past.length === 0) {
1150
+ console.warn(`${lc} past.length === 0, so there is no tjp.`)
1151
+ if (logalot) { console.log(`${lc} ibgib is not tjp in data, not in tjp rel8n, and past is empty. so tjp is undefined (I: bf06f664917dcf4492fb9c4c106a6222)`); }
1152
+ return undefined; // <<<< returns early
1153
+ }
1154
+ const pastIbGibAddr = past[past.length - 1];
1155
+ const resGetPastIbGib = await getFromSpace({ addr: pastIbGibAddr, space });
1156
+ if (!resGetPastIbGib.success || resGetPastIbGib.ibGibs?.length !== 1) { throw new Error(`get past failed. addr: ${pastIbGibAddr}`); }
1157
+ const pastIbGib = resGetPastIbGib.ibGibs![0];
1158
+
1159
+ // call this method recursively!
1160
+ return await getTjpIbGib({ ibGib: pastIbGib, naive, space });
1161
+ } catch (error) {
1162
+ console.error(`${lc} ${error.message}`);
1163
+ throw error;
1164
+ }
1165
+ }
1166
+
1167
+ // #region creates
1168
+
1169
+ /**
1170
+ * Routing function to various `create_____` functions.
1171
+ *
1172
+ * @returns address of newly created special.
1173
+ */
1174
+ export async function createSpecial({
1175
+ type,
1176
+ space,
1177
+ zeroSpace,
1178
+ fnUpdateBootstrap,
1179
+ fnBroadcast,
1180
+ }: {
1181
+ type: SpecialIbGibType,
1182
+ space: IbGibSpaceAny,
1183
+ zeroSpace: IbGibSpaceAny,
1184
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1185
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1186
+ }): Promise<IbGibAddr | null> {
1187
+ const lc = `[${createSpecial.name}]`;
1188
+ try {
1189
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized. (E: 66e7d3ff098248f0a5ddda51853c92e6)`); }
1190
+
1191
+ switch (type) {
1192
+ case "roots": return createSpecial_Roots({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1193
+ case "tags": return createSpecial_Tags({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1194
+ case "latest": return createSpecial_Latest({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1195
+ case "secrets": return createSpecial_Secrets({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1196
+ case "encryptions": return createSpecial_Encryptions({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1197
+ case "outerspaces": return createSpecial_OuterSpaces({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1198
+ case "autosyncs": return createSpecial_Autosyncs({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1199
+ case "robbots": return createSpecial_Robbots({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1200
+ case "apps": return createSpecial_Apps({ space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1201
+ default: return createSpecial_Default({ type, space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1202
+ }
1203
+ } catch (error) {
1204
+ console.error(`${lc} ${error.message}`);
1205
+ throw error;
1206
+ }
1207
+ }
1208
+
1209
+ /**
1210
+ * Creates a new special ibgib, persists it and if not skipped, relates
1211
+ * it to the current root.
1212
+ *
1213
+ * @returns newly created ibgib (not just address)
1214
+ */
1215
+ export async function createSpecialIbGib({
1216
+ type,
1217
+ skipRel8ToRoot,
1218
+ space,
1219
+ zeroSpace,
1220
+ fnUpdateBootstrap,
1221
+ fnBroadcast,
1222
+ }: {
1223
+ type: SpecialIbGibType,
1224
+ skipRel8ToRoot?: boolean,
1225
+ space: IbGibSpaceAny,
1226
+ zeroSpace: IbGibSpaceAny,
1227
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1228
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1229
+ }): Promise<IbGib_V1> {
1230
+ const lc = `[${createSpecialIbGib.name}][${type || 'falsy type?'}]`;
1231
+ try {
1232
+ if (logalot) { console.log(`${lc} starting...`); }
1233
+ const specialIb = getSpecialIbGibIb({ type });
1234
+ const src = factory.primitive({ ib: specialIb });
1235
+ const resNewSpecial = await V1.fork({
1236
+ src,
1237
+ destIb: specialIb,
1238
+ linkedRel8ns: [Rel8n.past, Rel8n.ancestor],
1239
+ tjp: { uuid: true, timestamp: true },
1240
+ dna: false,
1241
+ nCounter: true,
1242
+ });
1243
+ await persistTransformResult({ resTransform: resNewSpecial, isMeta: true, space });
1244
+ if (type !== 'roots' && !skipRel8ToRoot) {
1245
+ await rel8ToCurrentRoot({
1246
+ ibGib: resNewSpecial.newIbGib,
1247
+ linked: true,
1248
+ space,
1249
+ zeroSpace,
1250
+ fnBroadcast,
1251
+ fnUpdateBootstrap,
1252
+ });
1253
+ }
1254
+ if (logalot) { console.log(`${lc} complete.`); }
1255
+ return resNewSpecial.newIbGib;
1256
+ } catch (error) {
1257
+ console.error(`${lc} ${error.message}`);
1258
+ throw error;
1259
+ }
1260
+ }
1261
+
1262
+ /**
1263
+ * Creates a new tags^gib instance (unique to current space), as well as
1264
+ * default initial tags, e.g. "home", "favorites", etc., and relates these
1265
+ * individual tags to the tags ibGib itself.
1266
+ *
1267
+ * Stores the tags ibGib's addr in config.
1268
+ */
1269
+ export async function createSpecial_Tags({
1270
+ space,
1271
+ zeroSpace,
1272
+ fnUpdateBootstrap,
1273
+ fnBroadcast,
1274
+ }: {
1275
+ space: IbGibSpaceAny,
1276
+ zeroSpace: IbGibSpaceAny,
1277
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1278
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1279
+ }): Promise<IbGibAddr | null> {
1280
+ const lc = `[${createSpecial_Tags.name}]`;
1281
+ try {
1282
+ if (!space) { throw new Error(`space required. (E: 9c05b9bd355943a39ca47afef67a50eb)`); }
1283
+
1284
+ const configKey = getSpecialConfigKey({ type: "tags" });
1285
+ const special = await createSpecialIbGib({
1286
+ type: "tags",
1287
+ space,
1288
+ zeroSpace,
1289
+ fnBroadcast,
1290
+ fnUpdateBootstrap,
1291
+ });
1292
+ let addr = getIbGibAddr({ ibGib: special });
1293
+ await setConfigAddr({ key: configKey, addr: addr, space, zeroSpace, fnUpdateBootstrap });
1294
+
1295
+ // at this point, our tags ibGib has no associated tag ibGibs.
1296
+ // add home, favorite tags
1297
+ const initialTagDatas: TagData_V1[] = [
1298
+ { text: 'home', icon: 'home-outline' },
1299
+ { text: 'favorite', icon: 'heart-outline' },
1300
+ ];
1301
+ for (const data of initialTagDatas) {
1302
+ const resCreate = await createTagIbGibAndSundry({ ...data, space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1303
+ addr = resCreate.newTagsAddr;
1304
+ await setConfigAddr({ key: configKey, addr: addr, space, zeroSpace, fnUpdateBootstrap });
1305
+ }
1306
+
1307
+ return addr;
1308
+ } catch (error) {
1309
+ console.error(`${lc} ${error.message}`);
1310
+ return null;
1311
+ }
1312
+ }
1313
+
1314
+ /**
1315
+ * 1. Creates a new tag ibgib with the given properties.
1316
+ * 2. Persists graph in given {@link space}
1317
+ * 3. Registers the new tag ibgib in that space, using the given
1318
+ * {@link fnUpdateBootstrap} and {@link fnBroadcast} functions.
1319
+ * 4. Relates the new tag to the special tags ibgib.
1320
+ *
1321
+ * @returns the new tag ibgib and new tags address.
1322
+ */
1323
+ export async function createTagIbGibAndSundry({
1324
+ text,
1325
+ icon,
1326
+ description,
1327
+ space,
1328
+ zeroSpace,
1329
+ fnUpdateBootstrap,
1330
+ fnBroadcast,
1331
+ }: {
1332
+ text: string,
1333
+ icon?: string,
1334
+ description?: string,
1335
+ space: IbGibSpaceAny,
1336
+ zeroSpace: IbGibSpaceAny,
1337
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1338
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1339
+ }): Promise<{ newTagIbGib: TagIbGib_V1, newTagsAddr: string }> {
1340
+ const lc = `[${createTagIbGibAndSundry.name}]`;
1341
+ try {
1342
+ if (logalot) { console.log(`${lc} starting...`); }
1343
+ if (!space) { throw new Error(`space required. (E: 5def0b1afab74b0c9286e3ac5060cb8f)`); }
1344
+
1345
+ if (!text) { throw new Error(`${lc} text required`); }
1346
+ icon = icon || DEFAULT_TAG_ICON;
1347
+ description = description || DEFAULT_TAG_DESCRIPTION;
1348
+ const tagIb = tagTextToIb(text);
1349
+ const tagPrimitive = factory.primitive({ ib: "tag" });
1350
+ const resNewTag = await factory.firstGen({
1351
+ parentIbGib: tagPrimitive,
1352
+ ib: tagIb,
1353
+ data: { text, icon, description },
1354
+ linkedRel8ns: [Rel8n.past, Rel8n.ancestor],
1355
+ tjp: { uuid: true, timestamp: true },
1356
+ dna: true,
1357
+ nCounter: true,
1358
+ });
1359
+ const newTag = resNewTag.newIbGib as TagIbGib_V1;
1360
+ await persistTransformResult({ resTransform: resNewTag, isMeta: true, space });
1361
+ await registerNewIbGib({ ibGib: newTag, space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1362
+ const newTagsAddr = await rel8TagToTagsIbGib({
1363
+ tagIbGib: newTag, space, zeroSpace, fnUpdateBootstrap, fnBroadcast,
1364
+ });
1365
+ return { newTagIbGib: newTag, newTagsAddr };
1366
+ } catch (error) {
1367
+ console.error(`${lc} ${error.message}`);
1368
+ throw error;
1369
+ } finally {
1370
+ if (logalot) { console.log(`${lc} complete.`); }
1371
+ }
1372
+ }
1373
+
1374
+ export async function createSpecial_Roots({
1375
+ space,
1376
+ zeroSpace,
1377
+ fnUpdateBootstrap,
1378
+ fnBroadcast,
1379
+ }: {
1380
+ space: IbGibSpaceAny,
1381
+ zeroSpace: IbGibSpaceAny,
1382
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1383
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1384
+ }): Promise<IbGibAddr | null> {
1385
+ const lc = `[${createSpecial_Roots.name}]`;
1386
+ try {
1387
+ if (!space) { throw new Error(`space required. (E: d12a8ea31163429fb6e53ff8e7579c57)`); }
1388
+
1389
+ const configKey = getSpecialConfigKey({ type: "roots" });
1390
+ // const rootsIbGib = await createSpecialIbGib({type: "roots", space});
1391
+ const rootsIbGib = await createSpecialIbGib({
1392
+ type: "roots",
1393
+ space,
1394
+ zeroSpace,
1395
+ fnBroadcast,
1396
+ fnUpdateBootstrap,
1397
+ });
1398
+ let rootsAddr: IbGibAddr | undefined = getIbGibAddr({ ibGib: rootsIbGib });
1399
+ await setConfigAddr({ key: configKey, addr: rootsAddr, space, zeroSpace, fnUpdateBootstrap });
1400
+
1401
+ // at this point, our ibGib has no associated ibGibs.
1402
+ // so we add initial roots
1403
+ const rootNames = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
1404
+
1405
+ const initialDatas: RootData[] = rootNames.map(n => {
1406
+ return {
1407
+ text: `${n}root`,
1408
+ icon: DEFAULT_ROOT_ICON,
1409
+ description: DEFAULT_ROOT_DESCRIPTION
1410
+ };
1411
+ });
1412
+ let firstRoot: IbGib_V1<RootData> | undefined = undefined;
1413
+ for (let i = 0; i < initialDatas.length; i++) {
1414
+ const data = initialDatas[i];
1415
+ const resCreate = await createRootIbGib({
1416
+ ...data,
1417
+ space,
1418
+ zeroSpace,
1419
+ fnUpdateBootstrap,
1420
+ fnBroadcast,
1421
+ });
1422
+ if (!firstRoot) { firstRoot = resCreate.newRootIbGib; }
1423
+ if (!resCreate.newRootIbGib) { throw new Error(`(UNEXPECTED) resCreate.newRootIbGib falsy? (E: c9fb2c94a9c1d762c1699b41a4d5ad23)`); }
1424
+ rootsAddr = resCreate.newRootsAddr;
1425
+ // update the config for the updated **roots** ibgib.
1426
+ // that roots ibgib is what points to the just created new root.
1427
+ await setConfigAddr({ key: configKey, addr: rootsAddr, space, zeroSpace, fnUpdateBootstrap });
1428
+ }
1429
+ if (!firstRoot) { throw new Error(`(UNEXPECTED) firstRoot still falsy? (E: b2e2c926d242966e7ed7159e4b129e23)`); }
1430
+
1431
+ // initialize current root
1432
+ await setCurrentRoot({ root: firstRoot, space, zeroSpace, fnUpdateBootstrap, fnBroadcast });
1433
+ // hack: the above line updates the roots in config. so get **that** addr.
1434
+
1435
+ rootsAddr = await getConfigAddr({ key: configKey, space });
1436
+ if (!rootsAddr) { throw new Error('(UNEXPECTED) no roots address in config? (E: 76345340699a4738a195e48803ef0d31)'); }
1437
+ return rootsAddr;
1438
+ } catch (error) {
1439
+ console.error(`${lc} ${error.message}`);
1440
+ return null;
1441
+ }
1442
+ }
1443
+
1444
+ async function createRootIbGib({
1445
+ text,
1446
+ icon,
1447
+ description,
1448
+ space,
1449
+ zeroSpace,
1450
+ fnUpdateBootstrap,
1451
+ fnBroadcast,
1452
+ }: {
1453
+ text: string,
1454
+ icon?: string,
1455
+ description?: string,
1456
+ space: IbGibSpaceAny,
1457
+ zeroSpace: IbGibSpaceAny,
1458
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1459
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1460
+ }): Promise<{ newRootIbGib: IbGib_V1<RootData>, newRootsAddr: string }> {
1461
+ const lc = `[${createRootIbGib.name}]`;
1462
+ try {
1463
+ if (!space) { throw new Error(`space required. (E: cfa876e5c8c64a53a463ca7a645571c8)`); }
1464
+
1465
+ text = text || DEFAULT_ROOT_TEXT;
1466
+ icon = icon || DEFAULT_ROOT_ICON;
1467
+ description = description || DEFAULT_ROOT_DESCRIPTION;
1468
+ const ib = getRootIb(text);
1469
+ const parentIbGib = factory.primitive({ ib: "root" });
1470
+ const resNewIbGib = await factory.firstGen({
1471
+ parentIbGib,
1472
+ ib,
1473
+ data: { text, icon, description },
1474
+ linkedRel8ns: [Rel8n.past, Rel8n.ancestor],
1475
+ tjp: { uuid: true, timestamp: true },
1476
+ dna: true,
1477
+ });
1478
+ const { newIbGib } = resNewIbGib;
1479
+ await persistTransformResult({
1480
+ resTransform: resNewIbGib,
1481
+ isMeta: true,
1482
+ space,
1483
+ });
1484
+ const newRootsAddr = await rel8ToSpecialIbGib({
1485
+ type: "roots",
1486
+ rel8nName: ROOT_REL8N_NAME,
1487
+ ibGibsToRel8: [newIbGib],
1488
+ // isMeta: true,
1489
+ space,
1490
+ zeroSpace,
1491
+ fnUpdateBootstrap,
1492
+ fnBroadcast,
1493
+ });
1494
+ return { newRootIbGib: newIbGib as IbGib_V1<RootData>, newRootsAddr };
1495
+ } catch (error) {
1496
+ console.error(`${lc} ${error.message}`);
1497
+ throw error;
1498
+ }
1499
+ }
1500
+
1501
+ async function createSpecial_Latest({
1502
+ space,
1503
+ zeroSpace,
1504
+ fnUpdateBootstrap,
1505
+ fnBroadcast,
1506
+ }: {
1507
+ space: IbGibSpaceAny,
1508
+ zeroSpace: IbGibSpaceAny,
1509
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1510
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1511
+ }): Promise<IbGibAddr | null> {
1512
+ const lc = `[${createSpecial_Latest.name}]`;
1513
+ try {
1514
+ if (!space) { throw new Error(`space required. (E: 173b08d7eb114238b32280c3efce9d1a)`); }
1515
+
1516
+ const configKey = getSpecialConfigKey({ type: "latest" });
1517
+ // const special =
1518
+ // await createSpecialIbGib({type: "latest", skipRel8ToRoot: true, space});
1519
+ const special = await createSpecialIbGib({
1520
+ type: "latest",
1521
+ space,
1522
+ skipRel8ToRoot: true,
1523
+ zeroSpace,
1524
+ fnBroadcast,
1525
+ fnUpdateBootstrap,
1526
+ });
1527
+ let specialAddr = getIbGibAddr({ ibGib: special });
1528
+ await setConfigAddr({ key: configKey, addr: specialAddr, space, zeroSpace, fnUpdateBootstrap });
1529
+
1530
+ // right now, the latest ibgib doesn't have any more initialization,
1531
+ // since it is supposed to be as ephemeral and non-tracked as possible.
1532
+
1533
+ return specialAddr;
1534
+ } catch (error) {
1535
+ console.error(`${lc} ${error.message}`);
1536
+ return null;
1537
+ }
1538
+ }
1539
+
1540
+ async function createSpecial_Secrets({
1541
+ space,
1542
+ zeroSpace,
1543
+ fnUpdateBootstrap,
1544
+ fnBroadcast,
1545
+ }: {
1546
+ space: IbGibSpaceAny,
1547
+ zeroSpace: IbGibSpaceAny,
1548
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1549
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1550
+ }): Promise<IbGibAddr | null> {
1551
+ const lc = `[${createSpecial_Secrets.name}]`;
1552
+ try {
1553
+ if (!space) { throw new Error(`space required. (E: 340960cd5ad24addb300b23d9722e30a)`); }
1554
+
1555
+ let secretsAddr: IbGibAddr;
1556
+ const configKey = getSpecialConfigKey({ type: "secrets" });
1557
+
1558
+ // special ibgib doesn't exist, so create it (empty)
1559
+ // const secretsIbgib = await createSpecialIbGib({type: "secrets", space});
1560
+ const secretsIbgib = await createSpecialIbGib({
1561
+ type: "secrets",
1562
+ space,
1563
+ zeroSpace,
1564
+ fnBroadcast,
1565
+ fnUpdateBootstrap,
1566
+ });
1567
+ secretsAddr = getIbGibAddr({ ibGib: secretsIbgib });
1568
+ await setConfigAddr({ key: configKey, addr: secretsAddr, space, zeroSpace, fnUpdateBootstrap });
1569
+
1570
+ return secretsAddr;
1571
+ } catch (error) {
1572
+ console.error(`${lc} ${error.message}`);
1573
+ return null;
1574
+ }
1575
+ }
1576
+
1577
+ async function createSpecial_Encryptions({
1578
+ space,
1579
+ zeroSpace,
1580
+ fnUpdateBootstrap,
1581
+ fnBroadcast,
1582
+ }: {
1583
+ space: IbGibSpaceAny,
1584
+ zeroSpace: IbGibSpaceAny,
1585
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1586
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1587
+ }): Promise<IbGibAddr | null> {
1588
+ const lc = `[${createSpecial_Encryptions.name}]`;
1589
+ try {
1590
+ if (!space) { throw new Error(`space required. (E: 5084e698b6924e7090697ca50075ca59)`); }
1591
+
1592
+ let addr: IbGibAddr;
1593
+ const configKey = getSpecialConfigKey({ type: "encryptions" });
1594
+
1595
+ // special ibgib doesn't exist, so create it (empty)
1596
+ // const encryptionsIbgib = await createSpecialIbGib({type: "encryptions", space});
1597
+ const encryptionsIbgib = await createSpecialIbGib({
1598
+ type: "encryptions",
1599
+ space,
1600
+ zeroSpace,
1601
+ fnBroadcast,
1602
+ fnUpdateBootstrap,
1603
+ });
1604
+ addr = getIbGibAddr({ ibGib: encryptionsIbgib });
1605
+ await setConfigAddr({ key: configKey, addr: addr, space, zeroSpace, fnUpdateBootstrap });
1606
+
1607
+ return addr;
1608
+ } catch (error) {
1609
+ console.error(`${lc} ${error.message}`);
1610
+ return null;
1611
+ }
1612
+ }
1613
+
1614
+ async function createSpecial_OuterSpaces({
1615
+ space,
1616
+ zeroSpace,
1617
+ fnUpdateBootstrap,
1618
+ fnBroadcast,
1619
+ }: {
1620
+ space: IbGibSpaceAny,
1621
+ zeroSpace: IbGibSpaceAny,
1622
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1623
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1624
+ }): Promise<IbGibAddr | null> {
1625
+ const lc = `[${createSpecial_OuterSpaces.name}]`;
1626
+ try {
1627
+ if (!space) { throw new Error(`space required. (E: 99dd9e92535c470482eb9f6625a33831)`); }
1628
+
1629
+ let outerSpacesAddr: IbGibAddr;
1630
+ const configKey = getSpecialConfigKey({ type: "outerspaces" });
1631
+
1632
+ // special outerspaces ibgib doesn't exist, so create it (empty)
1633
+ // const outerSpacesIbGib = await createSpecialIbGib({type: "outerspaces", space});
1634
+ const outerSpacesIbGib = await createSpecialIbGib({
1635
+ type: "outerspaces",
1636
+ space,
1637
+ zeroSpace,
1638
+ fnBroadcast,
1639
+ fnUpdateBootstrap,
1640
+ });
1641
+ outerSpacesAddr = getIbGibAddr({ ibGib: outerSpacesIbGib });
1642
+ await setConfigAddr({ key: configKey, addr: outerSpacesAddr, space, zeroSpace, fnUpdateBootstrap });
1643
+
1644
+ return outerSpacesAddr;
1645
+ } catch (error) {
1646
+ console.error(`${lc} ${error.message}`);
1647
+ return null;
1648
+ }
1649
+ }
1650
+
1651
+ async function createSpecial_Autosyncs({
1652
+ space,
1653
+ zeroSpace,
1654
+ fnUpdateBootstrap,
1655
+ fnBroadcast,
1656
+ }: {
1657
+ space: IbGibSpaceAny,
1658
+ zeroSpace: IbGibSpaceAny,
1659
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1660
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1661
+ }): Promise<IbGibAddr | null> {
1662
+ const lc = `[${createSpecial_Autosyncs.name}]`;
1663
+ try {
1664
+ if (!space) { throw new Error(`space required. (E: f01cf6a4a460486796e16d505d629522)`); }
1665
+
1666
+ let autosyncsAddr: IbGibAddr;
1667
+ const configKey = getSpecialConfigKey({ type: "autosyncs" });
1668
+
1669
+ const autosyncsIbGib = await createSpecialIbGib({
1670
+ type: "autosyncs",
1671
+ space,
1672
+ zeroSpace,
1673
+ fnBroadcast,
1674
+ fnUpdateBootstrap,
1675
+ });
1676
+ autosyncsAddr = getIbGibAddr({ ibGib: autosyncsIbGib });
1677
+ await setConfigAddr({ key: configKey, addr: autosyncsAddr, space, zeroSpace, fnUpdateBootstrap });
1678
+
1679
+ return autosyncsAddr;
1680
+ } catch (error) {
1681
+ console.error(`${lc} ${error.message}`);
1682
+ return null;
1683
+ }
1684
+ }
1685
+
1686
+ export async function createSpecial_Robbots({
1687
+ space,
1688
+ zeroSpace,
1689
+ fnUpdateBootstrap,
1690
+ fnBroadcast,
1691
+ }: {
1692
+ space: IbGibSpaceAny,
1693
+ zeroSpace: IbGibSpaceAny,
1694
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1695
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1696
+ }): Promise<IbGibAddr | null> {
1697
+ const lc = `[${createSpecial_Robbots.name}]`;
1698
+ try {
1699
+ if (!space) { throw new Error(`space required. (E: f01cf6a4a460486796e16d505d629522)`); }
1700
+
1701
+ let robbotsAddr: IbGibAddr;
1702
+ const configKey = getSpecialConfigKey({ type: "robbots" });
1703
+
1704
+ const robbotsIbGib = await createSpecialIbGib({
1705
+ type: "robbots",
1706
+ space,
1707
+ zeroSpace,
1708
+ fnBroadcast,
1709
+ fnUpdateBootstrap,
1710
+ });
1711
+ robbotsAddr = getIbGibAddr({ ibGib: robbotsIbGib });
1712
+ await setConfigAddr({ key: configKey, addr: robbotsAddr, space, zeroSpace, fnUpdateBootstrap });
1713
+
1714
+ return robbotsAddr;
1715
+ } catch (error) {
1716
+ console.error(`${lc} ${error.message}`);
1717
+ return null;
1718
+ }
1719
+ }
1720
+
1721
+ export async function createSpecial_Apps({
1722
+ space,
1723
+ zeroSpace,
1724
+ fnUpdateBootstrap,
1725
+ fnBroadcast,
1726
+ }: {
1727
+ space: IbGibSpaceAny,
1728
+ zeroSpace: IbGibSpaceAny,
1729
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1730
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1731
+ }): Promise<IbGibAddr | null> {
1732
+ const lc = `[${createSpecial_Apps.name}]`;
1733
+ try {
1734
+ if (!space) { throw new Error(`space required. (E: f01cf6a4a460486796e16d505d629522)`); }
1735
+
1736
+ let appsAddr: IbGibAddr | undefined | null;
1737
+ const configKey = getSpecialConfigKey({ type: "apps" });
1738
+
1739
+ const appsIbGib = await createSpecialIbGib({
1740
+ type: "apps",
1741
+ space,
1742
+ zeroSpace,
1743
+ fnBroadcast,
1744
+ fnUpdateBootstrap,
1745
+ });
1746
+ appsAddr = getIbGibAddr({ ibGib: appsIbGib });
1747
+ await setConfigAddr({ key: configKey, addr: appsAddr, space, zeroSpace, fnUpdateBootstrap });
1748
+
1749
+ // at this point, our apps ibGib has no associated app ibGibs.
1750
+ // so create our initial apps.
1751
+ // appsAddr = await createApps_Chat({ space, zeroSpace, fnUpdateBootstrap, fnBroadcast });
1752
+ appsAddr = await createApp({
1753
+ defaultAppData: DEFAULT_CHAT_APP_DATA_V1,
1754
+ defaultAppRel8ns: DEFAULT_CHAT_APP_REL8NS_V1,
1755
+ space, zeroSpace, fnUpdateBootstrap, fnBroadcast,
1756
+ });
1757
+ // appsAddr = await createApps_Raw({ space, zeroSpace, fnUpdateBootstrap, fnBroadcast });
1758
+ appsAddr = await createApp({
1759
+ defaultAppData: DEFAULT_RAW_APP_DATA_V1,
1760
+ defaultAppRel8ns: DEFAULT_RAW_APP_REL8NS_V1,
1761
+ space, zeroSpace, fnUpdateBootstrap, fnBroadcast,
1762
+ });
1763
+ // appsAddr = await createApps_Todo({ space, zeroSpace, fnUpdateBootstrap, fnBroadcast });
1764
+ appsAddr = await createApp({
1765
+ defaultAppData: DEFAULT_TODO_APP_DATA_V1,
1766
+ defaultAppRel8ns: DEFAULT_TODO_APP_REL8NS_V1,
1767
+ space, zeroSpace, fnUpdateBootstrap, fnBroadcast,
1768
+ });
1769
+
1770
+ return appsAddr;
1771
+ } catch (error) {
1772
+ console.error(`${lc} ${error.message}`);
1773
+ return null;
1774
+ }
1775
+ }
1776
+
1777
+ export async function createApp({
1778
+ defaultAppData,
1779
+ defaultAppRel8ns,
1780
+ space,
1781
+ zeroSpace,
1782
+ fnUpdateBootstrap,
1783
+ fnBroadcast,
1784
+ }: {
1785
+ /**
1786
+ * object that contains all of the default "data" for the app. Data here
1787
+ * does not mean an app's data like other apps. In ibgib, data is
1788
+ * everywhere. This means particularly the defalut app ibGib's data, i.e.
1789
+ * `ibGib.data`.
1790
+ */
1791
+ defaultAppData: any,
1792
+ defaultAppRel8ns: any,
1793
+ space: IbGibSpaceAny,
1794
+ zeroSpace: IbGibSpaceAny,
1795
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1796
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1797
+ }): Promise<IbGibAddr | null> {
1798
+ const lc = `[${createApp.name}]`;
1799
+ try {
1800
+ if (logalot) { console.log(`${lc} starting... (I: 677f68789abdc7316887e8f38c764e22)`); }
1801
+
1802
+ // #region torn from chat-app-v1.ts because of circular dependency...eesh
1803
+
1804
+ if (logalot) { console.log(`${lc} starting...`); }
1805
+ let data = clone(defaultAppData);
1806
+ let rel8ns = defaultAppRel8ns ? clone(defaultAppRel8ns) : undefined;
1807
+ data.uuid = data.uuid ?? await getUUID();
1808
+ let { classname } = data;
1809
+
1810
+ const ib = getAppIb({ appData: data, classname });
1811
+
1812
+ const resNewApp = await factory.firstGen({
1813
+ ib,
1814
+ parentIbGib: factory.primitive({ ib: `app ${classname}` }),
1815
+ data: data,
1816
+ rel8ns,
1817
+ dna: true,
1818
+ linkedRel8ns: [Rel8n.ancestor, Rel8n.past],
1819
+ nCounter: true,
1820
+ tjp: { timestamp: true },
1821
+ }) as TransformResult<AppIbGib_V1>;
1822
+
1823
+ // #endregion torn from chat-app-v1.ts because of circular dependency...eesh
1824
+
1825
+ await persistTransformResult({ resTransform: resNewApp, space });
1826
+ await registerNewIbGib({
1827
+ ibGib: resNewApp.newIbGib,
1828
+ fnBroadcast, fnUpdateBootstrap,
1829
+ space, zeroSpace
1830
+ });
1831
+ let appsAddr = await rel8ToSpecialIbGib({
1832
+ type: "apps",
1833
+ rel8nName: APP_REL8N_NAME,
1834
+ ibGibsToRel8: [resNewApp.newIbGib],
1835
+ fnBroadcast, fnUpdateBootstrap,
1836
+ space, zeroSpace,
1837
+ });
1838
+
1839
+ return appsAddr;
1840
+
1841
+ } catch (error) {
1842
+ console.error(`${lc} ${error.message}`);
1843
+ throw error;
1844
+ } finally {
1845
+ if (logalot) { console.log(`${lc} complete.`); }
1846
+ }
1847
+ }
1848
+
1849
+ async function createSpecial_Default({
1850
+ type,
1851
+ space,
1852
+ zeroSpace,
1853
+ fnUpdateBootstrap,
1854
+ fnBroadcast,
1855
+ }: {
1856
+ type: SpecialIbGibType,
1857
+ space: IbGibSpaceAny,
1858
+ zeroSpace: IbGibSpaceAny,
1859
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1860
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1861
+ }): Promise<IbGibAddr | null> {
1862
+ const lc = `[${createSpecial_Autosyncs.name}]`;
1863
+ try {
1864
+ if (!space) { throw new Error(`space required. (E: c7bbafcbe901418db4c6048f17f53091)`); }
1865
+
1866
+ if (logalot) { console.log(`${lc} creating special of type: ${type} (I: 283c8bb30ed6f9698b74b886c6078622)`); }
1867
+
1868
+ let specialAddr: IbGibAddr;
1869
+ const configKey = getSpecialConfigKey({ type });
1870
+
1871
+ const specialIbGib = await createSpecialIbGib({
1872
+ type,
1873
+ space,
1874
+ zeroSpace,
1875
+ fnBroadcast,
1876
+ fnUpdateBootstrap,
1877
+ });
1878
+ specialAddr = getIbGibAddr({ ibGib: specialIbGib });
1879
+ await setConfigAddr({ key: configKey, addr: specialAddr, space, zeroSpace, fnUpdateBootstrap });
1880
+
1881
+ return specialAddr;
1882
+ } catch (error) {
1883
+ console.error(`${lc} ${error.message}`);
1884
+ return null;
1885
+ }
1886
+ }
1887
+
1888
+ /**
1889
+ * 1. Creates a new robbot ibgib with the given properties.
1890
+ * 2. Persists graph in given {@link space}
1891
+ * 3. Registers the new robbot ibgib in that space, using the given
1892
+ * {@link fnUpdateBootstrap} and {@link fnBroadcast} functions.
1893
+ * 4. Relates the new robbot to the special robbots ibgib.
1894
+ *
1895
+ * @returns the new robbot ibgib and new robbots address.
1896
+ */
1897
+ // export async function createRobbotIbGib({
1898
+ // robbotData,
1899
+ // space,
1900
+ // zeroSpace,
1901
+ // fnUpdateBootstrap,
1902
+ // fnBroadcast,
1903
+ // }: {
1904
+ // robbotData: RobbotData_V1,
1905
+ // space: IbGibSpaceAny,
1906
+ // zeroSpace: IbGibSpaceAny,
1907
+ // fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
1908
+ // fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
1909
+ // }): Promise<{ newRobbotIbGib: RobbotIbGib_V1, newRobbotsAddr: string }> {
1910
+ // const lc = `[${createRobbotIbGib.name}]`;
1911
+ // try {
1912
+ // if (logalot) { console.log(`${lc} starting...`); }
1913
+ // if (!space) { throw new Error(`space required. (E: 5def0b1afab74b0c9286e3ac5060cb8f)`); }
1914
+ // if (!robbotData) { throw new Error(`robbotData required (E: cd0304401a2f5a63d86dd71f76f31222)`); }
1915
+
1916
+ // const ib = getRobbotIb({ robbotData });
1917
+ // const resNewRobbot = await factory.firstGen({
1918
+ // parentIbGib: factory.primitive({ ib: "robbot" }),
1919
+ // ib,
1920
+ // data: robbotData,
1921
+ // linkedRel8ns: [Rel8n.past, Rel8n.ancestor],
1922
+ // tjp: { uuid: true, timestamp: true },
1923
+ // dna: true,
1924
+ // nCounter: true,
1925
+ // });
1926
+ // const newRobbot = (resNewRobbot.newIbGib as RobbotIbGib_V1);
1927
+ // await persistTransformResult({ resTransform: resNewRobbot, isMeta: true, space });
1928
+ // await registerNewIbGib({ ibGib: newRobbot, space, zeroSpace, fnBroadcast, fnUpdateBootstrap });
1929
+ // const newRobbotsAddr = await rel8ToSpecialIbGib({
1930
+ // type: "robbots",
1931
+ // rel8nName: ROBBOT_REL8N_NAME,
1932
+ // ibGibsToRel8: [newRobbot],
1933
+ // space,
1934
+ // zeroSpace,
1935
+ // fnUpdateBootstrap,
1936
+ // fnBroadcast,
1937
+ // });
1938
+ // return { newRobbotIbGib: newRobbot, newRobbotsAddr };
1939
+ // } catch (error) {
1940
+ // console.error(`${lc} ${error.message}`);
1941
+ // throw error;
1942
+ // } finally {
1943
+ // if (logalot) { console.log(`${lc} complete.`); }
1944
+ // }
1945
+ // }
1946
+
1947
+
1948
+ /**
1949
+ * We are NOT searching through all of our data looking for a needle in a haystack.
1950
+ * What we ARE doing is we are looking through the past of the existing latest and
1951
+ * the prospective latest (the given ibGib param) and comparing between the two.
1952
+ *
1953
+ * Since `past` rel8n is usually a linked rel8n now, we may have to traverse it all
1954
+ * the way to its beginning for each possibility.
1955
+ *
1956
+ * @returns either {@param ibGibAddr} or {@param existingLatestAddr}
1957
+ */
1958
+ async function getLatestAddr_Brute({
1959
+ ibGib, ibGibAddr,
1960
+ existingLatest, existingLatestAddr,
1961
+ tjpAddr,
1962
+ space,
1963
+ }: {
1964
+ ibGib: IbGib_V1<any>, ibGibAddr: string,
1965
+ existingLatest: IbGib_V1<any>, existingLatestAddr: string,
1966
+ tjpAddr: string,
1967
+ space: IbGibSpaceAny,
1968
+ }): Promise<string> {
1969
+ const lc = `[${getLatestAddr_Brute.name}][${ibGibAddr}]`;
1970
+ try {
1971
+ if (logalot) { console.log(`${lc} starting...`); }
1972
+
1973
+ if (!space) { throw new Error(`space required. (E: 64eb9a271f5d43deadec30b9638746c8)`); }
1974
+
1975
+ // no nCounter, so we need to brute force.
1976
+ // The easiest way is to check each's past, as the most common
1977
+ // scenario would be registering a newer one, or less likely, a timing issue
1978
+ // with registering a previous ibGib frame.
1979
+
1980
+ let ibGibPast = ibGib.rel8ns?.past || [];
1981
+ let existingLatestPast = existingLatest.rel8ns?.past || [];
1982
+
1983
+ // going to check a bunch of specific, easy cases to narrow things down.
1984
+
1985
+ if (ibGibPast.length === 1 && existingLatestPast.length === 0) {
1986
+ if (logalot) { console.log(`prospective has a past, so it "must" be newer. (won't quote "must" anymore)`); }
1987
+ return ibGibAddr;
1988
+ } else if (existingLatestPast.length === 1 && ibGibPast.length === 0) {
1989
+ if (logalot) { console.log(`existing has a past, so it must be newer.`); }
1990
+ return existingLatestAddr;
1991
+ } else if (existingLatestPast.length === 0 && ibGibPast.length === 0) {
1992
+ console.warn(`${lc} neither existing latest nor prospective new ibGib has a past, so keeping existing.`);
1993
+ return existingLatestAddr;
1994
+ } else if (existingLatestPast.includes(ibGibAddr)) {
1995
+ if (logalot) { console.log(`existing by definition is newer`); }
1996
+ return existingLatestAddr;
1997
+ } else if (ibGibPast.includes(existingLatestAddr)) {
1998
+ if (logalot) { console.log(`ibGib by definition is newer`); }
1999
+ return ibGibAddr;
2000
+ } else if (existingLatestAddr === ibGibAddr) {
2001
+ if (logalot) { console.log(`they're the same!`); }
2002
+ return existingLatestAddr;
2003
+ } else if (existingLatestAddr === tjpAddr && existingLatest.rel8ns?.tjp?.length === 1) {
2004
+ if (logalot) { console.log(`ibGib must be newer because the existingLatestAddr is the tjp, which is by definition first in unique past.`); }
2005
+ return ibGibAddr;
2006
+ } else if (ibGibAddr === tjpAddr && ibGib.rel8ns?.tjp?.length === 1) {
2007
+ if (logalot) { console.log(`existing must be newer because the ibGibAddr is the tjp, which is by definition first in unique past.`); }
2008
+ return existingLatestAddr;
2009
+ }
2010
+
2011
+ // well, neither one really gives us any indicator alone
2012
+ // so load each one in the past
2013
+ if (logalot) { console.log(`${lc} brute forcing through iterating the pasts.`); }
2014
+ let newerAddr: string | undefined;
2015
+ let firstIterationCount = -1; // klugy hack, but is an ugly method anyway (brute after all!)
2016
+
2017
+ let getPastCount: (x: IbGib_V1<any>, n: number, otherAddr: string) => Promise<number> =
2018
+ async (x, n, otherAddr) => {
2019
+ let xPast = x.rel8ns?.past || [];
2020
+ if (xPast.includes(otherAddr)) {
2021
+ // no need to proceed further, since the other is found in the past of x, so x is newer
2022
+ newerAddr = getIbGibAddr({ ibGib: x });
2023
+ return -1;
2024
+ }
2025
+ if (xPast.length === 0) { return n; } // no more past to increment
2026
+ let newCount = n + xPast.length;
2027
+ if (firstIterationCount !== -1 && newCount > firstIterationCount) {
2028
+ // we've determined that the second iteration has a longer past,
2029
+ // so we don't need to look further
2030
+ newerAddr = getIbGibAddr({ ibGib: x });
2031
+ return -1;
2032
+ }
2033
+ // load up the earliest one and call recursively
2034
+ let resNextX = await getFromSpace({ addr: xPast[0], space });
2035
+ if (!resNextX.success || resNextX.ibGibs?.length !== 1) { throw new Error(`Couldn't load past addr (xPast[0]): ${xPast[0]}`); }
2036
+ return getPastCount(resNextX.ibGibs![0], n + xPast.length, otherAddr);
2037
+ }
2038
+
2039
+ if (logalot) { console.log(`${lc} doing ibGibPastCount`); }
2040
+ let ibGibPastCount = await getPastCount(ibGib, 0, existingLatestAddr);
2041
+ if (newerAddr) { return newerAddr; }
2042
+
2043
+ // we didn't hit upon it, so set the firstIterationCount so we don't spend unnecessary cycles
2044
+ if (logalot) { console.log(`${lc} Doing existingPastCount`); }
2045
+ firstIterationCount = ibGibPastCount;
2046
+ let existingPastCount = await getPastCount(existingLatest, 0, ibGibAddr);
2047
+ if (newerAddr) { return newerAddr; }
2048
+
2049
+ // we didn't yet determine it, so whichever has the longer past is newer
2050
+ if (ibGibPastCount > existingPastCount) {
2051
+ if (logalot) { console.log(`${lc} ibGibPastCount (${ibGibPastCount}) is longer than existingPastCount (${existingPastCount}), so ibGib is newer.`); }
2052
+ newerAddr = ibGibAddr;
2053
+ } else {
2054
+ if (logalot) { console.log(`${lc} existingPastCount (${existingPastCount}) is longer than ibGibPastCount (${ibGibPastCount}), so ibGib is newer.`); }
2055
+ newerAddr = existingLatestAddr;
2056
+ }
2057
+ return newerAddr;
2058
+
2059
+ } catch (error) {
2060
+ console.error(`${lc} ${error.message}`);
2061
+ throw error;
2062
+ } finally {
2063
+ if (logalot) { console.log(`${lc} complete.`); }
2064
+ }
2065
+ }
2066
+
2067
+ // #endregion
2068
+
2069
+
2070
+ /**
2071
+ * Relates the given tag to the TagsIbGib, saves the generated
2072
+ * TagsIbGib and updates the settings to point to the new TagsIbGib.
2073
+ *
2074
+ * @param tagIbGib to add to Tags
2075
+ */
2076
+ export function rel8TagToTagsIbGib({
2077
+ tagIbGib,
2078
+ space,
2079
+ zeroSpace,
2080
+ fnUpdateBootstrap,
2081
+ fnBroadcast,
2082
+ }: {
2083
+ tagIbGib: IbGib_V1,
2084
+ space: IbGibSpaceAny,
2085
+ zeroSpace: IbGibSpaceAny,
2086
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
2087
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
2088
+ }): Promise<IbGibAddr> {
2089
+ return rel8ToSpecialIbGib({
2090
+ type: "tags",
2091
+ rel8nName: TAG_REL8N_NAME,
2092
+ ibGibsToRel8: [tagIbGib],
2093
+ space,
2094
+ zeroSpace,
2095
+ fnUpdateBootstrap,
2096
+ fnBroadcast,
2097
+ });
2098
+ }
2099
+
2100
+
2101
+ /**
2102
+ * Throws an error if any duplicates found in either array.
2103
+ *
2104
+ * ## notes
2105
+ *
2106
+ * Only pass in ibGib or ibGibAddrs, not both. Warns if both are passed in though.
2107
+ *
2108
+ * @throws if both params are falsy or if addrs || mapped addrs contains duplicates.
2109
+ */
2110
+ export function throwIfDuplicates({
2111
+ ibGibs,
2112
+ ibGibAddrs,
2113
+ }: {
2114
+ ibGibs?: IbGib_V1[],
2115
+ ibGibAddrs?: IbGibAddr[],
2116
+ }): void {
2117
+ const lc = `[${throwIfDuplicates.name}]`;
2118
+ try {
2119
+ if (!ibGibs && !ibGibAddrs) { throw new Error(`either ibGibs or ibGibAddrs required. (E: 37776788620f4966b0964945ce181fc6)`); }
2120
+ if (ibGibs && ibGibAddrs) { console.warn(`${lc} both ibGibs and ibGibAddrs provided. You should only provide one. Only ibGibAddrs will be checked. (W: dc13f9e197834e2daaba3bcfd08418db)`); }
2121
+
2122
+ const addrs = ibGibAddrs ? ibGibAddrs.concat() : ibGibs!.map(x => getIbGibAddr({ ibGib: x }));
2123
+ for (let i = 0; i < addrs.length; i++) {
2124
+ const addr = addrs[i];
2125
+ if (addrs.filter(x => x === addr).length > 1) { throw new Error(`duplicate addr found: ${addr} (E: 70fbef040dd449c38c667d53b8092053)`); }
2126
+ }
2127
+ } catch (error) {
2128
+ console.error(`${lc} ${error.message}`);
2129
+ throw error;
2130
+ }
2131
+ }
2132
+
2133
+ export function getSpaceLockAddr({
2134
+ space,
2135
+ scope,
2136
+ }: {
2137
+ space: IbGibSpaceAny,
2138
+ scope: SpaceLockScope,
2139
+ }): IbGibAddr {
2140
+ const lc = `[${getSpaceLockAddr.name}]`;
2141
+ try {
2142
+ if (logalot) { console.log(`${lc} starting...`); }
2143
+
2144
+ if (!space) { throw new Error(`space required. (E: 3ba16e6c3e5e47948b0e63448da11752)`); }
2145
+ if (!space.data?.uuid) { throw new Error(`invalid space (space.data.uuid falsy) (E: 273262b32f2ef27b2e690bc699f33822)`); }
2146
+ if (!scope) { throw new Error(`scope required. (E: f47801d6c45e2247b42a53d9b604b522)`); }
2147
+ while (scope.includes(IBGIB_DELIMITER)) {
2148
+ if (logalot) { console.log(`${lc} scope contains ibgib delimiter...replacing... (I: 0f456fd7cc6552a799673a0c5b4a7d22)`); }
2149
+ scope = scope.replace(IBGIB_DELIMITER, '_');
2150
+ }
2151
+
2152
+ const spaceId = space.data!.uuid;
2153
+ const ib = `${SPACE_LOCK_IB_TERM} ${spaceId} ${scope}`;
2154
+ const gib = GIB;
2155
+ return getIbGibAddr({ ib, gib });
2156
+ } catch (error) {
2157
+ console.error(`${lc} ${error.message}`);
2158
+ throw error;
2159
+ } finally {
2160
+ if (logalot) { console.log(`${lc} complete.`); }
2161
+ }
2162
+ }
2163
+
2164
+ /**
2165
+ * Executes some function `fn` against/in a given `space` while
2166
+ * performing lock plumbing in that space.
2167
+ *
2168
+ * By this, I mean that this function takes care of acquiring the lock,
2169
+ * attempting retries according to the parameters, and then unlocking
2170
+ * the space regardless of result/error.
2171
+ *
2172
+ * @returns result of inner `fn` with `TResult`
2173
+ */
2174
+ export async function execInSpaceWithLocking<TResult>({
2175
+ space,
2176
+ scope,
2177
+ secondsValid,
2178
+ maxDelayMs,
2179
+ fn,
2180
+ callerInstanceId,
2181
+ maxLockAttempts,
2182
+ }: {
2183
+ space: IbGibSpaceAny,
2184
+ scope: string,
2185
+ secondsValid: number,
2186
+ /**
2187
+ * If resource locked, will delay at max this ms.
2188
+ */
2189
+ maxDelayMs?: number,
2190
+ fn: () => Promise<TResult>,
2191
+ /**
2192
+ * for use differentiating among tabs.
2193
+ *
2194
+ * ## intent
2195
+ * the idea is the ibgibs service has an instance id and passes it in here.
2196
+ */
2197
+ callerInstanceId?: string,
2198
+ /**
2199
+ * if given, will try to acquire lock at most this many times, else
2200
+ * will default to {@link DEFAULT_MAX_DELAY_RETRY_LOCK_ACQUIRE_ATTEMPTS}.
2201
+ */
2202
+ maxLockAttempts?: number,
2203
+ }): Promise<TResult> {
2204
+ const lc = `[${execInSpaceWithLocking.name}]`;
2205
+ try {
2206
+ if (logalot) { console.log(`${lc} starting...`); }
2207
+
2208
+ // #region validation
2209
+
2210
+ if (!space) { throw new Error(`space required. (E: 66fd8f21a5b2b572d18cdeb9472a7722)`); }
2211
+ if (!secondsValid) { throw new Error(`secondsValid required. (E: 92c5610e57ceede5ce83cff86d5c2a22)`); }
2212
+ if (secondsValid < 0) { throw new Error(`secondsValid must be positive. (E: 970a7510c517235d7a355a843d18d222)`); }
2213
+ // if (secondsValid > MAX_LOCK_SECONDS_VALID) { throw new Error(`secondsValid arg (${secondsValid}) exceeds max secondsValid (${MAX_LOCK_SECONDS_VALID}) (E: 4cffe49a23c8f1698bf7c78eaccbb722)`); }
2214
+ if (!fn) { throw new Error(`fnGet required (E: 7022e280252ec2faf756b6db05c56e22)`); }
2215
+
2216
+ // #endregion validation
2217
+
2218
+ let resultFn: TResult;
2219
+
2220
+ if (logalot) { console.log(`${lc} attempting to acquire lock with scope ${scope} (I: fed36d42a975b1c52897a4df804ac722)`); }
2221
+ let lockIbGib: IbGibSpaceLockIbGib;
2222
+ maxDelayMs =
2223
+ (maxDelayMs ?? 0) > 0 ? maxDelayMs : DEFAULT_MAX_DELAY_MS_RETRY_LOCK_ACQUIRE
2224
+ let attempts = 0;
2225
+ maxLockAttempts =
2226
+ maxLockAttempts ?? -1 > 0 ?
2227
+ maxLockAttempts :
2228
+ DEFAULT_MAX_DELAY_RETRY_LOCK_ACQUIRE_ATTEMPTS;
2229
+ do {
2230
+ lockIbGib = await lockSpace({
2231
+ space,
2232
+ scope,
2233
+ secondsValid,
2234
+ instanceId: callerInstanceId,
2235
+ });
2236
+ if (lockIbGib?.data?.success) { break; }
2237
+ /** Delay a small random amount of time before trying again. */
2238
+ let delayMs = Math.ceil(Math.random() * maxDelayMs!);
2239
+ await delay(delayMs);
2240
+ } while (attempts < maxLockAttempts!);
2241
+ if (lockIbGib?.data?.success) {
2242
+ if (logalot) { console.log(`${lc} lock acquired. (I: d847fa953ee131a57e1a89c537342722)`); }
2243
+ } else {
2244
+ throw new Error(`could not acquire lock after ${attempts} attempts with intermittent delays of ${maxDelayMs} ms (E: 898c559dbd0c9bf20c14cf87a8f1e222)`);
2245
+ }
2246
+
2247
+ // execute the actual get inside an additional try..catch..finally
2248
+ // to ensure (attempted) unlockSpace call.
2249
+ const lc2 = `${lc}[fn]`;
2250
+ try {
2251
+ if (logalot) { console.log(`${lc2} starting... (I: c2f7bedf95d3f1dd1f76de34a68d3f22)`); }
2252
+ resultFn = await fn();
2253
+ } catch (error) {
2254
+ console.error(`${lc2} ${error.message}`);
2255
+ throw error;
2256
+ } finally {
2257
+ if (logalot) { console.log(`${lc2} unlocking space with scope: ${scope} (I: 21034d9c3395499756e9000e40417d22)`); }
2258
+ await unlockSpace({ space: space, scope: scope, instanceId: callerInstanceId });
2259
+ if (logalot) { console.log(`${lc2} complete. (I: 79d8ac03714a4d429c7e6c2ac6e18d22)`); }
2260
+ }
2261
+
2262
+ return resultFn;
2263
+ } catch (error) {
2264
+ console.error(`${lc} ${error.message}`);
2265
+ throw error;
2266
+ } finally {
2267
+ if (logalot) { console.log(`${lc} complete.`); }
2268
+ }
2269
+ }
2270
+
2271
+ /**
2272
+ * Gets the bootstrap ibgib in the given `zeroSpace`.
2273
+ *
2274
+ * @example
2275
+ *
2276
+ * ```json
2277
+ * {
2278
+ * "ib":"bootstrap",
2279
+ * "gib":"gib",
2280
+ * "data":{
2281
+ * "defaultSpaceId":"d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33",
2282
+ * "spaceIds":["d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33"]
2283
+ * },
2284
+ * "rel8ns":{
2285
+ * "d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33":[
2286
+ * "witness space IonicSpace_V1 oij d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33^B336251655E8C56B38E9E86F20E0E42E6C153785F1A0A798ADE6916E71CF055B"
2287
+ * ]
2288
+ * }
2289
+ * }
2290
+ * ```
2291
+ *
2292
+ * @returns bootstrapIbGib if found
2293
+ */
2294
+ export async function getValidatedBootstrapIbGib({
2295
+ zeroSpace,
2296
+ }: {
2297
+ zeroSpace: IbGibSpaceAny,
2298
+ }): Promise<BootstrapIbGib | null> {
2299
+ const lc = `[${getValidatedBootstrapIbGib.name}]`;
2300
+ try {
2301
+ if (logalot) { console.log(`${lc} starting...`); }
2302
+
2303
+ if (!zeroSpace) { throw new Error(`zeroSpace required. (E: 66fd8f21a5b2b572d18cdeb9472a7722)`); }
2304
+
2305
+ const bootstrapAddr = BOOTSTRAP_IBGIB_ADDR;
2306
+
2307
+ if (logalot) { console.log(`${lc} getting from zeroSpace...`); }
2308
+ const argGet = await zeroSpace.argy({
2309
+ ibMetadata: getSpaceArgMetadata({ space: zeroSpace }),
2310
+ argData: {
2311
+ cmd: 'get',
2312
+ ibGibAddrs: [bootstrapAddr],
2313
+ isMeta: true,
2314
+ },
2315
+ });
2316
+ const resGetBootstrapIbGib = await zeroSpace.witness(argGet);
2317
+
2318
+ if (resGetBootstrapIbGib?.data?.success && resGetBootstrapIbGib.ibGibs?.length === 1) {
2319
+ // bootstrap found
2320
+ const bootstrapIbGib = resGetBootstrapIbGib!.ibGibs![0]!;
2321
+ if (logalot) { console.log(`${lc} bootstrapibGib found: ${pretty(bootstrapIbGib)}`); }
2322
+ if (await validateBootstrapIbGib(bootstrapIbGib)) {
2323
+ return (bootstrapIbGib as BootstrapIbGib);
2324
+ } else {
2325
+ if (logalot) { console.log(`${lc} bootstrapIbGib was invalid. (I: cce66b26805404fc85525d565e1f8b22)`); }
2326
+ return null;
2327
+ }
2328
+ } else {
2329
+ if (logalot) { console.log(`${lc} bootstrapIbGib NOT found. (I: 421562993bf3464eb507d2967d311e22)`); }
2330
+ return null;
2331
+ }
2332
+ } catch (error) {
2333
+ console.error(`${lc} ${error.message}`);
2334
+ throw error;
2335
+ } finally {
2336
+ if (logalot) { console.log(`${lc} complete.`); }
2337
+ }
2338
+ }
2339
+
2340
+
2341
+ /**
2342
+ * So when loading the local user space, even if the class changes or default
2343
+ * constructors change, the internal `data` is loaded from file.
2344
+ */
2345
+ export async function getLocalSpace<TSpace extends IbGibSpaceAny>({
2346
+ zeroSpace,
2347
+ bootstrapIbGib,
2348
+ localSpaceId,
2349
+ lock,
2350
+ callerInstanceId,
2351
+ fnDtoToSpace,
2352
+ localSpaceCacheSvc,
2353
+ }: {
2354
+ /**
2355
+ * A zero space is a space that is built with default settings that
2356
+ * is not specific to a user.
2357
+ *
2358
+ * This is the foundational space that all spaces share, out of which
2359
+ * a bootstrap ibgib can be found that will itself have rel8ns to other
2360
+ * spaces.
2361
+ *
2362
+ * ## notes
2363
+ *
2364
+ * This is what I was calling a zeroSpace, but that will in the future
2365
+ * mean a default space among a group of spaces.
2366
+ */
2367
+ zeroSpace: IbGibSpaceAny,
2368
+ /**
2369
+ * bootstrap ibGib, if provided, which was found in the zero space.
2370
+ * The bootstrap ibgib should have a rel8n entry to the local space.
2371
+ *
2372
+ * ## future
2373
+ *
2374
+ * We will have multiple local spaces to choose from, with a default
2375
+ * being the one that we're referring to atow.
2376
+ */
2377
+ bootstrapIbGib?: BootstrapIbGib,
2378
+ /**
2379
+ * If provided, will look for the space via this id in the bootstrap ibgib.
2380
+ * If not provided, will use the bootstrap ibgib's default spaceId.
2381
+ */
2382
+ localSpaceId?: SpaceId,
2383
+ /**
2384
+ * If true, we will lock on getting the bootstrap ibgib (if needed), as
2385
+ * well as getting the user space.
2386
+ */
2387
+ lock?: boolean,
2388
+ /**
2389
+ * for use differentiating among tabs.
2390
+ *
2391
+ * ## intent
2392
+ * the idea is the ibgibs service has an instance id and passes it in here.
2393
+ */
2394
+ callerInstanceId: string,
2395
+ /**
2396
+ * function that turns the space dto into the space witness.
2397
+ *
2398
+ * ## intent
2399
+ *
2400
+ * When loading a space, the only part stored is the ibgib data. You still
2401
+ * need a function that hydrates a witness class with that data.
2402
+ */
2403
+ fnDtoToSpace: (spaceDto: IbGib_V1) => Promise<TSpace>,
2404
+ /**
2405
+ * Optional caching service
2406
+ */
2407
+ localSpaceCacheSvc?: IbGibCacheService,
2408
+ }): Promise<TSpace> {
2409
+ const lc = `[${getLocalSpace.name}]`;
2410
+ try {
2411
+ if (!zeroSpace) { throw new Error(`zeroSpace required. (E: 0793781a98c456a666cfa9eb960bcd22)`); }
2412
+
2413
+ if (!bootstrapIbGib) {
2414
+ if (logalot) { console.log(`${lc} bootstrap falsy, so loading it... (I: f2366e38283495a38b5501297aa34422)`); }
2415
+ if (lock) {
2416
+ const bootstrapAddr = BOOTSTRAP_IBGIB_ADDR;
2417
+ if (logalot) { console.log(`${lc} using locked version of loading bootstrap... (I: 52b9b11999674e586d051c2b23f59b22)`); }
2418
+ bootstrapIbGib = await execInSpaceWithLocking<BootstrapIbGib>({
2419
+ space: zeroSpace,
2420
+ scope: bootstrapAddr,
2421
+ fn: async () => {
2422
+ let validatedBootstrapIbGib = await getValidatedBootstrapIbGib({ zeroSpace });
2423
+ if (validatedBootstrapIbGib) {
2424
+ return validatedBootstrapIbGib;
2425
+ } else {
2426
+ throw new Error(`(UNEXPECTED) unable to get bootstrap ibgib? (E: c111dc7627acbfb992134ddf4064ea23)`);
2427
+ }
2428
+ },
2429
+ callerInstanceId,
2430
+ secondsValid: DEFAULT_SECONDS_VALID_LOCAL,
2431
+ });
2432
+ } else {
2433
+ if (logalot) { console.log(`${lc} lock is false, so just getting the bootstrap ibgib (I: 49d55e5a824510bb3ee0ccd9f5ec3322)`); }
2434
+ bootstrapIbGib = await getValidatedBootstrapIbGib({ zeroSpace }) ?? undefined;
2435
+ }
2436
+ }
2437
+
2438
+ if (!bootstrapIbGib) { throw new Error(`bootstrapIbGib falsy. not initialized? (E: 91f5c82b5e124178d958701ebcb09822)`); }
2439
+
2440
+ if (!bootstrapIbGib.data) { throw new Error(`(UNEXPECTED) bootstrapIbGib.data required. invalid bootstrap? (E: 568c6b837c1a39f7a25d188a90167423)`); }
2441
+
2442
+ localSpaceId = localSpaceId ?? bootstrapIbGib!.data[BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY];
2443
+ const localSpaceAddr = bootstrapIbGib.rel8ns![localSpaceId]![0]; // guaranteed b/c bootstrap was validated
2444
+
2445
+ const fnGet: () => Promise<TSpace> = async () => {
2446
+
2447
+ const argGet = await zeroSpace.argy({
2448
+ ibMetadata: getSpaceArgMetadata({ space: zeroSpace }),
2449
+ argData: {
2450
+ cmd: 'get',
2451
+ ibGibAddrs: [localSpaceAddr],
2452
+ isMeta: true,
2453
+ },
2454
+ });
2455
+ const resLocalSpace = await zeroSpace.witness(argGet);
2456
+ if (resLocalSpace?.data?.success && resLocalSpace.ibGibs?.length === 1) {
2457
+ const localSpaceDto = resLocalSpace.ibGibs[0] as TSpace;
2458
+ const localSpace = await fnDtoToSpace(localSpaceDto);
2459
+ if (localSpaceCacheSvc) { localSpace.cacheSvc = localSpaceCacheSvc; }
2460
+ return localSpace;
2461
+ } else {
2462
+ throw new Error(`Could not get local space addr (${localSpaceAddr}) specified in bootstrap space (${getIbGibAddr({ ibGib: bootstrapIbGib })}). (E: 6d6b45e7eae4472697ddc971438e4922)`);
2463
+ }
2464
+ }
2465
+
2466
+ if (lock) {
2467
+ if (logalot) { console.log(`${lc} getting localSpaceId (${localSpaceId}) WITH locking (I: c48a0e4ac5971cdbc57273dd35f8a522)`); }
2468
+ return await execInSpaceWithLocking({
2469
+ space: zeroSpace,
2470
+ scope: localSpaceId,
2471
+ fn: () => { return fnGet(); },
2472
+ callerInstanceId,
2473
+ secondsValid: DEFAULT_SECONDS_VALID_LOCAL,
2474
+ });
2475
+ } else {
2476
+ if (logalot) { console.log(`${lc} getting localSpaceId (${localSpaceId}) WITHOUT locking (I: 48e504835dee4006839df8820d860b22)`); }
2477
+ return fnGet();
2478
+ }
2479
+
2480
+ } catch (error) {
2481
+ console.error(`${lc} ${error.message}`);
2482
+ throw error;
2483
+ }
2484
+ }
2485
+
2486
+ export async function lockSpace({
2487
+ space,
2488
+ scope,
2489
+ secondsValid,
2490
+ instanceId,
2491
+ }: IbGibSpaceLockOptions): Promise<IbGibSpaceLockIbGib> {
2492
+ const lc = `[${lockSpace.name}]`;
2493
+ try {
2494
+ if (logalot) { console.log(`${lc} starting...`); }
2495
+
2496
+ if (!space) { throw new Error(`space required. (E: 5c0a7197a75f483a82d74e5eea60df37)`); }
2497
+ if (!scope) { throw new Error(`scope required. (E: c7f1dde9570f4df3b450faa2c2f85122)`); }
2498
+ if (!secondsValid) { throw new Error(`secondsValid required and positive (E: b42b6733638b46c06c9aff59a6c49822)`); }
2499
+ if (secondsValid < 0) { throw new Error(`secondsValid must be positive (E: bbe3b6d567583bfb35a1c0825eb29622)`); }
2500
+
2501
+ /** what we will return */
2502
+ let resLockIbGib: IbGibSpaceLockIbGib;
2503
+
2504
+ /** space lock address is deterministic ibgib primitive (gib === 'gib') */
2505
+ const spaceLockAddr = getSpaceLockAddr({ space, scope });
2506
+
2507
+ // check for existing lock...
2508
+ let existingLock: IbGibSpaceLockIbGib | undefined;
2509
+
2510
+ // ...in space's backing store (file, IndexedDB entry, DB, etc.)
2511
+
2512
+ let getLock = await getFromSpace({
2513
+ addr: spaceLockAddr, isMeta: true, space, force: true
2514
+ });
2515
+ if (getLock.success && getLock.ibGibs?.length === 1) {
2516
+ existingLock = getLock.ibGibs[0] as IbGibSpaceLockIbGib;
2517
+ if (existingLock?.data?.expirationUTC) {
2518
+ if (isExpired({ expirationTimestampUTC: existingLock.data.expirationUTC })) {
2519
+ // lock expired, so log only. skipping delete atow because should be overwritten
2520
+ // when the new lock is in place.
2521
+ if (logalot) { console.log(`${lc} ignoring expired existing lock in space at ${spaceLockAddr}. Should be overwritten (I: 7421c5b051724b189f88cecbbd449b22)`); }
2522
+ existingLock = undefined;
2523
+ }
2524
+ } else {
2525
+ console.error(`${lc} exisingLock.data.expirationUTC falsy? (E: 218dc0e935534a149d0be153c532cf25)`);
2526
+ existingLock = undefined;
2527
+ }
2528
+ } else {
2529
+ if (logalot) { console.log(`${lc} existing lock not found for ${spaceLockAddr} (I: 191af56ec4e2db3d19084e46bf949222)`); }
2530
+ }
2531
+
2532
+ // populate resLockIbGib if/else existingLock
2533
+ if (existingLock && existingLock.data) {
2534
+ if (!existingLock.data) { throw new Error(`(UNEXPECTED) existingLock.data falsy? (E: 00069b4662e5566d971760d15e293723)`); }
2535
+ // valid lock already exists, so return informing caller. this includes
2536
+ // the existing lock's info, i.e. its expiration
2537
+ resLockIbGib = clone(existingLock) as IbGibSpaceLockIbGib;
2538
+ resLockIbGib.data!.alreadyLocked = true;
2539
+ resLockIbGib.data!.success = false;
2540
+ } else {
2541
+ // not yet locked so immediately lock in memory for space, then via
2542
+ // `space.put(lock)`
2543
+ const { ib, gib } = getIbAndGib({ ibGibAddr: spaceLockAddr });
2544
+ resLockIbGib = {
2545
+ ib, gib,
2546
+ data: {
2547
+ scope,
2548
+ secondsValid,
2549
+ instanceId,
2550
+ expirationUTC: getExpirationUTCString({ seconds: secondsValid }),
2551
+ }
2552
+ } as IbGibSpaceLockIbGib;
2553
+ const resPut = await putInSpace({ ibGib: resLockIbGib, isMeta: true, force: true, space });
2554
+ if (resPut.success) {
2555
+ resLockIbGib.data!.success = true;
2556
+ } else {
2557
+ const emsg = `${lc} there was an error putting the lock in the space: ${resPut.errorMsg}`;
2558
+ resLockIbGib.data!.success = false;
2559
+ resLockIbGib.data!.errorMsg = emsg;
2560
+ console.error(emsg);
2561
+ }
2562
+ }
2563
+
2564
+ return resLockIbGib;
2565
+ } catch (error) {
2566
+ console.error(`${lc} ${error.message}`);
2567
+ throw error;
2568
+ } finally {
2569
+ if (logalot) { console.log(`${lc} complete.`); }
2570
+ }
2571
+ }
2572
+
2573
+ export async function unlockSpace({
2574
+ space,
2575
+ scope,
2576
+ instanceId,
2577
+ }: IbGibSpaceLockOptions): Promise<IbGibSpaceLockIbGib | undefined> {
2578
+ const lc = `[${unlockSpace.name}]`;
2579
+ try {
2580
+ if (logalot) { console.log(`${lc} starting...`); }
2581
+
2582
+ if (!space) { throw new Error(`space required. (E: 5c0a7197a75f483a82d74e5eea60df37)`); }
2583
+ if (!scope) { throw new Error(`scope required. (E: c7f1dde9570f4df3b450faa2c2f85122)`); }
2584
+
2585
+ /** space lock address is deterministic ibgib primitive (gib === 'gib') */
2586
+ const spaceLockAddr = getSpaceLockAddr({ space, scope });
2587
+
2588
+ // delete in file if it exists
2589
+ let resDelete = await deleteFromSpace({ addr: spaceLockAddr, isMeta: true, space, force: true });
2590
+ if (resDelete.success) {
2591
+ const { ib, gib } = getIbAndGib({ ibGibAddr: spaceLockAddr });
2592
+ return {
2593
+ ib, gib,
2594
+ data: {
2595
+ // action: 'unlock',
2596
+ success: true,
2597
+ instanceId,
2598
+ scope,
2599
+ }
2600
+ } as IbGibSpaceLockIbGib;
2601
+ } else {
2602
+ /** May NOT be an error, just setting this here is convenient to code. */
2603
+ const emsg = `Delete lock in space failed. delete errorMsg: ${resDelete.errorMsg}`;
2604
+ if (emsg.includes('not implemented')) {
2605
+ // rethrow error if space has not implemented a delete command handler
2606
+ throw new Error(emsg);
2607
+ } else if (
2608
+ emsg.toLowerCase().includes(`does not exist`) ||
2609
+ emsg.toLowerCase().includes(`doesn't exist`) ||
2610
+ emsg.toLowerCase().includes(`not found`)
2611
+ ) {
2612
+ // don't want to warn if just file didn't exist
2613
+ if (logalot) { console.log(`${lc} ${emsg} (I: b647916fd0f3e5366e9387131be21c22)`); }
2614
+ const { ib, gib } = getIbAndGib({ ibGibAddr: spaceLockAddr });
2615
+ return {
2616
+ ib, gib,
2617
+ data: {
2618
+ action: 'unlock',
2619
+ success: true,
2620
+ instanceId,
2621
+ scope,
2622
+ }
2623
+ } as IbGibSpaceLockIbGib;
2624
+ } else {
2625
+ console.warn(`${lc} ${emsg} (W: 14c84fcac15944bd9a417099964d5d9d)`);
2626
+ }
2627
+ }
2628
+ } catch (error) {
2629
+ console.error(`${lc} ${error.message}`);
2630
+ throw error;
2631
+ } finally {
2632
+ if (logalot) { console.log(`${lc} complete.`); }
2633
+ }
2634
+ }
2635
+
2636
+ /**
2637
+ * Updates the bootstrap^gib record in the default space data store
2638
+ * with the 'space' rel8n set to the `newSpaceAddr`.
2639
+ *
2640
+ * This way, if the app closes at this point, it will know to look at this
2641
+ * space when it loads.
2642
+ *
2643
+ * ## notes
2644
+ *
2645
+ * I'm probably typing this all over, but the bootstrap^gib is the
2646
+ * first record that the app looks at to know what space to load.
2647
+ * The space itself has configuration and has a proper gib hash
2648
+ * so it's verifiable. But the initial bootstrap^gib record is
2649
+ * NOT, since it only has 'gib' as its address (and thus its not hashed).
2650
+ *
2651
+ * In the future, this could be alleviated by asking other witnesses
2652
+ * "Hey what was my last bootstrap^gib hash", but there's no way to
2653
+ * do it beyond this without using some kind of authentication/secret(s)
2654
+ * that generate the record, i.e. encrypting bootstrap^gib cleverly.
2655
+ */
2656
+ export async function updateBootstrapIbGib({
2657
+ space,
2658
+ zeroSpace,
2659
+ setSpaceAsDefault,
2660
+ createIfNotFound,
2661
+ }: {
2662
+ /**
2663
+ * space to add/replace in the bootstrap^gib ibgib.
2664
+ */
2665
+ space: IbGibSpaceAny,
2666
+ /**
2667
+ * default "zero" space which contains a bootstrap^gib ibgib.
2668
+ */
2669
+ zeroSpace?: IbGibSpaceAny,
2670
+ /**
2671
+ * set the space as the default local space in the bootstrap^gib ibgib.
2672
+ */
2673
+ setSpaceAsDefault?: boolean,
2674
+ /**
2675
+ * If bootstrap not found, create a new one
2676
+ */
2677
+ createIfNotFound?: boolean,
2678
+ }): Promise<void> {
2679
+ const lc = `[${updateBootstrapIbGib.name}]`;
2680
+ try {
2681
+ if (logalot) { console.log(`${lc} starting...`); }
2682
+
2683
+ if (!space) { throw new Error(`space required. (E: 6fc19548fa7d1d5219e19871f280a322)`); }
2684
+ if (!space.data?.uuid) { throw new Error(`space.data.uuid required (E: 6483188ca6f2ed2085fd355595f3ab22)`); }
2685
+ if (!zeroSpace) { throw new Error(`zeroSpace required. (E: abbf156e18b1018d96273e22a260f122)`); }
2686
+
2687
+ const spaceId = space.data.uuid;
2688
+ const newSpaceAddr = getIbGibAddr({ ibGib: space });
2689
+
2690
+ /** validated bootstrap ibgib or null */
2691
+ let bootstrapIbGib = await getValidatedBootstrapIbGib({ zeroSpace });
2692
+ if (!bootstrapIbGib) {
2693
+ if (!createIfNotFound) {
2694
+ if (logalot) { console.log(`${lc} bootstrapIbGib not found but createIfNotFound falsy, so returning early. (I: 5c67d85a8599a5ba4a6780f26a66ea22)`); }
2695
+ return; /* <<<< returns early */
2696
+ }
2697
+
2698
+ // create the bootstrap^gib space that points to user space
2699
+ if (logalot) { console.log(`${lc} creating new bootstrap ibgib for spaceId (${spaceId}) with address of (${newSpaceAddr}) (I: 651959c27bf2ebc5be1f7f44e2b9e422)`); }
2700
+ const { ib: bootstrapIb, gib } = getIbAndGib({ ibGibAddr: BOOTSTRAP_IBGIB_ADDR });
2701
+ bootstrapIbGib = (factory.primitive as any)({ ib: bootstrapIb });
2702
+ bootstrapIbGib!.gib = gib;
2703
+ bootstrapIbGib!.data = {
2704
+ /**
2705
+ * first space is automatically set as default, regardless of
2706
+ * `setSpaceAsDefault` value
2707
+ */
2708
+ [BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY]: spaceId,
2709
+ [BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY]: [spaceId],
2710
+ } as BootstrapData;
2711
+ bootstrapIbGib!.rel8ns = {
2712
+ /**
2713
+ * individual spaces indexed by spaceId, should be length === 1
2714
+ * with the value being the most recent.
2715
+ */
2716
+ [spaceId]: [newSpaceAddr],
2717
+ } as BootstrapRel8ns;
2718
+ } else {
2719
+ // at this point, we have a validated bootstrap.
2720
+ // update existing bootstrap with newSpaceAddr for given spaceId
2721
+ if (logalot) { console.log(`${lc} updating existing bootstrap (I: 977d4aa7ed47bef73b35743c05ce0722)`); }
2722
+ if (bootstrapIbGib.data!.spaceIds.includes(spaceId)) {
2723
+ if (logalot) { console.log(`${lc} space already rel8d to bootstrap, possibly updating its addr (I: b5e2c515ef732d4bbcf02625a9e7c722)`); }
2724
+ const existingSpaceAddr = bootstrapIbGib.rel8ns![spaceId]![0];
2725
+ if (existingSpaceAddr === newSpaceAddr) {
2726
+ if (logalot) { console.log(`${lc} bootstrap already rel8d to space (I: c1be027b23e7350c64790a631cae2822)`); }
2727
+ } else {
2728
+ if (logalot) { console.log(`${lc} updating rel8ns[${spaceId}] with newSpaceAddr (${newSpaceAddr}). (Old address: ${existingSpaceAddr})(I: 297a9b0061fd471b8d28a06e04a6ad22)`); }
2729
+ bootstrapIbGib.rel8ns![spaceId] = [newSpaceAddr];
2730
+ }
2731
+ } else {
2732
+ // new space being rel8d to bootstrap
2733
+ if (logalot) { console.log(`${lc} new space being rel8d. adding spaceId to data.spaceIds and amending rel8ns (I: 40903d71719aa1d56e498299f5699a22)`); }
2734
+ bootstrapIbGib.data!.spaceIds.push(spaceId);
2735
+ bootstrapIbGib.rel8ns![spaceId] = [newSpaceAddr];
2736
+ }
2737
+ if (setSpaceAsDefault) {
2738
+ if (logalot) { console.log(`${lc} setting spaceId (${spaceId}) as default space (I: f85eda6c6ad2b0bec9750ce3c7795b22)`); }
2739
+ bootstrapIbGib.data![BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY] = spaceId;
2740
+ }
2741
+ }
2742
+
2743
+ if (!bootstrapIbGib) { throw new Error(`(UNEXPECTED) bootstrapIbGib still falsy? (E: cf21d7f1756710a66aa0e6a28d762723)`); }
2744
+
2745
+ if (logalot) { console.log(`${lc} saving bootstrapIbGib: ${pretty(bootstrapIbGib)} (I: 3cceca0b98dde90e4a58a734be252322)`); }
2746
+
2747
+ // save the bootstrap^gib in the zero space for future space loadings,
2748
+ // especially when the app first starts up
2749
+ const argPutBootstrap = await zeroSpace.argy({
2750
+ ibMetadata: bootstrapIbGib!.ib,
2751
+ argData: {
2752
+ cmd: 'put', isMeta: true, force: true,
2753
+ ibGibAddrs: [getIbGibAddr({ ibGib: bootstrapIbGib })],
2754
+ },
2755
+ ibGibs: [bootstrapIbGib],
2756
+ });
2757
+ if (logalot) { console.log(`${lc} zeroSpace will witness/put... (I: 1be9aca22ffc4951b6690965a7aeae5b)`); }
2758
+ const resPutBootstrap = await zeroSpace.witness(argPutBootstrap);
2759
+ if (resPutBootstrap?.data?.success) {
2760
+ if (logalot) { console.log(`${lc} zero space put complete. (I: ac3056d655e841f1a3c442bcc64942f9)`); }
2761
+ } else {
2762
+ throw new Error(`${resPutBootstrap?.data?.errors?.join('|') || "There was a problem with zeroSpace witnessing the bootstrap^gib primitive pointing to the new user space"}`);
2763
+ }
2764
+ if (logalot) { console.log(`${lc} complete.`); }
2765
+ } catch (error) {
2766
+ console.error(`${lc} ${error.message}`);
2767
+ throw error;
2768
+ }
2769
+
2770
+ }
2771
+
2772
+ export function getSpaceArgMetadata({ space }: { space: IbGibSpaceAny }): string {
2773
+ return `${space.ib} ${getTimestampInTicks()}`;
2774
+ }
2775
+
2776
+ export function getSpaceResultMetadata({ space }: { space: IbGibSpaceAny }): string {
2777
+ return `${space.ib} ${getTimestampInTicks()}`;
2778
+ }
2779
+
2780
+ export function getSpaceIb({
2781
+ space,
2782
+ classname,
2783
+ }: {
2784
+ space: IbGibSpaceAny,
2785
+ classname: string,
2786
+ }): Ib {
2787
+ const lc = `[${getSpaceIb.name}]`;
2788
+ try {
2789
+ if (!space) { throw new Error(`space required (E: 4dabec34ee77d67c9cc30ee3c3049622)`); }
2790
+ if (!space.data) { throw new Error(`space.data required (E: 058d298876ebbeada7bbddb9e3da2f23)`); }
2791
+ if (!classname) { throw new Error(`classname required (E: fa3af4613ad56742dab51d1b0d839322)`); }
2792
+ if (classname.includes(' ')) { throw new Error(`invalid classname. cannot contain spaces (E: 243adbf720dcce7904e2665933208b22)`); }
2793
+ const name = space.data?.name || IBGIB_SPACE_NAME_DEFAULT;
2794
+ if (name.includes(' ')) { throw new Error(`invalid space name. cannot contain spaces (E: a8450e1651081412c8ac018520182422)`); }
2795
+ const id = space.data?.uuid || undefined;
2796
+ if (!id) { throw new Error(`invalid space, space.data.uuid falsy (E: 50ae723a9ab24f4fc7132613e65faf23)`); }
2797
+ if (id.includes(' ')) { throw new Error(`invalid space id. cannot contain spaces (E: 8696830fe7f54bfa85e670a063f3e089)`); }
2798
+ let ib = `witness space ${classname} ${name} ${id}`;
2799
+ return ib;
2800
+ } catch (error) {
2801
+ console.error(`${lc} ${error.message}`);
2802
+ throw error;
2803
+ }
2804
+ }
2805
+
2806
+ export function isSpaceIb({
2807
+ ib
2808
+ }: {
2809
+ ib: Ib
2810
+ }): boolean {
2811
+ const lc = `[${isSpaceIb.name}]`;
2812
+ try {
2813
+ if (logalot) { console.log(`${lc} starting... (I: 53ad337f8148616a77caca5dff0f8722)`); }
2814
+ return ib.startsWith('witness space ') || ib.startsWith('outerspace sync ');
2815
+ } catch (error) {
2816
+ console.error(`${lc} ${error.message}`);
2817
+ throw error;
2818
+ } finally {
2819
+ if (logalot) { console.log(`${lc} complete.`); }
2820
+ }
2821
+ }
2822
+
2823
+ /**
2824
+ * ## current schema FOR LOCAL (IONIC) SPACES:
2825
+ *
2826
+ * `witness space [classname] [spaceName] [spaceId]`
2827
+ *
2828
+ * ## current schema FOR SYNC (AWS) OUTERSPACES:
2829
+ *
2830
+ * `outerspace sync [spaceName]
2831
+ *
2832
+ * ## I need to fix this somehow...
2833
+ *
2834
+ * This is why I'm working on streamlining space management
2835
+ *
2836
+ * (To start with, I just had to get the ball rolling and tried my best.)
2837
+ *
2838
+ * ## NOTES
2839
+ *
2840
+ * * both schemas are space-delimited
2841
+ */
2842
+ export function getInfoFromSpaceIb({
2843
+ spaceIb,
2844
+ }: {
2845
+ spaceIb: Ib,
2846
+ }): {
2847
+ /**
2848
+ * currently this can be SpaceId OR undefined.
2849
+ */
2850
+ spaceClassname: string | undefined,
2851
+ /**
2852
+ * non-unique spaceName of the given space (i.e. most likely space.data.name)
2853
+ */
2854
+ spaceName: string,
2855
+ /**
2856
+ * currently this returns EITHER SpaceId OR undefined.
2857
+ *
2858
+ * This is because of my using different schemas for local/outer spaces...
2859
+ *
2860
+ * (for better or worse)
2861
+ */
2862
+ spaceId: SpaceId | undefined,
2863
+ spaceType: 'outer' | 'local' | 'inner',
2864
+ } {
2865
+ const lc = `[${getInfoFromSpaceIb.name}]`;
2866
+ try {
2867
+ if (!spaceIb) { throw new Error(`spaceIb required (E: fa5424cfb7e846e2851562f2f417944f)`); }
2868
+
2869
+ if (spaceIb.startsWith('witness space ')) {
2870
+ const pieces = spaceIb.split(' ');
2871
+ return {
2872
+ spaceClassname: pieces[2],
2873
+ spaceName: pieces[3],
2874
+ spaceId: pieces[4],
2875
+ spaceType: 'local',
2876
+ };
2877
+ } else if (spaceIb.startsWith('outerspace sync ')) {
2878
+ const pieces = spaceIb.split(' ');
2879
+ return {
2880
+ spaceClassname: undefined,
2881
+ spaceName: pieces[2],
2882
+ spaceId: undefined,
2883
+ spaceType: 'outer',
2884
+ };
2885
+ } else {
2886
+ throw new Error(`unknown spaceIb schema (spaceIb: ${spaceIb}) (E: a6d361be85d5f25d6a464a5896186322)`);
2887
+ }
2888
+ } catch (error) {
2889
+ console.error(`${lc} ${error.message}`);
2890
+ throw error;
2891
+ }
2892
+ }
2893
+ /**
2894
+ * Helper function that generates a unique-ish id.
2895
+ *
2896
+ * atow this is just `return (await getUUID()).slice(0, DEFAULT_TX_ID_LENGTH);`
2897
+ *
2898
+ * ## notes
2899
+ *
2900
+ * The thinking is that it only has to be a couple characters in length
2901
+ * because this is supposed to only be a unique id within the scope of
2902
+ * a tx which has its own tjp (gib) used as the id for the entire communication
2903
+ * saga.
2904
+ *
2905
+ * @returns txId
2906
+ */
2907
+ export async function getNewTxId({
2908
+ length,
2909
+ }: {
2910
+ /**
2911
+ * length of txId
2912
+ *
2913
+ * @default DEFAULT_TX_ID_LENGTH
2914
+ */
2915
+ length?: number,
2916
+ } = { length: DEFAULT_TX_ID_LENGTH }): Promise<TxId> {
2917
+ const lc = `[${getNewTxId.name}]`;
2918
+ try {
2919
+ length = length || DEFAULT_TX_ID_LENGTH;
2920
+ return (await getUUID()).slice(0, length) as TxId;
2921
+ } catch (error) {
2922
+ console.error(`${lc} ${error.message}`);
2923
+ throw error;
2924
+ }
2925
+ }
2926
+
2927
+ /**
2928
+ * wrapper for dealing with a space.
2929
+ *
2930
+ * convenience function for creating an arg ibgib to send to the given space
2931
+ * using Cmd/CmdModifiers for getting latest addrs.
2932
+ *
2933
+ * @returns space result ibgib from the given `space.witness` call.
2934
+ */
2935
+ export async function getLatestAddrs({
2936
+ ibGibs,
2937
+ addrs,
2938
+ tjps,
2939
+ tjpAddrs,
2940
+ space,
2941
+ }: {
2942
+ ibGibs?: IbGib_V1[],
2943
+ addrs?: IbGibAddr[],
2944
+ tjps?: IbGib_V1[],
2945
+ tjpAddrs?: IbGibAddr[],
2946
+ space: IbGibSpaceAny,
2947
+ }): Promise<IbGibSpaceResultIbGib<IbGib_V1, IbGibSpaceResultData, IbGibSpaceResultRel8ns>> {
2948
+ let lc = `[${getLatestAddrs.name}]`;
2949
+ try {
2950
+ if (logalot) { console.log(`${lc} starting...`); }
2951
+
2952
+ if (!space) { throw new Error(`space required. (E: 4d188d6c863246f28aa575753a052304)`); }
2953
+
2954
+ // so we don't have to do a bunch of conditional checks all over
2955
+ ibGibs = ibGibs ?? []; addrs = addrs ?? [];
2956
+ tjps = tjps ?? []; tjpAddrs = tjpAddrs ?? [];
2957
+
2958
+ if (
2959
+ addrs.length === 0 && ibGibs.length === 0 &&
2960
+ tjps.length === 0 && tjpAddrs.length === 0
2961
+ ) {
2962
+ throw new Error(`Either addrs, ibGibs, tjps, or tjpAddrs required. (E: 7c6ebfbab98d4d21a431b144457fd991)`);
2963
+ }
2964
+
2965
+ /**
2966
+ * Addrs that we'll ultimately send to the space. They start off as
2967
+ * tjpAddrs (and derived from `tjps` if any), and then add the incoming
2968
+ * ibgibs/addrs if we don't already have their corresponding tjpAddrs
2969
+ * being queried.
2970
+ * */
2971
+ const addrsToQuery = new Set<IbGibAddr>(
2972
+ tjpAddrs.concat(tjps.map(ibGib => getIbGibAddr({ ibGib })))
2973
+ );
2974
+
2975
+ // add ibgibs/addrs only if they do not already have their corresponding
2976
+ // tjpAddr in the query addrs. We can do this by checking if the tjpGib
2977
+ // is located in the ibGibAddr.gib, which has the form
2978
+ // [punctiliarHash].[tjpGib]
2979
+ const tjpGibs = Array.from(addrsToQuery).map(x => getIbAndGib({ ibGibAddr: x }).gib);
2980
+ ibGibs.map(ibGib => getIbGibAddr({ ibGib }))
2981
+ .concat(addrs)
2982
+ .forEach(ibGibAddr => {
2983
+ const { gib } = getIbAndGib({ ibGibAddr });
2984
+ const addrHasExistingTjpGib =
2985
+ tjpGibs.some(tjpGib => gib.includes(tjpGib));
2986
+ if (!addrHasExistingTjpGib) { addrsToQuery.add(ibGibAddr); }
2987
+ });
2988
+
2989
+ if (logalot) { console.log(`${lc}[testing] ${space.data?.name || space.ib} (${space.data?.uuid || '[space.data.uuid falsy]'}) addrsToQuery: ${Array.from(addrsToQuery)} (I: 8a2202912c364238b6d6eb09577bc246)`); }
2990
+
2991
+ // construct the arg and execute
2992
+ const argGet = await space.argy({
2993
+ ibMetadata: getSpaceArgMetadata({ space }),
2994
+ argData: {
2995
+ cmd: 'get',
2996
+ cmdModifiers: ['latest', 'addrs'],
2997
+ ibGibAddrs: Array.from(addrsToQuery),
2998
+ },
2999
+ });
3000
+ return await space.witness(argGet);
3001
+ } catch (error) {
3002
+ console.error(`${lc} ${error.message}`);
3003
+ throw error;
3004
+ }
3005
+ }
3006
+
3007
+ /**
3008
+ * I'm making this because i want to archive on a special tags index ibgib.
3009
+ * But we can't allow archiving on any old special ibgib, like the 'latest' index.
3010
+ * so this checks to see if it's on the forbidden list (winging that atm), and if it
3011
+ * is then this throws.
3012
+ *
3013
+ * use this function inside of commands that act on ibgibs that are special.
3014
+ */
3015
+ export function throwIfContextIsSpecial({
3016
+ ibGib_Context,
3017
+ }: {
3018
+ ibGib_Context: IbGib_V1,
3019
+ }): void {
3020
+ const lc = `[${throwIfContextIsSpecial.name}]`;
3021
+ try {
3022
+ if (logalot) { console.log(`${lc} starting... (I: 5f583fd94d27731a65d514e731b8aa22)`); }
3023
+
3024
+ if (!isSpecial({ ibGib: ibGib_Context })) { return; /* <<<< returns early */; }
3025
+
3026
+ const FORBIDDEN_MANUAL_SPECIAL_TYPES: SpecialIbGibType[] = [
3027
+ 'latest', 'roots', 'autosyncs'
3028
+ ];
3029
+ if (FORBIDDEN_MANUAL_SPECIAL_TYPES.some(x => ibGib_Context.ib.includes(x))) {
3030
+ throw new Error(`cannot perform a modification of this type on this special ibgib (E: b8fb718a7323fc54454464b973412722)`);
3031
+ }
3032
+ } catch (error) {
3033
+ console.error(`${lc} ${error.message}`);
3034
+ throw error;
3035
+ } finally {
3036
+ if (logalot) { console.log(`${lc} complete.`); }
3037
+ }
3038
+ }
3039
+
3040
+ export async function trash({
3041
+ ibGib_Context,
3042
+ rel8nName_Context,
3043
+ addr,
3044
+ space,
3045
+ zeroSpace,
3046
+ fnUpdateBootstrap,
3047
+ fnBroadcast,
3048
+ }: {
3049
+ ibGib_Context: IbGib_V1,
3050
+ rel8nName_Context: string,
3051
+ addr: IbGibAddr,
3052
+ space: IbGibSpaceAny,
3053
+ zeroSpace: IbGibSpaceAny,
3054
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
3055
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
3056
+ }): Promise<void> {
3057
+ const lc = `[${trash.name}]`;
3058
+ try {
3059
+ if (logalot) { console.log(`${lc} starting... (I: 2dc486bb2d516e4534f437aaf5ec7f22)`); }
3060
+ if (!ibGib_Context) { throw new Error(`ibGib_Context required (E: 75f7bfa93145d6dffe85d488443ca722)`); }
3061
+ if (!rel8nName_Context) { throw new Error(`rel8nName_Context required (E: 12aaa43de9e34b68b25dc9a2a68ad6b9)`); }
3062
+ if (!addr) { throw new Error(`addr required (E: e27df3bdc5a2554697cc9597afc4e422)`); }
3063
+ if (!space) { throw new Error(`space required (E: 2e3562486ed2956a770ed9e8d77a3f22)`); }
3064
+
3065
+ const contextIsSpecialIbGib = isSpecial({ ibGib: ibGib_Context });
3066
+ if (contextIsSpecialIbGib) { throwIfContextIsSpecial({ ibGib_Context }); }
3067
+
3068
+ const resNewContext = await V1.rel8({
3069
+ src: ibGib_Context,
3070
+ rel8nsToAddByAddr: { [TRASH_REL8N_NAME]: [addr] },
3071
+ rel8nsToRemoveByAddr: { [rel8nName_Context]: [addr] },
3072
+ dna: true,
3073
+ nCounter: true,
3074
+ });
3075
+
3076
+ await persistTransformResult({ resTransform: resNewContext, isMeta: contextIsSpecialIbGib, space });
3077
+
3078
+ if (contextIsSpecialIbGib) {
3079
+ const newSpecialAddr = getIbGibAddr({ ibGib: resNewContext.newIbGib });
3080
+ const specialType = getSpecialTypeFromIb({ ib: ibGib_Context.ib });
3081
+ const configKey = getSpecialConfigKey({ type: specialType });
3082
+ await setConfigAddr({ key: configKey, addr: newSpecialAddr, space, zeroSpace, fnUpdateBootstrap });
3083
+ }
3084
+
3085
+ // if (fnBroadcast && fnUpdateBootstrap) {
3086
+ await registerNewIbGib({
3087
+ ibGib: resNewContext.newIbGib,
3088
+ fnBroadcast,
3089
+ fnUpdateBootstrap,
3090
+ zeroSpace,
3091
+ space,
3092
+ });
3093
+ // };
3094
+ } catch (error) {
3095
+ console.error(`${lc} ${error.message}`);
3096
+ throw error;
3097
+ } finally {
3098
+ if (logalot) { console.log(`${lc} complete.`); }
3099
+ }
3100
+ }
3101
+
3102
+
3103
+ export async function archive({
3104
+ ibGib_Context,
3105
+ rel8nName_Context,
3106
+ addr,
3107
+ space,
3108
+ zeroSpace,
3109
+ fnUpdateBootstrap,
3110
+ fnBroadcast,
3111
+ }: {
3112
+ ibGib_Context: IbGib_V1,
3113
+ rel8nName_Context: string,
3114
+ addr: IbGibAddr,
3115
+ space: IbGibSpaceAny,
3116
+ zeroSpace: IbGibSpaceAny,
3117
+ fnUpdateBootstrap?: (newSpace: IbGibSpaceAny) => Promise<void>,
3118
+ fnBroadcast?: (info: IbGibTimelineUpdateInfo) => void,
3119
+ }): Promise<void> {
3120
+ const lc = `[${archive.name}]`;
3121
+ try {
3122
+ if (logalot) { console.log(`${lc} starting... (I: 38098552b830495187299bb24fcddff0)`); }
3123
+ if (!ibGib_Context) { throw new Error(`ibGib_Context required (E: d819e8c4db5b4c0bb721300ba434cd40)`); }
3124
+ if (!rel8nName_Context) { throw new Error(`rel8nName_Context required (E: de061adff8c04429a211aa09116a532d)`); }
3125
+ if (!addr) { throw new Error(`addr required (E: 7059ebb8ef6149ea94e22f961d6b5c81)`); }
3126
+ if (!space) { throw new Error(`space required (E: e19566f2d42347798621447edcae312e)`); }
3127
+
3128
+ const contextIsSpecialIbGib = isSpecial({ ibGib: ibGib_Context });
3129
+ if (contextIsSpecialIbGib) { throwIfContextIsSpecial({ ibGib_Context }); }
3130
+
3131
+ const resNewContext = await V1.rel8({
3132
+ src: ibGib_Context,
3133
+ rel8nsToAddByAddr: { [ARCHIVE_REL8N_NAME]: [addr] },
3134
+ rel8nsToRemoveByAddr: { [rel8nName_Context]: [addr] },
3135
+ dna: true,
3136
+ nCounter: true,
3137
+ });
3138
+
3139
+ await persistTransformResult({ resTransform: resNewContext, isMeta: contextIsSpecialIbGib, space });
3140
+
3141
+ if (contextIsSpecialIbGib) {
3142
+ const newSpecialAddr = getIbGibAddr({ ibGib: resNewContext.newIbGib });
3143
+ const specialType = getSpecialTypeFromIb({ ib: ibGib_Context.ib });
3144
+ const configKey = getSpecialConfigKey({ type: specialType });
3145
+ await setConfigAddr({ key: configKey, addr: newSpecialAddr, space, zeroSpace, fnUpdateBootstrap });
3146
+ }
3147
+
3148
+ // if (fnBroadcast && fnUpdateBootstrap) {
3149
+ await registerNewIbGib({
3150
+ ibGib: resNewContext.newIbGib,
3151
+ fnBroadcast,
3152
+ fnUpdateBootstrap,
3153
+ zeroSpace,
3154
+ space,
3155
+ });
3156
+ // } else {
3157
+ // console.warn(`${lc} fnBroadcast or fnUpdateBootstrap falsy, so we can't register `)
3158
+ // };
3159
+ } catch (error) {
3160
+ console.error(`${lc} ${error.message}`);
3161
+ throw error;
3162
+ } finally {
3163
+ if (logalot) { console.log(`${lc} complete.`); }
3164
+ }
3165
+ }
3166
+
3167
+ export function spaceNameIsValid(name: string): boolean {
3168
+ const lc = `[${spaceNameIsValid.name}]`;
3169
+ try {
3170
+ // non-falsy
3171
+ if (!name) {
3172
+ console.error(`${lc} name is falsy`)
3173
+ return false;
3174
+ }
3175
+
3176
+ // valid characters are alphanumerics, numbers, underscores, hyphens
3177
+ const regexOnlyIncluded = /[\w-]+/;
3178
+ const matchOnlyIncluded = name.match(regexOnlyIncluded);
3179
+ if (matchOnlyIncluded?.length !== 1 || matchOnlyIncluded[0].length !== name.length) {
3180
+ console.error(`${lc} name can only contain letters, numbers, underscores, hyphens`);
3181
+ return false;
3182
+ }
3183
+
3184
+ // start with alphanumeric
3185
+ const regexStart = /[a-zA-Z\d]/;
3186
+ const matchStart = name[0].match(regexStart);
3187
+ if (!matchStart) {
3188
+ console.error(`${lc} name must start with a letter or number`);
3189
+ return false;
3190
+ }
3191
+
3192
+ return true;
3193
+ } catch (error) {
3194
+ console.error(`${lc} ${error.message}`);
3195
+ return false;
3196
+ }
3197
+ }
3198
+
3199
+ export async function validateBootstrapIbGib(bootstrapSpace: IbGib_V1): Promise<boolean> {
3200
+ const lc = `[${validateBootstrapIbGib.name}]`;
3201
+ const errors: string[] = [];
3202
+ try {
3203
+ const addr = getIbGibAddr({ ibGib: bootstrapSpace });
3204
+ if (addr !== BOOTSTRAP_IBGIB_ADDR) {
3205
+ errors.push(`invalid bootstrapSpace addr. Should equal "${BOOTSTRAP_IBGIB_ADDR}" (E: ecfdbed719284db7a1aa3f867f706fe9)`);
3206
+ }
3207
+ let intrinsicErrors = await validateIbGibIntrinsically({ ibGib: bootstrapSpace });
3208
+ if (intrinsicErrors?.length ?? -1 > 0) { intrinsicErrors!.forEach(e => errors.push(e)); }
3209
+
3210
+ const data = (bootstrapSpace.data as BootstrapData);
3211
+ const rel8ns = (bootstrapSpace.rel8ns as BootstrapRel8ns);
3212
+
3213
+ if (Object.keys(data || {}).length === 0) {
3214
+ errors.push(`invalid bootstrapSpace data. data required (E: 5a9bd15dd0644f9b93cafbbba660cfdf)`);
3215
+ }
3216
+ if ((data.spaceIds ?? []).length === 0) {
3217
+ errors.push(`invalid bootstrapSpace, data.spaceIds required. (E: 6b91ddc12cfd41e59ded7d7502c1909f)`);
3218
+ }
3219
+ if (Object.keys(bootstrapSpace.rel8ns || {}).length === 0) {
3220
+ errors.push(`invalid bootstrapSpace rel8ns (empty). Should have at least one rel8n, with rel8nName corresponding to the spaceId (E: b188ce4ae25e49f794f35e141bc2ecde)`);
3221
+ }
3222
+ data.spaceIds.forEach((spaceId: string) => {
3223
+ if ((rel8ns[spaceId] ?? []).length === 0) {
3224
+ errors.push(`invalid bootstrap. Each spaceId listed in data should have a corresponding address in rel8ns. spaceId (${spaceId}) missing. (E: 62dd0d76e29a415a98b4b27deb8db17e)`);
3225
+ }
3226
+ });
3227
+ if (!data[BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY]) {
3228
+ errors.push(`invalid bootstrap. data.${BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY} required. (E: f763af2e275f445cbf1db5801bacafad)`);
3229
+ } else if ((rel8ns[data[BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY]] ?? []).length === 0) {
3230
+ errors.push(`invalid bootstrap. data.${BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY} (${data[BOOTSTRAP_DATA_DEFAULT_SPACE_ID_KEY]}) not found in rel8ns. (E: 44d0799d232f4a51a0b0019ebebe019f)`);
3231
+ }
3232
+ if (errors.length === 0) {
3233
+ return true;
3234
+ } else {
3235
+ console.error(`${lc} errors: ${errors.join('|')}`);
3236
+ return false;
3237
+ }
3238
+ } catch (error) {
3239
+ console.error(`${lc} ${error.message}`);
3240
+ return false;
3241
+ }
3242
+ }