@ternent/core 0.0.1

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 (313) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +17 -0
  3. package/.github/workflows/deploy-armour.yml +42 -0
  4. package/.github/workflows/deploy-identity.yml +42 -0
  5. package/.github/workflows/deploy-seal.yml +42 -0
  6. package/.github/workflows/deploy-ui.yml +42 -0
  7. package/.github/workflows/deploy-utils.yml +42 -0
  8. package/.github/workflows/release-create.yml +59 -0
  9. package/.github/workflows/release-publish.yml +54 -0
  10. package/.nvmrc +1 -0
  11. package/.ops/publish.mjs +31 -0
  12. package/package.json +16 -0
  13. package/packages/README.md +0 -0
  14. package/packages/armour/CHANGELOG.md +66 -0
  15. package/packages/armour/CLAUDE.md +8 -0
  16. package/packages/armour/README.md +103 -0
  17. package/packages/armour/SPEC.md +92 -0
  18. package/packages/armour/package.json +45 -0
  19. package/packages/armour/src/constants.ts +5 -0
  20. package/packages/armour/src/deps.d.ts +56 -0
  21. package/packages/armour/src/errors.ts +172 -0
  22. package/packages/armour/src/files.ts +73 -0
  23. package/packages/armour/src/identity.ts +72 -0
  24. package/packages/armour/src/index.ts +56 -0
  25. package/packages/armour/src/init.ts +10 -0
  26. package/packages/armour/src/passphrase.ts +33 -0
  27. package/packages/armour/src/recipients.ts +73 -0
  28. package/packages/armour/src/text.ts +68 -0
  29. package/packages/armour/src/types.ts +93 -0
  30. package/packages/armour/test/armour.test.ts +270 -0
  31. package/packages/armour/tsconfig.build.json +12 -0
  32. package/packages/armour/tsconfig.json +12 -0
  33. package/packages/armour/vite.config.ts +29 -0
  34. package/packages/concord/CHANGELOG.md +83 -0
  35. package/packages/concord/CLAUDE.md +9 -0
  36. package/packages/concord/README.md +146 -0
  37. package/packages/concord/SPEC.md +287 -0
  38. package/packages/concord/package.json +51 -0
  39. package/packages/concord/src/app.ts +717 -0
  40. package/packages/concord/src/errors.ts +9 -0
  41. package/packages/concord/src/index.ts +20 -0
  42. package/packages/concord/src/types.ts +127 -0
  43. package/packages/concord/test/concord.test.ts +978 -0
  44. package/packages/concord/tsconfig.json +12 -0
  45. package/packages/concord/vite.browser.config.ts +27 -0
  46. package/packages/concord/vite.config.ts +35 -0
  47. package/packages/concord/vite.config.ts.timestamp-1774262297922-ffd76e35ea668.mjs +83 -0
  48. package/packages/identity/CHANGELOG.md +47 -0
  49. package/packages/identity/README.md +236 -0
  50. package/packages/identity/package.json +41 -0
  51. package/packages/identity/src/index.ts +538 -0
  52. package/packages/identity/test/identity.test.ts +172 -0
  53. package/packages/identity/tsconfig.build.json +12 -0
  54. package/packages/identity/vite.config.ts +17 -0
  55. package/packages/ledger/CHANGELOG.md +69 -0
  56. package/packages/ledger/CLAUDE.md +9 -0
  57. package/packages/ledger/SPEC.md +304 -0
  58. package/packages/ledger/package.json +48 -0
  59. package/packages/ledger/src/index.ts +2 -0
  60. package/packages/ledger/src/ledger.ts +1286 -0
  61. package/packages/ledger/src/seal-cli.d.ts +25 -0
  62. package/packages/ledger/src/types.ts +294 -0
  63. package/packages/ledger/test/ledger.test.ts +838 -0
  64. package/packages/ledger/tsconfig.json +12 -0
  65. package/packages/ledger/vite.browser.config.ts +27 -0
  66. package/packages/ledger/vite.config.ts +39 -0
  67. package/packages/seal/CHANGELOG.md +137 -0
  68. package/packages/seal/CLAUDE.md +8 -0
  69. package/packages/seal/README.md +258 -0
  70. package/packages/seal/bin/seal +6 -0
  71. package/packages/seal/package.json +59 -0
  72. package/packages/seal/src/artifact.ts +380 -0
  73. package/packages/seal/src/cli.ts +372 -0
  74. package/packages/seal/src/commands/identity.ts +52 -0
  75. package/packages/seal/src/commands/manifest.ts +71 -0
  76. package/packages/seal/src/commands/publicKey.ts +7 -0
  77. package/packages/seal/src/commands/sign.ts +56 -0
  78. package/packages/seal/src/commands/verify.ts +54 -0
  79. package/packages/seal/src/crypto.ts +85 -0
  80. package/packages/seal/src/errors.ts +88 -0
  81. package/packages/seal/src/index.ts +5 -0
  82. package/packages/seal/src/manifest.ts +114 -0
  83. package/packages/seal/src/node.ts +18 -0
  84. package/packages/seal/src/proof.ts +344 -0
  85. package/packages/seal/test/artifact.test.ts +86 -0
  86. package/packages/seal/test/cli.test.ts +208 -0
  87. package/packages/seal/test/crypto.test.ts +21 -0
  88. package/packages/seal/test/manifest.test.ts +32 -0
  89. package/packages/seal/test/proof.test.ts +60 -0
  90. package/packages/seal/tsconfig.json +12 -0
  91. package/packages/seal/vite.config.ts +54 -0
  92. package/packages/ui/CHANGELOG.md +393 -0
  93. package/packages/ui/README.md +57 -0
  94. package/packages/ui/jsconfig.json +19 -0
  95. package/packages/ui/package.json +64 -0
  96. package/packages/ui/scripts/check-tokens.js +56 -0
  97. package/packages/ui/scripts/generate-theme-css.mjs +85 -0
  98. package/packages/ui/src/design-system/base.css +8 -0
  99. package/packages/ui/src/design-system/docs/ACCESSIBILITY_RULES.md +186 -0
  100. package/packages/ui/src/design-system/docs/AI_SYSTEM.md +281 -0
  101. package/packages/ui/src/design-system/docs/PATTERN_RULES.md +83 -0
  102. package/packages/ui/src/design-system/docs/PRIMITIVE_RULES.md +258 -0
  103. package/packages/ui/src/design-system/docs/TOKEN_RULES.md +235 -0
  104. package/packages/ui/src/design-system/docs/VISUAL_DIRECTION.md +68 -0
  105. package/packages/ui/src/design-system/foundation.js +420 -0
  106. package/packages/ui/src/design-system/tokens.css +140 -0
  107. package/packages/ui/src/design-system/tokens.js +327 -0
  108. package/packages/ui/src/design-system/utils.js +246 -0
  109. package/packages/ui/src/main.js +4 -0
  110. package/packages/ui/src/patterns/FeatureCard/FeatureCard.spec.md +24 -0
  111. package/packages/ui/src/patterns/FeatureCard/FeatureCard.types.ts +8 -0
  112. package/packages/ui/src/patterns/FeatureCard/FeatureCard.vue +175 -0
  113. package/packages/ui/src/patterns/FormField/FormField.spec.md +65 -0
  114. package/packages/ui/src/patterns/FormField/FormField.types.ts +11 -0
  115. package/packages/ui/src/patterns/FormField/FormField.vue +87 -0
  116. package/packages/ui/src/patterns/IdentityGlyph/IdentityGlyph.vue +61 -0
  117. package/packages/ui/src/patterns/IdentityGlyph/IdentityHandle.vue +58 -0
  118. package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.types.ts +36 -0
  119. package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.utils.ts +585 -0
  120. package/packages/ui/src/patterns/IdentityGlyph/index.ts +5 -0
  121. package/packages/ui/src/patterns/KeyValueList/KeyValueList.spec.md +28 -0
  122. package/packages/ui/src/patterns/KeyValueList/KeyValueList.types.ts +16 -0
  123. package/packages/ui/src/patterns/KeyValueList/KeyValueList.vue +50 -0
  124. package/packages/ui/src/patterns/LandingPage/LandingIcon.vue +90 -0
  125. package/packages/ui/src/patterns/LandingPage/LandingPage.spec.md +24 -0
  126. package/packages/ui/src/patterns/LandingPage/LandingPage.types.ts +212 -0
  127. package/packages/ui/src/patterns/LandingPage/LandingPage.vue +599 -0
  128. package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.test.ts +33 -0
  129. package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.vue +44 -0
  130. package/packages/ui/src/patterns/Logo/Logo.spec.md +22 -0
  131. package/packages/ui/src/patterns/Logo/Logo.vue +160 -0
  132. package/packages/ui/src/patterns/PageSurface/PageSurface.spec.md +15 -0
  133. package/packages/ui/src/patterns/PageSurface/PageSurface.vue +85 -0
  134. package/packages/ui/src/patterns/PanelChrome/PanelChrome.spec.md +39 -0
  135. package/packages/ui/src/patterns/PanelChrome/PanelChrome.types.ts +1 -0
  136. package/packages/ui/src/patterns/PanelChrome/PanelChrome.vue +187 -0
  137. package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.spec.md +31 -0
  138. package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.types.ts +23 -0
  139. package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.vue +354 -0
  140. package/packages/ui/src/patterns/RecordList/RecordList.spec.md +35 -0
  141. package/packages/ui/src/patterns/RecordList/RecordList.test.ts +42 -0
  142. package/packages/ui/src/patterns/RecordList/RecordList.types.ts +9 -0
  143. package/packages/ui/src/patterns/RecordList/RecordList.utils.ts +5 -0
  144. package/packages/ui/src/patterns/RecordList/RecordList.vue +134 -0
  145. package/packages/ui/src/patterns/SectionClarifier/SectionClarifier.vue +85 -0
  146. package/packages/ui/src/patterns/SectionIntro/SectionIntro.spec.md +25 -0
  147. package/packages/ui/src/patterns/SectionIntro/SectionIntro.types.ts +7 -0
  148. package/packages/ui/src/patterns/SectionIntro/SectionIntro.vue +141 -0
  149. package/packages/ui/src/patterns/SidebarNav/SidebarNav.spec.md +34 -0
  150. package/packages/ui/src/patterns/SidebarNav/SidebarNav.types.ts +17 -0
  151. package/packages/ui/src/patterns/SidebarNav/SidebarNav.vue +110 -0
  152. package/packages/ui/src/patterns/SplitView/SplitView.spec.md +28 -0
  153. package/packages/ui/src/patterns/SplitView/SplitView.test.ts +22 -0
  154. package/packages/ui/src/patterns/SplitView/SplitView.types.ts +3 -0
  155. package/packages/ui/src/patterns/SplitView/SplitView.utils.ts +13 -0
  156. package/packages/ui/src/patterns/SplitView/SplitView.vue +39 -0
  157. package/packages/ui/src/patterns/StepList/StepList.spec.md +15 -0
  158. package/packages/ui/src/patterns/StepList/StepList.types.ts +4 -0
  159. package/packages/ui/src/patterns/StepList/StepList.vue +91 -0
  160. package/packages/ui/src/patterns/Verification/VerificationBadge.vue +97 -0
  161. package/packages/ui/src/patterns/Verification/VerificationComponents.test.ts +153 -0
  162. package/packages/ui/src/patterns/Verification/VerificationDetailsPanel.vue +270 -0
  163. package/packages/ui/src/patterns/Verification/VerificationSummary.vue +171 -0
  164. package/packages/ui/src/patterns/Verification/index.ts +6 -0
  165. package/packages/ui/src/patterns/Verification/verification.types.ts +8 -0
  166. package/packages/ui/src/patterns/Verification/verification.utils.test.ts +37 -0
  167. package/packages/ui/src/patterns/Verification/verification.utils.ts +75 -0
  168. package/packages/ui/src/patterns/index.ts +25 -0
  169. package/packages/ui/src/primitives/Accordian/Accordian.vue +11 -0
  170. package/packages/ui/src/primitives/Accordian/AccordianItem.vue +14 -0
  171. package/packages/ui/src/primitives/Accordion/Accordion.props.ts +21 -0
  172. package/packages/ui/src/primitives/Accordion/Accordion.spec.md +50 -0
  173. package/packages/ui/src/primitives/Accordion/Accordion.types.ts +4 -0
  174. package/packages/ui/src/primitives/Accordion/Accordion.variants.ts +12 -0
  175. package/packages/ui/src/primitives/Accordion/Accordion.vue +71 -0
  176. package/packages/ui/src/primitives/Accordion/AccordionItem.props.ts +14 -0
  177. package/packages/ui/src/primitives/Accordion/AccordionItem.vue +40 -0
  178. package/packages/ui/src/primitives/Badge/Badge.props.ts +17 -0
  179. package/packages/ui/src/primitives/Badge/Badge.spec.md +17 -0
  180. package/packages/ui/src/primitives/Badge/Badge.types.ts +15 -0
  181. package/packages/ui/src/primitives/Badge/Badge.variants.ts +48 -0
  182. package/packages/ui/src/primitives/Badge/Badge.vue +31 -0
  183. package/packages/ui/src/primitives/Button/Button.props.ts +29 -0
  184. package/packages/ui/src/primitives/Button/Button.spec.md +139 -0
  185. package/packages/ui/src/primitives/Button/Button.types.ts +19 -0
  186. package/packages/ui/src/primitives/Button/Button.variants.ts +72 -0
  187. package/packages/ui/src/primitives/Button/Button.vue +90 -0
  188. package/packages/ui/src/primitives/Card/Card.props.ts +17 -0
  189. package/packages/ui/src/primitives/Card/Card.spec.md +29 -0
  190. package/packages/ui/src/primitives/Card/Card.types.ts +12 -0
  191. package/packages/ui/src/primitives/Card/Card.variants.ts +27 -0
  192. package/packages/ui/src/primitives/Card/Card.vue +37 -0
  193. package/packages/ui/src/primitives/Checkbox/Checkbox.props.ts +21 -0
  194. package/packages/ui/src/primitives/Checkbox/Checkbox.spec.md +51 -0
  195. package/packages/ui/src/primitives/Checkbox/Checkbox.types.ts +4 -0
  196. package/packages/ui/src/primitives/Checkbox/Checkbox.variants.ts +34 -0
  197. package/packages/ui/src/primitives/Checkbox/Checkbox.vue +92 -0
  198. package/packages/ui/src/primitives/Dialog/Dialog.props.ts +29 -0
  199. package/packages/ui/src/primitives/Dialog/Dialog.spec.md +52 -0
  200. package/packages/ui/src/primitives/Dialog/Dialog.types.ts +3 -0
  201. package/packages/ui/src/primitives/Dialog/Dialog.variants.ts +27 -0
  202. package/packages/ui/src/primitives/Dialog/Dialog.vue +78 -0
  203. package/packages/ui/src/primitives/Drawer/Drawer.props.ts +33 -0
  204. package/packages/ui/src/primitives/Drawer/Drawer.spec.md +50 -0
  205. package/packages/ui/src/primitives/Drawer/Drawer.types.ts +5 -0
  206. package/packages/ui/src/primitives/Drawer/Drawer.variants.ts +35 -0
  207. package/packages/ui/src/primitives/Drawer/Drawer.vue +88 -0
  208. package/packages/ui/src/primitives/FieldMessage/FieldMessage.props.ts +17 -0
  209. package/packages/ui/src/primitives/FieldMessage/FieldMessage.spec.md +35 -0
  210. package/packages/ui/src/primitives/FieldMessage/FieldMessage.types.ts +5 -0
  211. package/packages/ui/src/primitives/FieldMessage/FieldMessage.variants.ts +14 -0
  212. package/packages/ui/src/primitives/FieldMessage/FieldMessage.vue +40 -0
  213. package/packages/ui/src/primitives/FileInput/FileInput.props.ts +41 -0
  214. package/packages/ui/src/primitives/FileInput/FileInput.types.ts +6 -0
  215. package/packages/ui/src/primitives/FileInput/FileInput.variants.ts +46 -0
  216. package/packages/ui/src/primitives/FileInput/FileInput.vue +163 -0
  217. package/packages/ui/src/primitives/Input/Input.props.ts +29 -0
  218. package/packages/ui/src/primitives/Input/Input.spec.md +79 -0
  219. package/packages/ui/src/primitives/Input/Input.types.ts +13 -0
  220. package/packages/ui/src/primitives/Input/Input.variants.ts +54 -0
  221. package/packages/ui/src/primitives/Input/Input.vue +99 -0
  222. package/packages/ui/src/primitives/Label/Label.props.ts +25 -0
  223. package/packages/ui/src/primitives/Label/Label.spec.md +31 -0
  224. package/packages/ui/src/primitives/Label/Label.types.ts +3 -0
  225. package/packages/ui/src/primitives/Label/Label.variants.ts +17 -0
  226. package/packages/ui/src/primitives/Label/Label.vue +38 -0
  227. package/packages/ui/src/primitives/Menu/Menu.props.ts +17 -0
  228. package/packages/ui/src/primitives/Menu/Menu.spec.md +38 -0
  229. package/packages/ui/src/primitives/Menu/Menu.types.ts +10 -0
  230. package/packages/ui/src/primitives/Menu/Menu.variants.ts +10 -0
  231. package/packages/ui/src/primitives/Menu/Menu.vue +57 -0
  232. package/packages/ui/src/primitives/Popover/Popover.props.ts +25 -0
  233. package/packages/ui/src/primitives/Popover/Popover.spec.md +49 -0
  234. package/packages/ui/src/primitives/Popover/Popover.types.ts +3 -0
  235. package/packages/ui/src/primitives/Popover/Popover.variants.ts +18 -0
  236. package/packages/ui/src/primitives/Popover/Popover.vue +74 -0
  237. package/packages/ui/src/primitives/RadioGroup/RadioGroup.props.ts +29 -0
  238. package/packages/ui/src/primitives/RadioGroup/RadioGroup.spec.md +50 -0
  239. package/packages/ui/src/primitives/RadioGroup/RadioGroup.types.ts +12 -0
  240. package/packages/ui/src/primitives/RadioGroup/RadioGroup.variants.ts +48 -0
  241. package/packages/ui/src/primitives/RadioGroup/RadioGroup.vue +87 -0
  242. package/packages/ui/src/primitives/Separator/Separator.props.ts +9 -0
  243. package/packages/ui/src/primitives/Separator/Separator.spec.md +15 -0
  244. package/packages/ui/src/primitives/Separator/Separator.types.ts +3 -0
  245. package/packages/ui/src/primitives/Separator/Separator.variants.ts +8 -0
  246. package/packages/ui/src/primitives/Separator/Separator.vue +23 -0
  247. package/packages/ui/src/primitives/Skeleton/Skeleton.props.ts +21 -0
  248. package/packages/ui/src/primitives/Skeleton/Skeleton.spec.md +18 -0
  249. package/packages/ui/src/primitives/Skeleton/Skeleton.types.ts +5 -0
  250. package/packages/ui/src/primitives/Skeleton/Skeleton.variants.ts +18 -0
  251. package/packages/ui/src/primitives/Skeleton/Skeleton.vue +37 -0
  252. package/packages/ui/src/primitives/Spinner/Spinner.props.ts +13 -0
  253. package/packages/ui/src/primitives/Spinner/Spinner.spec.md +16 -0
  254. package/packages/ui/src/primitives/Spinner/Spinner.types.ts +5 -0
  255. package/packages/ui/src/primitives/Spinner/Spinner.variants.ts +15 -0
  256. package/packages/ui/src/primitives/Spinner/Spinner.vue +33 -0
  257. package/packages/ui/src/primitives/SplitButton/SplitButton.vue +108 -0
  258. package/packages/ui/src/primitives/Switch/Switch.props.ts +21 -0
  259. package/packages/ui/src/primitives/Switch/Switch.spec.md +49 -0
  260. package/packages/ui/src/primitives/Switch/Switch.types.ts +3 -0
  261. package/packages/ui/src/primitives/Switch/Switch.variants.ts +34 -0
  262. package/packages/ui/src/primitives/Switch/Switch.vue +71 -0
  263. package/packages/ui/src/primitives/Tabs/Tabs.props.ts +25 -0
  264. package/packages/ui/src/primitives/Tabs/Tabs.spec.md +48 -0
  265. package/packages/ui/src/primitives/Tabs/Tabs.types.ts +11 -0
  266. package/packages/ui/src/primitives/Tabs/Tabs.variants.ts +28 -0
  267. package/packages/ui/src/primitives/Tabs/Tabs.vue +59 -0
  268. package/packages/ui/src/primitives/Textarea/Textarea.props.ts +33 -0
  269. package/packages/ui/src/primitives/Textarea/Textarea.spec.md +59 -0
  270. package/packages/ui/src/primitives/Textarea/Textarea.types.ts +5 -0
  271. package/packages/ui/src/primitives/Textarea/Textarea.variants.ts +27 -0
  272. package/packages/ui/src/primitives/Textarea/Textarea.vue +74 -0
  273. package/packages/ui/src/primitives/Tooltip/Tooltip.props.ts +21 -0
  274. package/packages/ui/src/primitives/Tooltip/Tooltip.spec.md +45 -0
  275. package/packages/ui/src/primitives/Tooltip/Tooltip.types.ts +3 -0
  276. package/packages/ui/src/primitives/Tooltip/Tooltip.variants.ts +4 -0
  277. package/packages/ui/src/primitives/Tooltip/Tooltip.vue +31 -0
  278. package/packages/ui/src/primitives/TreeView/TreeView.types.ts +10 -0
  279. package/packages/ui/src/primitives/TreeView/TreeView.vue +113 -0
  280. package/packages/ui/src/primitives/TreeView/TreeViewNode.vue +190 -0
  281. package/packages/ui/src/primitives/index.ts +29 -0
  282. package/packages/ui/src/style.css +7 -0
  283. package/packages/ui/src/style.js +1 -0
  284. package/packages/ui/src/themes/armour.css +147 -0
  285. package/packages/ui/src/themes/aurora.css +147 -0
  286. package/packages/ui/src/themes/citrine-ash.css +147 -0
  287. package/packages/ui/src/themes/concord.css +147 -0
  288. package/packages/ui/src/themes/garnet-honey.css +147 -0
  289. package/packages/ui/src/themes/harbor-rose.css +147 -0
  290. package/packages/ui/src/themes/ledger.css +147 -0
  291. package/packages/ui/src/themes/neon-noir.css +74 -0
  292. package/packages/ui/src/themes/obsidian-iris.css +147 -0
  293. package/packages/ui/src/themes/pixpax.css +147 -0
  294. package/packages/ui/src/themes/print.css +147 -0
  295. package/packages/ui/src/themes/prism.css +147 -0
  296. package/packages/ui/src/themes/proof.css +145 -0
  297. package/packages/ui/src/themes/semanticThemeContract.js +2256 -0
  298. package/packages/ui/src/themes/spruce-ink.css +147 -0
  299. package/packages/ui/src/themes/sunset.css +147 -0
  300. package/packages/ui/tailwind.config.js +64 -0
  301. package/packages/ui/vite.config.js +35 -0
  302. package/packages/ui/vite.config.js.timestamp-1780697224943-89fbc929987bc.mjs +38 -0
  303. package/packages/utils/CHANGELOG.md +111 -0
  304. package/packages/utils/README.md +3 -0
  305. package/packages/utils/package.json +46 -0
  306. package/packages/utils/src/index.test.js +39 -0
  307. package/packages/utils/src/index.ts +289 -0
  308. package/packages/utils/tsconfig.build.json +12 -0
  309. package/packages/utils/vite.config.js +28 -0
  310. package/pnpm-workspace.yaml +8 -0
  311. package/scripts/vite/package-lib-config.ts +59 -0
  312. package/tsconfig.json +24 -0
  313. package/tsconfig.node.json +9 -0
