@superbee/core 0.2.0-pre.2 → 0.2.0-pre.21

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 (517) hide show
  1. package/README.md +1000 -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 +50 -0
  19. package/dist/body-replace-guards.d.ts.map +1 -0
  20. package/dist/body-replace-guards.js +158 -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/connectors.d.ts +230 -0
  59. package/dist/connectors.d.ts.map +1 -0
  60. package/dist/connectors.js +1150 -0
  61. package/dist/connectors.js.map +1 -0
  62. package/dist/cross-bundle.d.ts +135 -0
  63. package/dist/cross-bundle.d.ts.map +1 -0
  64. package/dist/cross-bundle.js +186 -0
  65. package/dist/cross-bundle.js.map +1 -0
  66. package/dist/dependencies.d.ts +94 -0
  67. package/dist/dependencies.d.ts.map +1 -0
  68. package/dist/dependencies.js +180 -0
  69. package/dist/dependencies.js.map +1 -0
  70. package/dist/document-codec.d.ts +6 -1
  71. package/dist/document-codec.d.ts.map +1 -1
  72. package/dist/document-codec.js +8 -4
  73. package/dist/document-codec.js.map +1 -1
  74. package/dist/document-field-actions.d.ts +31 -1
  75. package/dist/document-field-actions.d.ts.map +1 -1
  76. package/dist/document-field-actions.js +137 -34
  77. package/dist/document-field-actions.js.map +1 -1
  78. package/dist/document-mutation.d.ts +22 -10
  79. package/dist/document-mutation.d.ts.map +1 -1
  80. package/dist/document-mutation.js +58 -27
  81. package/dist/document-mutation.js.map +1 -1
  82. package/dist/document-write-policy.d.ts.map +1 -1
  83. package/dist/document-write-policy.js +4 -2
  84. package/dist/document-write-policy.js.map +1 -1
  85. package/dist/engine.d.ts +9 -2
  86. package/dist/engine.d.ts.map +1 -1
  87. package/dist/engine.js +11 -4
  88. package/dist/engine.js.map +1 -1
  89. package/dist/file-journaled-backend-internal.d.ts +21 -0
  90. package/dist/file-journaled-backend-internal.d.ts.map +1 -0
  91. package/dist/file-journaled-backend-internal.js +12 -0
  92. package/dist/file-journaled-backend-internal.js.map +1 -0
  93. package/dist/file-journaled-backend.d.ts +200 -0
  94. package/dist/file-journaled-backend.d.ts.map +1 -0
  95. package/dist/file-journaled-backend.js +1334 -0
  96. package/dist/file-journaled-backend.js.map +1 -0
  97. package/dist/filesystem-host.d.ts +15 -0
  98. package/dist/filesystem-host.d.ts.map +1 -0
  99. package/dist/filesystem-host.js +20 -0
  100. package/dist/filesystem-host.js.map +1 -0
  101. package/dist/filesystem-identity.d.ts +86 -4
  102. package/dist/filesystem-identity.d.ts.map +1 -1
  103. package/dist/filesystem-identity.js +338 -102
  104. package/dist/filesystem-identity.js.map +1 -1
  105. package/dist/filesystem-lock.d.ts +16 -3
  106. package/dist/filesystem-lock.d.ts.map +1 -1
  107. package/dist/filesystem-lock.js +323 -77
  108. package/dist/filesystem-lock.js.map +1 -1
  109. package/dist/filesystem-push-role.d.ts +77 -0
  110. package/dist/filesystem-push-role.d.ts.map +1 -0
  111. package/dist/filesystem-push-role.js +165 -0
  112. package/dist/filesystem-push-role.js.map +1 -0
  113. package/dist/filesystem.d.ts +17 -0
  114. package/dist/filesystem.d.ts.map +1 -0
  115. package/dist/filesystem.js +18 -0
  116. package/dist/filesystem.js.map +1 -0
  117. package/dist/frontmatter.d.ts +8 -0
  118. package/dist/frontmatter.d.ts.map +1 -1
  119. package/dist/frontmatter.js +14 -4
  120. package/dist/frontmatter.js.map +1 -1
  121. package/dist/governed-body-write.d.ts +106 -0
  122. package/dist/governed-body-write.d.ts.map +1 -0
  123. package/dist/governed-body-write.js +251 -0
  124. package/dist/governed-body-write.js.map +1 -0
  125. package/dist/host-policy-snapshot.d.ts +9 -0
  126. package/dist/host-policy-snapshot.d.ts.map +1 -0
  127. package/dist/host-policy-snapshot.js +33 -0
  128. package/dist/host-policy-snapshot.js.map +1 -0
  129. package/dist/host-text.d.ts +31 -0
  130. package/dist/host-text.d.ts.map +1 -0
  131. package/dist/host-text.js +77 -0
  132. package/dist/host-text.js.map +1 -0
  133. package/dist/hosted-client/binding.d.ts +7 -0
  134. package/dist/hosted-client/binding.d.ts.map +1 -0
  135. package/dist/hosted-client/binding.js +10 -0
  136. package/dist/hosted-client/binding.js.map +1 -0
  137. package/dist/hosted-client/blob-delivery.d.ts +67 -0
  138. package/dist/hosted-client/blob-delivery.d.ts.map +1 -0
  139. package/dist/hosted-client/blob-delivery.js +169 -0
  140. package/dist/hosted-client/blob-delivery.js.map +1 -0
  141. package/dist/hosted-client/blob-write.d.ts +386 -0
  142. package/dist/hosted-client/blob-write.d.ts.map +1 -0
  143. package/dist/hosted-client/blob-write.js +770 -0
  144. package/dist/hosted-client/blob-write.js.map +1 -0
  145. package/dist/hosted-client/bundle-listing.d.ts +46 -0
  146. package/dist/hosted-client/bundle-listing.d.ts.map +1 -0
  147. package/dist/hosted-client/bundle-listing.js +36 -0
  148. package/dist/hosted-client/bundle-listing.js.map +1 -0
  149. package/dist/hosted-client/client.d.ts +219 -0
  150. package/dist/hosted-client/client.d.ts.map +1 -0
  151. package/dist/hosted-client/client.js +309 -0
  152. package/dist/hosted-client/client.js.map +1 -0
  153. package/dist/hosted-client/directory.d.ts +46 -0
  154. package/dist/hosted-client/directory.d.ts.map +1 -0
  155. package/dist/hosted-client/directory.js +74 -0
  156. package/dist/hosted-client/directory.js.map +1 -0
  157. package/dist/hosted-client/errors.d.ts +26 -0
  158. package/dist/hosted-client/errors.d.ts.map +1 -0
  159. package/dist/hosted-client/errors.js +35 -0
  160. package/dist/hosted-client/errors.js.map +1 -0
  161. package/dist/hosted-client/inbox.d.ts +320 -0
  162. package/dist/hosted-client/inbox.d.ts.map +1 -0
  163. package/dist/hosted-client/inbox.js +379 -0
  164. package/dist/hosted-client/inbox.js.map +1 -0
  165. package/dist/hosted-client/index.d.ts +30 -0
  166. package/dist/hosted-client/index.d.ts.map +1 -0
  167. package/dist/hosted-client/index.js +32 -0
  168. package/dist/hosted-client/index.js.map +1 -0
  169. package/dist/hosted-client/journal.d.ts +103 -0
  170. package/dist/hosted-client/journal.d.ts.map +1 -0
  171. package/dist/hosted-client/journal.js +464 -0
  172. package/dist/hosted-client/journal.js.map +1 -0
  173. package/dist/hosted-client/node.d.ts +74 -0
  174. package/dist/hosted-client/node.d.ts.map +1 -0
  175. package/dist/hosted-client/node.js +219 -0
  176. package/dist/hosted-client/node.js.map +1 -0
  177. package/dist/hosted-client/reference.d.ts +12 -0
  178. package/dist/hosted-client/reference.d.ts.map +1 -0
  179. package/dist/hosted-client/reference.js +29 -0
  180. package/dist/hosted-client/reference.js.map +1 -0
  181. package/dist/hosted-client/write.d.ts +217 -0
  182. package/dist/hosted-client/write.d.ts.map +1 -0
  183. package/dist/hosted-client/write.js +418 -0
  184. package/dist/hosted-client/write.js.map +1 -0
  185. package/dist/hosted-transport/answer-rows.d.ts +243 -0
  186. package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
  187. package/dist/hosted-transport/answer-rows.js +454 -0
  188. package/dist/hosted-transport/answer-rows.js.map +1 -0
  189. package/dist/hosted-transport/blob-reads.d.ts +35 -0
  190. package/dist/hosted-transport/blob-reads.d.ts.map +1 -0
  191. package/dist/hosted-transport/blob-reads.js +179 -0
  192. package/dist/hosted-transport/blob-reads.js.map +1 -0
  193. package/dist/hosted-transport/blob-write.d.ts +34 -0
  194. package/dist/hosted-transport/blob-write.d.ts.map +1 -0
  195. package/dist/hosted-transport/blob-write.js +115 -0
  196. package/dist/hosted-transport/blob-write.js.map +1 -0
  197. package/dist/hosted-transport/carrier.d.ts +151 -0
  198. package/dist/hosted-transport/carrier.d.ts.map +1 -0
  199. package/dist/hosted-transport/carrier.js +260 -0
  200. package/dist/hosted-transport/carrier.js.map +1 -0
  201. package/dist/hosted-transport/history-pages.d.ts +55 -0
  202. package/dist/hosted-transport/history-pages.d.ts.map +1 -0
  203. package/dist/hosted-transport/history-pages.js +69 -0
  204. package/dist/hosted-transport/history-pages.js.map +1 -0
  205. package/dist/hosted-transport/index.d.ts +18 -0
  206. package/dist/hosted-transport/index.d.ts.map +1 -0
  207. package/dist/hosted-transport/index.js +18 -0
  208. package/dist/hosted-transport/index.js.map +1 -0
  209. package/dist/hosted-transport/operations.d.ts +85 -0
  210. package/dist/hosted-transport/operations.d.ts.map +1 -0
  211. package/dist/hosted-transport/operations.js +128 -0
  212. package/dist/hosted-transport/operations.js.map +1 -0
  213. package/dist/hosted-transport/paged-reads.d.ts +87 -0
  214. package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
  215. package/dist/hosted-transport/paged-reads.js +238 -0
  216. package/dist/hosted-transport/paged-reads.js.map +1 -0
  217. package/dist/hosted-transport/read-adapter.d.ts +256 -0
  218. package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
  219. package/dist/hosted-transport/read-adapter.js +568 -0
  220. package/dist/hosted-transport/read-adapter.js.map +1 -0
  221. package/dist/hosted-transport/root-write.d.ts +114 -0
  222. package/dist/hosted-transport/root-write.d.ts.map +1 -0
  223. package/dist/hosted-transport/root-write.js +171 -0
  224. package/dist/hosted-transport/root-write.js.map +1 -0
  225. package/dist/hosted-transport/whole-document-transport.d.ts +204 -0
  226. package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
  227. package/dist/hosted-transport/whole-document-transport.js +447 -0
  228. package/dist/hosted-transport/whole-document-transport.js.map +1 -0
  229. package/dist/index.d.ts +34 -10
  230. package/dist/index.d.ts.map +1 -1
  231. package/dist/index.js +26 -8
  232. package/dist/index.js.map +1 -1
  233. package/dist/indexeddb-backend.d.ts +28 -6
  234. package/dist/indexeddb-backend.d.ts.map +1 -1
  235. package/dist/indexeddb-backend.js +471 -92
  236. package/dist/indexeddb-backend.js.map +1 -1
  237. package/dist/journaled-backend.d.ts +247 -10
  238. package/dist/journaled-backend.d.ts.map +1 -1
  239. package/dist/journaled-backend.js +302 -1
  240. package/dist/journaled-backend.js.map +1 -1
  241. package/dist/kind-authoring.d.ts +170 -0
  242. package/dist/kind-authoring.d.ts.map +1 -0
  243. package/dist/kind-authoring.js +451 -0
  244. package/dist/kind-authoring.js.map +1 -0
  245. package/dist/kind-conformance-error.d.ts +15 -0
  246. package/dist/kind-conformance-error.d.ts.map +1 -0
  247. package/dist/kind-conformance-error.js +20 -0
  248. package/dist/kind-conformance-error.js.map +1 -0
  249. package/dist/kind-coverage.d.ts +99 -0
  250. package/dist/kind-coverage.d.ts.map +1 -0
  251. package/dist/kind-coverage.js +186 -0
  252. package/dist/kind-coverage.js.map +1 -0
  253. package/dist/kind-field-mutation.d.ts +4 -0
  254. package/dist/kind-field-mutation.d.ts.map +1 -1
  255. package/dist/kind-field-mutation.js +28 -3
  256. package/dist/kind-field-mutation.js.map +1 -1
  257. package/dist/kinds-load.js +4 -4
  258. package/dist/kinds-load.js.map +1 -1
  259. package/dist/kinds.d.ts +65 -2
  260. package/dist/kinds.d.ts.map +1 -1
  261. package/dist/kinds.js +341 -21
  262. package/dist/kinds.js.map +1 -1
  263. package/dist/link-types.d.ts +26 -0
  264. package/dist/link-types.d.ts.map +1 -0
  265. package/dist/link-types.js +57 -0
  266. package/dist/link-types.js.map +1 -0
  267. package/dist/links.d.ts +21 -3
  268. package/dist/links.d.ts.map +1 -1
  269. package/dist/links.js +213 -14
  270. package/dist/links.js.map +1 -1
  271. package/dist/managed-document-metadata.d.ts +2 -0
  272. package/dist/managed-document-metadata.d.ts.map +1 -0
  273. package/dist/managed-document-metadata.js +9 -0
  274. package/dist/managed-document-metadata.js.map +1 -0
  275. package/dist/memory-backend.d.ts +2 -2
  276. package/dist/memory-backend.d.ts.map +1 -1
  277. package/dist/memory-backend.js +26 -11
  278. package/dist/memory-backend.js.map +1 -1
  279. package/dist/paths.d.ts +16 -0
  280. package/dist/paths.d.ts.map +1 -1
  281. package/dist/paths.js +25 -0
  282. package/dist/paths.js.map +1 -1
  283. package/dist/platform.d.ts +13 -2
  284. package/dist/platform.d.ts.map +1 -1
  285. package/dist/platform.js +16 -0
  286. package/dist/platform.js.map +1 -1
  287. package/dist/portable-export.d.ts +119 -0
  288. package/dist/portable-export.d.ts.map +1 -0
  289. package/dist/portable-export.js +501 -0
  290. package/dist/portable-export.js.map +1 -0
  291. package/dist/publication-filesystem.d.ts +1 -1
  292. package/dist/publication-filesystem.d.ts.map +1 -1
  293. package/dist/publication-filesystem.js +1 -1
  294. package/dist/publication-filesystem.js.map +1 -1
  295. package/dist/qualified-links.d.ts +67 -0
  296. package/dist/qualified-links.d.ts.map +1 -0
  297. package/dist/qualified-links.js +117 -0
  298. package/dist/qualified-links.js.map +1 -0
  299. package/dist/query-filter.d.ts +16 -2
  300. package/dist/query-filter.d.ts.map +1 -1
  301. package/dist/query-filter.js +40 -1
  302. package/dist/query-filter.js.map +1 -1
  303. package/dist/query-order.d.ts +22 -0
  304. package/dist/query-order.d.ts.map +1 -0
  305. package/dist/query-order.js +42 -0
  306. package/dist/query-order.js.map +1 -0
  307. package/dist/recipe-lifecycle/errors.d.ts +96 -0
  308. package/dist/recipe-lifecycle/errors.d.ts.map +1 -0
  309. package/dist/recipe-lifecycle/errors.js +122 -0
  310. package/dist/recipe-lifecycle/errors.js.map +1 -0
  311. package/dist/recipe-lifecycle/evolution.d.ts +108 -0
  312. package/dist/recipe-lifecycle/evolution.d.ts.map +1 -0
  313. package/dist/recipe-lifecycle/evolution.js +884 -0
  314. package/dist/recipe-lifecycle/evolution.js.map +1 -0
  315. package/dist/recipe-lifecycle/index.d.ts +26 -0
  316. package/dist/recipe-lifecycle/index.d.ts.map +1 -0
  317. package/dist/recipe-lifecycle/index.js +22 -0
  318. package/dist/recipe-lifecycle/index.js.map +1 -0
  319. package/dist/recipe-lifecycle/install.d.ts +127 -0
  320. package/dist/recipe-lifecycle/install.d.ts.map +1 -0
  321. package/dist/recipe-lifecycle/install.js +526 -0
  322. package/dist/recipe-lifecycle/install.js.map +1 -0
  323. package/dist/recipe-lifecycle/legacy-aliases.d.ts +16 -0
  324. package/dist/recipe-lifecycle/legacy-aliases.d.ts.map +1 -0
  325. package/dist/recipe-lifecycle/legacy-aliases.js +51 -0
  326. package/dist/recipe-lifecycle/legacy-aliases.js.map +1 -0
  327. package/dist/recipe-lifecycle/stamp.d.ts +136 -0
  328. package/dist/recipe-lifecycle/stamp.d.ts.map +1 -0
  329. package/dist/recipe-lifecycle/stamp.js +326 -0
  330. package/dist/recipe-lifecycle/stamp.js.map +1 -0
  331. package/dist/recipes.d.ts +29 -1
  332. package/dist/recipes.d.ts.map +1 -1
  333. package/dist/recipes.js +137 -1
  334. package/dist/recipes.js.map +1 -1
  335. package/dist/record-overlay/apply.d.ts +43 -0
  336. package/dist/record-overlay/apply.d.ts.map +1 -0
  337. package/dist/record-overlay/apply.js +527 -0
  338. package/dist/record-overlay/apply.js.map +1 -0
  339. package/dist/record-overlay/canonical.d.ts +22 -0
  340. package/dist/record-overlay/canonical.d.ts.map +1 -0
  341. package/dist/record-overlay/canonical.js +47 -0
  342. package/dist/record-overlay/canonical.js.map +1 -0
  343. package/dist/record-overlay/digest.d.ts +4 -0
  344. package/dist/record-overlay/digest.d.ts.map +1 -0
  345. package/dist/record-overlay/digest.js +11 -0
  346. package/dist/record-overlay/digest.js.map +1 -0
  347. package/dist/record-overlay/errors.d.ts +44 -0
  348. package/dist/record-overlay/errors.d.ts.map +1 -0
  349. package/dist/record-overlay/errors.js +55 -0
  350. package/dist/record-overlay/errors.js.map +1 -0
  351. package/dist/record-overlay/findings.d.ts +5 -0
  352. package/dist/record-overlay/findings.d.ts.map +1 -0
  353. package/dist/record-overlay/findings.js +83 -0
  354. package/dist/record-overlay/findings.js.map +1 -0
  355. package/dist/record-overlay/index.d.ts +23 -0
  356. package/dist/record-overlay/index.d.ts.map +1 -0
  357. package/dist/record-overlay/index.js +21 -0
  358. package/dist/record-overlay/index.js.map +1 -0
  359. package/dist/record-overlay/plan.d.ts +12 -0
  360. package/dist/record-overlay/plan.d.ts.map +1 -0
  361. package/dist/record-overlay/plan.js +285 -0
  362. package/dist/record-overlay/plan.js.map +1 -0
  363. package/dist/record-overlay/policy.d.ts +69 -0
  364. package/dist/record-overlay/policy.d.ts.map +1 -0
  365. package/dist/record-overlay/policy.js +228 -0
  366. package/dist/record-overlay/policy.js.map +1 -0
  367. package/dist/record-overlay/redact.d.ts +3 -0
  368. package/dist/record-overlay/redact.d.ts.map +1 -0
  369. package/dist/record-overlay/redact.js +27 -0
  370. package/dist/record-overlay/redact.js.map +1 -0
  371. package/dist/record-overlay/signal-kind.d.ts +30 -0
  372. package/dist/record-overlay/signal-kind.d.ts.map +1 -0
  373. package/dist/record-overlay/signal-kind.js +38 -0
  374. package/dist/record-overlay/signal-kind.js.map +1 -0
  375. package/dist/record-overlay/signal-text.d.ts +45 -0
  376. package/dist/record-overlay/signal-text.d.ts.map +1 -0
  377. package/dist/record-overlay/signal-text.js +116 -0
  378. package/dist/record-overlay/signal-text.js.map +1 -0
  379. package/dist/record-overlay/source-index.d.ts +19 -0
  380. package/dist/record-overlay/source-index.d.ts.map +1 -0
  381. package/dist/record-overlay/source-index.js +102 -0
  382. package/dist/record-overlay/source-index.js.map +1 -0
  383. package/dist/record-overlay/types.d.ts +291 -0
  384. package/dist/record-overlay/types.d.ts.map +1 -0
  385. package/dist/record-overlay/types.js +2 -0
  386. package/dist/record-overlay/types.js.map +1 -0
  387. package/dist/remote-backend.d.ts +67 -67
  388. package/dist/remote-backend.d.ts.map +1 -1
  389. package/dist/remote-backend.js +169 -201
  390. package/dist/remote-backend.js.map +1 -1
  391. package/dist/remote-document-codec.d.ts +15 -0
  392. package/dist/remote-document-codec.d.ts.map +1 -0
  393. package/dist/remote-document-codec.js +103 -0
  394. package/dist/remote-document-codec.js.map +1 -0
  395. package/dist/remote-error.d.ts +38 -0
  396. package/dist/remote-error.d.ts.map +1 -0
  397. package/dist/remote-error.js +59 -0
  398. package/dist/remote-error.js.map +1 -0
  399. package/dist/remote-operations.d.ts +6 -3
  400. package/dist/remote-operations.d.ts.map +1 -1
  401. package/dist/remote-operations.js +23 -5
  402. package/dist/remote-operations.js.map +1 -1
  403. package/dist/remote-parsers.d.ts +86 -0
  404. package/dist/remote-parsers.d.ts.map +1 -0
  405. package/dist/remote-parsers.js +181 -0
  406. package/dist/remote-parsers.js.map +1 -0
  407. package/dist/remote.d.ts +5 -2
  408. package/dist/remote.d.ts.map +1 -1
  409. package/dist/remote.js +5 -1
  410. package/dist/remote.js.map +1 -1
  411. package/dist/search/backend-source.d.ts +6 -0
  412. package/dist/search/backend-source.d.ts.map +1 -0
  413. package/dist/search/backend-source.js +33 -0
  414. package/dist/search/backend-source.js.map +1 -0
  415. package/dist/search/contract.d.ts +151 -0
  416. package/dist/search/contract.d.ts.map +1 -0
  417. package/dist/search/contract.js +78 -0
  418. package/dist/search/contract.js.map +1 -0
  419. package/dist/search/errors.d.ts +30 -0
  420. package/dist/search/errors.d.ts.map +1 -0
  421. package/dist/search/errors.js +50 -0
  422. package/dist/search/errors.js.map +1 -0
  423. package/dist/search/identity.d.ts +12 -0
  424. package/dist/search/identity.d.ts.map +1 -0
  425. package/dist/search/identity.js +90 -0
  426. package/dist/search/identity.js.map +1 -0
  427. package/dist/search/index.d.ts +16 -0
  428. package/dist/search/index.d.ts.map +1 -0
  429. package/dist/search/index.js +15 -0
  430. package/dist/search/index.js.map +1 -0
  431. package/dist/search/memory-source.d.ts +36 -0
  432. package/dist/search/memory-source.d.ts.map +1 -0
  433. package/dist/search/memory-source.js +235 -0
  434. package/dist/search/memory-source.js.map +1 -0
  435. package/dist/search/rank.d.ts +34 -0
  436. package/dist/search/rank.d.ts.map +1 -0
  437. package/dist/search/rank.js +129 -0
  438. package/dist/search/rank.js.map +1 -0
  439. package/dist/search/row-text.d.ts +38 -0
  440. package/dist/search/row-text.d.ts.map +1 -0
  441. package/dist/search/row-text.js +88 -0
  442. package/dist/search/row-text.js.map +1 -0
  443. package/dist/search/search.d.ts +28 -0
  444. package/dist/search/search.d.ts.map +1 -0
  445. package/dist/search/search.js +257 -0
  446. package/dist/search/search.js.map +1 -0
  447. package/dist/search/source.d.ts +67 -0
  448. package/dist/search/source.d.ts.map +1 -0
  449. package/dist/search/source.js +2 -0
  450. package/dist/search/source.js.map +1 -0
  451. package/dist/search/text.d.ts +47 -0
  452. package/dist/search/text.d.ts.map +1 -0
  453. package/dist/search/text.js +148 -0
  454. package/dist/search/text.js.map +1 -0
  455. package/dist/site-capabilities.d.ts +339 -0
  456. package/dist/site-capabilities.d.ts.map +1 -0
  457. package/dist/site-capabilities.js +717 -0
  458. package/dist/site-capabilities.js.map +1 -0
  459. package/dist/source-engine/guard.d.ts +82 -0
  460. package/dist/source-engine/guard.d.ts.map +1 -0
  461. package/dist/source-engine/guard.js +396 -0
  462. package/dist/source-engine/guard.js.map +1 -0
  463. package/dist/source-engine/index.d.ts +24 -0
  464. package/dist/source-engine/index.d.ts.map +1 -0
  465. package/dist/source-engine/index.js +20 -0
  466. package/dist/source-engine/index.js.map +1 -0
  467. package/dist/source-engine/machine-state.d.ts +74 -0
  468. package/dist/source-engine/machine-state.d.ts.map +1 -0
  469. package/dist/source-engine/machine-state.js +48 -0
  470. package/dist/source-engine/machine-state.js.map +1 -0
  471. package/dist/source-engine/observe.d.ts +18 -0
  472. package/dist/source-engine/observe.d.ts.map +1 -0
  473. package/dist/source-engine/observe.js +598 -0
  474. package/dist/source-engine/observe.js.map +1 -0
  475. package/dist/source-engine/robots.d.ts +53 -0
  476. package/dist/source-engine/robots.d.ts.map +1 -0
  477. package/dist/source-engine/robots.js +158 -0
  478. package/dist/source-engine/robots.js.map +1 -0
  479. package/dist/source-engine/types.d.ts +285 -0
  480. package/dist/source-engine/types.d.ts.map +1 -0
  481. package/dist/source-engine/types.js +2 -0
  482. package/dist/source-engine/types.js.map +1 -0
  483. package/dist/source-harness/conformance.d.ts +16 -0
  484. package/dist/source-harness/conformance.d.ts.map +1 -0
  485. package/dist/source-harness/conformance.js +265 -0
  486. package/dist/source-harness/conformance.js.map +1 -0
  487. package/dist/source-harness/fixture.d.ts +95 -0
  488. package/dist/source-harness/fixture.d.ts.map +1 -0
  489. package/dist/source-harness/fixture.js +261 -0
  490. package/dist/source-harness/fixture.js.map +1 -0
  491. package/dist/source-harness/index.d.ts +22 -0
  492. package/dist/source-harness/index.d.ts.map +1 -0
  493. package/dist/source-harness/index.js +19 -0
  494. package/dist/source-harness/index.js.map +1 -0
  495. package/dist/source-harness/peer.d.ts +74 -0
  496. package/dist/source-harness/peer.d.ts.map +1 -0
  497. package/dist/source-harness/peer.js +106 -0
  498. package/dist/source-harness/peer.js.map +1 -0
  499. package/dist/storage.d.ts +6 -2
  500. package/dist/storage.d.ts.map +1 -1
  501. package/dist/storage.js +4 -1
  502. package/dist/storage.js.map +1 -1
  503. package/dist/types.d.ts +33 -5
  504. package/dist/types.d.ts.map +1 -1
  505. package/dist/uncertain-write.d.ts +156 -19
  506. package/dist/uncertain-write.d.ts.map +1 -1
  507. package/dist/uncertain-write.js +90 -31
  508. package/dist/uncertain-write.js.map +1 -1
  509. package/dist/versioning.d.ts +9 -4
  510. package/dist/versioning.d.ts.map +1 -1
  511. package/dist/versioning.js +9 -4
  512. package/dist/versioning.js.map +1 -1
  513. package/dist/write-identity.d.ts +5 -0
  514. package/dist/write-identity.d.ts.map +1 -0
  515. package/dist/write-identity.js +9 -0
  516. package/dist/write-identity.js.map +1 -0
  517. package/package.json +94 -2
