@superbee/core 0.2.0-pre.3 → 0.2.0-pre.30

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 (534) hide show
  1. package/README.md +1082 -0
  2. package/dist/artifact-contract.d.ts +90 -0
  3. package/dist/artifact-contract.d.ts.map +1 -0
  4. package/dist/artifact-contract.js +205 -0
  5. package/dist/artifact-contract.js.map +1 -0
  6. package/dist/backend.d.ts +19 -1
  7. package/dist/backend.d.ts.map +1 -1
  8. package/dist/backend.js +83 -36
  9. package/dist/backend.js.map +1 -1
  10. package/dist/blob-contract.d.ts +80 -0
  11. package/dist/blob-contract.d.ts.map +1 -0
  12. package/dist/blob-contract.js +131 -0
  13. package/dist/blob-contract.js.map +1 -0
  14. package/dist/blob-journal.d.ts +302 -0
  15. package/dist/blob-journal.d.ts.map +1 -0
  16. package/dist/blob-journal.js +540 -0
  17. package/dist/blob-journal.js.map +1 -0
  18. package/dist/body-replace-guards.d.ts +52 -0
  19. package/dist/body-replace-guards.d.ts.map +1 -0
  20. package/dist/body-replace-guards.js +176 -0
  21. package/dist/body-replace-guards.js.map +1 -0
  22. package/dist/builtin-recipes/content.d.ts +93 -0
  23. package/dist/builtin-recipes/content.d.ts.map +1 -0
  24. package/dist/builtin-recipes/content.js +288 -0
  25. package/dist/builtin-recipes/content.js.map +1 -0
  26. package/dist/builtin-recipes.d.ts +10 -0
  27. package/dist/builtin-recipes.d.ts.map +1 -0
  28. package/dist/builtin-recipes.js +97 -0
  29. package/dist/builtin-recipes.js.map +1 -0
  30. package/dist/bundle-health/index.d.ts +14 -0
  31. package/dist/bundle-health/index.d.ts.map +1 -0
  32. package/dist/bundle-health/index.js +12 -0
  33. package/dist/bundle-health/index.js.map +1 -0
  34. package/dist/bundle-health/kind-findings.d.ts +44 -0
  35. package/dist/bundle-health/kind-findings.d.ts.map +1 -0
  36. package/dist/bundle-health/kind-findings.js +68 -0
  37. package/dist/bundle-health/kind-findings.js.map +1 -0
  38. package/dist/bundle-health/legacy-page.d.ts +39 -0
  39. package/dist/bundle-health/legacy-page.d.ts.map +1 -0
  40. package/dist/bundle-health/legacy-page.js +50 -0
  41. package/dist/bundle-health/legacy-page.js.map +1 -0
  42. package/dist/bundle-health/report.d.ts +202 -0
  43. package/dist/bundle-health/report.d.ts.map +1 -0
  44. package/dist/bundle-health/report.js +438 -0
  45. package/dist/bundle-health/report.js.map +1 -0
  46. package/dist/bundle-ops.d.ts +1 -1
  47. package/dist/bundle-ops.d.ts.map +1 -1
  48. package/dist/bundle-ops.js +2 -2
  49. package/dist/bundle-ops.js.map +1 -1
  50. package/dist/bundle.d.ts +2 -1
  51. package/dist/bundle.d.ts.map +1 -1
  52. package/dist/bundle.js +3 -3
  53. package/dist/bundle.js.map +1 -1
  54. package/dist/code-point-order.d.ts +8 -0
  55. package/dist/code-point-order.d.ts.map +1 -0
  56. package/dist/code-point-order.js +20 -0
  57. package/dist/code-point-order.js.map +1 -0
  58. package/dist/connector-request.d.ts +87 -0
  59. package/dist/connector-request.d.ts.map +1 -0
  60. package/dist/connector-request.js +370 -0
  61. package/dist/connector-request.js.map +1 -0
  62. package/dist/connectors.d.ts +262 -0
  63. package/dist/connectors.d.ts.map +1 -0
  64. package/dist/connectors.js +1179 -0
  65. package/dist/connectors.js.map +1 -0
  66. package/dist/cross-bundle.d.ts +135 -0
  67. package/dist/cross-bundle.d.ts.map +1 -0
  68. package/dist/cross-bundle.js +186 -0
  69. package/dist/cross-bundle.js.map +1 -0
  70. package/dist/dependencies.d.ts +94 -0
  71. package/dist/dependencies.d.ts.map +1 -0
  72. package/dist/dependencies.js +180 -0
  73. package/dist/dependencies.js.map +1 -0
  74. package/dist/document-codec.d.ts +5 -1
  75. package/dist/document-codec.d.ts.map +1 -1
  76. package/dist/document-codec.js +7 -4
  77. package/dist/document-codec.js.map +1 -1
  78. package/dist/document-field-actions.d.ts +31 -1
  79. package/dist/document-field-actions.d.ts.map +1 -1
  80. package/dist/document-field-actions.js +137 -34
  81. package/dist/document-field-actions.js.map +1 -1
  82. package/dist/document-mutation.d.ts +22 -10
  83. package/dist/document-mutation.d.ts.map +1 -1
  84. package/dist/document-mutation.js +61 -27
  85. package/dist/document-mutation.js.map +1 -1
  86. package/dist/document-write-policy.d.ts.map +1 -1
  87. package/dist/document-write-policy.js +4 -2
  88. package/dist/document-write-policy.js.map +1 -1
  89. package/dist/domain-model.d.ts +73 -0
  90. package/dist/domain-model.d.ts.map +1 -0
  91. package/dist/domain-model.js +169 -0
  92. package/dist/domain-model.js.map +1 -0
  93. package/dist/engine.d.ts +16 -8
  94. package/dist/engine.d.ts.map +1 -1
  95. package/dist/engine.js +42 -4
  96. package/dist/engine.js.map +1 -1
  97. package/dist/file-journaled-backend-internal.d.ts +21 -0
  98. package/dist/file-journaled-backend-internal.d.ts.map +1 -0
  99. package/dist/file-journaled-backend-internal.js +12 -0
  100. package/dist/file-journaled-backend-internal.js.map +1 -0
  101. package/dist/file-journaled-backend.d.ts +160 -0
  102. package/dist/file-journaled-backend.d.ts.map +1 -0
  103. package/dist/file-journaled-backend.js +1345 -0
  104. package/dist/file-journaled-backend.js.map +1 -0
  105. package/dist/filesystem-host.d.ts +15 -0
  106. package/dist/filesystem-host.d.ts.map +1 -0
  107. package/dist/filesystem-host.js +20 -0
  108. package/dist/filesystem-host.js.map +1 -0
  109. package/dist/filesystem-identity.d.ts +86 -4
  110. package/dist/filesystem-identity.d.ts.map +1 -1
  111. package/dist/filesystem-identity.js +338 -102
  112. package/dist/filesystem-identity.js.map +1 -1
  113. package/dist/filesystem-lock.d.ts +16 -3
  114. package/dist/filesystem-lock.d.ts.map +1 -1
  115. package/dist/filesystem-lock.js +323 -77
  116. package/dist/filesystem-lock.js.map +1 -1
  117. package/dist/filesystem-push-role.d.ts +77 -0
  118. package/dist/filesystem-push-role.d.ts.map +1 -0
  119. package/dist/filesystem-push-role.js +165 -0
  120. package/dist/filesystem-push-role.js.map +1 -0
  121. package/dist/filesystem.d.ts +17 -0
  122. package/dist/filesystem.d.ts.map +1 -0
  123. package/dist/filesystem.js +18 -0
  124. package/dist/filesystem.js.map +1 -0
  125. package/dist/governed-body-write.d.ts +106 -0
  126. package/dist/governed-body-write.d.ts.map +1 -0
  127. package/dist/governed-body-write.js +251 -0
  128. package/dist/governed-body-write.js.map +1 -0
  129. package/dist/host-policy-snapshot.d.ts +9 -0
  130. package/dist/host-policy-snapshot.d.ts.map +1 -0
  131. package/dist/host-policy-snapshot.js +33 -0
  132. package/dist/host-policy-snapshot.js.map +1 -0
  133. package/dist/host-text.d.ts +31 -0
  134. package/dist/host-text.d.ts.map +1 -0
  135. package/dist/host-text.js +77 -0
  136. package/dist/host-text.js.map +1 -0
  137. package/dist/hosted-client/binding.d.ts +7 -0
  138. package/dist/hosted-client/binding.d.ts.map +1 -0
  139. package/dist/hosted-client/binding.js +10 -0
  140. package/dist/hosted-client/binding.js.map +1 -0
  141. package/dist/hosted-client/blob-delivery.d.ts +67 -0
  142. package/dist/hosted-client/blob-delivery.d.ts.map +1 -0
  143. package/dist/hosted-client/blob-delivery.js +169 -0
  144. package/dist/hosted-client/blob-delivery.js.map +1 -0
  145. package/dist/hosted-client/blob-write.d.ts +386 -0
  146. package/dist/hosted-client/blob-write.d.ts.map +1 -0
  147. package/dist/hosted-client/blob-write.js +770 -0
  148. package/dist/hosted-client/blob-write.js.map +1 -0
  149. package/dist/hosted-client/bundle-listing.d.ts +46 -0
  150. package/dist/hosted-client/bundle-listing.d.ts.map +1 -0
  151. package/dist/hosted-client/bundle-listing.js +36 -0
  152. package/dist/hosted-client/bundle-listing.js.map +1 -0
  153. package/dist/hosted-client/client.d.ts +219 -0
  154. package/dist/hosted-client/client.d.ts.map +1 -0
  155. package/dist/hosted-client/client.js +309 -0
  156. package/dist/hosted-client/client.js.map +1 -0
  157. package/dist/hosted-client/directory.d.ts +46 -0
  158. package/dist/hosted-client/directory.d.ts.map +1 -0
  159. package/dist/hosted-client/directory.js +74 -0
  160. package/dist/hosted-client/directory.js.map +1 -0
  161. package/dist/hosted-client/errors.d.ts +26 -0
  162. package/dist/hosted-client/errors.d.ts.map +1 -0
  163. package/dist/hosted-client/errors.js +35 -0
  164. package/dist/hosted-client/errors.js.map +1 -0
  165. package/dist/hosted-client/inbox.d.ts +320 -0
  166. package/dist/hosted-client/inbox.d.ts.map +1 -0
  167. package/dist/hosted-client/inbox.js +379 -0
  168. package/dist/hosted-client/inbox.js.map +1 -0
  169. package/dist/hosted-client/index.d.ts +30 -0
  170. package/dist/hosted-client/index.d.ts.map +1 -0
  171. package/dist/hosted-client/index.js +32 -0
  172. package/dist/hosted-client/index.js.map +1 -0
  173. package/dist/hosted-client/journal.d.ts +103 -0
  174. package/dist/hosted-client/journal.d.ts.map +1 -0
  175. package/dist/hosted-client/journal.js +464 -0
  176. package/dist/hosted-client/journal.js.map +1 -0
  177. package/dist/hosted-client/node.d.ts +74 -0
  178. package/dist/hosted-client/node.d.ts.map +1 -0
  179. package/dist/hosted-client/node.js +219 -0
  180. package/dist/hosted-client/node.js.map +1 -0
  181. package/dist/hosted-client/reference.d.ts +12 -0
  182. package/dist/hosted-client/reference.d.ts.map +1 -0
  183. package/dist/hosted-client/reference.js +29 -0
  184. package/dist/hosted-client/reference.js.map +1 -0
  185. package/dist/hosted-client/write.d.ts +217 -0
  186. package/dist/hosted-client/write.d.ts.map +1 -0
  187. package/dist/hosted-client/write.js +418 -0
  188. package/dist/hosted-client/write.js.map +1 -0
  189. package/dist/hosted-transport/answer-rows.d.ts +243 -0
  190. package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
  191. package/dist/hosted-transport/answer-rows.js +454 -0
  192. package/dist/hosted-transport/answer-rows.js.map +1 -0
  193. package/dist/hosted-transport/blob-reads.d.ts +35 -0
  194. package/dist/hosted-transport/blob-reads.d.ts.map +1 -0
  195. package/dist/hosted-transport/blob-reads.js +179 -0
  196. package/dist/hosted-transport/blob-reads.js.map +1 -0
  197. package/dist/hosted-transport/blob-write.d.ts +34 -0
  198. package/dist/hosted-transport/blob-write.d.ts.map +1 -0
  199. package/dist/hosted-transport/blob-write.js +115 -0
  200. package/dist/hosted-transport/blob-write.js.map +1 -0
  201. package/dist/hosted-transport/carrier.d.ts +151 -0
  202. package/dist/hosted-transport/carrier.d.ts.map +1 -0
  203. package/dist/hosted-transport/carrier.js +260 -0
  204. package/dist/hosted-transport/carrier.js.map +1 -0
  205. package/dist/hosted-transport/history-pages.d.ts +55 -0
  206. package/dist/hosted-transport/history-pages.d.ts.map +1 -0
  207. package/dist/hosted-transport/history-pages.js +69 -0
  208. package/dist/hosted-transport/history-pages.js.map +1 -0
  209. package/dist/hosted-transport/index.d.ts +18 -0
  210. package/dist/hosted-transport/index.d.ts.map +1 -0
  211. package/dist/hosted-transport/index.js +18 -0
  212. package/dist/hosted-transport/index.js.map +1 -0
  213. package/dist/hosted-transport/operations.d.ts +85 -0
  214. package/dist/hosted-transport/operations.d.ts.map +1 -0
  215. package/dist/hosted-transport/operations.js +128 -0
  216. package/dist/hosted-transport/operations.js.map +1 -0
  217. package/dist/hosted-transport/paged-reads.d.ts +87 -0
  218. package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
  219. package/dist/hosted-transport/paged-reads.js +238 -0
  220. package/dist/hosted-transport/paged-reads.js.map +1 -0
  221. package/dist/hosted-transport/read-adapter.d.ts +256 -0
  222. package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
  223. package/dist/hosted-transport/read-adapter.js +568 -0
  224. package/dist/hosted-transport/read-adapter.js.map +1 -0
  225. package/dist/hosted-transport/root-write.d.ts +114 -0
  226. package/dist/hosted-transport/root-write.d.ts.map +1 -0
  227. package/dist/hosted-transport/root-write.js +171 -0
  228. package/dist/hosted-transport/root-write.js.map +1 -0
  229. package/dist/hosted-transport/whole-document-transport.d.ts +204 -0
  230. package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
  231. package/dist/hosted-transport/whole-document-transport.js +447 -0
  232. package/dist/hosted-transport/whole-document-transport.js.map +1 -0
  233. package/dist/index.d.ts +34 -10
  234. package/dist/index.d.ts.map +1 -1
  235. package/dist/index.js +26 -8
  236. package/dist/index.js.map +1 -1
  237. package/dist/indexeddb-backend.d.ts +28 -37
  238. package/dist/indexeddb-backend.d.ts.map +1 -1
  239. package/dist/indexeddb-backend.js +467 -113
  240. package/dist/indexeddb-backend.js.map +1 -1
  241. package/dist/journaled-backend.d.ts +232 -15
  242. package/dist/journaled-backend.d.ts.map +1 -1
  243. package/dist/journaled-backend.js +254 -55
  244. package/dist/journaled-backend.js.map +1 -1
  245. package/dist/kind-authoring.d.ts +170 -0
  246. package/dist/kind-authoring.d.ts.map +1 -0
  247. package/dist/kind-authoring.js +451 -0
  248. package/dist/kind-authoring.js.map +1 -0
  249. package/dist/kind-conformance-error.d.ts +15 -0
  250. package/dist/kind-conformance-error.d.ts.map +1 -0
  251. package/dist/kind-conformance-error.js +20 -0
  252. package/dist/kind-conformance-error.js.map +1 -0
  253. package/dist/kind-coverage.d.ts +99 -0
  254. package/dist/kind-coverage.d.ts.map +1 -0
  255. package/dist/kind-coverage.js +186 -0
  256. package/dist/kind-coverage.js.map +1 -0
  257. package/dist/kind-field-mutation.d.ts +4 -0
  258. package/dist/kind-field-mutation.d.ts.map +1 -1
  259. package/dist/kind-field-mutation.js +28 -3
  260. package/dist/kind-field-mutation.js.map +1 -1
  261. package/dist/kinds-load.js +4 -4
  262. package/dist/kinds-load.js.map +1 -1
  263. package/dist/kinds.d.ts +65 -2
  264. package/dist/kinds.d.ts.map +1 -1
  265. package/dist/kinds.js +341 -21
  266. package/dist/kinds.js.map +1 -1
  267. package/dist/link-types.d.ts +26 -0
  268. package/dist/link-types.d.ts.map +1 -0
  269. package/dist/link-types.js +57 -0
  270. package/dist/link-types.js.map +1 -0
  271. package/dist/links.d.ts +21 -3
  272. package/dist/links.d.ts.map +1 -1
  273. package/dist/links.js +213 -14
  274. package/dist/links.js.map +1 -1
  275. package/dist/managed-document-metadata.d.ts +2 -0
  276. package/dist/managed-document-metadata.d.ts.map +1 -0
  277. package/dist/managed-document-metadata.js +9 -0
  278. package/dist/managed-document-metadata.js.map +1 -0
  279. package/dist/memory-backend.d.ts.map +1 -1
  280. package/dist/memory-backend.js +6 -2
  281. package/dist/memory-backend.js.map +1 -1
  282. package/dist/paths.d.ts +16 -0
  283. package/dist/paths.d.ts.map +1 -1
  284. package/dist/paths.js +25 -0
  285. package/dist/paths.js.map +1 -1
  286. package/dist/platform.d.ts +13 -2
  287. package/dist/platform.d.ts.map +1 -1
  288. package/dist/platform.js +16 -0
  289. package/dist/platform.js.map +1 -1
  290. package/dist/portable-export.d.ts +119 -0
  291. package/dist/portable-export.d.ts.map +1 -0
  292. package/dist/portable-export.js +501 -0
  293. package/dist/portable-export.js.map +1 -0
  294. package/dist/publication-filesystem.d.ts +1 -1
  295. package/dist/publication-filesystem.d.ts.map +1 -1
  296. package/dist/publication-filesystem.js +1 -1
  297. package/dist/publication-filesystem.js.map +1 -1
  298. package/dist/qualified-links.d.ts +67 -0
  299. package/dist/qualified-links.d.ts.map +1 -0
  300. package/dist/qualified-links.js +117 -0
  301. package/dist/qualified-links.js.map +1 -0
  302. package/dist/query-filter.d.ts +16 -2
  303. package/dist/query-filter.d.ts.map +1 -1
  304. package/dist/query-filter.js +40 -1
  305. package/dist/query-filter.js.map +1 -1
  306. package/dist/query-order.d.ts +22 -0
  307. package/dist/query-order.d.ts.map +1 -0
  308. package/dist/query-order.js +42 -0
  309. package/dist/query-order.js.map +1 -0
  310. package/dist/recipe-lifecycle/errors.d.ts +96 -0
  311. package/dist/recipe-lifecycle/errors.d.ts.map +1 -0
  312. package/dist/recipe-lifecycle/errors.js +122 -0
  313. package/dist/recipe-lifecycle/errors.js.map +1 -0
  314. package/dist/recipe-lifecycle/evolution.d.ts +112 -0
  315. package/dist/recipe-lifecycle/evolution.d.ts.map +1 -0
  316. package/dist/recipe-lifecycle/evolution.js +941 -0
  317. package/dist/recipe-lifecycle/evolution.js.map +1 -0
  318. package/dist/recipe-lifecycle/index.d.ts +30 -0
  319. package/dist/recipe-lifecycle/index.d.ts.map +1 -0
  320. package/dist/recipe-lifecycle/index.js +26 -0
  321. package/dist/recipe-lifecycle/index.js.map +1 -0
  322. package/dist/recipe-lifecycle/install.d.ts +155 -0
  323. package/dist/recipe-lifecycle/install.d.ts.map +1 -0
  324. package/dist/recipe-lifecycle/install.js +594 -0
  325. package/dist/recipe-lifecycle/install.js.map +1 -0
  326. package/dist/recipe-lifecycle/legacy-aliases.d.ts +16 -0
  327. package/dist/recipe-lifecycle/legacy-aliases.d.ts.map +1 -0
  328. package/dist/recipe-lifecycle/legacy-aliases.js +51 -0
  329. package/dist/recipe-lifecycle/legacy-aliases.js.map +1 -0
  330. package/dist/recipe-lifecycle/stamp.d.ts +151 -0
  331. package/dist/recipe-lifecycle/stamp.d.ts.map +1 -0
  332. package/dist/recipe-lifecycle/stamp.js +364 -0
  333. package/dist/recipe-lifecycle/stamp.js.map +1 -0
  334. package/dist/recipes.d.ts +29 -1
  335. package/dist/recipes.d.ts.map +1 -1
  336. package/dist/recipes.js +137 -1
  337. package/dist/recipes.js.map +1 -1
  338. package/dist/record-overlay/apply.d.ts +43 -0
  339. package/dist/record-overlay/apply.d.ts.map +1 -0
  340. package/dist/record-overlay/apply.js +527 -0
  341. package/dist/record-overlay/apply.js.map +1 -0
  342. package/dist/record-overlay/canonical.d.ts +22 -0
  343. package/dist/record-overlay/canonical.d.ts.map +1 -0
  344. package/dist/record-overlay/canonical.js +47 -0
  345. package/dist/record-overlay/canonical.js.map +1 -0
  346. package/dist/record-overlay/digest.d.ts +4 -0
  347. package/dist/record-overlay/digest.d.ts.map +1 -0
  348. package/dist/record-overlay/digest.js +11 -0
  349. package/dist/record-overlay/digest.js.map +1 -0
  350. package/dist/record-overlay/errors.d.ts +44 -0
  351. package/dist/record-overlay/errors.d.ts.map +1 -0
  352. package/dist/record-overlay/errors.js +55 -0
  353. package/dist/record-overlay/errors.js.map +1 -0
  354. package/dist/record-overlay/findings.d.ts +5 -0
  355. package/dist/record-overlay/findings.d.ts.map +1 -0
  356. package/dist/record-overlay/findings.js +83 -0
  357. package/dist/record-overlay/findings.js.map +1 -0
  358. package/dist/record-overlay/index.d.ts +23 -0
  359. package/dist/record-overlay/index.d.ts.map +1 -0
  360. package/dist/record-overlay/index.js +21 -0
  361. package/dist/record-overlay/index.js.map +1 -0
  362. package/dist/record-overlay/plan.d.ts +12 -0
  363. package/dist/record-overlay/plan.d.ts.map +1 -0
  364. package/dist/record-overlay/plan.js +285 -0
  365. package/dist/record-overlay/plan.js.map +1 -0
  366. package/dist/record-overlay/policy.d.ts +69 -0
  367. package/dist/record-overlay/policy.d.ts.map +1 -0
  368. package/dist/record-overlay/policy.js +228 -0
  369. package/dist/record-overlay/policy.js.map +1 -0
  370. package/dist/record-overlay/redact.d.ts +3 -0
  371. package/dist/record-overlay/redact.d.ts.map +1 -0
  372. package/dist/record-overlay/redact.js +27 -0
  373. package/dist/record-overlay/redact.js.map +1 -0
  374. package/dist/record-overlay/signal-kind.d.ts +30 -0
  375. package/dist/record-overlay/signal-kind.d.ts.map +1 -0
  376. package/dist/record-overlay/signal-kind.js +38 -0
  377. package/dist/record-overlay/signal-kind.js.map +1 -0
  378. package/dist/record-overlay/signal-text.d.ts +45 -0
  379. package/dist/record-overlay/signal-text.d.ts.map +1 -0
  380. package/dist/record-overlay/signal-text.js +116 -0
  381. package/dist/record-overlay/signal-text.js.map +1 -0
  382. package/dist/record-overlay/source-index.d.ts +19 -0
  383. package/dist/record-overlay/source-index.d.ts.map +1 -0
  384. package/dist/record-overlay/source-index.js +102 -0
  385. package/dist/record-overlay/source-index.js.map +1 -0
  386. package/dist/record-overlay/types.d.ts +291 -0
  387. package/dist/record-overlay/types.d.ts.map +1 -0
  388. package/dist/record-overlay/types.js +2 -0
  389. package/dist/record-overlay/types.js.map +1 -0
  390. package/dist/remote-backend.d.ts +67 -67
  391. package/dist/remote-backend.d.ts.map +1 -1
  392. package/dist/remote-backend.js +168 -201
  393. package/dist/remote-backend.js.map +1 -1
  394. package/dist/remote-document-codec.d.ts +2 -1
  395. package/dist/remote-document-codec.d.ts.map +1 -1
  396. package/dist/remote-document-codec.js +3 -2
  397. package/dist/remote-document-codec.js.map +1 -1
  398. package/dist/remote-error.d.ts +38 -0
  399. package/dist/remote-error.d.ts.map +1 -0
  400. package/dist/remote-error.js +59 -0
  401. package/dist/remote-error.js.map +1 -0
  402. package/dist/remote-operations.d.ts +0 -40
  403. package/dist/remote-operations.d.ts.map +1 -1
  404. package/dist/remote-operations.js +26 -6
  405. package/dist/remote-operations.js.map +1 -1
  406. package/dist/remote-parsers.d.ts +86 -0
  407. package/dist/remote-parsers.d.ts.map +1 -0
  408. package/dist/remote-parsers.js +181 -0
  409. package/dist/remote-parsers.js.map +1 -0
  410. package/dist/remote.d.ts +5 -2
  411. package/dist/remote.d.ts.map +1 -1
  412. package/dist/remote.js +5 -1
  413. package/dist/remote.js.map +1 -1
  414. package/dist/search/backend-source.d.ts +6 -0
  415. package/dist/search/backend-source.d.ts.map +1 -0
  416. package/dist/search/backend-source.js +33 -0
  417. package/dist/search/backend-source.js.map +1 -0
  418. package/dist/search/contract.d.ts +151 -0
  419. package/dist/search/contract.d.ts.map +1 -0
  420. package/dist/search/contract.js +78 -0
  421. package/dist/search/contract.js.map +1 -0
  422. package/dist/search/errors.d.ts +30 -0
  423. package/dist/search/errors.d.ts.map +1 -0
  424. package/dist/search/errors.js +50 -0
  425. package/dist/search/errors.js.map +1 -0
  426. package/dist/search/identity.d.ts +12 -0
  427. package/dist/search/identity.d.ts.map +1 -0
  428. package/dist/search/identity.js +90 -0
  429. package/dist/search/identity.js.map +1 -0
  430. package/dist/search/index.d.ts +16 -0
  431. package/dist/search/index.d.ts.map +1 -0
  432. package/dist/search/index.js +15 -0
  433. package/dist/search/index.js.map +1 -0
  434. package/dist/search/memory-source.d.ts +36 -0
  435. package/dist/search/memory-source.d.ts.map +1 -0
  436. package/dist/search/memory-source.js +235 -0
  437. package/dist/search/memory-source.js.map +1 -0
  438. package/dist/search/rank.d.ts +34 -0
  439. package/dist/search/rank.d.ts.map +1 -0
  440. package/dist/search/rank.js +129 -0
  441. package/dist/search/rank.js.map +1 -0
  442. package/dist/search/row-text.d.ts +38 -0
  443. package/dist/search/row-text.d.ts.map +1 -0
  444. package/dist/search/row-text.js +88 -0
  445. package/dist/search/row-text.js.map +1 -0
  446. package/dist/search/search.d.ts +28 -0
  447. package/dist/search/search.d.ts.map +1 -0
  448. package/dist/search/search.js +257 -0
  449. package/dist/search/search.js.map +1 -0
  450. package/dist/search/source.d.ts +67 -0
  451. package/dist/search/source.d.ts.map +1 -0
  452. package/dist/search/source.js +2 -0
  453. package/dist/search/source.js.map +1 -0
  454. package/dist/search/text.d.ts +47 -0
  455. package/dist/search/text.d.ts.map +1 -0
  456. package/dist/search/text.js +150 -0
  457. package/dist/search/text.js.map +1 -0
  458. package/dist/site-capabilities.d.ts +345 -0
  459. package/dist/site-capabilities.d.ts.map +1 -0
  460. package/dist/site-capabilities.js +724 -0
  461. package/dist/site-capabilities.js.map +1 -0
  462. package/dist/source-engine/guard.d.ts +78 -0
  463. package/dist/source-engine/guard.d.ts.map +1 -0
  464. package/dist/source-engine/guard.js +423 -0
  465. package/dist/source-engine/guard.js.map +1 -0
  466. package/dist/source-engine/index.d.ts +26 -0
  467. package/dist/source-engine/index.d.ts.map +1 -0
  468. package/dist/source-engine/index.js +21 -0
  469. package/dist/source-engine/index.js.map +1 -0
  470. package/dist/source-engine/machine-state.d.ts +74 -0
  471. package/dist/source-engine/machine-state.d.ts.map +1 -0
  472. package/dist/source-engine/machine-state.js +48 -0
  473. package/dist/source-engine/machine-state.js.map +1 -0
  474. package/dist/source-engine/observe.d.ts +18 -0
  475. package/dist/source-engine/observe.d.ts.map +1 -0
  476. package/dist/source-engine/observe.js +620 -0
  477. package/dist/source-engine/observe.js.map +1 -0
  478. package/dist/source-engine/overlay.d.ts +49 -0
  479. package/dist/source-engine/overlay.d.ts.map +1 -0
  480. package/dist/source-engine/overlay.js +114 -0
  481. package/dist/source-engine/overlay.js.map +1 -0
  482. package/dist/source-engine/robots.d.ts +53 -0
  483. package/dist/source-engine/robots.d.ts.map +1 -0
  484. package/dist/source-engine/robots.js +158 -0
  485. package/dist/source-engine/robots.js.map +1 -0
  486. package/dist/source-engine/types.d.ts +287 -0
  487. package/dist/source-engine/types.d.ts.map +1 -0
  488. package/dist/source-engine/types.js +2 -0
  489. package/dist/source-engine/types.js.map +1 -0
  490. package/dist/source-harness/conformance.d.ts +16 -0
  491. package/dist/source-harness/conformance.d.ts.map +1 -0
  492. package/dist/source-harness/conformance.js +266 -0
  493. package/dist/source-harness/conformance.js.map +1 -0
  494. package/dist/source-harness/fixture.d.ts +95 -0
  495. package/dist/source-harness/fixture.d.ts.map +1 -0
  496. package/dist/source-harness/fixture.js +273 -0
  497. package/dist/source-harness/fixture.js.map +1 -0
  498. package/dist/source-harness/index.d.ts +22 -0
  499. package/dist/source-harness/index.d.ts.map +1 -0
  500. package/dist/source-harness/index.js +19 -0
  501. package/dist/source-harness/index.js.map +1 -0
  502. package/dist/source-harness/peer.d.ts +74 -0
  503. package/dist/source-harness/peer.d.ts.map +1 -0
  504. package/dist/source-harness/peer.js +106 -0
  505. package/dist/source-harness/peer.js.map +1 -0
  506. package/dist/source-profiles/index.d.ts +31 -0
  507. package/dist/source-profiles/index.d.ts.map +1 -0
  508. package/dist/source-profiles/index.js +206 -0
  509. package/dist/source-profiles/index.js.map +1 -0
  510. package/dist/source-profiles/link-header.d.ts +6 -0
  511. package/dist/source-profiles/link-header.d.ts.map +1 -0
  512. package/dist/source-profiles/link-header.js +131 -0
  513. package/dist/source-profiles/link-header.js.map +1 -0
  514. package/dist/storage.d.ts +6 -2
  515. package/dist/storage.d.ts.map +1 -1
  516. package/dist/storage.js +4 -1
  517. package/dist/storage.js.map +1 -1
  518. package/dist/types.d.ts +38 -5
  519. package/dist/types.d.ts.map +1 -1
  520. package/dist/types.js +0 -10
  521. package/dist/types.js.map +1 -1
  522. package/dist/uncertain-write.d.ts +161 -19
  523. package/dist/uncertain-write.d.ts.map +1 -1
  524. package/dist/uncertain-write.js +90 -31
  525. package/dist/uncertain-write.js.map +1 -1
  526. package/dist/versioning.d.ts +9 -4
  527. package/dist/versioning.d.ts.map +1 -1
  528. package/dist/versioning.js +9 -4
  529. package/dist/versioning.js.map +1 -1
  530. package/dist/write-identity.d.ts +5 -0
  531. package/dist/write-identity.d.ts.map +1 -0
  532. package/dist/write-identity.js +9 -0
  533. package/dist/write-identity.js.map +1 -0
  534. package/package.json +103 -2