@@ -0,0 +1,717 @@
1
+ import {
2
+ createLedger,
3
+ type LedgerAppendInput,
4
+ type LedgerCommitResult,
5
+ type LedgerContainer,
6
+ type LedgerDecryptor,
7
+ type LedgerIdentityContext,
8
+ type LedgerReplayEntry,
9
+ type LedgerVerificationResult,
10
+ } from "@ternent/ledger";
11
+ import type { SerializedIdentity } from "@ternent/identity";
12
+ import { ConcordBoundaryError } from "./errors.js";
13
+ import type {
14
+ ConcordApp,
15
+ ConcordAppOptions,
16
+ ConcordCommandContext,
17
+ ConcordCommandResult,
18
+ ConcordCommitInput,
19
+ ConcordCommitResult,
20
+ ConcordCreateParams,
21
+ ConcordReplayContext,
22
+ ConcordReplayMetadata,
23
+ ConcordReplayOptions,
24
+ ConcordReplayPlugin,
25
+ ConcordState,
26
+ } from "./types.js";
27
+
28
+ type CommandRegistration = {
29
+ plugin: ConcordReplayPlugin;
30
+ handler: NonNullable<ConcordReplayPlugin["commands"]>[string];
31
+ };
32
+
33
+ type RebuildStateOptions = {
34
+ ready: boolean;
35
+ integrityValid: boolean;
36
+ verification: LedgerVerificationResult | null;
37
+ };
38
+
39
+ type ReplayRange = Pick<ConcordReplayMetadata, "fromEntryId" | "toEntryId" | "isPartial">;
40
+
41
+ type ReplayDraft = {
42
+ nextState: ConcordState;
43
+ contexts: Map<string, ConcordReplayContext>;
44
+ };
45
+
46
+ function createDefaultNow() {
47
+ return new Date().toISOString();
48
+ }
49
+
50
+ function isObject(value: unknown): value is Record<string, unknown> {
51
+ return typeof value === "object" && value !== null;
52
+ }
53
+
54
+ function isLedgerReplayEntry(value: unknown): value is LedgerReplayEntry {
55
+ if (!isObject(value)) {
56
+ return false;
57
+ }
58
+
59
+ if (
60
+ typeof value.entryId !== "string" ||
61
+ typeof value.kind !== "string" ||
62
+ typeof value.author !== "string" ||
63
+ typeof value.authoredAt !== "string"
64
+ ) {
65
+ return false;
66
+ }
67
+
68
+ if (!(value.meta === null || isObject(value.meta))) {
69
+ return false;
70
+ }
71
+
72
+ if (!isObject(value.payload) || typeof value.payload.type !== "string") {
73
+ return false;
74
+ }
75
+
76
+ return (
77
+ value.payload.type === "plain" ||
78
+ value.payload.type === "encrypted" ||
79
+ value.payload.type === "decrypted"
80
+ );
81
+ }
82
+
83
+ function assertReplayEntries(value: unknown): LedgerReplayEntry[] {
84
+ if (Array.isArray(value) && value.every(isLedgerReplayEntry)) {
85
+ return value;
86
+ }
87
+
88
+ throw new ConcordBoundaryError(
89
+ "INVALID_LEDGER_PROJECTION",
90
+ "Concord requires ledger replay output to be LedgerReplayEntry[].",
91
+ );
92
+ }
93
+
94
+ function createInitialReplayState(plugins: ConcordReplayPlugin[]): Record<string, unknown> {
95
+ return Object.fromEntries(plugins.map((plugin) => [plugin.id, plugin.initialState?.()]));
96
+ }
97
+
98
+ function assertKnownPlugin(pluginsById: Map<string, ConcordReplayPlugin>, pluginId: string): void {
99
+ if (!pluginsById.has(pluginId)) {
100
+ throw new ConcordBoundaryError("UNKNOWN_PLUGIN", `Unknown Concord plugin: ${pluginId}`);
101
+ }
102
+ }
103
+
104
+ function normalizeAppendInputs(
105
+ value: LedgerAppendInput | LedgerAppendInput[],
106
+ ): LedgerAppendInput[] {
107
+ const inputs = Array.isArray(value) ? value : [value];
108
+ if (inputs.length === 0) {
109
+ throw new ConcordBoundaryError(
110
+ "COMMAND_PRODUCED_NO_ENTRIES",
111
+ "Concord command handlers must return at least one LedgerAppendInput.",
112
+ );
113
+ }
114
+ return inputs;
115
+ }
116
+
117
+ function hasInteractiveIdentity(
118
+ identity: SerializedIdentity | undefined,
119
+ ): identity is SerializedIdentity {
120
+ return Boolean(identity);
121
+ }
122
+
123
+ function requireInteractiveIdentity(identity: SerializedIdentity | undefined): SerializedIdentity {
124
+ if (!hasInteractiveIdentity(identity)) {
125
+ throw new ConcordBoundaryError(
126
+ "READ_ONLY_RUNTIME",
127
+ "Concord requires an identity for create, command, commit, and other signed mutations.",
128
+ );
129
+ }
130
+
131
+ return identity;
132
+ }
133
+
134
+ function resolveAuthorFromIdentity(identity: SerializedIdentity): string {
135
+ if (!identity.keyId || typeof identity.keyId !== "string") {
136
+ throw new ConcordBoundaryError(
137
+ "INVALID_IDENTITY",
138
+ "Concord identity is missing a valid keyId.",
139
+ );
140
+ }
141
+
142
+ return `did:key:${identity.keyId}`;
143
+ }
144
+
145
+ function resolveSignerFromIdentity(identity: SerializedIdentity): LedgerIdentityContext["signer"] {
146
+ return { identity };
147
+ }
148
+
149
+ function resolveDecryptorFromIdentity(identity: SerializedIdentity): LedgerDecryptor | undefined {
150
+ return { identity } as LedgerDecryptor;
151
+ }
152
+
153
+ function resolveReadOnlyLedgerIdentity(): LedgerIdentityContext {
154
+ return {
155
+ signer: {
156
+ identity: {
157
+ keyId: "readonly",
158
+ } as SerializedIdentity,
159
+ },
160
+ authorResolver() {
161
+ throw new ConcordBoundaryError(
162
+ "READ_ONLY_RUNTIME",
163
+ "Concord cannot derive an author without an interactive identity.",
164
+ );
165
+ },
166
+ };
167
+ }
168
+
169
+ function resolveLedgerIdentity(identity: SerializedIdentity | undefined): LedgerIdentityContext {
170
+ if (!identity) {
171
+ return resolveReadOnlyLedgerIdentity();
172
+ }
173
+
174
+ return {
175
+ signer: resolveSignerFromIdentity(identity),
176
+ authorResolver: () => resolveAuthorFromIdentity(identity),
177
+ decryptor: resolveDecryptorFromIdentity(identity),
178
+ };
179
+ }
180
+
181
+ function createReplayRange(options?: ConcordReplayOptions): ReplayRange {
182
+ return {
183
+ fromEntryId: options?.fromEntryId,
184
+ toEntryId: options?.toEntryId,
185
+ isPartial: Boolean(options?.fromEntryId || options?.toEntryId),
186
+ };
187
+ }
188
+
189
+ function createReplayMetadata(range: ReplayRange): ConcordReplayMetadata {
190
+ return {
191
+ phase: "reset",
192
+ entryCount: 0,
193
+ fromEntryId: range.fromEntryId,
194
+ toEntryId: range.toEntryId,
195
+ isPartial: range.isPartial,
196
+ };
197
+ }
198
+
199
+ const REPLAY_YIELD_INTERVAL = 25;
200
+
201
+ async function yieldToHost(): Promise<void> {
202
+ if (typeof requestAnimationFrame === "function") {
203
+ await new Promise<void>((resolve) => {
204
+ requestAnimationFrame(() => resolve());
205
+ });
206
+ return;
207
+ }
208
+
209
+ await new Promise<void>((resolve) => {
210
+ globalThis.setTimeout(resolve, 0);
211
+ });
212
+ }
213
+
214
+ export async function createConcordApp(input: ConcordAppOptions): Promise<ConcordApp> {
215
+ if (hasInteractiveIdentity(input.identity)) {
216
+ resolveAuthorFromIdentity(input.identity);
217
+ }
218
+
219
+ const appIdentity = input.identity;
220
+
221
+ const plugins = [...input.plugins];
222
+ const now = input.now ?? createDefaultNow;
223
+ const policy = {
224
+ autoCommit: input.policy?.autoCommit ?? false,
225
+ };
226
+
227
+ const pluginsById = new Map<string, ConcordReplayPlugin>();
228
+ const commands = new Map<string, CommandRegistration>();
229
+
230
+ for (const plugin of plugins) {
231
+ if (pluginsById.has(plugin.id)) {
232
+ throw new ConcordBoundaryError(
233
+ "DUPLICATE_PLUGIN_ID",
234
+ `Duplicate Concord plugin id: ${plugin.id}`,
235
+ );
236
+ }
237
+
238
+ pluginsById.set(plugin.id, plugin);
239
+
240
+ for (const [commandType, handler] of Object.entries(plugin.commands ?? {})) {
241
+ if (commands.has(commandType)) {
242
+ throw new ConcordBoundaryError(
243
+ "DUPLICATE_COMMAND_TYPE",
244
+ `Duplicate Concord command type: ${commandType}`,
245
+ );
246
+ }
247
+
248
+ commands.set(commandType, { plugin, handler });
249
+ }
250
+ }
251
+
252
+ const ledger =
253
+ input.ledger ??
254
+ (await createLedger<LedgerReplayEntry[]>({
255
+ identity: resolveLedgerIdentity(appIdentity),
256
+ initialProjection: [],
257
+ projector: (entries: LedgerReplayEntry[], entry: LedgerReplayEntry) => [...entries, entry],
258
+ storage: input.storage,
259
+ now,
260
+ protocol: input.protocol,
261
+ seal: input.seal,
262
+ armour: input.armour,
263
+ autoCommit: false,
264
+ replayPolicy: {
265
+ verify: false,
266
+ decrypt: true,
267
+ },
268
+ }));
269
+
270
+ let state: ConcordState = {
271
+ ready: false,
272
+ integrityValid: false,
273
+ stagedCount: 0,
274
+ replay: createInitialReplayState(plugins),
275
+ verification: null,
276
+ };
277
+
278
+ const listeners = new Set<(value: Readonly<ConcordState>) => void>();
279
+ let committedVerificationCache: {
280
+ headCommitId: string | null;
281
+ verification: LedgerVerificationResult;
282
+ } | null = null;
283
+ let replayEntriesCache: {
284
+ fingerprint: string;
285
+ replayEntries: unknown;
286
+ } | null = null;
287
+
288
+ function getReplayState<T = unknown>(pluginId: string, source = state): T {
289
+ assertKnownPlugin(pluginsById, pluginId);
290
+ return source.replay[pluginId] as T;
291
+ }
292
+
293
+ function publish(nextState: ConcordState): void {
294
+ state = nextState;
295
+ for (const listener of listeners) {
296
+ listener(state);
297
+ }
298
+ }
299
+
300
+ function getCommittedHeadCommitId(): string | null {
301
+ return ledger.getState().container?.head ?? null;
302
+ }
303
+
304
+ function getReplayFingerprint(): string {
305
+ const ledgerState = ledger.getState();
306
+ return `${ledgerState.container?.head ?? "none"}::${ledgerState.staged
307
+ .map((entry) => entry.entryId)
308
+ .join("|")}`;
309
+ }
310
+
311
+ async function getReplayEntries(options?: ConcordReplayOptions): Promise<unknown> {
312
+ if (options?.fromEntryId || options?.toEntryId) {
313
+ return await ledger.replay(options);
314
+ }
315
+
316
+ const fingerprint = getReplayFingerprint();
317
+ if (replayEntriesCache?.fingerprint === fingerprint) {
318
+ return replayEntriesCache.replayEntries;
319
+ }
320
+
321
+ const replayEntries = await ledger.replay();
322
+ replayEntriesCache = {
323
+ fingerprint,
324
+ replayEntries,
325
+ };
326
+ return replayEntries;
327
+ }
328
+
329
+ function createReplayDraft(options: RebuildStateOptions, range: ReplayRange): ReplayDraft {
330
+ const nextState: ConcordState = {
331
+ ready: options.ready,
332
+ integrityValid: options.integrityValid,
333
+ stagedCount: ledger.getState().staged.length,
334
+ replay: createInitialReplayState(plugins),
335
+ verification: options.verification,
336
+ };
337
+
338
+ const contexts = new Map<string, ConcordReplayContext>();
339
+
340
+ for (const plugin of plugins) {
341
+ const metadata = createReplayMetadata(range);
342
+ const context: ConcordReplayContext = {
343
+ pluginId: plugin.id,
344
+ decryptAvailable: hasInteractiveIdentity(appIdentity),
345
+ replay: metadata,
346
+ getState() {
347
+ return nextState.replay[plugin.id] as unknown;
348
+ },
349
+ setState(next) {
350
+ const prev = nextState.replay[plugin.id];
351
+ nextState.replay[plugin.id] =
352
+ typeof next === "function" ? (next as (value: unknown) => unknown)(prev) : next;
353
+ },
354
+ };
355
+ contexts.set(plugin.id, context);
356
+ }
357
+
358
+ return { nextState, contexts };
359
+ }
360
+
361
+ async function rebuildFromReplayEntries(
362
+ replayEntries: unknown,
363
+ options: RebuildStateOptions,
364
+ range: ReplayRange = createReplayRange(),
365
+ ): Promise<void> {
366
+ const entries = assertReplayEntries(replayEntries);
367
+ const { nextState, contexts } = createReplayDraft(options, range);
368
+
369
+ for (const plugin of plugins) {
370
+ const context = contexts.get(plugin.id) as ConcordReplayContext;
371
+ context.replay.phase = "reset";
372
+ context.replay.entryIndex = undefined;
373
+ context.replay.entryCount = entries.length;
374
+ await plugin.reset?.(context as never);
375
+ }
376
+
377
+ for (const plugin of plugins) {
378
+ const context = contexts.get(plugin.id) as ConcordReplayContext;
379
+ context.replay.phase = "beginReplay";
380
+ context.replay.entryIndex = undefined;
381
+ context.replay.entryCount = entries.length;
382
+ await plugin.beginReplay?.(context as never);
383
+ }
384
+
385
+ // Any ordered replay slice is deterministic, but authoritative full-state
386
+ // reconstruction still requires replay from genesis or a valid checkpoint.
387
+ for (let index = 0; index < entries.length; index += 1) {
388
+ const entry = entries[index];
389
+ for (const plugin of plugins) {
390
+ const context = contexts.get(plugin.id) as ConcordReplayContext;
391
+ context.replay.phase = "applyEntry";
392
+ context.replay.entryIndex = index;
393
+ context.replay.entryCount = entries.length;
394
+ await plugin.applyEntry?.(entry, context as never);
395
+ }
396
+
397
+ if ((index + 1) % REPLAY_YIELD_INTERVAL === 0) {
398
+ await yieldToHost();
399
+ }
400
+ }
401
+
402
+ for (const plugin of plugins) {
403
+ const context = contexts.get(plugin.id) as ConcordReplayContext;
404
+ context.replay.phase = "endReplay";
405
+ context.replay.entryIndex = undefined;
406
+ context.replay.entryCount = entries.length;
407
+ await plugin.endReplay?.(context as never);
408
+ }
409
+
410
+ publish(nextState);
411
+ }
412
+
413
+ function requireReady(): void {
414
+ if (!state.ready) {
415
+ throw new ConcordBoundaryError(
416
+ "APP_NOT_READY",
417
+ "Concord app must be loaded or created before commands can run.",
418
+ );
419
+ }
420
+ }
421
+
422
+ function createCommandContext(): ConcordCommandContext {
423
+ const identity = requireInteractiveIdentity(appIdentity);
424
+ return {
425
+ now,
426
+ identity,
427
+ getReplayState<T = unknown>(pluginId: string): T {
428
+ return getReplayState<T>(pluginId);
429
+ },
430
+ };
431
+ }
432
+
433
+ function publishIntegrityFailure(
434
+ verification: LedgerVerificationResult,
435
+ resetReplay: boolean,
436
+ ): void {
437
+ publish({
438
+ ready: false,
439
+ integrityValid: false,
440
+ stagedCount: ledger.getState().staged.length,
441
+ replay: resetReplay ? createInitialReplayState(plugins) : state.replay,
442
+ verification,
443
+ });
444
+ }
445
+
446
+ async function ensureCommittedHistoryUsable(options?: {
447
+ allowInspectionOnly?: boolean;
448
+ resetReplayOnFailure?: boolean;
449
+ }): Promise<boolean> {
450
+ const currentHeadCommitId = getCommittedHeadCommitId();
451
+ const verification =
452
+ committedVerificationCache?.headCommitId === currentHeadCommitId
453
+ ? committedVerificationCache.verification
454
+ : await ledger.verify();
455
+
456
+ if (committedVerificationCache?.headCommitId !== currentHeadCommitId) {
457
+ committedVerificationCache = {
458
+ headCommitId: currentHeadCommitId,
459
+ verification,
460
+ };
461
+ }
462
+
463
+ if (verification.committedHistoryValid) {
464
+ return true;
465
+ }
466
+
467
+ publishIntegrityFailure(verification, options?.resetReplayOnFailure ?? true);
468
+
469
+ if (options?.allowInspectionOnly) {
470
+ return false;
471
+ }
472
+
473
+ throw new ConcordBoundaryError(
474
+ "INVALID_COMMITTED_HISTORY",
475
+ "Concord cannot project runtime state from invalid committed history.",
476
+ );
477
+ }
478
+
479
+ async function replay(options?: ConcordReplayOptions): Promise<void> {
480
+ if (!(await ensureCommittedHistoryUsable())) {
481
+ return;
482
+ }
483
+
484
+ const replayEntries = await getReplayEntries(options);
485
+ await rebuildFromReplayEntries(
486
+ replayEntries,
487
+ {
488
+ ready: state.ready,
489
+ integrityValid: true,
490
+ verification: state.verification,
491
+ },
492
+ createReplayRange(options),
493
+ );
494
+ }
495
+
496
+ async function create(params?: ConcordCreateParams): Promise<void> {
497
+ requireInteractiveIdentity(appIdentity);
498
+ await ledger.create(params);
499
+
500
+ if (!(await ensureCommittedHistoryUsable())) {
501
+ return;
502
+ }
503
+
504
+ await rebuildFromReplayEntries(await getReplayEntries(), {
505
+ ready: true,
506
+ integrityValid: true,
507
+ verification: null,
508
+ });
509
+ }
510
+
511
+ async function load(): Promise<void> {
512
+ const loaded = await ledger.loadFromStorage();
513
+ if (!loaded) {
514
+ requireInteractiveIdentity(appIdentity);
515
+ await ledger.create();
516
+ }
517
+
518
+ if (
519
+ !(await ensureCommittedHistoryUsable({
520
+ allowInspectionOnly: true,
521
+ resetReplayOnFailure: true,
522
+ }))
523
+ ) {
524
+ return;
525
+ }
526
+
527
+ await rebuildFromReplayEntries(await getReplayEntries(), {
528
+ ready: true,
529
+ integrityValid: true,
530
+ verification: null,
531
+ });
532
+ }
533
+
534
+ async function command<TInput = unknown>(
535
+ type: string,
536
+ inputValue: TInput,
537
+ ): Promise<ConcordCommandResult> {
538
+ requireReady();
539
+ requireInteractiveIdentity(appIdentity);
540
+
541
+ const registration = commands.get(type);
542
+ if (!registration) {
543
+ throw new ConcordBoundaryError("UNKNOWN_COMMAND", `Unknown Concord command type: ${type}`);
544
+ }
545
+
546
+ const appendInputs = normalizeAppendInputs(
547
+ await registration.handler(createCommandContext(), inputValue),
548
+ );
549
+
550
+ const appendResults = await ledger.appendMany(appendInputs);
551
+ let commitResult: LedgerCommitResult | undefined;
552
+
553
+ if (policy.autoCommit) {
554
+ commitResult = await ledger.commit();
555
+ }
556
+
557
+ if (!(await ensureCommittedHistoryUsable())) {
558
+ return {
559
+ commitId: commitResult?.commit.commitId,
560
+ entryIds: appendResults.map((result) => result.entry.entryId),
561
+ stagedCount: ledger.getState().staged.length,
562
+ };
563
+ }
564
+
565
+ await rebuildFromReplayEntries(await getReplayEntries(), {
566
+ ready: true,
567
+ integrityValid: true,
568
+ verification: null,
569
+ });
570
+
571
+ return {
572
+ commitId: commitResult?.commit.commitId,
573
+ entryIds: appendResults.map((result) => result.entry.entryId),
574
+ stagedCount: state.stagedCount,
575
+ };
576
+ }
577
+
578
+ async function commit(input?: ConcordCommitInput): Promise<ConcordCommitResult> {
579
+ requireReady();
580
+ requireInteractiveIdentity(appIdentity);
581
+
582
+ const result = await ledger.commit(input);
583
+
584
+ if (!(await ensureCommittedHistoryUsable())) {
585
+ return {
586
+ commitId: result.commit.commitId,
587
+ entryIds: result.committedEntryIds,
588
+ };
589
+ }
590
+
591
+ await rebuildFromReplayEntries(await getReplayEntries(), {
592
+ ready: true,
593
+ integrityValid: true,
594
+ verification: null,
595
+ });
596
+
597
+ return {
598
+ commitId: result.commit.commitId,
599
+ entryIds: result.committedEntryIds,
600
+ };
601
+ }
602
+
603
+ async function clearStaged(): Promise<void> {
604
+ requireReady();
605
+ requireInteractiveIdentity(appIdentity);
606
+
607
+ await ledger.clearStaged();
608
+
609
+ if (!(await ensureCommittedHistoryUsable())) {
610
+ return;
611
+ }
612
+
613
+ await rebuildFromReplayEntries(await getReplayEntries(), {
614
+ ready: true,
615
+ integrityValid: true,
616
+ verification: null,
617
+ });
618
+ }
619
+
620
+ async function recompute(): Promise<void> {
621
+ if (!(await ensureCommittedHistoryUsable())) {
622
+ return;
623
+ }
624
+
625
+ const replayEntries = await ledger.recompute();
626
+ await rebuildFromReplayEntries(replayEntries, {
627
+ ready: state.ready,
628
+ integrityValid: true,
629
+ verification: state.verification,
630
+ });
631
+ }
632
+
633
+ async function verify(): Promise<LedgerVerificationResult> {
634
+ const verification = await ledger.verify();
635
+ committedVerificationCache = {
636
+ headCommitId: getCommittedHeadCommitId(),
637
+ verification,
638
+ };
639
+ publish({
640
+ ready: state.ready && verification.committedHistoryValid,
641
+ integrityValid: verification.committedHistoryValid,
642
+ stagedCount: state.stagedCount,
643
+ replay: state.replay,
644
+ verification,
645
+ });
646
+ return verification;
647
+ }
648
+
649
+ async function exportLedger(): Promise<LedgerContainer> {
650
+ return ledger.export();
651
+ }
652
+
653
+ async function importLedger(container: LedgerContainer): Promise<void> {
654
+ await ledger.import(container);
655
+
656
+ if (
657
+ !(await ensureCommittedHistoryUsable({
658
+ allowInspectionOnly: true,
659
+ resetReplayOnFailure: true,
660
+ }))
661
+ ) {
662
+ return;
663
+ }
664
+
665
+ await rebuildFromReplayEntries(await getReplayEntries(), {
666
+ ready: true,
667
+ integrityValid: true,
668
+ verification: null,
669
+ });
670
+ }
671
+
672
+ function subscribe(listener: (value: Readonly<ConcordState>) => void): () => void {
673
+ listeners.add(listener);
674
+ return () => {
675
+ listeners.delete(listener);
676
+ };
677
+ }
678
+
679
+ async function destroy(): Promise<void> {
680
+ for (const plugin of plugins) {
681
+ await plugin.destroy?.();
682
+ }
683
+
684
+ await ledger.destroy();
685
+ committedVerificationCache = null;
686
+ replayEntriesCache = null;
687
+ publish({
688
+ ready: false,
689
+ integrityValid: false,
690
+ stagedCount: 0,
691
+ replay: createInitialReplayState(plugins),
692
+ verification: null,
693
+ });
694
+ listeners.clear();
695
+ }
696
+
697
+ return {
698
+ create,
699
+ load,
700
+ command,
701
+ commit,
702
+ clearStaged,
703
+ replay,
704
+ recompute,
705
+ verify,
706
+ exportLedger,
707
+ importLedger,
708
+ getState() {
709
+ return state;
710
+ },
711
+ getReplayState<T = unknown>(pluginId: string): T {
712
+ return getReplayState<T>(pluginId);
713
+ },
714
+ subscribe,
715
+ destroy,
716
+ };
717
+ }
@@ -0,0 +1,9 @@
1
+ export class ConcordBoundaryError extends Error {
2
+ code: string;
3
+
4
+ constructor(code: string, message: string) {
5
+ super(message);
6
+ this.name = "ConcordBoundaryError";
7
+ this.code = code;
8
+ }
9
+ }