@@ -0,0 +1,1150 @@
1
+ /**
2
+ * `@superbee/core/connectors`: target-agnostic Connector declarations.
3
+ *
4
+ * A Connector is a `type: Connector` document under `connectors/` that says what an integration
5
+ * is: the credential kind (whose hosts come only from core's {@link AUTH_PROVIDERS} table, never
6
+ * from the document), keyless `domains`, `record_sources` that map normalized records onto one
7
+ * target Kind's fields, and actions and events as declared shapes. The document holds no authority:
8
+ * nothing here fetches, executes or receives. {@link parseConnectorDoc} is the one parser, every
9
+ * nested block is closed by {@link CONNECTOR_KEYS}, and {@link connectorPinDigest} reads the same
10
+ * table, so a key cannot be accepted without being pinned.
11
+ *
12
+ * The mapping contract is code: {@link mapRecord} never lets a mapping gap erase a stored value (an
13
+ * unobserved field is carried, an unmapped value is kept or refuses the record, and only an
14
+ * explicit empty value clears an optional field); {@link changeBetween} builds the record-overlay
15
+ * change; {@link findingsFor} turns diagnostics into findings for `planFindings`.
16
+ * Storage-neutral: no Node globals or builtins.
17
+ */
18
+ import { compareCodePoints } from "./code-point-order.js";
19
+ import { isStandardDocumentSetField } from "./document-field-actions.js";
20
+ import { PROGRESS_STATUS_FIELD, isKindListField, isPresent } from "./kinds.js";
21
+ import { isManagedDocumentMetadataField } from "./managed-document-metadata.js";
22
+ import { overlayCanonicalJson, same } from "./record-overlay/canonical.js";
23
+ import { OverlayRefusal, REFUSAL_CODES } from "./record-overlay/errors.js";
24
+ import { NEVER_OWNED, readOverlayPolicy, workStateFields } from "./record-overlay/policy.js";
25
+ import { definitionCanonicalJson } from "./recipe-lifecycle/stamp.js";
26
+ import { sha256Hex } from "./versioning.js";
27
+ export const CONNECTOR_TYPE = "Connector";
28
+ export const CONNECTORS_PREFIX = "connectors/";
29
+ /** The normalization rules of {@link connectorPinDigest}; bumped whenever a default or sort rule changes. */
30
+ export const CONNECTOR_PIN_RULES = 1;
31
+ /**
32
+ * Credential providers: provider key to the exact hostnames a `token` or `oauth2` credential may be
33
+ * sent to. A document names a row; it never states a host. Each row is added in its own reviewed
34
+ * change, verified against the vendor's primary documentation. The pin records the hosts a row
35
+ * resolves to, so adding a host to an existing row changes the pin of every Connection on that
36
+ * provider, which then needs approving again.
37
+ */
38
+ export const AUTH_PROVIDERS = Object.freeze({
39
+ github: Object.freeze(["api.github.com"]),
40
+ });
41
+ const GITHUB_APP_HOSTS = Object.freeze(["api.github.com"]);
42
+ /** The accepted keys of every closed block. The shape check and the pin digest read only this table. */
43
+ export const CONNECTOR_KEYS = Object.freeze({
44
+ auth: Object.freeze(["kind", "provider", "permissions", "scheme"]),
45
+ source: Object.freeze(["name", "profile", "target_kind", "mode", "identity", "fields", "completeness"]),
46
+ identity: Object.freeze(["resource", "id"]),
47
+ mapping: Object.freeze(["from", "values", "unmapped"]),
48
+ action: Object.freeze(["name", "method", "host", "input", "confirm"]),
49
+ input: Object.freeze(["type", "required", "values"]),
50
+ event: Object.freeze(["name", "signature", "references"]),
51
+ signature: Object.freeze(["scheme", "header"]),
52
+ });
53
+ /**
54
+ * The top-level keys a Connector declares beside OKF's `type`, `title` and `description`. None may
55
+ * be an OKF standard or managed field of either edition: OKF v0.2 reserves `sources` for
56
+ * provenance, which is why the declaration's sources live under `record_sources`.
57
+ */
58
+ export const CONNECTOR_DOCUMENT_KEYS = Object.freeze(["version", "auth", "domains", "record_sources", "actions", "events"]);
59
+ export const CONNECTOR_ERROR_CODES = Object.freeze([
60
+ "CONNECTOR_MALFORMED",
61
+ "CONNECTOR_UNKNOWN_KEY",
62
+ "CONNECTOR_AUTH_INVALID",
63
+ "CONNECTOR_SECRET_PRESENT",
64
+ "CONNECTOR_DOMAIN_INVALID",
65
+ "CONNECTOR_DUPLICATE",
66
+ "CONNECTOR_PROFILE_INVALID",
67
+ "CONNECTOR_TARGET_KIND_INVALID",
68
+ "CONNECTOR_MODE_UNSUPPORTED",
69
+ "CONNECTOR_IDENTITY_UNSTABLE",
70
+ "CONNECTOR_FIELD_INVALID",
71
+ "CONNECTOR_FIELD_NEVER_OWNED",
72
+ "CONNECTOR_FIELD_UNDECLARED",
73
+ "CONNECTOR_VALUES_INVALID",
74
+ "CONNECTOR_LIST_FIELD_UNSUPPORTED",
75
+ "CONNECTOR_ACTION_INVALID",
76
+ "CONNECTOR_EVENT_INVALID",
77
+ ]);
78
+ // ------------------------------------------------------------------------------------------ grammar
79
+ const SLUG = /^[a-z][a-z0-9-]*$/;
80
+ const DOC_SLUG = /^[a-z0-9][a-z0-9-]*$/;
81
+ const FIELD_NAME = /^[A-Za-z][A-Za-z0-9_]*$/;
82
+ const PATH_SEGMENT = /^[A-Za-z0-9_-]+$/;
83
+ const LOWER_NAME = /^[a-z][a-z0-9_]*$/;
84
+ const HEADER = /^[a-z0-9-]+$/;
85
+ const PARAMETER = "[A-Za-z0-9._~-]+";
86
+ const HOST_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
87
+ const URL_CHARS = /^[A-Za-z0-9\-._~:/?[\]@!$&'()*+,;=%]*$/;
88
+ const PROTOTYPE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
89
+ const RESERVED_TARGET_KINDS = new Set(["Convention", "View", "Connector", "Reference", "Source Feed", "Signal"]);
90
+ const AUTH_KINDS = ["none", "token", "github-app", "oauth2", "signed-webhook"];
91
+ const AUTH_KEYS_BY_KIND = {
92
+ none: [], token: ["provider"], "github-app": ["permissions"], oauth2: ["provider"], "signed-webhook": ["scheme"],
93
+ };
94
+ const METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
95
+ const INPUT_TYPES = new Set(["string", "text", "integer", "boolean"]);
96
+ const MAX_TEMPLATE = 2048;
97
+ function pointer(path) {
98
+ return path.map((s) => "/" + String(s).replaceAll("~", "~0").replaceAll("/", "~1")).join("");
99
+ }
100
+ function record(value) {
101
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
102
+ }
103
+ /** A hostname as the declaration grammar admits it, or the reason it is refused. */
104
+ function hostnameProblem(host) {
105
+ if (typeof host !== "string" || host.length === 0 || host.length > 253)
106
+ return "must be a hostname of 1 to 253 characters";
107
+ const labels = host.split(".");
108
+ if (labels.length < 2)
109
+ return "needs two or more labels";
110
+ if (!labels.every((label) => HOST_LABEL.test(label)))
111
+ return "takes lowercase labels of letters, digits and inner hyphens, with no trailing dot, scheme, port, path or wildcard";
112
+ const last = labels[labels.length - 1];
113
+ if (/^[0-9]+$/.test(last) || /^0x[0-9a-f]*$/.test(last))
114
+ return "may not end in a numeric label (an address literal)";
115
+ return undefined;
116
+ }
117
+ /** The hosts a credential of this kind may be sent to: a table row or a fixed set, never document text. */
118
+ export function credentialHosts(auth) {
119
+ if (auth.kind === "github-app")
120
+ return GITHUB_APP_HOSTS;
121
+ if ((auth.kind === "token" || auth.kind === "oauth2") && typeof auth.provider === "string" && Object.hasOwn(AUTH_PROVIDERS, auth.provider)) {
122
+ return AUTH_PROVIDERS[auth.provider];
123
+ }
124
+ return [];
125
+ }
126
+ // ------------------------------------------------------------------------------------ secret scan
127
+ const SECRET_KEYS = new Set(["token", "secret", "password", "passwd", "client_secret", "private_key", "api_key", "apikey", "authorization", "bearer", "cookie"]);
128
+ const SECRET_PREFIXES = ["ghp_", "gho_", "ghu_", "ghs_", "ghr_", "github_pat_"];
129
+ /**
130
+ * A URL authority carrying userinfo (`scheme://name@` or `scheme://name:password@`) anywhere in the
131
+ * text. A linear scan with no `URL` global, so the rule holds in every realm.
132
+ */
133
+ function hasUserinfo(text) {
134
+ for (let at = text.indexOf("://"); at !== -1; at = text.indexOf("://", at + 3)) {
135
+ for (let i = at + 3; i < text.length; i++) {
136
+ const c = text[i];
137
+ if (c === "@")
138
+ return true;
139
+ if (c === "/" || c === "?" || c === "#" || c.trim() === "")
140
+ break;
141
+ }
142
+ }
143
+ return false;
144
+ }
145
+ function secretShaped(text) {
146
+ const s = text.trimStart();
147
+ return SECRET_PREFIXES.some((prefix) => s.startsWith(prefix)) || /^xox[abprs]-/.test(s) || /^bearer /i.test(s)
148
+ || (text.includes("-----BEGIN") && text.includes("PRIVATE KEY")) || hasUserinfo(text);
149
+ }
150
+ /** Token shapes in prose: a prefix followed by token characters, or `Bearer` followed by a token-like run. */
151
+ function bodyHoldsSecret(body) {
152
+ if (/(?:^|[^A-Za-z0-9_])(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{8}/.test(body))
153
+ return true;
154
+ if (/(?:^|[^A-Za-z0-9_])xox[abprs]-[A-Za-z0-9-]{8}/.test(body))
155
+ return true;
156
+ if (body.includes("-----BEGIN") && body.includes("PRIVATE KEY"))
157
+ return true;
158
+ if (hasUserinfo(body))
159
+ return true;
160
+ for (const match of body.matchAll(/\bbearer[ \t]+([A-Za-z0-9._~+/=-]{16,})/gi)) {
161
+ if (/[0-9]/.test(match[1]) && /[A-Za-z]/.test(match[1]))
162
+ return true;
163
+ }
164
+ return false;
165
+ }
166
+ const holdsSecret = (text) => secretShaped(text) || bodyHoldsSecret(text);
167
+ /**
168
+ * Keys hold no prose, so a token glued to a word still counts. The shapes use the providers' real
169
+ * token lengths, so data names that merely contain a prefix ("highs_", "weighs_") stay legal.
170
+ */
171
+ const keyHoldsSecret = (key) => holdsSecret(key) || /gh[pousr]_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{22,}|xox[abprs]-[A-Za-z0-9-]{10,}/.test(key);
172
+ /**
173
+ * Maps whose keys are data names (target fields, provider natives, input and permission names), not
174
+ * credential slots: their keys get the token-shape rules only, never the key-name list.
175
+ */
176
+ function keysAreDataNames(path) {
177
+ const n = path.length;
178
+ return (n === 3 && path[0] === "record_sources" && path[2] === "fields")
179
+ || (n === 5 && path[0] === "record_sources" && path[2] === "fields" && path[4] === "values")
180
+ || (n === 3 && path[0] === "actions" && path[2] === "input")
181
+ || (n === 2 && path[0] === "auth" && path[1] === "permissions");
182
+ }
183
+ /**
184
+ * Walk the frontmatter once, visiting each container at most once: a container reached twice is a
185
+ * YAML alias (shared or circular), refused as `CONNECTOR_MALFORMED` so the scan, and every reader
186
+ * after it, stays bounded by the document's size. Secret refusals name a path and never a value; a
187
+ * secret-looking key reports its parent.
188
+ */
189
+ function scanDocument(value, path, out, seen) {
190
+ const add = (at, what) => {
191
+ const p = pointer(at);
192
+ if (!out.has(p))
193
+ out.set(p, { code: "CONNECTOR_SECRET_PRESENT", path: p, message: `${what} at '${p || "/"}'; a declaration never carries a credential` });
194
+ };
195
+ if (typeof value === "string") {
196
+ if (holdsSecret(value))
197
+ add(path, "a secret-shaped value");
198
+ return;
199
+ }
200
+ if (!Array.isArray(value) && !record(value))
201
+ return;
202
+ if (seen.has(value)) {
203
+ const p = pointer(path);
204
+ out.set(`alias:${p}`, { code: "CONNECTOR_MALFORMED", path: p, message: `a Connector may not use YAML aliases (at '${p || "/"}')` });
205
+ return;
206
+ }
207
+ seen.add(value);
208
+ if (Array.isArray(value)) {
209
+ value.forEach((item, index) => scanDocument(item, [...path, index], out, seen));
210
+ return;
211
+ }
212
+ const data = keysAreDataNames(path);
213
+ for (const [key, item] of Object.entries(value)) {
214
+ if ((!data && SECRET_KEYS.has(key.toLowerCase())) || keyHoldsSecret(key))
215
+ add(path, "a secret-looking key");
216
+ else
217
+ scanDocument(item, [...path, key], out, seen);
218
+ }
219
+ }
220
+ function escapeRegExp(text) {
221
+ return text.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
222
+ }
223
+ /**
224
+ * An absolute https URL template. A placeholder lies inside one path segment (at most one per
225
+ * segment, with optional literal text around it) or is a whole query value; never in the host. No
226
+ * userinfo, port, fragment or dot segment.
227
+ */
228
+ function parseResourceTemplate(template) {
229
+ const fail = (reason) => ({ ok: false, reason });
230
+ if (typeof template !== "string" || template.length === 0 || template.length > MAX_TEMPLATE)
231
+ return fail(`must be a URL template of at most ${MAX_TEMPLATE} characters`);
232
+ if (!template.startsWith("https://"))
233
+ return fail("must be an absolute https URL");
234
+ if (template.includes("#"))
235
+ return fail("may not carry a fragment");
236
+ if (!URL_CHARS.test(template.replace(/\{[^{}]*\}/g, "x")))
237
+ return fail("holds characters a URL does not");
238
+ const rest = template.slice("https://".length);
239
+ const cut = rest.search(/[/?]/);
240
+ const authority = cut === -1 ? rest : rest.slice(0, cut);
241
+ if (/[{}]/.test(authority))
242
+ return fail("may not place a placeholder in the host");
243
+ if (authority.includes("@"))
244
+ return fail("may not carry userinfo");
245
+ if (authority.includes(":"))
246
+ return fail("may not carry a port");
247
+ const hostProblem = hostnameProblem(authority);
248
+ if (hostProblem)
249
+ return fail(`host ${hostProblem}`);
250
+ const tail = cut === -1 ? "" : rest.slice(cut);
251
+ const queryAt = tail.indexOf("?");
252
+ const pathPart = queryAt === -1 ? tail : tail.slice(0, queryAt);
253
+ const query = queryAt === -1 ? undefined : tail.slice(queryAt + 1);
254
+ const parts = [{ literal: `https://${authority}` }];
255
+ const names = new Set();
256
+ const placeholder = (name) => {
257
+ if (!LOWER_NAME.test(name))
258
+ return `placeholder '{${name}}' must be named [a-z][a-z0-9_]*`;
259
+ if (names.has(name))
260
+ return `placeholder '{${name}}' appears twice`;
261
+ names.add(name);
262
+ parts.push({ name });
263
+ return undefined;
264
+ };
265
+ const decoded = (text) => {
266
+ try {
267
+ return decodeURIComponent(text);
268
+ }
269
+ catch {
270
+ return undefined;
271
+ }
272
+ };
273
+ if (pathPart !== "") {
274
+ for (const segment of pathPart.slice(1).split("/")) {
275
+ parts.push({ literal: "/" });
276
+ const match = /^([^{}]*)\{([^{}]*)\}([^{}]*)$/.exec(segment);
277
+ const literals = match ? [match[1], match[3]] : [segment];
278
+ const plain = literals.map(decoded);
279
+ if (plain.some((text) => text === undefined))
280
+ return fail("holds a malformed percent-encoding");
281
+ if (!match && (plain[0] === "." || plain[0] === ".."))
282
+ return fail("may not carry a dot segment, plain or percent-encoded");
283
+ if (plain.some((text) => /[/\\]/.test(text)))
284
+ return fail("may not encode a path separator");
285
+ if (match) {
286
+ parts.push({ literal: match[1] });
287
+ const problem = placeholder(match[2]);
288
+ if (problem)
289
+ return fail(problem);
290
+ parts.push({ literal: match[3] });
291
+ }
292
+ else if (/[{}]/.test(segment))
293
+ return fail("allows one placeholder per path segment");
294
+ else
295
+ parts.push({ literal: segment });
296
+ }
297
+ }
298
+ if (query !== undefined) {
299
+ parts.push({ literal: "?" });
300
+ const pieces = query.split("&");
301
+ for (const [index, piece] of pieces.entries()) {
302
+ if (index > 0)
303
+ parts.push({ literal: "&" });
304
+ const eq = piece.indexOf("=");
305
+ const key = eq === -1 ? piece : piece.slice(0, eq);
306
+ const value = eq === -1 ? "" : piece.slice(eq + 1);
307
+ if (/[{}]/.test(key))
308
+ return fail("allows a placeholder only as a whole query value");
309
+ parts.push({ literal: eq === -1 ? key : `${key}=` });
310
+ const whole = /^\{([^{}]*)\}$/.exec(value);
311
+ if (whole) {
312
+ const problem = placeholder(whole[1]);
313
+ if (problem)
314
+ return fail(problem);
315
+ }
316
+ else if (/[{}]/.test(value))
317
+ return fail("allows a placeholder only as a whole query value");
318
+ else
319
+ parts.push({ literal: value });
320
+ }
321
+ }
322
+ const matcher = new RegExp(`^${parts.map((p) => ("name" in p ? `(${PARAMETER})` : escapeRegExp(p.literal))).join("")}$`);
323
+ return { ok: true, value: { parts, matcher } };
324
+ }
325
+ /** A record path: segments `[A-Za-z0-9_-]+` joined by dots, none a prototype name. */
326
+ function pathProblem(path) {
327
+ if (typeof path !== "string" || path === "" || path.length > 512)
328
+ return "must be a dotted record path";
329
+ for (const segment of path.split(".")) {
330
+ if (!PATH_SEGMENT.test(segment))
331
+ return "takes segments of letters, digits, '_' and '-' joined by '.'";
332
+ if (PROTOTYPE_KEYS.has(segment))
333
+ return `may not name '${segment}'`;
334
+ }
335
+ return undefined;
336
+ }
337
+ /** An id template over record paths with at least one placeholder. */
338
+ function parseIdTemplate(template) {
339
+ if (typeof template !== "string" || template === "" || template.length > MAX_TEMPLATE)
340
+ return { ok: false, reason: "must be a template string" };
341
+ const parts = [];
342
+ const paths = [];
343
+ const re = /\{([^{}]*)\}/g;
344
+ let last = 0;
345
+ for (const match of template.matchAll(re)) {
346
+ parts.push({ literal: template.slice(last, match.index) });
347
+ const problem = pathProblem(match[1]);
348
+ if (problem)
349
+ return { ok: false, reason: `placeholder '{${match[1]}}' ${problem}` };
350
+ parts.push({ path: match[1] });
351
+ paths.push(match[1]);
352
+ last = match.index + match[0].length;
353
+ }
354
+ parts.push({ literal: template.slice(last) });
355
+ if (parts.some((p) => "literal" in p && /[{}]/.test(p.literal)))
356
+ return { ok: false, reason: "has an unbalanced brace" };
357
+ if (paths.length === 0)
358
+ return { ok: false, reason: "needs at least one {record.path} placeholder" };
359
+ // Between two placeholders a literal must separate them, or two records could render one id.
360
+ if (parts.some((p, i) => "literal" in p && p.literal === "" && i > 0 && i < parts.length - 1)) {
361
+ return { ok: false, reason: "needs literal text between placeholders" };
362
+ }
363
+ return { ok: true, parts, paths };
364
+ }
365
+ // ----------------------------------------------------------------------------------------- parser
366
+ class Errors {
367
+ list = [];
368
+ add(code, path, message) {
369
+ this.list.push({ code, path: pointer(path), message });
370
+ }
371
+ closed(block, value, at) {
372
+ const accepted = CONNECTOR_KEYS[block];
373
+ for (const key of Object.keys(value)) {
374
+ if (!accepted.includes(key))
375
+ this.add("CONNECTOR_UNKNOWN_KEY", [...at, key], `'${key}' is not a ${block} key (accepted: ${accepted.join(", ")})`);
376
+ }
377
+ }
378
+ }
379
+ function list(value, at, errors, what) {
380
+ if (value === undefined || value === null)
381
+ return [];
382
+ if (!Array.isArray(value)) {
383
+ errors.add("CONNECTOR_MALFORMED", at, `${what} must be a list`);
384
+ return undefined;
385
+ }
386
+ return value;
387
+ }
388
+ function parseAuth(raw, errors) {
389
+ if (raw === undefined) {
390
+ errors.add("CONNECTOR_MALFORMED", ["auth"], "auth is required");
391
+ return undefined;
392
+ }
393
+ if (!record(raw)) {
394
+ errors.add("CONNECTOR_MALFORMED", ["auth"], "auth must be a mapping");
395
+ return undefined;
396
+ }
397
+ errors.closed("auth", raw, ["auth"]);
398
+ const kind = raw.kind;
399
+ if (!AUTH_KINDS.includes(kind)) {
400
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", "kind"], `auth.kind must be one of ${AUTH_KINDS.join(", ")}`);
401
+ return undefined;
402
+ }
403
+ const before = errors.list.length;
404
+ for (const key of ["provider", "permissions", "scheme"]) {
405
+ if (key in raw && !AUTH_KEYS_BY_KIND[kind].includes(key))
406
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", key], `'${key}' belongs to another auth kind than ${kind}`);
407
+ }
408
+ const auth = { kind };
409
+ if (kind === "token" || kind === "oauth2") {
410
+ if (typeof raw.provider !== "string" || !Object.hasOwn(AUTH_PROVIDERS, raw.provider)) {
411
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", "provider"], `${kind} needs a provider core's table names (${Object.keys(AUTH_PROVIDERS).join(", ")})`);
412
+ }
413
+ else
414
+ auth.provider = raw.provider;
415
+ }
416
+ else if (kind === "github-app") {
417
+ const permissions = {};
418
+ if (raw.permissions !== undefined && !record(raw.permissions))
419
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", "permissions"], "permissions must be a mapping");
420
+ else
421
+ for (const [name, access] of Object.entries(raw.permissions ?? {})) {
422
+ if (!LOWER_NAME.test(name) || PROTOTYPE_KEYS.has(name))
423
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", "permissions", name], "a permission name is [a-z][a-z0-9_]*");
424
+ else if (access !== "read" && access !== "write")
425
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", "permissions", name], "a permission is read or write");
426
+ else
427
+ permissions[name] = access;
428
+ }
429
+ auth.permissions = permissions;
430
+ }
431
+ else if (kind === "signed-webhook") {
432
+ if (raw.scheme !== "hmac-sha256")
433
+ errors.add("CONNECTOR_AUTH_INVALID", ["auth", "scheme"], "signed-webhook takes scheme hmac-sha256");
434
+ else
435
+ auth.scheme = "hmac-sha256";
436
+ }
437
+ return errors.list.length === before ? auth : undefined;
438
+ }
439
+ function parseDomains(raw, auth, errors) {
440
+ const entries = list(raw, ["domains"], errors, "domains") ?? [];
441
+ const seen = new Set();
442
+ const credential = new Set(auth ? credentialHosts(auth) : []);
443
+ entries.forEach((entry, index) => {
444
+ const problem = hostnameProblem(entry);
445
+ if (problem)
446
+ errors.add("CONNECTOR_DOMAIN_INVALID", ["domains", index], `a domain ${problem}`);
447
+ else if (seen.has(entry))
448
+ errors.add("CONNECTOR_DOMAIN_INVALID", ["domains", index], `domain '${entry}' is listed twice`);
449
+ else if (credential.has(entry))
450
+ errors.add("CONNECTOR_DOMAIN_INVALID", ["domains", index], `domain '${entry}' is already a credential host of auth kind ${auth.kind}`);
451
+ seen.add(entry);
452
+ });
453
+ return [...seen];
454
+ }
455
+ /** Why a target field can never be a mapping key, whatever the Kind declares. */
456
+ function neverOwnedReason(field) {
457
+ if (NEVER_OWNED.has(field))
458
+ return "a field no source may own";
459
+ if (isManagedDocumentMetadataField(field))
460
+ return "managed document metadata";
461
+ if (field === PROGRESS_STATUS_FIELD || workStateFields(undefined, []).has(field))
462
+ return "work state";
463
+ if (field === "body")
464
+ return "the document body";
465
+ return undefined;
466
+ }
467
+ function parseMapping(field, raw, at, errors) {
468
+ const before = errors.list.length;
469
+ if (!FIELD_NAME.test(field) || PROTOTYPE_KEYS.has(field)) {
470
+ errors.add("CONNECTOR_FIELD_INVALID", at, `mapping key '${field}' must be a field name [A-Za-z][A-Za-z0-9_]*`);
471
+ return undefined;
472
+ }
473
+ const reason = neverOwnedReason(field);
474
+ if (reason)
475
+ errors.add("CONNECTOR_FIELD_NEVER_OWNED", at, `'${field}' is ${reason} and can never be mapped`);
476
+ const entry = typeof raw === "string" ? { from: raw } : raw;
477
+ if (!record(entry)) {
478
+ errors.add("CONNECTOR_MALFORMED", at, "a mapping is a record path or { from, values, unmapped }");
479
+ return undefined;
480
+ }
481
+ if (typeof raw !== "string")
482
+ errors.closed("mapping", entry, at);
483
+ const problem = pathProblem(entry.from);
484
+ if (problem)
485
+ errors.add("CONNECTOR_FIELD_INVALID", [...at, ...(typeof raw === "string" ? [] : ["from"])], `from ${problem}`);
486
+ const mapping = { from: entry.from };
487
+ if (entry.values !== undefined) {
488
+ if (!record(entry.values) || Object.keys(entry.values).length === 0)
489
+ errors.add("CONNECTOR_VALUES_INVALID", [...at, "values"], "values must be a non-empty mapping of native value to target value");
490
+ else {
491
+ const values = {};
492
+ for (const [native, target] of Object.entries(entry.values)) {
493
+ if (PROTOTYPE_KEYS.has(native) || typeof target !== "string" || target === "")
494
+ errors.add("CONNECTOR_VALUES_INVALID", [...at, "values", native], "each values row maps a native string to a non-empty target string");
495
+ else
496
+ values[native] = target;
497
+ }
498
+ mapping.values = values;
499
+ }
500
+ }
501
+ if (entry.unmapped !== undefined && entry.values === undefined)
502
+ errors.add("CONNECTOR_VALUES_INVALID", [...at, "unmapped"], "unmapped needs a values table");
503
+ else if (entry.unmapped !== undefined && entry.unmapped !== "keep" && entry.unmapped !== "refuse")
504
+ errors.add("CONNECTOR_VALUES_INVALID", [...at, "unmapped"], "unmapped is keep or refuse");
505
+ else if (entry.values !== undefined)
506
+ mapping.unmapped = entry.unmapped ?? "keep";
507
+ return errors.list.length === before ? mapping : undefined;
508
+ }
509
+ function parseSource(raw, index, errors) {
510
+ const at = ["record_sources", index];
511
+ if (!record(raw)) {
512
+ errors.add("CONNECTOR_MALFORMED", at, "a source must be a mapping");
513
+ return undefined;
514
+ }
515
+ const before = errors.list.length;
516
+ errors.closed("source", raw, at);
517
+ if (typeof raw.name !== "string" || !SLUG.test(raw.name))
518
+ errors.add("CONNECTOR_MALFORMED", [...at, "name"], "a source name is a slug [a-z][a-z0-9-]*");
519
+ if (typeof raw.profile !== "string" || !SLUG.test(raw.profile))
520
+ errors.add("CONNECTOR_PROFILE_INVALID", [...at, "profile"], "profile is a slug [a-z][a-z0-9-]* naming a catalog profile");
521
+ if (typeof raw.target_kind !== "string" || raw.target_kind.trim() === "")
522
+ errors.add("CONNECTOR_TARGET_KIND_INVALID", [...at, "target_kind"], "target_kind must name a Kind");
523
+ else if (RESERVED_TARGET_KINDS.has(raw.target_kind))
524
+ errors.add("CONNECTOR_TARGET_KIND_INVALID", [...at, "target_kind"], `a source may not target ${raw.target_kind}`);
525
+ if (raw.mode !== undefined && typeof raw.mode !== "string")
526
+ errors.add("CONNECTOR_MALFORMED", [...at, "mode"], "mode must be a string");
527
+ else if (raw.mode !== undefined && raw.mode !== "overlay") {
528
+ errors.add("CONNECTOR_MODE_UNSUPPORTED", [...at, "mode"], raw.mode === "mirror" ? "mirror mode is not supported before I4; use overlay" : "mode is overlay");
529
+ }
530
+ if (raw.completeness !== "complete" && raw.completeness !== "partial")
531
+ errors.add("CONNECTOR_MALFORMED", [...at, "completeness"], "completeness is complete or partial");
532
+ const fields = {};
533
+ if (!record(raw.fields) || Object.keys(raw.fields).length === 0)
534
+ errors.add("CONNECTOR_MALFORMED", [...at, "fields"], "fields must map at least one target field");
535
+ else
536
+ for (const [field, entry] of Object.entries(raw.fields)) {
537
+ const mapping = parseMapping(field, entry, [...at, "fields", field], errors);
538
+ if (mapping)
539
+ fields[field] = mapping;
540
+ }
541
+ let identity;
542
+ if (!record(raw.identity))
543
+ errors.add("CONNECTOR_MALFORMED", [...at, "identity"], "identity must be a mapping of resource and id");
544
+ else {
545
+ errors.closed("identity", raw.identity, [...at, "identity"]);
546
+ const resource = parseResourceTemplate(raw.identity.resource);
547
+ if (!resource.ok)
548
+ errors.add("CONNECTOR_IDENTITY_UNSTABLE", [...at, "identity", "resource"], `identity.resource ${resource.reason}`);
549
+ const id = parseIdTemplate(raw.identity.id);
550
+ if (!id.ok)
551
+ errors.add("CONNECTOR_IDENTITY_UNSTABLE", [...at, "identity", "id"], `identity.id ${id.reason}`);
552
+ else {
553
+ const owned = Object.values(fields).map((m) => m.from);
554
+ const shared = id.paths.filter((p) => owned.includes(p));
555
+ if (shared.length)
556
+ errors.add("CONNECTOR_IDENTITY_UNSTABLE", [...at, "identity", "id"], `identity is never an owned value, but ${shared.join(", ")} is also mapped`);
557
+ }
558
+ if (resource.ok && id.ok)
559
+ identity = { resource: raw.identity.resource, id: raw.identity.id };
560
+ }
561
+ if (errors.list.length !== before || !identity)
562
+ return undefined;
563
+ return {
564
+ name: raw.name, profile: raw.profile, target_kind: raw.target_kind, mode: "overlay",
565
+ identity, fields, completeness: raw.completeness,
566
+ };
567
+ }
568
+ function parseAction(raw, index, reach, errors) {
569
+ const at = ["actions", index];
570
+ if (!record(raw)) {
571
+ errors.add("CONNECTOR_MALFORMED", at, "an action must be a mapping");
572
+ return undefined;
573
+ }
574
+ const before = errors.list.length;
575
+ errors.closed("action", raw, at);
576
+ if (typeof raw.name !== "string" || !SLUG.test(raw.name))
577
+ errors.add("CONNECTOR_MALFORMED", [...at, "name"], "an action name is a slug [a-z][a-z0-9-]*");
578
+ if (typeof raw.method !== "string" || !METHODS.has(raw.method))
579
+ errors.add("CONNECTOR_MALFORMED", [...at, "method"], `method is one of ${[...METHODS].join(", ")}`);
580
+ if (hostnameProblem(raw.host) || !reach.has(raw.host))
581
+ errors.add("CONNECTOR_ACTION_INVALID", [...at, "host"], "an action host must be a credential host of the auth kind or a declared domain");
582
+ if (raw.confirm !== undefined && raw.confirm !== "always" && raw.confirm !== "never")
583
+ errors.add("CONNECTOR_MALFORMED", [...at, "confirm"], "confirm is always or never");
584
+ const input = {};
585
+ if (raw.input !== undefined && !record(raw.input))
586
+ errors.add("CONNECTOR_MALFORMED", [...at, "input"], "input must be a mapping");
587
+ else
588
+ for (const [name, spec] of Object.entries(raw.input ?? {})) {
589
+ const where = [...at, "input", name];
590
+ if (!LOWER_NAME.test(name) || PROTOTYPE_KEYS.has(name)) {
591
+ errors.add("CONNECTOR_ACTION_INVALID", where, "an input name is [a-z][a-z0-9_]*");
592
+ continue;
593
+ }
594
+ if (!record(spec)) {
595
+ errors.add("CONNECTOR_MALFORMED", where, "an input is { type, required, values }");
596
+ continue;
597
+ }
598
+ errors.closed("input", spec, where);
599
+ if (typeof spec.type !== "string" || !INPUT_TYPES.has(spec.type))
600
+ errors.add("CONNECTOR_MALFORMED", [...where, "type"], `an input type is one of ${[...INPUT_TYPES].join(", ")}`);
601
+ if (spec.required !== undefined && typeof spec.required !== "boolean")
602
+ errors.add("CONNECTOR_MALFORMED", [...where, "required"], "required is true or false");
603
+ if (spec.values !== undefined && (!Array.isArray(spec.values) || !spec.values.every((v) => typeof v === "string" && v !== ""))) {
604
+ errors.add("CONNECTOR_MALFORMED", [...where, "values"], "values is a list of strings");
605
+ }
606
+ else if (spec.values !== undefined && spec.type !== "string")
607
+ errors.add("CONNECTOR_ACTION_INVALID", [...where, "values"], "values restrict a string input only");
608
+ input[name] = {
609
+ type: spec.type, required: spec.required === true,
610
+ ...(Array.isArray(spec.values) ? { values: [...spec.values] } : {}),
611
+ };
612
+ }
613
+ if (errors.list.length !== before)
614
+ return undefined;
615
+ return { name: raw.name, method: raw.method, host: raw.host, input, confirm: raw.confirm ?? "always" };
616
+ }
617
+ function parseEvent(raw, index, sourceNames, errors) {
618
+ const at = ["events", index];
619
+ if (!record(raw)) {
620
+ errors.add("CONNECTOR_MALFORMED", at, "an event must be a mapping");
621
+ return undefined;
622
+ }
623
+ const before = errors.list.length;
624
+ errors.closed("event", raw, at);
625
+ if (typeof raw.name !== "string" || !SLUG.test(raw.name))
626
+ errors.add("CONNECTOR_MALFORMED", [...at, "name"], "an event name is a slug [a-z][a-z0-9-]*");
627
+ const signature = raw.signature;
628
+ if (!record(signature))
629
+ errors.add("CONNECTOR_MALFORMED", [...at, "signature"], "signature must be a mapping of scheme and header");
630
+ else {
631
+ errors.closed("signature", signature, [...at, "signature"]);
632
+ if (signature.scheme !== "hmac-sha256")
633
+ errors.add("CONNECTOR_EVENT_INVALID", [...at, "signature", "scheme"], "signature.scheme is hmac-sha256");
634
+ if (typeof signature.header !== "string" || !HEADER.test(signature.header))
635
+ errors.add("CONNECTOR_EVENT_INVALID", [...at, "signature", "header"], "signature.header is a lowercase token [a-z0-9-]+");
636
+ }
637
+ const references = list(raw.references, [...at, "references"], errors, "references") ?? [];
638
+ const seen = new Set();
639
+ references.forEach((ref, i) => {
640
+ if (typeof ref !== "string" || !sourceNames.has(ref))
641
+ errors.add("CONNECTOR_EVENT_INVALID", [...at, "references", i], "references name declared sources");
642
+ else if (seen.has(ref))
643
+ errors.add("CONNECTOR_DUPLICATE", [...at, "references", i], `reference '${ref}' is listed twice`);
644
+ else
645
+ seen.add(ref);
646
+ });
647
+ if (errors.list.length !== before || !record(signature))
648
+ return undefined;
649
+ return { name: raw.name, signature: { scheme: "hmac-sha256", header: signature.header }, references: [...seen] };
650
+ }
651
+ function duplicates(items, block, what, errors) {
652
+ const seen = new Set();
653
+ items.forEach((item, index) => {
654
+ const name = record(item) ? item.name : undefined;
655
+ if (typeof name !== "string")
656
+ return;
657
+ if (seen.has(name))
658
+ errors.add("CONNECTOR_DUPLICATE", [block, index, "name"], `${what} '${name}' is declared twice`);
659
+ seen.add(name);
660
+ });
661
+ }
662
+ /** Kind rules for one source: ownable, declared, scalar, and enum-safe mapping keys. */
663
+ function sourceKindErrors(source, index, kind) {
664
+ const errors = new Errors();
665
+ const work = workStateFields(kind, ["0.1", "0.2"]);
666
+ const declared = new Set([...kind.fields.required, ...kind.fields.optional, "title"]);
667
+ for (const [field, mapping] of Object.entries(source.fields)) {
668
+ const at = ["record_sources", index, "fields", field];
669
+ if (work.has(field)) {
670
+ errors.add("CONNECTOR_FIELD_NEVER_OWNED", at, `'${field}' holds the work state of ${kind.governs} and can never be mapped`);
671
+ continue;
672
+ }
673
+ if (!declared.has(field)) {
674
+ errors.add("CONNECTOR_FIELD_UNDECLARED", at, `${kind.governs} does not declare '${field}'`);
675
+ continue;
676
+ }
677
+ const allowed = Object.hasOwn(kind.fields.values, field) ? kind.fields.values[field] : undefined;
678
+ if (isKindListField(kind, field)) {
679
+ // Core's replace-all refuses an OKF standard or managed field whatever the Kind types it as.
680
+ if (isManagedDocumentMetadataField(field) || isStandardDocumentSetField(field, "0.1") || isStandardDocumentSetField(field, "0.2")) {
681
+ errors.add("CONNECTOR_LIST_FIELD_UNSUPPORTED", at, `'${field}' is an OKF standard or managed field; a connector cannot own it as a list`);
682
+ continue;
683
+ }
684
+ // A list maps whole: the profile flattens it, and there is no per-item value table yet.
685
+ if (mapping.values || Array.isArray(allowed))
686
+ errors.add("CONNECTOR_LIST_FIELD_UNSUPPORTED", at, `'${field}' is a ${kind.fields.types[field]}; a list field maps whole lists, without a values table or allowed values`);
687
+ continue;
688
+ }
689
+ if (!Array.isArray(allowed))
690
+ continue;
691
+ if (!mapping.values) {
692
+ errors.add("CONNECTOR_VALUES_INVALID", at, `'${field}' is an enum of ${kind.governs}; map it through a values table`);
693
+ continue;
694
+ }
695
+ for (const [native, target] of Object.entries(mapping.values)) {
696
+ if (!allowed.includes(target))
697
+ errors.add("CONNECTOR_VALUES_INVALID", [...at, "values", native], `${kind.governs}.${field} does not allow '${target}'`);
698
+ }
699
+ }
700
+ return errors.list;
701
+ }
702
+ /**
703
+ * Parse one Connector document, returning every refusal found. With `ctx.kinds`, a source whose
704
+ * target Kind is in the registry is also checked against it; one whose Kind is absent is left to
705
+ * {@link checkConnectorAgainstKinds} with a warning. A secret-shaped key or value, or a YAML alias,
706
+ * refuses the document alone, so no other refusal can echo the one or expand the other.
707
+ */
708
+ export function parseConnectorDoc(doc, ctx = {}) {
709
+ const fm = (doc.frontmatter ?? {});
710
+ const secrets = new Map();
711
+ scanDocument(fm, [], secrets, new WeakSet());
712
+ if (typeof doc.body === "string" && bodyHoldsSecret(doc.body)) {
713
+ secrets.set("(body)", { code: "CONNECTOR_SECRET_PRESENT", path: "", message: "a secret-shaped token in the body; a declaration never carries a credential" });
714
+ }
715
+ if (secrets.size)
716
+ return { ok: false, errors: [...secrets.values()] };
717
+ const errors = new Errors();
718
+ if (typeof doc.id !== "string" || !doc.id.startsWith(CONNECTORS_PREFIX) || !DOC_SLUG.test(doc.id.slice(CONNECTORS_PREFIX.length))) {
719
+ errors.add("CONNECTOR_MALFORMED", [], `a Connector lives at ${CONNECTORS_PREFIX}<slug>`);
720
+ }
721
+ if (fm.type !== CONNECTOR_TYPE)
722
+ errors.add("CONNECTOR_MALFORMED", ["type"], `type must be ${CONNECTOR_TYPE}`);
723
+ if (typeof fm.title !== "string" || fm.title.trim() === "")
724
+ errors.add("CONNECTOR_MALFORMED", ["title"], "title is required");
725
+ if (fm.description !== undefined && typeof fm.description !== "string")
726
+ errors.add("CONNECTOR_MALFORMED", ["description"], "description must be a string");
727
+ if (fm.version !== undefined && typeof fm.version !== "string" && !(typeof fm.version === "number" && Number.isFinite(fm.version))) {
728
+ errors.add("CONNECTOR_MALFORMED", ["version"], "version must be a string");
729
+ }
730
+ const auth = parseAuth(fm.auth, errors);
731
+ const domains = parseDomains(fm.domains, auth, errors);
732
+ const rawSources = list(fm.record_sources, ["record_sources"], errors, "record_sources");
733
+ const rawActions = list(fm.actions, ["actions"], errors, "actions");
734
+ const rawEvents = list(fm.events, ["events"], errors, "events");
735
+ if (rawSources && rawActions && rawEvents && rawSources.length + rawActions.length + rawEvents.length === 0) {
736
+ errors.add("CONNECTOR_MALFORMED", [], "a Connector declares at least one source, action or event");
737
+ }
738
+ duplicates(rawSources ?? [], "record_sources", "source", errors);
739
+ duplicates(rawActions ?? [], "actions", "action", errors);
740
+ duplicates(rawEvents ?? [], "events", "event", errors);
741
+ const sources = (rawSources ?? []).map((raw, index) => parseSource(raw, index, errors));
742
+ const reach = new Set([...(auth ? credentialHosts(auth) : []), ...domains]);
743
+ const actions = (rawActions ?? []).map((raw, index) => parseAction(raw, index, reach, errors));
744
+ const names = new Set((rawSources ?? []).flatMap((raw) => (record(raw) && typeof raw.name === "string" ? [raw.name] : [])));
745
+ const events = (rawEvents ?? []).map((raw, index) => parseEvent(raw, index, names, errors));
746
+ const warnings = [];
747
+ if (ctx.kinds) {
748
+ sources.forEach((source, index) => {
749
+ if (!source)
750
+ return;
751
+ const kind = ctx.kinds.kinds.get(source.target_kind);
752
+ if (kind)
753
+ errors.list.push(...sourceKindErrors(source, index, kind));
754
+ else
755
+ warnings.push({ code: "CONNECTOR_TARGET_KIND_DEFERRED", message: `source '${source.name}' targets ${source.target_kind}, which the given registry does not declare; its Kind rules run against the bundle`, field: pointer(["record_sources", index, "target_kind"]), severity: "warning" });
756
+ });
757
+ }
758
+ if (errors.list.length || !auth)
759
+ return { ok: false, errors: errors.list };
760
+ const connector = {
761
+ id: doc.id,
762
+ title: fm.title,
763
+ ...(typeof fm.description === "string" ? { description: fm.description } : {}),
764
+ ...(fm.version !== undefined ? { version: String(fm.version) } : {}),
765
+ auth,
766
+ domains,
767
+ sources: sources,
768
+ actions: actions,
769
+ events: events,
770
+ };
771
+ return { ok: true, connector, warnings };
772
+ }
773
+ /** The Kind rules over a bundle's registry; a source whose target Kind is not declared is refused. */
774
+ export function checkConnectorAgainstKinds(connector, kinds) {
775
+ return connector.sources.flatMap((source, index) => {
776
+ const kind = kinds.kinds.get(source.target_kind);
777
+ if (!kind)
778
+ return [{ code: "CONNECTOR_TARGET_KIND_INVALID", path: pointer(["record_sources", index, "target_kind"]), message: `${source.target_kind} is not declared in this bundle` }];
779
+ return sourceKindErrors(source, index, kind);
780
+ });
781
+ }
782
+ // --------------------------------------------------------------------------------------------- pin
783
+ function pinBlock(block, value, nested = {}) {
784
+ const source = value;
785
+ const out = {};
786
+ for (const key of CONNECTOR_KEYS[block]) {
787
+ const v = source[key];
788
+ out[key] = v !== undefined && nested[key] ? nested[key](v) : v;
789
+ }
790
+ return out;
791
+ }
792
+ const byName = (items) => [...items].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
793
+ const mapOf = (block, defaults = (item) => item) => (value) => Object.fromEntries(Object.entries(value).map(([key, item]) => [key, pinBlock(block, defaults(item))]));
794
+ /** The parser's defaults, filled again here so a Connector built without the parser pins the same. */
795
+ const PIN_DEFAULTS = {
796
+ auth: (a) => (a.kind === "github-app" && a.permissions === undefined ? { ...a, permissions: {} } : a),
797
+ source: (s) => ({ ...s, mode: s.mode ?? "overlay" }),
798
+ mapping: (m) => {
799
+ const mapping = m;
800
+ return mapping.values !== undefined && mapping.unmapped === undefined ? { ...mapping, unmapped: "keep" } : mapping;
801
+ },
802
+ action: (a) => ({ ...a, input: a.input ?? {}, confirm: a.confirm ?? "always" }),
803
+ input: (i) => ({ ...i, required: i.required ?? false }),
804
+ event: (e) => ({ ...e, references: e.references ?? [] }),
805
+ };
806
+ /**
807
+ * The digest a Connection records at enable time: every key {@link CONNECTOR_KEYS} accepts, with
808
+ * defaults filled and order-free lists sorted, plus the credential hosts core resolves the auth kind
809
+ * to, under the recipe stamp's canonical JSON. Blind to
810
+ * `title`, `description`, `version`, the body, `x_recipe` and unknown top-level keys.
811
+ */
812
+ export function connectorPinDigest(connector) {
813
+ const pinned = {
814
+ rules: CONNECTOR_PIN_RULES,
815
+ auth: pinBlock("auth", PIN_DEFAULTS.auth(connector.auth)),
816
+ // Core's resolution of the provider, so a host joining a row changes every pin on that provider.
817
+ credential_hosts: [...credentialHosts(connector.auth)].sort(),
818
+ domains: [...connector.domains].sort(),
819
+ // The parsed sources, under the same label whichever document key they were read from.
820
+ sources: byName(connector.sources).map((source) => pinBlock("source", PIN_DEFAULTS.source(source), {
821
+ identity: (v) => pinBlock("identity", v),
822
+ fields: mapOf("mapping", PIN_DEFAULTS.mapping),
823
+ })),
824
+ actions: byName(connector.actions).map((action) => pinBlock("action", PIN_DEFAULTS.action(action), { input: mapOf("input", PIN_DEFAULTS.input) })),
825
+ events: byName(connector.events).map((event) => pinBlock("event", PIN_DEFAULTS.event(event), {
826
+ signature: (v) => pinBlock("signature", v),
827
+ references: (v) => [...v].sort(),
828
+ })),
829
+ };
830
+ return `sha256:${sha256Hex(definitionCanonicalJson(pinned))}`;
831
+ }
832
+ /**
833
+ * Bind a Source Feed to one source of a Connector. The feed chooses only among the resources the
834
+ * identity template admits, within the mapping keys, under the source's own completeness and
835
+ * profile. Every refusal is `POLICY_INVALID` naming the feed and the key. `okfVersion` is the content
836
+ * bundle's edition; absent means v0.1, as an undeclared edition does, and an owned list field needs
837
+ * v0.2 because only field actions write lists.
838
+ */
839
+ export function bindSourceFeed(connector, feed, kinds, options = {}) {
840
+ const fm = (feed.frontmatter ?? {});
841
+ const refuse = (key, message, details = {}) => {
842
+ throw new OverlayRefusal(REFUSAL_CODES.POLICY_INVALID, `${feed.id}: ${message}`, { feed: feed.id, key, ...details });
843
+ };
844
+ if (fm.connector !== connector.id)
845
+ refuse("connector", `connector must name ${connector.id}`);
846
+ const source = connector.sources.find((s) => s.name === fm.source);
847
+ if (!source)
848
+ refuse("source", `source must name a source of ${connector.id}`);
849
+ const bound = source;
850
+ for (const key of ["target_kind", "completeness", "profile"]) {
851
+ if (fm[key] !== undefined && fm[key] !== null && fm[key] !== bound[key])
852
+ refuse(key, `${key} must be the source's ${bound[key]} when present`);
853
+ }
854
+ if (typeof fm.url !== "string" || fm.url.length > MAX_TEMPLATE)
855
+ refuse("url", "url is required");
856
+ for (const key of ["owned_fields", "auto_apply", "needs_review"]) {
857
+ const v = fm[key];
858
+ if (v !== undefined && v !== null && typeof v !== "string" && !(Array.isArray(v) && v.every((item) => typeof item === "string")))
859
+ refuse(key, `${key} must be a list of field or change names`);
860
+ }
861
+ for (const key of ["missing_threshold", "exclude_pattern", "wall_time_zone"]) {
862
+ const v = fm[key];
863
+ if (v !== undefined && v !== null && typeof v !== "string" && typeof v !== "number")
864
+ refuse(key, `${key} must be a single value`);
865
+ }
866
+ const url = fm.url;
867
+ const template = parseResourceTemplate(bound.identity.resource);
868
+ if (!template.ok)
869
+ refuse("url", "the source's identity template is invalid");
870
+ const { parts, matcher } = template.value;
871
+ const match = matcher.exec(url);
872
+ if (!match)
873
+ refuse("url", "url is not a resource the source's identity template admits");
874
+ const parameters = {};
875
+ let group = 1;
876
+ for (const part of parts) {
877
+ if (!("name" in part))
878
+ continue;
879
+ const value = match[group++];
880
+ if (value === "." || value === "..")
881
+ refuse("url", `parameter ${part.name} may not be '.' or '..'`);
882
+ parameters[part.name] = value;
883
+ }
884
+ const resource = parts.map((part) => ("name" in part ? parameters[part.name] : part.literal)).join("");
885
+ if (resource !== url)
886
+ refuse("url", "url must equal the rendered identity template byte for byte");
887
+ // The policy reader owns the feed keys' grammar; the Kind-aware read follows the binding checks.
888
+ const owned = readOverlayPolicy(feed).owned_fields;
889
+ const outside = owned.filter((field) => !Object.hasOwn(bound.fields, field));
890
+ if (outside.length)
891
+ refuse("owned_fields", `owned_fields outside the source's mapping: ${outside.join(", ")}`, { fields: outside });
892
+ const kind = kinds.kinds.get(bound.target_kind);
893
+ if (!kind)
894
+ refuse("target_kind", `${bound.target_kind} is not declared in this bundle`);
895
+ const kindErrors = sourceKindErrors(bound, connector.sources.indexOf(bound), kind);
896
+ if (kindErrors.length)
897
+ refuse("connector", `the source does not fit ${bound.target_kind}: ${kindErrors.map((e) => e.code).join(", ")}`, { codes: kindErrors.map((e) => e.code) });
898
+ const policy = readOverlayPolicy(feed, { targetKind: kind });
899
+ const ownedLists = policy.owned_fields.filter((field) => policy.list_fields?.includes(field));
900
+ const edition = options.okfVersion ?? "0.1";
901
+ if (ownedLists.length && edition !== "0.2") {
902
+ refuse("owned_fields", `owned list fields need an OKF v0.2 bundle, and this bundle is v${edition}: ${ownedLists.join(", ")}`, { fields: ownedLists, okf_version: edition });
903
+ }
904
+ return { policy: { ...policy, target_kind: bound.target_kind, completeness: bound.completeness }, source: bound, kind: kind, parameters, resource };
905
+ }
906
+ /** Own properties only, through plain mappings, never a prototype name. */
907
+ function readPath(value, path) {
908
+ let current = value;
909
+ for (const segment of path.split(".")) {
910
+ if (PROTOTYPE_KEYS.has(segment) || !record(current) || !Object.hasOwn(current, segment))
911
+ return { found: false };
912
+ current = current[segment];
913
+ }
914
+ return current === undefined ? { found: false } : { found: true, value: current };
915
+ }
916
+ const MAX_ID = 2048;
917
+ /**
918
+ * Render the id template. A missing, blank or non-scalar value is `IDENTITY_MISSING`; a value holding
919
+ * any character of a literal that separates two placeholders (so two records could share one id, for
920
+ * multi-character separators too), or an id past {@link MAX_ID} characters, is `IDENTITY_INVALID`. A finite number renders as `String(n)`, so a
921
+ * profile emits one type per id.
922
+ */
923
+ function renderId(template, value) {
924
+ const parsed = parseIdTemplate(template);
925
+ if (!parsed.ok)
926
+ return { ok: false, code: "IDENTITY_INVALID", path: template };
927
+ const separators = parsed.paths.length > 1
928
+ ? parsed.parts.filter((p, i) => "literal" in p && i > 0 && i < parsed.parts.length - 1).map((p) => p.literal)
929
+ : [];
930
+ let id = "";
931
+ for (const part of parsed.parts) {
932
+ if ("literal" in part) {
933
+ id += part.literal;
934
+ continue;
935
+ }
936
+ const read = readPath(value, part.path);
937
+ const v = read.found ? read.value : undefined;
938
+ const text = typeof v === "string" ? v : typeof v === "number" && Number.isFinite(v) ? String(v) : undefined;
939
+ if (text === undefined || !isPresent(text))
940
+ return { ok: false, code: "IDENTITY_MISSING", path: part.path };
941
+ if (text.length > MAX_ID || separators.some((separator) => [...separator].some((ch) => text.includes(ch)))) {
942
+ return { ok: false, code: "IDENTITY_INVALID", path: part.path };
943
+ }
944
+ id += text;
945
+ }
946
+ if (id.length > MAX_ID)
947
+ return { ok: false, code: "IDENTITY_INVALID", path: template };
948
+ return { ok: true, id };
949
+ }
950
+ /** The record's identity: the bound resource and the id template rendered over the record. */
951
+ export function renderIdentity(source, bound, value) {
952
+ const id = renderId(source.identity.id, value);
953
+ if (!id.ok)
954
+ return { ok: false, diagnostic: { code: id.code, field: "identity", from: id.path, native: null, external_id: null } };
955
+ return { ok: true, resource: bound.resource, id: id.id };
956
+ }
957
+ const scalar = (v) => typeof v === "string" || typeof v === "boolean" || (typeof v === "number" && Number.isFinite(v));
958
+ /** A list of nonblank text, deduplicated and in code point order; undefined for anything else. */
959
+ function textSet(native) {
960
+ if (!Array.isArray(native) || !native.every((member) => typeof member === "string" && isPresent(member)))
961
+ return undefined;
962
+ return [...new Set(native)].sort(compareCodePoints);
963
+ }
964
+ /**
965
+ * Map one normalized record onto the target Kind's fields. An absent path leaves the field
966
+ * unobserved. An empty value (`null` or blank text) clears an optional field and leaves a
967
+ * required one, or `title`, unobserved with `VALUE_MISSING`. A list or mapping is unobserved with
968
+ * `VALUE_NOT_SCALAR`. A native outside a values table is looked up as its string (a number as
969
+ * `String(n)`, a boolean as `true` or `false`) and then follows `unmapped`: `keep` leaves the field
970
+ * unobserved with `VALUE_UNMAPPED`; `refuse` refuses the whole record, which still counts as present.
971
+ * A record whose identity cannot be rendered is refused and not present.
972
+ *
973
+ * A field the Kind declares as `string-list` or `https-url-list` is always a list: a list of
974
+ * nonblank text is written deduplicated and in code point order, and `null` is `[]` (empty, so the
975
+ * owned list empties). An empty list on a required field is `VALUE_MISSING`; anything else, a
976
+ * member that is not nonblank text included, is unobserved with `VALUE_NOT_LIST`.
977
+ */
978
+ export function mapRecord(source, value, kind) {
979
+ const id = renderId(source.identity.id, value);
980
+ if (!id.ok)
981
+ return { kind: "refused", present: false, external_id: null, diagnostics: [{ code: id.code, field: "identity", from: id.path, native: null, external_id: null }] };
982
+ const externalId = id.id;
983
+ // `title` counts as declared by every Kind, so an empty value never clears it either.
984
+ const required = new Set([...kind.fields.required, "title"]);
985
+ const fields = {};
986
+ const unobserved = [];
987
+ const cleared = [];
988
+ const refused = [];
989
+ const diagnostics = [];
990
+ const note = (code, field, from, native) => {
991
+ diagnostics.push({ code, field, from, native, external_id: externalId });
992
+ };
993
+ for (const [field, mapping] of Object.entries(source.fields)) {
994
+ const read = readPath(value, mapping.from);
995
+ if (!read.found) {
996
+ unobserved.push(field);
997
+ continue;
998
+ }
999
+ const native = read.value;
1000
+ if (isKindListField(kind, field)) {
1001
+ const members = native === null ? [] : textSet(native);
1002
+ if (members === undefined) {
1003
+ unobserved.push(field);
1004
+ note("VALUE_NOT_LIST", field, mapping.from, native);
1005
+ }
1006
+ else if (members.length === 0 && required.has(field)) {
1007
+ unobserved.push(field);
1008
+ note("VALUE_MISSING", field, mapping.from, native);
1009
+ }
1010
+ else
1011
+ fields[field] = members;
1012
+ continue;
1013
+ }
1014
+ const empty = () => {
1015
+ if (required.has(field)) {
1016
+ unobserved.push(field);
1017
+ note("VALUE_MISSING", field, mapping.from, native);
1018
+ }
1019
+ else
1020
+ cleared.push(field);
1021
+ };
1022
+ if (native === null) {
1023
+ empty();
1024
+ continue;
1025
+ }
1026
+ if (!scalar(native)) {
1027
+ unobserved.push(field);
1028
+ note("VALUE_NOT_SCALAR", field, mapping.from, native);
1029
+ continue;
1030
+ }
1031
+ if (mapping.values) {
1032
+ const key = typeof native === "string" ? native : String(native);
1033
+ if (Object.hasOwn(mapping.values, key)) {
1034
+ fields[field] = mapping.values[key];
1035
+ continue;
1036
+ }
1037
+ if (!isPresent(native)) {
1038
+ empty();
1039
+ continue;
1040
+ }
1041
+ if (mapping.unmapped === "refuse") {
1042
+ refused.push(field);
1043
+ note("RECORD_REFUSED", field, mapping.from, native);
1044
+ }
1045
+ else {
1046
+ unobserved.push(field);
1047
+ note("VALUE_UNMAPPED", field, mapping.from, native);
1048
+ }
1049
+ continue;
1050
+ }
1051
+ if (!isPresent(native)) {
1052
+ empty();
1053
+ continue;
1054
+ }
1055
+ fields[field] = native;
1056
+ }
1057
+ if (refused.length)
1058
+ return { kind: "refused", present: true, external_id: externalId, diagnostics };
1059
+ return { kind: "mapped", external_id: externalId, fields, unobserved, cleared, diagnostics };
1060
+ }
1061
+ /**
1062
+ * The record-overlay change between the last accepted `after` of this record (carried values
1063
+ * included; null for a first observation) and a new mapping. An unobserved field is carried from
1064
+ * `previous` and left out of `changed_fields`, so no write and no removal is planned for it; a
1065
+ * cleared field is absent from `after`. Only the source's mapping keys can change.
1066
+ */
1067
+ export function changeBetween(previous, next, meta) {
1068
+ const before = previous ? { ...previous } : null;
1069
+ const after = { ...next.fields };
1070
+ for (const field of next.unobserved) {
1071
+ if (previous && Object.hasOwn(previous, field) && previous[field] !== undefined)
1072
+ after[field] = previous[field];
1073
+ }
1074
+ const unobserved = new Set(next.unobserved);
1075
+ const mapped = [...new Set([...Object.keys(next.fields), ...next.unobserved, ...next.cleared])].sort();
1076
+ return {
1077
+ external_id: meta.external_id,
1078
+ change_kind: meta.change_kind,
1079
+ changed_fields: mapped.filter((field) => !unobserved.has(field) && !same(before?.[field], after[field])),
1080
+ before,
1081
+ after,
1082
+ content_digest: `sha256:${sha256Hex(overlayCanonicalJson(after))}`,
1083
+ };
1084
+ }
1085
+ const clip = (text, n) => (text.length > n ? text.slice(0, n - 1) + "…" : text);
1086
+ const shown = (native) => (native === null || native === undefined ? null : clip(typeof native === "string" ? native : JSON.stringify(native) ?? String(native), 80));
1087
+ const FINDING_TEXT = {
1088
+ VALUE_UNMAPPED: (field, native) => `${field}: value ${JSON.stringify(native)} has no row in the values table; the stored value was kept`,
1089
+ RECORD_REFUSED: (field, native) => `${field}: value ${JSON.stringify(native)} has no row in the values table; the record was refused and left unchanged`,
1090
+ VALUE_NOT_SCALAR: (field) => `${field}: the provider sent a list or mapping; the stored value was kept`,
1091
+ VALUE_NOT_LIST: (field) => `${field}: the provider sent something other than a list of text; the stored value was kept`,
1092
+ VALUE_MISSING: (field) => `${field}: required, and the provider sent no value; the stored value was kept`,
1093
+ IDENTITY_MISSING: () => "records without a value for the identity template were skipped",
1094
+ IDENTITY_INVALID: () => "records whose identity could collide with another's or is too long were skipped",
1095
+ };
1096
+ function* take(items, n) {
1097
+ if (n <= 0)
1098
+ return;
1099
+ for (const item of items) {
1100
+ yield item;
1101
+ if (--n === 0)
1102
+ return;
1103
+ }
1104
+ }
1105
+ /** A native named by its shape: `list`, `mapping`, `empty` (absent, `null` or blank text) or `scalar`. */
1106
+ function nativeShape(native) {
1107
+ if (Array.isArray(native))
1108
+ return "list";
1109
+ if (record(native))
1110
+ return "mapping";
1111
+ return native === null || native === undefined || (typeof native === "string" && !isPresent(native)) ? "empty" : "scalar";
1112
+ }
1113
+ /**
1114
+ * Findings for `planFindings`, so one problem across many records raises one Signal: an unknown
1115
+ * value one per source, field, code and native value; a value of the wrong shape or an empty one
1116
+ * one per source, field, code and shape; identity gaps one per source. `facts` hold the code, field
1117
+ * and the clipped native value or the shape, never a record id, so a dismissed finding stays
1118
+ * dismissed until they change; the summary names the count and a sample of record ids.
1119
+ */
1120
+ export function findingsFor(diagnostics, scope) {
1121
+ const groups = new Map();
1122
+ for (const d of diagnostics) {
1123
+ const identity = d.code === "IDENTITY_MISSING" || d.code === "IDENTITY_INVALID";
1124
+ const key = identity
1125
+ ? `mapping:${scope.source}:${d.code}`
1126
+ : d.code === "VALUE_NOT_SCALAR" || d.code === "VALUE_NOT_LIST" || d.code === "VALUE_MISSING"
1127
+ ? `mapping:${scope.source}:${d.field}:${d.code}:${nativeShape(d.native)}`
1128
+ // The string the values table was searched with, so 7 and "7" are one finding as one lookup.
1129
+ : `mapping:${scope.source}:${d.field}:${d.code}:${sha256Hex(typeof d.native === "string" ? d.native : String(d.native)).slice(0, 16)}`;
1130
+ let group = groups.get(key);
1131
+ if (!group)
1132
+ groups.set(key, group = { diagnostic: d, ids: new Set() });
1133
+ if (d.external_id !== null)
1134
+ group.ids.add(d.external_id);
1135
+ }
1136
+ return [...groups].map(([key, { diagnostic: d, ids }]) => {
1137
+ const byShape = d.code === "VALUE_NOT_SCALAR" || d.code === "VALUE_NOT_LIST" || d.code === "VALUE_MISSING";
1138
+ const native = d.code === "IDENTITY_MISSING" || d.code === "IDENTITY_INVALID" || byShape ? null : shown(d.native);
1139
+ const sample = [...take(ids, 3)].map((id) => clip(id, 60)).join(", ");
1140
+ const records = ids.size ? `${ids.size} record${ids.size === 1 ? "" : "s"} (${sample}${ids.size > 3 ? ", …" : ""})` : "records";
1141
+ return {
1142
+ key,
1143
+ title: clip(`Mapping ${d.code} on ${scope.source}.${d.field}`, 140),
1144
+ summary: clip(`${FINDING_TEXT[d.code](d.field, native)}: ${records}`, 600),
1145
+ priority: d.code === "RECORD_REFUSED" || d.code === "IDENTITY_MISSING" || d.code === "IDENTITY_INVALID" ? "normal" : "low",
1146
+ facts: byShape ? { code: d.code, field: d.field, shape: nativeShape(d.native) } : { code: d.code, field: d.field, native },
1147
+ };
1148
+ });
1149
+ }
1150
+ //# sourceMappingURL=connectors.js.map