package/README.md ADDED
@@ -0,0 +1,1082 @@
1
+ # @superbee/core
2
+
3
+ The Superbee knowledge engine reads and writes Open Knowledge Format bundles, with versioned
4
+ writes, cross-links and storage backends. It is a library; installing it does not install the
5
+ `superbee` executable.
6
+
7
+ Core and `@superbee/server` are released together, independently of the `superbee` CLI. A CLI
8
+ release bundles the engine source selected at build time; it does not load `@superbee/core@latest`.
9
+
10
+ ## Document presentation values
11
+
12
+ The additive `@superbee/core/artifact-contract` subpath provides pure v1 document target,
13
+ observation, request, receipt and capability decoders. See the [contract reference](https://github.com/Holaxis-ai/superbee/blob/main/packages/core/ARTIFACT-CONTRACT.md)
14
+ for identity, provenance, lifecycle and host responsibilities.
15
+
16
+ ## Site capabilities
17
+
18
+ The additive `@superbee/core/site-capabilities` subpath is the contract for read-only tools a
19
+ site or a recipe pack provides (`SiteCapability`), and the one owner of their composition rules:
20
+ `composeSiteCapabilities` refuses duplicate names, `superbee_` names outside the generic tier and
21
+ names outside a pack's or site's namespace, orders generic, pack and site tools deterministically,
22
+ applies a surface's exclusions after those checks, enforces size limits and, with
23
+ `requireReadOnly`, refuses any capability not declared read-only. It returns frozen snapshots and
24
+ an identity digest for the surface's list. `toProviderTool`, `toPortalAdditionalTool`,
25
+ `toToolDescriptor` and `siteCapabilityFromPortalTool` are the explicit mappings to the existing
26
+ tool shapes. `checkSiteCapability` and `siteCapabilityConformanceCases` test a capability against
27
+ recorded fixtures (read-only declaration, dated results, closed error codes that never echo input,
28
+ bounded and deterministic output, same-origin URLs, citations of records the result contains, and
29
+ refusal of unknown arguments). `siteRecordSource` mints a `site-record` citation only for a path
30
+ the host admits, bound to the publication's digests.
31
+
32
+ Annotations such as `readOnlyHint` and `untrustedContentHint` are self-declared and enforce
33
+ nothing at runtime; the checks above test recorded fixtures, not the code. Treat them as a control
34
+ only for in-process code from the same reviewed deploy. The module has no Node built-in import, so
35
+ it runs unchanged in a Worker or a browser.
36
+
37
+ ## Mutation attribution and local history
38
+
39
+ `mutateDocument` and `prepareDocumentMutationCandidate` persist a supplied `actor` by default:
40
+ `actor` in OKF v0.1, `superbee_updated_by` in v0.2 (plus `actor` when the Kind requires it).
41
+ Use `persistActor: false` to keep candidate attribution unchanged, or `persistActor: true` to apply
42
+ the attribution policy even without a supplied actor. Supplying the actor option alone leaves an
43
+ otherwise unchanged candidate a no-op, preserving its original attribution and bytes. In v0.1,
44
+ deliberate edits to the candidate's legacy `actor` field still count as authored changes in `patch`
45
+ and `replace-document` modes; overwrite retains its automatic attribution comparison. The pure preview
46
+ prepares patches. This default changes library writes that previously
47
+ required `persistActor: true`; callers needing the old opt-in behavior can explicitly pass false.
48
+
49
+ `producer` controls `generated.by` independently of the version mutator. Local filesystem history
50
+ returns only the current revision, including for Git-tracked bundles. It reads mutation attribution
51
+ from `superbee_updated_by`, legacy `updated_by`, or `actor`; when absent it returns the stable string
52
+ `unattributed`. It does not infer an identity from the reader's environment or the content producer.
53
+ History-keeping backends retain their existing per-write attribution.
54
+
55
+ ## Filesystem platform migration
56
+
57
+ The Windows extraction changes the default Node.js filesystem backend. Its built-in host policy
58
+ supports macOS and Linux. On native Windows or another unsupported host, filesystem construction
59
+ requires an explicit `FilesystemHostPolicy`; there is no automatic Windows fallback.
60
+
61
+ | Entry point | Default on macOS/Linux | On other hosts |
62
+ | --- | --- | --- |
63
+ | `new FilesystemBackend(root)` | Uses the built-in policy | Pass `{ hostPolicy }` as the second argument |
64
+ | `initBundle(root, options)` | Uses the built-in policy | Pass `{ hostPolicy }` as the third argument |
65
+ | `createFilesystemRuntime()` | Captures the built-in policy | Pass a host policy as its argument |
66
+ | `withFilesystemMutationLock(target, body, options)` | Uses the built-in policy | Include `hostPolicy` in `options` |
67
+ | `filesystemMutationLockPath(target, portableRoot)` | Uses the built-in policy | Pass a host policy as the third argument |
68
+
69
+ Without that policy, these filesystem entry points throw `InvalidInputError` on unsupported hosts;
70
+ backend construction and bundle initialization refuse before creating or modifying the target.
71
+ The package has no npm `os` restriction: storage contracts and non-filesystem backends remain
72
+ available independently of the default filesystem policy. Choose a backend and imports appropriate
73
+ to your runtime; installing the library alone does not establish native filesystem support.
74
+
75
+ There is no Windows adapter on npm to install. The experimental first-party adapters are built
76
+ from source in a separate repository, https://github.com/Holaxis-ai/superbee-windows-cli.
77
+ Supplying a policy is an integration contract for an adapter owner, not a promise that arbitrary
78
+ policies make Windows supported.
79
+
80
+ ## Keep the backend returned by initBundle
81
+
82
+ `initBundle` now returns `{ root, backend }`. Pass that returned bundle to engine operations so they
83
+ retain the backend and host policy selected during initialization:
84
+
85
+ ```ts
86
+ import { initBundle, readDoc, writeDoc } from '@superbee/core';
87
+
88
+ const bundle = await initBundle('./knowledge'); // Default filesystem: macOS/Linux.
89
+ await writeDoc(bundle, {
90
+ id: 'notes/example',
91
+ frontmatter: { type: 'Note', title: 'Example' },
92
+ body: 'Shared knowledge.\n',
93
+ });
94
+ const document = await readDoc(bundle, 'notes/example');
95
+ ```
96
+
97
+ Reading `bundle.root` still works. Code that assumes the return value has exactly one property,
98
+ serializes the whole return value, or uses backend presence as a synonym for a remote bundle must
99
+ be updated. A filesystem bundle now has a backend too. Serialize only the fields your application
100
+ intends to persist; do not reconstruct `{ root: bundle.root }` when continuing engine operations
101
+ that need the selected backend.
102
+
103
+ ## Integrating an explicit filesystem policy
104
+
105
+ `@superbee/core/filesystem` exports `createFilesystemRuntime`, `FilesystemBackend`, and the
106
+ `FilesystemHostPolicy` type. An application that owns its host adapter can bind it once:
107
+
108
+ ```ts
109
+ import { createFilesystemRuntime, type FilesystemHostPolicy } from '@superbee/core/filesystem';
110
+
111
+ export async function openFilesystemBundle(root: string, hostPolicy: FilesystemHostPolicy) {
112
+ const filesystem = createFilesystemRuntime(hostPolicy);
113
+ return filesystem.initBundle(root);
114
+ }
115
+ ```
116
+
117
+ The resulting runtime supplies `backend`, `initBundle`, `withMutationLock`, and `mutationLockPath`
118
+ with the same captured policy. Direct backend construction uses
119
+ `new FilesystemBackend(root, { hostPolicy })`. Lock helpers are also exported from `@superbee/core`.
120
+
121
+ A policy supplies the lock-parent directory, owner-namespace key, private-mode enforcement choice,
122
+ and host error classifications. Core still owns lock ownership, version checks, retry bounds and
123
+ cleanup. Policies are structural snapshots: public data and methods are captured, while callbacks
124
+ may continue to observe live operating-system facts. Do not use opaque private fields or methods
125
+ pre-bound to a mutable receiver.
126
+
127
+ ## Recipe lifecycle
128
+
129
+ `@superbee/core/recipe-lifecycle` installs and evolves a parsed recipe on any bundle. The caller
130
+ acquires the recipe (`parseRecipeFiles` from `@superbee/core/recipes` turns files into a
131
+ `LoadedRecipe`) and presents the results; the `superbee` CLI is one such caller. It uses no Node
132
+ globals or builtins, so it runs in Node, a browser or a Worker over any storage backend.
133
+
134
+ ```ts
135
+ import { MemoryBackend } from "@superbee/core/memory-backend";
136
+ import { parseRecipeFiles } from "@superbee/core/recipes";
137
+ import { applyRecipeEvolution, installRecipe, planRecipeEvolution } from "@superbee/core/recipe-lifecycle";
138
+
139
+ const bundle = { root: "memory://glossary", backend: new MemoryBackend() };
140
+ const v1 = parseRecipeFiles(glossaryV1Files, "memory:glossary@1");
141
+ if (!v1.ok) throw new Error(v1.error.message);
142
+ await installRecipe(bundle, v1.recipe);
143
+
144
+ const v2 = parseRecipeFiles(glossaryV2Files, "memory:glossary@2");
145
+ if (!v2.ok) throw new Error(v2.error.message);
146
+ const plan = await planRecipeEvolution(bundle, v2.recipe);
147
+ if (plan.ready && plan.writes > 0) {
148
+ await applyRecipeEvolution(bundle, v2.recipe, plan.plan_token, { actor: "process:glossary-sync" });
149
+ }
150
+ ```
151
+
152
+ `prepareRecipeInstall(recipe, { okfVersion, now })` prepares those same edition-specific candidates
153
+ without a backend or target reads. Retain the explicit `now` across preview and replay; v0.1 uses
154
+ it for the existing install timestamp, while v0.2 adds no legacy clock. The returned `identity`
155
+ contains the source digest taken before materialization; Convention, Connector and Reference
156
+ candidates already carry their recipe stamps. Preparation does not mutate the parsed source.
157
+
158
+ `inspectRecipeInstallTarget(existingOrNull, candidate, prepared)` separately reports existence,
159
+ source-content equality, recipe stamp identity and stamp integrity. For a matching installed
160
+ artifact, require `sameContent`, `stamp: "current"` and `stampIntegrity: "current"`; an identity match
161
+ alone does not establish readable digest rules or an intact definition stamp. Neither function
162
+ installs, adopts stamps, validates the target registry or grants write authority. An admitted host
163
+ can use the candidates with its existing canonical writer after its own coherent target and
164
+ compatibility checks; preparation is not an approved-preview transaction or a new apply API.
165
+
166
+ `installRecipe` is create-only: each convention, Connector, View pair and Reference is written with
167
+ an expect-absent compare-and-swap, an existing convention or Connector is left untouched (its row
168
+ reports `source_differs` when it differs from the recipe), and a differing View or Reference target
169
+ is refused before anything is written. Before any write it checks each Connector against the
170
+ authoring rules of the bundle's edition (`RecipeInputError` when OKF v0.2 would refuse it) and its
171
+ sources against the Kinds the bundle will hold (its conventions plus the recipe's own), throwing
172
+ `ConnectorTargetUnsatisfied` when one does not fit. `planRecipeEvolution` is read-only: it merges each recipe
173
+ convention into the installed one additively, rechecks existing instances, and returns blockers
174
+ and a `plan_token` bound to everything it read. `applyRecipeEvolution` recomputes the plan,
175
+ refuses a different token, and writes each changed convention at exactly the version the plan
176
+ saw. The bundle has no cross-document transaction, so a failure part way reports the definitions
177
+ written and those still pending. A Connector has no additive merge: the plan replaces it whole,
178
+ after the conventions, and only when this recipe's stamp holds and its definition digest shows
179
+ nobody changed it; an edited, unstamped-and-different or foreign Connector is a blocker and is
180
+ never written, stamp included.
181
+
182
+ Every convention and Connector either path writes carries an `x_recipe` stamp in the same write:
183
+ the recipe's id, version, declared convention count (`of`), `recipeSourceDigest` of the recipe,
184
+ the `definitionDigest` of the document as written, and the `digest_rules` version. A recipe that
185
+ carries connectors adds `connectors`, its declared Connector count, which readers that predate it
186
+ ignore; a recipe without connectors writes and digests exactly as before. A plan's `changed` is
187
+ about content only; its `writes` also counts definitions whose stamp is stale or missing, so a
188
+ ready plan with `writes > 0` has work to apply. `installedRecipes(docs, known?)` reads each
189
+ recipe's state (`installed`, `partial`, `diverged`, `unknown`) from the stamps alone, without the
190
+ recipe source; give it the conventions and the Connectors (`installedConventionDocs` and
191
+ `installedConnectorDocs`).
192
+
193
+ Failures are typed subclasses of `RecipeLifecycleError`: `RecipeInputError` (the recipe cannot
194
+ target this bundle's edition), `ConnectorTargetUnsatisfied` (`recipe`, `connector`, `source`,
195
+ `kind`, `code`, `fields`), `RecipeAssetConflict` (`recipe`, `key`), `RecipeEditionChanged`,
196
+ `RecipeEvolutionPlanChanged` (`expected`, `current`), `RecipeEvolutionBlocked` (`blockers`),
197
+ `RecipeEvolutionInterrupted` (`reason`, `completed`, `pending`, `cause`) and
198
+ `RecipeEvolutionPostcondition` (`reason`, `completed`).
199
+
200
+ ## Record overlay
201
+
202
+ `@superbee/core/record-overlay` is the write half of binding documents to external records. A
203
+ Source Feed policy names the fields a feed owns; the caller fetches and diffs its records and hands
204
+ the changes to the planner. It uses no Node globals or builtins, so it runs in Node, a browser or a
205
+ Worker over any storage backend.
206
+
207
+ ```ts
208
+ import { MemoryBackend } from "@superbee/core/memory-backend";
209
+ import { buildKindRegistry } from "@superbee/core/kinds";
210
+ import { applyRecordOverlay, buildSourceIndex, planRecordOverlay, readOverlayPolicy } from "@superbee/core/record-overlay";
211
+
212
+ const bundle = { root: "memory://guide", backend: new MemoryBackend() };
213
+ const registry = buildKindRegistry(conventionDocs);
214
+ const guide = { bundle, registry, okfVersion: "0.2" as const };
215
+ const policy = readOverlayPolicy(sourceFeedDoc, { targetKind: registry.kinds.get("Event") });
216
+ const overlays = await buildSourceIndex(bundle, { type: policy.target_kind });
217
+ const plan = await planRecordOverlay({ content: guide, ops: guide, policy, changes, observedAt, overlays });
218
+ const receipt = await applyRecordOverlay(plan, { content: guide, ops: guide, policy, actor: "process:calendar-sync", overlays });
219
+ ```
220
+
221
+ For each owned field the plan compares the last accepted feed value, the new feed value and the
222
+ document's value: a document that already says the new value is left alone, one that still says
223
+ the old value is written, and anything else is an `OVERLAY_DOC_CONFLICT` with a Signal, so an
224
+ editor's override is never overwritten. Each step carries the version it was planned against.
225
+ `applyRecordOverlay` writes every step through `mutateDocument` with a guard core builds itself and
226
+ runs on the fresh read: an owned-field write reaches documents of the policy's target Kind only,
227
+ may change only owned and derived fields, never the body, and needs the record's `sources`
228
+ identity; a Signal may be created only as a new, unclaimed document of the attention Kind under its
229
+ path, and updated only in a fixed set of fields plus one appended observation line. Signal writes
230
+ are bound to the provider of the policy the caller passes, never to a field of the plan, and an id
231
+ held by another provider's Signal is refused. A Signal's text must be the planner's rendering of
232
+ its own fields and the run (its observation line, links to the plan's feed and to documents, the
233
+ adoption hint): every plan the planner makes passes, and other text is a held `INVALID_INPUT` row,
234
+ so a mismatch is visible and planned again rather than dropped. Nothing of the
235
+ target Kind is created and nothing is deleted. A feed never owns its target's work state: the
236
+ progress storage fields and the target Kind's claim fields are refused like `status`. The
237
+ attention Kind (default `Signal`) must declare a claim owner and state field and the fields
238
+ `change_kind`, `observed_at`, `provider`, `external_id` and `diff_summary`.
239
+
240
+ Site rules are hooks with safe defaults: `textGate` (identity), `interval` (none, so a disappeared
241
+ record is always raised), `correlate`, `sourceProperties` (an upsert of the record's `sources`
242
+ entry in the same write, OKF v0.2 only), `assertCandidate` (site candidate checks after the core
243
+ guards) and `deriveFields` (fields such as `updated_at` written in the same version). Refusals are
244
+ receipt rows with a `REFUSAL_CODES` code; `RETRYABLE` and `HOLD_STORE` say which leave a record to be
245
+ planned again. A value core's write policy refuses (an OKF v0.2 field type, for example) is a held
246
+ `INVALID_INPUT` row, so one bad record never stops the rest of a run. A malformed plan (an unknown
247
+ step, a non-canonical target, written values that are not JSON data with finite numbers, prototype
248
+ keys, non-scalar or identity `sources` properties), actor or bundle edition throws `OverlayRefusal`
249
+ before any write, and so does a plan with owned-field steps whose policy names no declared target
250
+ Kind or whose owned or derived list names a field a feed can never own.
251
+
252
+ `SourceIndex` holds one head read for a whole run. It is bounded to that run: an edited or deleted
253
+ overlay is still caught at write time, but a second document given an identity during the run is
254
+ only refused as ambiguous on the next run.
255
+
256
+ ## Connectors
257
+
258
+ `@superbee/core/connectors` parses Connector declarations: `type: Connector` documents under
259
+ `connectors/` that say what an integration is, independent of any one target Kind. A declaration
260
+ holds no authority and nothing in the subpath fetches, executes or receives. It uses no Node globals
261
+ or builtins.
262
+
263
+ ```ts
264
+ import { bindSourceFeed, changeBetween, connectorPinDigest, findingsFor, mapRecord, parseConnectorDoc } from "@superbee/core/connectors";
265
+
266
+ const parsed = parseConnectorDoc(connectorDoc, { kinds: registry });
267
+ if (!parsed.ok) throw new Error(parsed.errors.map((e) => `${e.code} ${e.path}`).join("; "));
268
+ const pin = connectorPinDigest(parsed.connector);
269
+ const bound = bindSourceFeed(parsed.connector, sourceFeedDoc, registry, { okfVersion: "0.2" });
270
+ const mapped = mapRecord(bound.source, normalizedRecord, bound.kind);
271
+ if (mapped.kind === "mapped") {
272
+ const change = changeBetween(lastAcceptedAfter, mapped, { external_id: mapped.external_id, change_kind: "changed" });
273
+ }
274
+ const findings = findingsFor(mapped.diagnostics, { source: bound.source.name });
275
+ ```
276
+
277
+ `auth.kind` is `none`, `token`, `github-app`, `oauth2` or `signed-webhook`. A credential's hosts
278
+ come only from core: `token` and `oauth2` name a row of `AUTH_PROVIDERS`, `github-app` is fixed,
279
+ and `credentialHosts` is the one reader. `domains` lists hosts for keyless fetches and may not
280
+ repeat a credential host. The sources live under `record_sources` (OKF v0.2 reserves `sources` for
281
+ provenance, and `CONNECTOR_DOCUMENT_KEYS` lists every top-level key a Connector adds, none of them
282
+ an OKF field); the parsed `Connector.sources` holds them. Each source names a catalog `profile`, a
283
+ `target_kind`, mode `overlay`, an identity template (`identity.resource`, an https URL whose
284
+ placeholders are feed parameters, and `identity.id` over record paths), `completeness`, and
285
+ `fields`: target field to a dotted record
286
+ path, or to `{ from, values, unmapped }`. Mapping keys are the most a Source Feed may own; never-owned
287
+ fields, managed metadata, work state, the body and list-typed fields are refused. Actions and events
288
+ are declared shapes only. Every nested block is closed by `CONNECTOR_KEYS`; `parseConnectorDoc`
289
+ returns every refusal with a typed `code` and a JSON pointer `path`. A token shape or URL userinfo
290
+ anywhere in a frontmatter string, key or the body, or a credential key name outside the data-name
291
+ maps, refuses the document alone, naming a path and never the value; so does a YAML alias, which
292
+ keeps every reader bounded by the document's size. The secret rule is a tripwire; the pin, the
293
+ credential store and the fetcher are the boundaries.
294
+
295
+ `connectorPinDigest` covers every key `CONNECTOR_KEYS` accepts, with defaults filled and order-free
296
+ lists sorted, plus the credential hosts core resolves the auth kind to, so a host joining an
297
+ `AUTH_PROVIDERS` row changes the pin. It is blind to the title, description, version, body and
298
+ unknown top-level keys. `CONNECTOR_PIN_RULES` changes whenever a default or sort rule does.
299
+
300
+ `bindSourceFeed` binds a Source Feed (`connector`, `source`, `url`, `owned_fields`) to one source:
301
+ the `url` must equal the identity template rendered from parameters of `[A-Za-z0-9._~-]+` byte for
302
+ byte, `owned_fields` must be mapping keys, and the source's `target_kind`, `completeness` and
303
+ `profile` win. `okfVersion` is the content bundle's edition (absent means v0.1); a feed owning a
304
+ Kind-declared list field binds only on v0.2. Each refusal is an `OverlayRefusal` with
305
+ `POLICY_INVALID`, the feed and the key.
306
+ `mapRecord` never lets a mapping gap erase a stored value: an absent path is unobserved, an
307
+ unmapped native is kept (`unmapped: keep`, the default) or refuses the record while it still counts
308
+ as present (`refuse`), and only an empty value (`null` or blank text) clears an optional field; on a
309
+ required field or `title` it is kept with a finding. An id template separates its placeholders with
310
+ literal text, and a record whose value holds that text, is blank or makes an id past 2048
311
+ characters is refused as `IDENTITY_MISSING` or `IDENTITY_INVALID`, so two records never share one id. `changeBetween` takes the last accepted `after`, carries
312
+ unobserved fields and leaves them out of `changed_fields`. `findingsFor` groups diagnostics per
313
+ source, field, code and value (a list, mapping or empty value by its shape) for `planFindings`, so
314
+ one problem across many records raises one Signal.
315
+
316
+ A recipe carries Connectors through its manifest's `connectors:` list (`parseRecipeFiles` reads
317
+ each with `parseConnectorDoc`, checking the sources that target the recipe's own Kinds, and refuses
318
+ a malformed one as `RECIPE_CONNECTOR_INVALID`); `@superbee/core/recipe-lifecycle` installs, stamps
319
+ and evolves them as definitions.
320
+
321
+ The public [superbee-plugins recipes](https://github.com/Holaxis-ai/superbee-plugins/tree/main/recipes)
322
+ own the `Source Feed`, `Signal` and Work Item definitions used by the GitHub integration. The generic
323
+ engine and synthetic conformance fixtures remain in this package.
324
+
325
+ ## Source engine
326
+
327
+ `@superbee/core/source-engine` is the portable half of the integration source engine: the one
328
+ loop that observes a bound Source Feed for an author's test, a local run or a managed runner. It
329
+ uses no Node globals or builtins and never calls `fetch`; the caller supplies the network as a
330
+ `Transport`.
331
+
332
+ ```ts
333
+ import { MemoryMachineState, fetchIdentity, observeSource } from "@superbee/core/source-engine";
334
+
335
+ const receipt = await observeSource({
336
+ connector, bound, profile, spec, transport, state: new MemoryMachineState(),
337
+ identity: fetchIdentity("Product/1.0 (+https://example.org/about)")!,
338
+ mode: "incremental", signal: AbortSignal.timeout(60_000),
339
+ commit: async (page) => {
340
+ const plan = await planRecordOverlay({ content, ops, policy: bound.policy, changes: page.changes, observedAt: page.observed_at, records: page.records });
341
+ await applyRecordOverlay(plan, { content, ops, policy: bound.policy, actor: "process:calendar-sync" });
342
+ },
343
+ });
344
+ ```
345
+
346
+ A `SourceProfile` (`id`, `version`, `kind`, `validateSpec`, `hostsFor`, `observe`) turns a bound
347
+ feed, a cursor and a transport into pages of normalized records: `{ records, nextCursor, more,
348
+ complete }`, all JSON data. It never opens a socket, never sees a credential and never writes. A
349
+ `Transport` answers `{ url, method, headers, body, maxBytes, credentialHost, signal }`; it never
350
+ follows a redirect and returns `{ ok: false, refused: "too_large" }` rather than a truncated body.
351
+ A credential-injecting transport adds its credential only when the engine's `credentialHost` is
352
+ true and its own exact match agrees: https, the default port, a host exactly one of the auth
353
+ kind's credential hosts.
354
+
355
+ The profile sees the engine's guarded transport, which checks every URL and every redirect hop:
356
+ https only, the lexical address rule (`refusedAddress`: no IP literal, local name, port or
357
+ userinfo), and a host within the profile's `hostsFor` and the connector's credential hosts plus
358
+ keyless `domains`. A `hostsFor` naming any other host refuses the run before a request. A redirect
359
+ never changes host when either side is a credential host. Keyless hosts are read through robots.txt
360
+ (RFC 9309, the declared identity's product token, the first 512 KiB) first, and a bot-mitigation
361
+ page from one is refused and never retried. Only `GET` is sent unless `rules.post` admits a POST
362
+ toward a credential host; a keyless host is GET only. A GET with a body, a POST redirect, and the
363
+ headers `authorization`, `cookie`, `proxy-authorization`, `host`, `user-agent` and the method
364
+ overrides are always refused; beyond `accept` a header needs `rules.header`, and the validators
365
+ `if-none-match` and `if-modified-since` go out in incremental mode only. The budget counts every
366
+ request, robots.txt and hops included. Each request ends after `requestTimeoutMs` (default 20
367
+ seconds). The deadline is the caller's `signal`; without one the run ends after `deadlineMs`
368
+ (default 5 minutes). The deadline bounds the profile's page too: a page still pending is discarded
369
+ and the run ends `deferred`. A commit or state write in progress is never abandoned, so a hung one
370
+ holds the run. Statuses map to outcomes: 401 and 403 `credential_refused` (no retry), 404 and
371
+ 410 `not_found`, 429 `deferred` with `retry_after`, others `provider_unavailable` (503 included,
372
+ with a retry); `statuses` moves a status between the sets. Any refusal or error status stops the
373
+ run, even when the profile catches the error, and the guarded transport is closed when the run
374
+ ends.
375
+
376
+ A profile runs in the engine's realm. The guard bounds what a declaration and a provider's data can
377
+ reach; profile code itself is bounded by the repository's static egress check
378
+ (`scripts/source-egress-check.mjs`) and by review, until profiles run in an isolate. The check
379
+ refuses globals outside a short list, imports outside an allowlist or the scanned roots, writes to
380
+ anything reached from a global, the prototype, property-definition and descriptor names however
381
+ they are read, `this` outside a class body and CommonJS; a key computed at run time is beyond any
382
+ static check.
383
+
384
+ Each page is mapped with `mapRecord`, diffed against the stored records into `RecordChange` rows
385
+ and handed to `commit`; the cursor and the records are stored only after `commit` resolves. A page
386
+ that promises more with no cursor, or with a cursor the run already asked for, fails the run as
387
+ `cursor_stuck`. `commit` may decline to write changes: a `retry` change (a lost compare-and-swap, a
388
+ claimed Signal) stays pending in the machine state and is planned again at the start of every run,
389
+ before anything is fetched, until it is written, refused for good, or superseded by a newer
390
+ observation of the record (its listing, or its absence from a complete pass); a replay keeps the
391
+ record's missing count, and a record that disappeared is not replayed. A replay whose commit throws
392
+ stays pending with a finding while the run reads on, and is dropped with a finding after
393
+ `MAX_REPLAY_FAILURES` (5). A page that supersedes pending changes stores their removal before its
394
+ commit, so a crash after the commit never replays an older change. A `hold` change is not pending,
395
+ and its record is planned again when it is next observed. Either way the cursor moves on, so one
396
+ record never stops a feed. An incremental
397
+ run continues the stored cursor (`resumed` when the last run left its pass open); a complete run
398
+ lists from the start, sends no validator, and stops on a not-modified answer. A pass is a complete
399
+ observation only when it ran in complete mode to its last page within the run, the profile claimed
400
+ it complete and the source is declared `complete`; only then is a listed record's absence counted,
401
+ and a record is `disappeared` after the feed's `missing_threshold` (default 2) of them. A cursor
402
+ from another profile id or version is never handed back. The receipt names the outcome, every
403
+ request, the pass, the stored cursor, the held and pending counts, the mapping diagnostics and the
404
+ findings for `planFindings`. `MachineState` is the port for the stored state, written with a
405
+ compare-and-swap; `MemoryMachineState` keeps it in memory.
406
+
407
+ ## Source harness
408
+
409
+ `@superbee/core/source-harness` is how a profile and a transport prove the engine's rules offline.
410
+ It returns rows and problem lists rather than asserting, so any test runner can use it, and it uses
411
+ no Node globals or builtins.
412
+
413
+ ```ts
414
+ import { peerTransport, runSourceFixture, runTransportConformance } from "@superbee/core/source-harness";
415
+
416
+ const { problems, missing } = await runSourceFixture(fixture, scenarios);
417
+ const rows = await runTransportConformance({ name: "mine", create: (peer, credential) => myTransport(peer, credential) });
418
+ ```
419
+
420
+ `runSourceScenario` replays a scenario's scripted provider answers (`exchanges`, each used once,
421
+ matched on method and URL; a `hang` answer aborts the run's deadline) through `observeSource`,
422
+ writing each committed page through record-overlay over a MemoryBackend bundle built from the
423
+ fixture's documents, with editor edits between runs. `checkScenario` holds the result to the rule
424
+ of its required scenario (`REQUIRED_SCENARIOS`: provider errors, an incomplete page, cursor resume,
425
+ a hostile cursor refused by the guard, disappearance only from complete observations, owned-field conflicts, unmapped
426
+ and empty values, a missing identity, a cross-host redirect and an undeclared host), and fails any
427
+ request to an undeclared host or one the fixture did not script; a name may add a variant
428
+ (`cursor.resume.page-number`). `missingScenarios` lists what a fixture lacks.
429
+
430
+ `runTransportConformance` is the suite every transport passes against a `ScriptedPeer`, the
431
+ network that records exactly what arrived: the credential reaches its host under its own header
432
+ name (a custom `accessToken` included) and no other host, lookalikes included (a longer or
433
+ shorter name, a trailing dot, userinfo, another port), nor its own host when the engine's
434
+ `credentialHost` is false; a redirect is returned unfollowed, a body
435
+ over the bound is refused while one at the bound is returned whole, and behind the engine a
436
+ redirect to another host or a private address reaches nothing. `peerTransport` is a memory
437
+ transport that passes it.
438
+
439
+ ## Bundle health
440
+
441
+ `@superbee/core/bundle-health` computes the health report behind `superbee status` from a core
442
+ `Bundle` on any backend: malformed documents, Kind lint and conformance debt, registry warnings,
443
+ unresolved links, orphans, freshness and invalid timestamps, trust tiers, the two graph lints, the
444
+ View surface, cross-bundle links, the OKF v0.1 upgrade finding and legacy naming. It uses no Node
445
+ globals or builtins, so it runs in Node, a browser or a Worker. The caller presents the report;
446
+ the CLI adds row caps, help text and its home and sync blocks.
447
+
448
+ ```ts
449
+ import { MemoryBackend } from "@superbee/core/memory-backend";
450
+ import { bundleHealth } from "@superbee/core/bundle-health";
451
+
452
+ const bundle = { root: "memory://guide", backend: new MemoryBackend() };
453
+ const report = await bundleHealth(bundle, { now: new Date() });
454
+ const gate = { malformed: report.malformed.length, kind_warnings: report.kindLint.length, unresolved_links: report.unresolvedLinks.length };
455
+ ```
456
+
457
+ The report reads only: one Kind registry load, one lenient query, two prefix-scoped blob listings
458
+ and one root-index read, with every finding derived in memory. A document whose frontmatter does
459
+ not parse is a `malformed` row (the root index first), never a thrown error. Top-level keys are
460
+ camelCase; rows keep snake_case keys (`age_ms`, `bundle_id`, `incompatible_values`) and arrive
461
+ whole, in report order, for the caller to cap. Sections whose presence depends on the bundle are
462
+ null when absent: `conformanceDebt` without any declared Kind, `trust` outside a non-empty v0.2
463
+ bundle, `crossBundle` without `docs/dependencies` or any `okf:` link, `okfUpgrade` outside a v0.1
464
+ bundle with a workflow-status collision. `crossLinkTypeViolations` is always an array, because a
465
+ local edge under a qualified declaration needs no dependencies. The stored values in
466
+ `invalidStaleAfter`, `invalidTimestamps` and `invalidLegacyTimestamps` are made JSON-safe;
467
+ `missingExpectedLinks[].status` is the raw stored value.
468
+
469
+ Nothing outside the bundle is read unless the caller binds it. `bindings` is a map of bound copies
470
+ by alias, or a hook core calls exactly once, after every read and with its own parse of
471
+ `docs/dependencies` (a self alias already dropped as `DEP_SELF`, using `selfBundleId` when
472
+ `docs/bundle` declares no id). Whatever the hook throws propagates unchanged. Each binding's
473
+ verdict comes from core's resolver, and `dependencies[].bound` is the binding's `label`.
474
+
475
+ `bundleKindFindings(documents, okfVersion, { actor, producer })` answers a different question:
476
+ which Kind problems a strict host write would refuse, preparing each document as its first edit,
477
+ so a timestamp or actor the write supplies is no finding. The CLI's publish preview runs it; the
478
+ report's `kindLint` stays advisory. Both use core's one validator, `validateAgainstKind`.
479
+
480
+ ## Search
481
+
482
+ `@superbee/core/search` is the one `documents.search.v1` engine: the request and result contract,
483
+ query normalization, ranking (`portal-webmcp-search/v1`), the any-term fallback, facets, cursors
484
+ and page budgets. A backend supplies what it searches through `SearchSource`: one `evidence()`
485
+ call per request and `text()` for the page's excerpts. `memorySearchSource` indexes a complete
486
+ corpus in memory. `searchSourceFromBackend(backend, options?)` reads a complete local
487
+ backend in bounded batches, applies the same memory-source bounds, and derives the corpus key
488
+ from the versions actually read. Validate the request before calling it and pass an opaque
489
+ bundle identity as the search scope. Failed reads refuse the whole corpus. The subpath uses
490
+ no Node globals or builtins.
491
+
492
+ ```ts
493
+ import { memorySearchSource, searchDocuments } from "@superbee/core/search";
494
+
495
+ const source = memorySearchSource(documents, { key: headsDigest });
496
+ const result = await searchDocuments(source, { query: "lock keeper", limit: 10 }, { scope: bundleId });
497
+ ```
498
+
499
+ `key` identifies the corpus; a cursor is refused for another key, scope, query, match or filters
500
+ (`type`, `tag`, `prefix`). A request outside the contract throws `SearchInputError`; a corpus over a
501
+ bound throws `SearchLimitError`; a request whose terms hold more than `maxEvidenceInstances` capped
502
+ instances (a `searchDocuments` option, at most `SEARCH_LIMITS.maxEvidenceInstances`) throws
503
+ `SearchBroadQueryError`, a `SearchLimitError` whose message asks for fewer or more specific terms.
504
+ A source must hold one document per id.
505
+ A document over 65,536 body terms is left out and counted: the result's `corpus` reports
506
+ `documents`, `indexed`, `skipped` and `reindexPending`. `searchRowText` is what one document
507
+ contributes to any index, and `textVersion()` names the algorithm, `SEARCH_TEXT_REVISION` and the
508
+ runtime's normalizer fingerprint, so a stored index can tell when its rows need re-deriving.
509
+
510
+ ## Hosted client
511
+
512
+ `@superbee/core/hosted-client` is the headless client of a hosted Superbee's sync routes
513
+ (`/sync/v1`). It is a supported public API under core's normal versioning; the `superbee` CLI uses
514
+ it. It runs wherever `fetch` does (Node, a browser or a Worker) and reads identity (`whoami`),
515
+ the bundle list (`bundles`, with `readBundleListing`), documents (`reader`), document history
516
+ (`history`) and operations run by id (`listOperations`, `runOperation`). `within(slug)` returns
517
+ the same client naming every bundle-scoped request's bundle as `<slug>/<bundle-id>`.
518
+
519
+ ```ts
520
+ import { bearerCredentials, createHostedClient } from "@superbee/core/hosted-client";
521
+
522
+ const client = createHostedClient({
523
+ target: { origin: "https://mcp.getsuperbee.com", syncPrefix: "/sync/v1" },
524
+ credentials: bearerCredentials(() => getToken()),
525
+ });
526
+ const identity = await client.whoami();
527
+ ```
528
+
529
+ The target is the origin and the sync prefix: `/sync/v1`, or `/agents/<id>/sync/v1` for an agent
530
+ connection. `syncRoutePrefix({ origin, audience })` derives the prefix from a protected-resource
531
+ audience and returns null when the audience has none. `credentials` is the transport carrier's
532
+ own credential provider: an async function returning the headers for one request. It is called
533
+ before every request, so it may renew a short-lived token. `bearerCredentials` builds one from a
534
+ token getter. The client never logs credentials, and its errors carry the route and code, never a
535
+ token.
536
+
537
+ Failures are typed and carry no wording for any particular surface:
538
+
539
+ - `HostedCarrierError`: transport. `denied` means no credential (nothing was sent) or the request
540
+ was refused before sending; `unavailable` means the answer is unknown. `isAnswerTooLarge`
541
+ distinguishes an answer larger than its bound.
542
+ - `MalformedAnswer` (`code` `MALFORMED_ANSWER`, `route`): the host answered something the
543
+ contract does not admit. Retrying gets the same answer.
544
+ - `RemoteError` (`code`, `status`): the host refused the request, with its own code.
545
+ - `HostedRouteNotServed` (`code` `ROUTE_NOT_SERVED`, status 404, `route`): a `RemoteError` for a
546
+ host from before the route.
547
+
548
+ Operation and history refusals the host states in a `200` (and `bundle_not_found`,
549
+ `unknown_operation` and `invalid_input`) come back as `{ ok: false, refusal }`, not as errors.
550
+
551
+ ### Writing a document
552
+
553
+ `write` sends one whole serialized document (frontmatter and body, as a file holds it) to a hosted
554
+ bundle: a create of an absent id, or a replace at exactly the version the caller read.
555
+
556
+ ```ts
557
+ const created = await client.write("notes", { id: "notes/today", document: text, expectAbsent: true });
558
+ const replaced = await client.write("notes", { id: "notes/today", document: edited, expectedVersion: version });
559
+ ```
560
+
561
+ It returns one of:
562
+
563
+ - `{ status: "committed", version }`: the host stored it; `version` is the new head.
564
+ - `{ status: "conflict", reason: "version_conflict", currentVersion }`: the document is not at the
565
+ expected version (for a create: it exists). `currentVersion` is the version the host serves, or
566
+ null when it serves none. Nothing was written.
567
+ - `{ status: "conflict", reason: "concurrent_change" }`: another write to the bundle overlapped
568
+ this one past the host's own retries. Nothing was written.
569
+ - `{ status: "refused", code, message }`: the host refused this write (`validation_failed`,
570
+ `invalid_input`, `AUTH_REQUIRED`, `PERMISSION_DENIED`, a sync quota's `REQUEST_CAPACITY_*`, or
571
+ `read_only` when the bundle serves no writes). Nothing was written.
572
+ - `{ status: "unknown", requestId }`: the host may or may not hold the write.
573
+
574
+ Every result names its `requestId`. A conflict is final for that identity: the client never
575
+ resends it, under that identity or a new one. Read the document and decide; another attempt is a
576
+ new `write`.
577
+
578
+ A write needs the bundle's capabilities (whether it serves writes, its OKF edition, the write
579
+ bound and the retention window). The client reads them once per bundle and workspace and holds the
580
+ answer for `capabilitiesTtlMs` (default 60,000 ms), shared by the client and its `within` clients
581
+ and never by `reader()`. So a write costs one round trip while its bundle's capabilities are fresh,
582
+ and two when they are read first; concurrent writes to one bundle share one read. A write refused
583
+ `read_only`, `invalid_input`, `validation_failed` or `PERMISSION_DENIED` (which is how a
584
+ `bundle_not_found` write answer arrives), or whose result is `unknown`, drops the held answer, and
585
+ a capabilities read that fails is never held, so the next write reads them again. Set
586
+ `capabilitiesTtlMs: 0` to read them before every write. The retention window that decides a
587
+ resend after an `absent` lookup (below) is never taken from a held answer: it is read again then.
588
+
589
+ Each write has one request identity and carries the client's binding, a digest of a random id the
590
+ client mints once (its `within` clients share it). When an answer is lost (a transport failure or
591
+ a deadline after sending), the client looks the identity up on the host and reports what the host
592
+ recorded. The host's outcome route tells a request it never received ("absent") from one still in
593
+ progress ("pending"), and the shared transport keeps the two apart. On either answer `write`
594
+ sends the same identity once more, which the host applies at most once; after "pending", an
595
+ earlier delivery is known to be held. Absence counts as
596
+ evidence only within the retention window the host serves in its capabilities
597
+ (`operationsRetentionMs`, less an hour's margin); past it, the result stays `unknown` and nothing
598
+ is resent. The client measures an intent's age by its own clock against the host's window, so that
599
+ margin holds only while the client's clock is within an hour of the host's. A resend refused before
600
+ it reaches the host's execution (no credential, access withdrawn, a spent quota) leaves the result
601
+ `unknown` too, since the first delivery may still commit; after a "pending" lookup, so does any
602
+ refusal before execution, a gateway's `400` included. The result's `refusal` (`{ code, message }`)
603
+ names that refusal, so an app can say why the write is waiting (for example, access withdrawn)
604
+ while it stays held; only the call that met the refusal reports it. Absence alone never settles
605
+ such a write: a delayed first delivery is authorized when it arrives, so it can still commit once
606
+ access returns. When the lookups themselves fail, or the caller's `signal` aborts after sending, the
607
+ result is `unknown`.
608
+
609
+ An `unknown` write can be resolved later in two ways:
610
+
611
+ - `lookupWrite(requestId)` makes one outcome lookup and never sends the write. When the host holds
612
+ no outcome yet (absent or pending), the answer stays `unknown`.
613
+ - `settleWrite(requestId)` looks the identity up and, when the host never recorded it within its
614
+ retention window or holds it pending, sends the same identity once more, as `write` would have.
615
+
616
+ Both answer `{ status: "unrecorded", requestId }` when the intent store holds no write under that
617
+ identity: it was never recorded there, or it already settled and left the store. Both return
618
+ `unknown` when the bundle's capabilities cannot be read, because the write may already have left.
619
+ `settleWrite` only looks up, sending nothing, when the host serves the bundle no writes. A
620
+ `signal` already aborted when `write`, `lookupWrite` or `settleWrite` is called is thrown as its
621
+ reason before any request; an abort during delivery leaves the write `unknown` and held.
622
+ `write` instead throws that failure, because it has sent nothing yet.
623
+
624
+ Until it settles, a write is held in an intent store: its operation, bundle (and workspace),
625
+ document id, document and digest, expected version, request identity and binding, which is
626
+ everything needed to rebuild the lookup exactly. The default store is in memory and lives as long
627
+ as the client. Pass `intentStore` (`put`, `get`, `delete`, and optionally `list`) to keep intents
628
+ durable across restarts (`openHostedClientJournal`, below, is one for Node); lookups use the
629
+ binding recorded with each intent. A service that
630
+ restarts with a durable store calls `heldWrites()` to find the writes that never settled
631
+ (including any recorded but never sent), then settles each with `settleWrite`. `heldWrites()`
632
+ throws `HostedIntentsNotListable` when the store has no `list`. A settled write leaves the store.
633
+
634
+ `write(..., { requestId })` with the identity of a held write resumes that write and applies
635
+ nothing new. A different write under a held identity, including the same write named in another
636
+ workspace, is refused before sending. The host refuses an identity it recorded for another
637
+ document (`invalid_input`). Calls on one client that name the same identity run one after another.
638
+
639
+ `write` throws only before anything is sent: a malformed input (`TypeError`), the typed error of
640
+ its capabilities read, or a failure of the intent store to record the write. Document deletion is
641
+ not part of `write`.
642
+
643
+ ### Writing a blob
644
+
645
+ `writeBlob` and `deleteBlob` write one ordinary file (a blob: an image, a diagram, any bytes) to a
646
+ hosted bundle. A create names an absent key; a replace and a delete name the head they expect,
647
+ its version **and** content type (a `BlobPin`), so a type-only change is a replace too.
648
+
649
+ ```ts
650
+ const svg = new TextEncoder().encode("<svg xmlns='http://www.w3.org/2000/svg'/>");
651
+ const created = await client.writeBlob("notes", { key: "diagrams/flow.svg", bytes: svg, contentType: "image/svg+xml", expectAbsent: true });
652
+ if (created.status !== "committed") throw new Error(created.status);
653
+ let pin = created.head!; // { key, version, contentType }
654
+
655
+ const replaced = await client.writeBlob("notes", { key: pin.key, bytes: edited, contentType: "image/svg+xml", expected: pin });
656
+ if (replaced.status === "committed") pin = replaced.head!;
657
+
658
+ const read = await client.readBlob("notes", pin.key, { pin }); // bytes verified, or null if no longer at the pin
659
+ const deleted = await client.deleteBlob("notes", { key: pin.key, expected: pin });
660
+ ```
661
+
662
+ Each call returns one of:
663
+
664
+ - `{ status: "committed", head, changed? }`: applied. `head` is the new head (null after a
665
+ delete); `changed` is false for a replace the head already matched.
666
+ - `{ status: "conflict", reason: "version_conflict", actual }`: the head is not at the expected pin
667
+ (for a create: the key exists). `actual` is the head now, or null when there is none (a delete of
668
+ an absent key). Final for that identity.
669
+ - `{ status: "conflict", reason: "concurrent_change", message }`: another write overlapped this one
670
+ past the host's own retries. Nothing was applied.
671
+ - `{ status: "refused", code, message }`: the host's own refusal, or one the client names. The
672
+ host's codes are those it records under the identity (`backend_unavailable`, ...) and those it
673
+ answers before dispatch and never records (`insufficient_scope` for a grant without blob writes,
674
+ `bundle_not_found`, and `request_capacity` in a `200` or a `429`). A refusal of the second kind
675
+ proves only that this attempt did not run, so it is final only when every earlier attempt of the
676
+ write is proven not applied, which is always so for a write the client sends; a write whose
677
+ earlier attempt was unclear is never sent again, only looked up, and stays `unknown`.
678
+ `insufficient_scope`, `bundle_not_found` and `invalid_input` also drop the held capabilities
679
+ answer, so the next write reads them again. The client's own codes are `invalid_input` (an
680
+ unsafe key, a key under one of Superbee's own record folders or a reserved file, an invalid content type, a body over the host's `blobWriteBytes` or 16 MiB, or a held
681
+ identity reused for another write), `scope_mismatch` (a held write, or a reused `requestId`, recorded
682
+ for another origin or sync prefix) or `unsettled_write` (an earlier write of the same key in the bundle is still
683
+ unsettled: settle or forget it first). Nothing was sent for a client-named refusal.
684
+ - `{ status: "held", deletions, baseline }`: the host holds this mass delete (`428
685
+ deletions_held`). Nothing was applied. A person confirms the count, and
686
+ `settleBlobWrite(requestId, { acceptDeletes })` with at least `deletions` sends it again; without
687
+ `acceptDeletes` it stays held and nothing is sent, and with fewer it throws
688
+ `HostedBlobDeletionsNotAccepted`, sending nothing. `deleteBlob(..., { acceptDeletes })` sends the
689
+ confirmation with the first attempt.
690
+ - `{ status: "unknown" }`: the host may or may not hold the write.
691
+
692
+ Every result names its `requestId`, `key` and `operation`.
693
+
694
+ **The capability gate.** A blob write needs the bundle's `blobWrites: "allowed"` and its
695
+ `blobWriteBytes` bound, from the same held capabilities answer `write` uses. When the host serves
696
+ the bundle no blob writes (an older host's answer reads as refused), `writeBlob` and `deleteBlob`
697
+ throw `HostedBlobWritesNotServed`, a `HostedRouteNotServed` (`isRouteNotServed` is true), before
698
+ anything is journaled or sent. This differs from `write()`, which answers a bundle without writes
699
+ with the result `refused: read_only`: the blob routes are a newer capability a caller probes for, as
700
+ it probes for any route a host may lack. The checks a send would otherwise fail (the capability, the
701
+ byte bound, the key and the content type) all run against that one answer before the write is
702
+ journaled, and the delivery is handed the same answer, so the two cannot disagree.
703
+
704
+ **Exactly once, never resent after it may have left.** Each blob write is journaled under one
705
+ request identity (with the client's binding, the bundle and workspace and the bytes), durably
706
+ claimed, then sent once. After an unclear answer the client looks the identity up on the host
707
+ (`/outcome`, under the binding recorded with the write) and reports what the host recorded. Unlike a
708
+ document write, an `absent` lookup is never permission to send again: once an attempt may have left,
709
+ a blob write is only ever looked up. A send proven never to have left (the credential provider
710
+ failed or was denied, the signal was already aborted, the identity was malformed, a local check
711
+ failed) is rolled back instead: `writeBlob` throws `HostedBlobWriteNotSent` with its `requestId` and
712
+ `reason`, and the write stays held and resendable (`held: true`), so `settleBlobWrite` or the same
713
+ `writeBlob` call (same `requestId`, same input) sends it. A transport failure after the request was
714
+ handed to the carrier is `unknown`.
715
+
716
+ **Recovery.** `heldBlobWrites()` lists every unsettled blob write (`requestId`, `bundleId`,
717
+ `workspace`, `key`, `operation`, `state`, `attempts`, `resendable`, `hold`, `createdAt`) and sweeps any
718
+ settled one a crash left behind.
719
+
720
+ - `lookupBlobWrite(requestId)` makes one outcome lookup and never sends. A write never sent (or
721
+ proven not applied) answers `{ status: "pending" }` without a request, and a held delete `held`.
722
+ - `settleBlobWrite(requestId, { acceptDeletes? })` submits the write when its state is pending or
723
+ unknown and every attempt is proven not applied (`attempts === notAppliedThrough`); otherwise it
724
+ only looks it up, and an absent lookup is `unknown`.
725
+ - `forgetBlobWrite(requestId)` drops an `unknown` write once one fresh lookup answers `absent`,
726
+ returning `{ status: "forgotten" }`. **The earlier delivery may still commit**; the host's
727
+ compare-and-swap is what keeps a later write from losing that update. Any other record is
728
+ `{ status: "kept", reason: "not_unknown" }` (settle an `in_flight` one first), a lookup that fails,
729
+ is refused or is malformed is `kept` with `lookup_unavailable`, and a lookup that finds the outcome
730
+ settles the write instead. A forgotten identity is no longer held, so `writeBlob` with that same
731
+ `requestId` journals and uploads it again under that identity. That is safe: the host answers a
732
+ delivery it recorded under the identity with the recorded result, and one it did not record is
733
+ checked against the expected pin like any write.
734
+
735
+ All three answer `{ status: "unrecorded", requestId }` when the journal holds no blob write under the
736
+ identity. A reused `requestId` with the same input resumes (settles) the held write; with different
737
+ input it is `refused: invalid_input` and nothing is admitted. Calls are serialized per request
738
+ identity and per key (the journal's guard covers a key across bundles), so concurrent calls of one
739
+ client never trip each other.
740
+
741
+ ```ts
742
+ // A service restarting after a crash: settle what it held, forgetting only proven-absent unknowns.
743
+ for (const held of await client.heldBlobWrites()) {
744
+ const settled = await client.settleBlobWrite(held.requestId);
745
+ if (settled.status === "unknown") console.warn(`blob write ${held.requestId} is still unknown`); // forgetBlobWrite once you choose to move on
746
+ }
747
+ ```
748
+
749
+ **Reads.** `readBlob(bundleId, key, { pin })` requires the pin: it reads exactly that version and
750
+ content type, verifies length, digest and type, and answers null when the key is no longer at the pin
751
+ (the host's `409`). `blobHead(bundleId, key)` is the explicit lookup of one head (or null), and
752
+ `blobHeads(bundleId)` the whole listing; both read the full verified listing (up to 10,000 heads in
753
+ 4 MiB pages), since the host has no single-key head route yet.
754
+
755
+ **Keys.** `isOrdinaryBlobKey` is the one key policy every blob writer uses, this client and the
756
+ CLI's checkout sync alike: a safe key that is not a reserved file (`index.md`, `log.md`) and does not
757
+ sit under Superbee's own record folders (`views/`, `pages/`, their registries, `conventions/`,
758
+ `recipes/`, `references/`, compared case-insensitively). Any other key is refused `invalid_input`
759
+ before any request.
760
+
761
+ **Aborts.** A `signal` aborted before the write is admitted throws its reason and journals nothing;
762
+ that includes an abort while the capabilities are still being read, so a hung credential provider
763
+ never holds the call (or its key) past the caller's abort. Once admitted, a write is held until it
764
+ settles, and an abort before its send leaves it resendable (`HostedBlobWriteNotSent`).
765
+
766
+ **Journal.** The default blob journal is in memory (`createMemoryBlobJournal`), owned by the client.
767
+ Give a blob journal to one client (its `within` clients share it). Two clients over one journal
768
+ coordinate only through its per-key guard: concurrent writes of different identities still settle,
769
+ but two calls on the same request identity from both clients can fail with a raw journal conflict.
770
+ Pass `blobJournal` to keep blob writes across restarts: in Node, `openHostedClientJournal(...).blobs`
771
+ (below). `driveJournaledBlob(journal, context, requestId, mode)` is the owner of the journaled
772
+ sequence (admit, claim, deliver, persist, retire) for a caller with its own journal and premises; its
773
+ `premises.snapshot(key, step)` returns each transaction's guard plus the caller's own metadata
774
+ changes, checked atomically with it.
775
+
776
+ **Limitations.**
777
+
778
+ - Lookups currently need `blobWrites`: a grant downgraded to read-only cannot settle its earlier
779
+ blob writes until the host authorizes the outcome route on its own (a hosted follow-up).
780
+ - A lookup across principals is unverified: a new token of the same principal finds its outcomes
781
+ (the binding travels in the journal), but what another principal's lookup gets is not yet proven.
782
+ - Every journal record of a write carries its bytes, and each transaction validates the record.
783
+ Validation is cached per payload, so a write costs about 0.17 s per MiB in Node (3 MiB about
784
+ 0.5 s, 16 MiB about 2.6 s, measured with the in-memory journal); without the cache it was about
785
+ 1.7 s per MiB (16 MiB about 28 s). The first validation of a payload still runs on the caller's
786
+ thread in one slice, about 1.2 s for 16 MiB.
787
+
788
+ ### Inbox
789
+
790
+ `createHostedInbox(client, { tenantId })` is a person's hosted inbox in one workspace, over the
791
+ same client (its credential, deadline and abort; a `within` client serves too). It reaches the
792
+ attention routes, `POST <prefix>/attention/<operationId>`, and names the workspace in the
793
+ `Superbee-Workspace` header on every request, since a sync sign-in spans all of the person's
794
+ workspaces. Notifications are a hosted capability: a host without these routes answers
795
+ `HostedRouteNotServed`, and nothing here emulates a local inbox.
796
+
797
+ ```ts
798
+ import { createHostedInbox } from "@superbee/core/hosted-client";
799
+
800
+ const identity = await client.whoami();
801
+ const inbox = createHostedInbox(client, { tenantId: identity.tenantIds[0]!, via: "my-agent" });
802
+ const receipt = await inbox.request({ recipient: { kind: "principal", principalId: identity.principalId }, kind: "needs_input", note: "Ready for you" });
803
+ const page = await inbox.list({ threadId: receipt.threadId, afterSeq: 0 });
804
+ ```
805
+
806
+ The methods are `list`, `threads`, `summary`, `get` (does not mark read), `open` (marks read),
807
+ `ack` (at the revision read), `request` (send) and `cancel` (withdraw your own send), with the
808
+ open contract's shapes (`InboxEntry`, `InboxThread`, `AttentionReceipt` and the inputs). A
809
+ recipient is a principal id; to notify yourself, use `whoami().principalId`. A document target
810
+ names `whoami().targetAuthorityKey` as its `authorityKey` and the document's current version; a
811
+ host that reports no key (the field is absent, as on every `/sync/v1` host today) cannot take one yet.
812
+
813
+ `request` mints one UUIDv7 ticket per send (or takes the one you pass). When an answer is lost or
814
+ the host marks its refusal retryable, it sends the same body under the same ticket again
815
+ (`sendAttempts`, default 3), which the host answers as a replay of the first send, never as a second
816
+ item. A send still unclear after that throws `HostedAttentionOutcomeUnknown` with its `ticket`:
817
+ `request({ ...input, ticket })` within 24 hours settles it. Refusals are
818
+ `HostedAttentionRefusal` (a `RemoteError`) with the catalog's `code` (`attention_disabled`,
819
+ `not_found`, `target_unavailable`, `target_unsupported`, `quota_exceeded`, `request_conflict`,
820
+ `version_conflict`, `ticket_expired` with `serverTime`, `workspace_not_available` and the rest),
821
+ `retryable`, and a message in this client's words: the host's message and hint are never repeated.
822
+
823
+ ### Durable intent store (Node only)
824
+
825
+ `openHostedClientJournal` from `@superbee/core/hosted-client/node` is a durable `intentStore`, and its
826
+ `blobs` a durable `blobJournal`, so a short-lived process that exits with a write `unknown` can recover
827
+ it after a restart.
828
+
829
+ ```ts
830
+ import { createHostedClient } from "@superbee/core/hosted-client";
831
+ import { openHostedClientJournal } from "@superbee/core/hosted-client/node";
832
+
833
+ const journal = await openHostedClientJournal({ directory: path.join(stateDir, "superbee-writes") });
834
+ const client = createHostedClient({ target, credentials, intentStore: journal, blobJournal: journal.blobs });
835
+ for (const held of await client.heldWrites()) await client.settleWrite(held.requestId);
836
+ for (const held of await client.heldBlobWrites()) await client.settleBlobWrite(held.requestId);
837
+ // ... writes ...
838
+ await journal.close();
839
+ ```
840
+
841
+ The journal is a `FileJournaledBackend` directory: an fsynced, checksummed log with snapshot
842
+ compaction. Its on-disk format is a supported compatibility promise: a format bump ships a reader
843
+ for every earlier layout, and an older reader refuses a newer store with `FileJournalFormatError`
844
+ rather than misreading it. The current format is 3 (see `JOURNALED-BACKENDS.md`). Each unsettled write is one metadata row, `hosted-client.write.<requestId>`,
845
+ holding the whole document and the binding it was sent under, so a new process with a new token
846
+ looks it up exactly as the first would have. A settled write is removed. Each unsettled blob write is
847
+ one row of the backend's own blob intent family, with its bytes, under the same transition rules as
848
+ the in-memory journal; a settled one is retired, and since every record of a blob write carries its
849
+ bytes, retiring or forgetting one compacts the store once its log or snapshot is past 1 MiB. Metadata
850
+ a blob write carries may not name the journal's own rows (`hosted-client.*`) or a checkout's
851
+ blob-sync state.
852
+
853
+ - **Ownership.** Open takes an exclusive same-user lock on the directory until `close()`. Another
854
+ process waits `lock.waitMs` (default 5,000 ms) and then gets `FilesystemMutationLockError`. A
855
+ lock left by a process that died on this host is reclaimed.
856
+ - **Local file system only.** The lock lives in a per-user runtime directory on this host, so it
857
+ cannot exclude a process on another host. Keep the journal on a local disk, never on NFS, SMB
858
+ or another shared mount. Open does not detect a network mount; this is the operator's rule.
859
+ - **Privacy.** The journal holds whole documents. A directory open creates is mode 0700, and its
860
+ files 0600. On POSIX, an existing directory that is a symbolic link, is owned by another user,
861
+ or grants its group or others any permission, is refused as `HostedClientJournalError`
862
+ `JOURNAL_DIRECTORY_NOT_PRIVATE`. The check runs again once the lock is held, and a directory
863
+ replaced in between is refused too. The directory is the journal's alone: an existing one may
864
+ hold only `store.log`, `store.snapshot` and `store.snapshot.tmp`, and any other entry (an
865
+ operating system's `.DS_Store` included) is refused as `JOURNAL_NOT_HOSTED_CLIENT`, as is a
866
+ `store.log` that is not a journal's log, and a store file that is a symbolic link, a
867
+ directory, has more than one link, or (on POSIX) belongs to another user. The listing and the
868
+ files are checked again under the lock. Another process of the same user changing the
869
+ directory while it opens is out of scope: in that window the open can truncate a torn or
870
+ foreign log tail.
871
+ - **Marker.** A new journal records `hosted-client.journal = { schemaVersion: 1 }` in its first
872
+ transaction. Open refuses, as `JOURNAL_NOT_HOSTED_CLIENT`, a store with rows but no marker or one
873
+ holding documents, blob files or a checkout's blob-sync state, so it is never pointed at a CLI
874
+ checkout store. A marked journal holding blob write intents is its own and reopens. A marker of another schema is `JOURNAL_SCHEMA_UNSUPPORTED`. Open decides on a read-only
875
+ pass first, so a refused directory's files are left byte for byte. A crash before the marker
876
+ lands leaves an empty store, which the next open marks.
877
+ - **Size.** Once the store is adopted, the log is compacted into the snapshot whenever it grows past the greater of 1 MiB
878
+ (`HOSTED_CLIENT_JOURNAL_COMPACT_FLOOR_BYTES`) and twice the last snapshot, so steady writing keeps
879
+ it near 1 MiB. The snapshot, which holds every write still unsettled, and any one transaction are
880
+ bounded at 256 MiB. A write that would pass that bound is refused before anything is recorded,
881
+ and its `write` throws. Unsettled writes count toward the bound until `settleWrite` resolves them.
882
+ - **Ordering.** Unlike the in-memory store, each call takes effect when its asynchronous steps
883
+ complete, so unawaited overlapping calls on one request identity can land out of call order.
884
+ Await each call before the next, as the client does.
885
+ - **Damage.** `FileJournalCorruptError` (a damaged record before the log's end or a damaged
886
+ snapshot) and `FileJournalFormatError` (a newer format) refuse the open and change no file. A torn
887
+ final record, which was never acknowledged, is truncated. Nothing is repaired automatically:
888
+ moving the directory aside starts an empty journal and loses the writes it held, which can then
889
+ be settled only by reading the documents on the host.
890
+
891
+ ### Export download (Node only)
892
+
893
+ `@superbee/core/hosted-client/node` downloads and verifies a hosted bundle's portable export. It
894
+ is a supported public API under the same versioning. It is Node-only because verification
895
+ (`@superbee/core/portable-export`) needs Node; `@superbee/core/hosted-client` never imports it,
896
+ so the client itself stays browser-safe.
897
+
898
+ ```ts
899
+ import { bearerCredentials, createHostedClient } from "@superbee/core/hosted-client";
900
+ import { downloadExport, EXPORT_WINDOW_MS } from "@superbee/core/hosted-client/node";
901
+ import { materializeExport } from "@superbee/core/portable-export";
902
+
903
+ const client = createHostedClient({ target, credentials: bearerCredentials(getToken), deadlineMs: EXPORT_WINDOW_MS });
904
+ const verified = await downloadExport(client, "team.notes", { signal });
905
+ const folder = await materializeExport(verified, parentDirectory);
906
+ ```
907
+
908
+ `downloadExport(client, bundleId, { signal })` returns a `VerifiedExport`: every document,
909
+ reserved file and blob at the bundle's latest confirmed revision, read whole and checked against
910
+ the host's digest manifests. It writes nothing. A stopped, tampered or oversized export never
911
+ becomes a result; placing the files is the caller's (`materializeExport`, or its own staging). It
912
+ reads a paged export one page at a time and joins the pages. On a host from before paging it reads
913
+ the whole archive. A bundle that changes between pages is read again from its first page, up to
914
+ three attempts, and a busy or stopped page is asked for once more. Create the client with
915
+ `deadlineMs: EXPORT_WINDOW_MS` (60 s), the host's export window; the default 15 s can cut a large
916
+ bundle. A `within(slug)` client names the bundle in that workspace.
917
+
918
+ Failures are typed:
919
+
920
+ - `HostedCarrierError`, `HostedRouteNotServed` (a host without the export route) and
921
+ `RemoteError` (the host's refusal: `bundle_not_found`, `result_too_large`, `validation_failed`,
922
+ an unauthenticated bearer and so on), as for the rest of the client.
923
+ - `ExportArchiveError` (`problem`): the verifier refused the archive. `incomplete` means the host
924
+ stopped it, and the same request may succeed.
925
+ - `HostedExportError` (`problem`): `stopped` (the answer's body failed part way), `too_large`
926
+ (past `MAX_EXPORT_BYTES`) or `source_changed` (the bundle kept changing between pages).
927
+ - The signal's reason, once `signal` or the client's own signal aborts.
928
+
929
+ ## Release compatibility
930
+
931
+ This migration describes the source after the Windows extraction. Previously published versions
932
+ are unchanged. Consult the release notes for the first affected version before upgrading a pinned
933
+ consumer. CLI and engine version numbers are independent; do not infer matching embedded code from
934
+ package names or version labels.
935
+
936
+ [Source and contribution guide](https://github.com/Holaxis-ai/superbee)
937
+
938
+ ### Node portable export boundary
939
+
940
+ `@superbee/core/portable-export` (also bundled as `superbee/portable-export`) exposes the same
941
+ store-only archive verifier used by the CLI: `verifyExport`, `ExportPageChain` and
942
+ `joinExportPages`. The page chain charges the sum of complete ZIP sizes, including manifests
943
+ and headers, against `MAX_EXPORT_BYTES` before retaining a page; returned `bytes` counts only
944
+ stored bundle content. It performs no acquisition, sign-in or network access. A complete accepted
945
+ handle can be passed to `materializeExport(handle, existingPrivateParent)` to create a fresh
946
+ private directory; callers own its cleanup and isolation while publishing. No target is replaced.
947
+ `exportProvenance(handle)` returns exact ordered raw-manifest SHA-256/size evidence plus a combined
948
+ inventory SHA-256. Keep that evidence outside the directory and private when it identifies a
949
+ private source. Handles cannot be reconstructed from JSON or moved between independently bundled
950
+ module instances; modified handles are refused. This surface requires Node, not a browser or Worker.
951
+
952
+ ## Built-in recipes and Kind authoring
953
+
954
+ `@superbee/core/builtin-recipes` is an opt-in catalog: `builtinNames()` lists names,
955
+ `builtinRecipeFiles(name)` returns detached source files, and `loadBuiltinRecipe(name)` returns
956
+ the existing parser's result or `null` for an unknown name. The generic parser, package root,
957
+ and `initBundle` do not import the catalog or install recipes.
958
+
959
+ `@superbee/core/kind-authoring` provides inference, forecasts, dismissal classification, templates,
960
+ and guarded draft/apply/dismiss operations on a caller-selected `Bundle` backend. An outside app
961
+ can use the existing backend and mutation seams without a CLI subprocess:
962
+
963
+ ```ts
964
+ import { MemoryBackend } from "@superbee/core/memory-backend";
965
+ import { query } from "@superbee/core/engine";
966
+ import { mutateDocument } from "@superbee/core/document-mutation";
967
+ import { buildKindRegistry } from "@superbee/core/kinds";
968
+ import { loadBuiltinRecipe } from "@superbee/core/builtin-recipes";
969
+ import { installRecipe } from "@superbee/core/recipe-lifecycle";
970
+ import { kindInstanceCandidate, prepareKindDraft, applyKindDraft } from "@superbee/core/kind-authoring";
971
+
972
+ const backend = new MemoryBackend(), bundle = { root: "memory://project", backend };
973
+ await backend.writeReserved("", "index.md", "---\nokf_version: '0.2'\n---\n# Project\n");
974
+ const loaded = loadBuiltinRecipe("work-tracking");
975
+ if (!loaded?.ok) throw new Error("Recipe unavailable");
976
+ await installRecipe(bundle, loaded.recipe);
977
+ const registry = buildKindRegistry(await query(backend, { prefix: "conventions/" }));
978
+ const task = registry.kinds.get("Task")!;
979
+ const candidate = kindInstanceCandidate(task, { title: "First task", superbee_progress_status: "todo" });
980
+ await mutateDocument({ bundle, id: "tasks/first", mode: "create-only", registry, strict: true,
981
+ buildCandidate: () => candidate });
982
+
983
+ // Once an ungoverned type has existing instances, inspect its draft before applying.
984
+ const text = { inferredBody: () => "The accepted shape of existing research records.\n" };
985
+ const plan = await prepareKindDraft(bundle, "Research", text); // requires existing Research docs
986
+ // Present candidateDoc, warnings and promotions; after acceptance:
987
+ await applyKindDraft(bundle, "Research", plan.token, text);
988
+ ```
989
+
990
+ `kindInstanceCandidate` accepts already-typed field values. Existing validators own their meaning;
991
+ there is no CLI flag coercion or path-alias resolution. Supply a canonical id and use strict
992
+ create-only mutation. The template keeps `type` equal to `governs`, preserves own prototype-shaped
993
+ fields, scaffolds sections only when body is omitted, and retains an explicit empty body.
994
+
995
+ Public draft/apply/dismiss operations trim type names and refuse blank or non-string names before
996
+ any backend access, matching convention parsing. `prepareKindDraft` selects a catalog definition
997
+ or infers from instances. Its token binds candidate
998
+ bytes (including supplied `inferredBody`/`inferredDescription`), edition, presence statistics and
999
+ warning forecast. Pass the same prose options to `applyKindDraft`, which recomputes the plan and
1000
+ checks edition and declaration-free redraft eligibility against every fresh CAS head. New
1001
+ conventions are expect-absent creates; accepted redrafts replace dismissal prose in place and
1002
+ report dropped links. `dismissKind` accepts presentation-owned description/body, records no schema
1003
+ obligations, refuses a real schema, and is idempotent. Definition writes retain recipe
1004
+ installation's generation-clock opt-out. `KindAuthoringError.reason` identifies domain refusals;
1005
+ normal mutation/storage errors retain their types.
1006
+
1007
+ Presentations with an established mutation adapter may pass the options from
1008
+ `prepareKindDraftMutation` or `prepareKindDismissal` through that adapter. These are the same
1009
+ specifications used by public apply/dismiss; keep preparation adjacent to mutation. Outside apps
1010
+ normally use `applyKindDraft`/`dismissKind`, which own the read-and-apply sequence. No cross-document
1011
+ transaction or authorization boundary is added.
1012
+
1013
+ The packed proof runs the public workflow over `MemoryBackend` in Node and a browser bundle without
1014
+ Node globals. This proves source/package availability. Hosted provisioning routes, MCP modeling
1015
+ tools, and publication of a core version remain separate adoption and release decisions.
1016
+
1017
+
1018
+ ## Declarative JSON sources
1019
+
1020
+ `@superbee/core/source-profiles` exports `getSourceProfile(id)`, frozen `SOURCE_PROFILES` metadata,
1021
+ and `restJsonProfile` (version 1). The catalog contains only `rest-json`; GitHub
1022
+ normalization ships separately in [`@superbee/integration-github`](https://github.com/Holaxis-ai/superbee-plugins/tree/main/integrations/github). Core also exports the existing
1023
+ `nextLinkTarget` parser, `SourcePluginHelpers` type and exact `SOURCE_PLUGIN_CORE_VERSION` for
1024
+ explicit host composition. The profile accepts a parsed `ConnectorSource` as its spec;
1025
+ `source.request` describes provider data, while `source.fields` maps normalized paths to Kind fields.
1026
+ It runs only through `observeSource` and the caller's recorded or credential-injecting transport.
1027
+ Installing a Connector does not run it.
1028
+
1029
+ The optional, pinned request block requires `host`, `method`, `path` and `items` (empty string for
1030
+ a root array). `query`, `body` and `headers` contain scalar literals or whole `{binding}`, `{cursor}`,
1031
+ `{page}` and `{watermark}` values. Path placeholders occupy whole segments. `request.fields` maps
1032
+ normalized dotted paths to `{from, type?}` or `{const}`; types are `text`, `integer`, `date`
1033
+ (ISO timestamp), `epoch-date` (seconds to ISO timestamp), and `url` (HTTP(S)). Presence-only `skip`
1034
+ rules count skipped records. No executable expressions or synthesis are supported.
1035
+
1036
+ Paging kinds are `none`, `cursor`, `link-header`, `offset` and `page-number`. Cursor paging uses
1037
+ `cursor_path` (opaque token, placed through `query`) or `next_path` (URL). Numeric paging requires
1038
+ `size` and `page_param` or `offset_param`, and restarts from the beginning after interruption.
1039
+ `watermark: {field, param, overlap_seconds}` applies overlap between passes and keeps the pass's
1040
+ starting watermark fixed across pages. `complete` is `always`, `never`, or `{when_path, equals}`;
1041
+ the engine additionally requires an uninterrupted complete-mode pass before counting absences.
1042
+ Optional `errors` sets (`deferred`, `credential_refused`, `not_found`) and `rate.requests` narrow
1043
+ the run's status interpretation and request budget.
1044
+
1045
+ Existing declaration pins without a request block remain unchanged. Credentialed engine consumers
1046
+ must now supply a request declaration: `declaration_required` refuses a missing one before any
1047
+ transport call. Parsing and installation remain inert. Provider read paths, read-shaped POST paths,
1048
+ headers and paging forms are core-owned, deeply frozen, and revalidated at every run; caller rules
1049
+ can only narrow them. Callback URL/response views are isolated from execution; changing a view
1050
+ cannot rewrite the request. Core rechecks its private run authority after narrowing callbacks.
1051
+ Keyless legacy profiles retain the declared-domain and robots rules.
1052
+
1053
+ GitHub's repository issues GET, version header and alternate numeric pagination form follow
1054
+ [issues documentation](https://docs.github.com/en/rest/issues/issues) and
1055
+ [pagination documentation](https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api).
1056
+ Numeric `/repositories/{id}/issues` paging additionally requires first-page repository metadata
1057
+ that proves the id belongs to the bound owner/repository and stays fixed for that pass. The issues
1058
+ listing does not guarantee that metadata; a Link alone cannot establish it. Without proof the
1059
+ engine refuses that alternate form. Its metadata proof is scoped to the current run, so resuming
1060
+ an interrupted alternate numeric URL in a later run also refuses; restart from the bound path.
1061
+ The conditional metadata test is not live-provider
1062
+ qualification. Same bound `/repos/{owner}/{repo}/issues` pagination remains usable.
1063
+
1064
+ GraphQL declarations use the real `graphql` syntax parser: one query operation, no variable defaults,
1065
+ and bound String/ID inputs. GraphQL execution is deferred to the later GraphQL profile slice:
1066
+ `graphql_scope_unsupported` refuses even a well-formed read body before transport. Syntax alone
1067
+ does not establish resource-scoped traversal. Mutations and malformed extension bodies are refused
1068
+ by the engine wrapper; caller hooks cannot enable them.
1069
+
1070
+ ## Declared Kind model
1071
+
1072
+ `@superbee/core/domain-model` exports `projectDomainModelV0(registry)` and its
1073
+ readonly input/output types. Pass a canonical registry from `buildKindRegistry`
1074
+ (`@superbee/core/kinds`) or `loadKinds`; the projection performs no parsing or I/O.
1075
+ It returns deeply frozen JSON with deterministically ordered Kind definitions,
1076
+ declared relationships, registry warnings and informational outside-scope endpoints.
1077
+ Structurally malformed inputs throw `TypeError`.
1078
+
1079
+ The v0 shape preserves authored array order and warning metadata. It does not
1080
+ include `fields.types` or derived `freshnessHorizonMs`; adapters that already add
1081
+ these fields keep that enrichment. Projection does not establish snapshot coherence,
1082
+ authorization or permission to disclose a bundle's declarations.