@freqhole/haruspex 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/dist/flows/add-peer/machine.d.ts +36 -0
  2. package/dist/flows/add-peer/machine.d.ts.map +1 -0
  3. package/dist/flows/add-peer/machine.js +500 -0
  4. package/dist/flows/add-peer/machine.test.d.ts +2 -0
  5. package/dist/flows/add-peer/machine.test.d.ts.map +1 -0
  6. package/dist/flows/add-peer/machine.test.js +421 -0
  7. package/dist/flows/add-peer/runner.d.ts +16 -0
  8. package/dist/flows/add-peer/runner.d.ts.map +1 -0
  9. package/dist/flows/add-peer/runner.js +349 -0
  10. package/dist/flows/add-peer/runner.test.d.ts +2 -0
  11. package/dist/flows/add-peer/runner.test.d.ts.map +1 -0
  12. package/dist/flows/add-peer/runner.test.js +205 -0
  13. package/dist/flows/add-peer/types.d.ts +271 -0
  14. package/dist/flows/add-peer/types.d.ts.map +1 -0
  15. package/dist/flows/add-peer/types.js +10 -0
  16. package/dist/flows/index.d.ts +6 -0
  17. package/dist/flows/index.d.ts.map +1 -0
  18. package/dist/flows/index.js +6 -0
  19. package/dist/identity/idbStore.d.ts +51 -0
  20. package/dist/identity/idbStore.d.ts.map +1 -0
  21. package/dist/identity/idbStore.js +183 -0
  22. package/dist/identity/idbStore.test.d.ts +2 -0
  23. package/dist/identity/idbStore.test.d.ts.map +1 -0
  24. package/dist/identity/idbStore.test.js +103 -0
  25. package/dist/identity/index.d.ts +8 -0
  26. package/dist/identity/index.d.ts.map +1 -0
  27. package/dist/identity/index.js +3 -0
  28. package/dist/identity/resolve.d.ts +31 -0
  29. package/dist/identity/resolve.d.ts.map +1 -0
  30. package/dist/identity/resolve.js +37 -0
  31. package/dist/identity/resolve.test.d.ts +2 -0
  32. package/dist/identity/resolve.test.d.ts.map +1 -0
  33. package/dist/identity/resolve.test.js +105 -0
  34. package/dist/identity/types.d.ts +21 -0
  35. package/dist/identity/types.d.ts.map +1 -0
  36. package/dist/identity/types.js +1 -0
  37. package/dist/identity/webLocks.d.ts +22 -0
  38. package/dist/identity/webLocks.d.ts.map +1 -0
  39. package/dist/identity/webLocks.js +67 -0
  40. package/dist/identity/webLocks.test.d.ts +2 -0
  41. package/dist/identity/webLocks.test.d.ts.map +1 -0
  42. package/dist/identity/webLocks.test.js +125 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +1 -0
  46. package/dist/index.test.d.ts +2 -0
  47. package/dist/index.test.d.ts.map +1 -0
  48. package/dist/index.test.js +7 -0
  49. package/dist/knock/index.d.ts +10 -0
  50. package/dist/knock/index.d.ts.map +1 -0
  51. package/dist/knock/index.js +5 -0
  52. package/dist/knock/pending-checker.d.ts +19 -0
  53. package/dist/knock/pending-checker.d.ts.map +1 -0
  54. package/dist/knock/pending-checker.js +27 -0
  55. package/dist/knock/pending-checker.test.d.ts +2 -0
  56. package/dist/knock/pending-checker.test.d.ts.map +1 -0
  57. package/dist/knock/pending-checker.test.js +93 -0
  58. package/dist/knock/requester.d.ts +26 -0
  59. package/dist/knock/requester.d.ts.map +1 -0
  60. package/dist/knock/requester.js +72 -0
  61. package/dist/knock/requester.test.d.ts +2 -0
  62. package/dist/knock/requester.test.d.ts.map +1 -0
  63. package/dist/knock/requester.test.js +108 -0
  64. package/dist/knock/responder.d.ts +13 -0
  65. package/dist/knock/responder.d.ts.map +1 -0
  66. package/dist/knock/responder.js +40 -0
  67. package/dist/knock/responder.test.d.ts +2 -0
  68. package/dist/knock/responder.test.d.ts.map +1 -0
  69. package/dist/knock/responder.test.js +89 -0
  70. package/dist/knock/store.d.ts +11 -0
  71. package/dist/knock/store.d.ts.map +1 -0
  72. package/dist/knock/store.js +211 -0
  73. package/dist/knock/store.test.d.ts +2 -0
  74. package/dist/knock/store.test.d.ts.map +1 -0
  75. package/dist/knock/store.test.js +195 -0
  76. package/dist/knock/types.d.ts +144 -0
  77. package/dist/knock/types.d.ts.map +1 -0
  78. package/dist/knock/types.js +26 -0
  79. package/dist/permissions/access.d.ts +54 -0
  80. package/dist/permissions/access.d.ts.map +1 -0
  81. package/dist/permissions/access.js +56 -0
  82. package/dist/permissions/access.test.d.ts +2 -0
  83. package/dist/permissions/access.test.d.ts.map +1 -0
  84. package/dist/permissions/access.test.js +72 -0
  85. package/dist/permissions/index.d.ts +5 -0
  86. package/dist/permissions/index.d.ts.map +1 -0
  87. package/dist/permissions/index.js +2 -0
  88. package/dist/permissions/role-table.d.ts +11 -0
  89. package/dist/permissions/role-table.d.ts.map +1 -0
  90. package/dist/permissions/role-table.js +16 -0
  91. package/dist/protocol/client.d.ts +67 -0
  92. package/dist/protocol/client.d.ts.map +1 -0
  93. package/dist/protocol/client.js +195 -0
  94. package/dist/protocol/client.test.d.ts +2 -0
  95. package/dist/protocol/client.test.d.ts.map +1 -0
  96. package/dist/protocol/client.test.js +282 -0
  97. package/dist/protocol/codec.d.ts +45 -0
  98. package/dist/protocol/codec.d.ts.map +1 -0
  99. package/dist/protocol/codec.js +111 -0
  100. package/dist/protocol/codec.test.d.ts +2 -0
  101. package/dist/protocol/codec.test.d.ts.map +1 -0
  102. package/dist/protocol/codec.test.js +93 -0
  103. package/dist/protocol/extensions.d.ts +33 -0
  104. package/dist/protocol/extensions.d.ts.map +1 -0
  105. package/dist/protocol/extensions.js +36 -0
  106. package/dist/protocol/extensions.test.d.ts +2 -0
  107. package/dist/protocol/extensions.test.d.ts.map +1 -0
  108. package/dist/protocol/extensions.test.js +65 -0
  109. package/dist/protocol/fixtures.test.d.ts +2 -0
  110. package/dist/protocol/fixtures.test.d.ts.map +1 -0
  111. package/dist/protocol/fixtures.test.js +51 -0
  112. package/dist/protocol/index.d.ts +9 -0
  113. package/dist/protocol/index.d.ts.map +1 -0
  114. package/dist/protocol/index.js +4 -0
  115. package/dist/protocol/messages.d.ts +480 -0
  116. package/dist/protocol/messages.d.ts.map +1 -0
  117. package/dist/protocol/messages.js +255 -0
  118. package/dist/protocol/messages.test.d.ts +2 -0
  119. package/dist/protocol/messages.test.d.ts.map +1 -0
  120. package/dist/protocol/messages.test.js +75 -0
  121. package/dist/share/codec.d.ts +58 -0
  122. package/dist/share/codec.d.ts.map +1 -0
  123. package/dist/share/codec.js +222 -0
  124. package/dist/share/codec.test.d.ts +2 -0
  125. package/dist/share/codec.test.d.ts.map +1 -0
  126. package/dist/share/codec.test.js +150 -0
  127. package/dist/share/detect.d.ts +24 -0
  128. package/dist/share/detect.d.ts.map +1 -0
  129. package/dist/share/detect.js +25 -0
  130. package/dist/share/detect.test.d.ts +2 -0
  131. package/dist/share/detect.test.d.ts.map +1 -0
  132. package/dist/share/detect.test.js +31 -0
  133. package/dist/share/index.d.ts +7 -0
  134. package/dist/share/index.d.ts.map +1 -0
  135. package/dist/share/index.js +3 -0
  136. package/dist/share/peer-addr.d.ts +32 -0
  137. package/dist/share/peer-addr.d.ts.map +1 -0
  138. package/dist/share/peer-addr.js +72 -0
  139. package/dist/share/peer-addr.test.d.ts +2 -0
  140. package/dist/share/peer-addr.test.d.ts.map +1 -0
  141. package/dist/share/peer-addr.test.js +58 -0
  142. package/dist/solid/auth-form.d.ts +43 -0
  143. package/dist/solid/auth-form.d.ts.map +1 -0
  144. package/dist/solid/auth-form.js +55 -0
  145. package/dist/solid/auth-form.test.d.ts +2 -0
  146. package/dist/solid/auth-form.test.d.ts.map +1 -0
  147. package/dist/solid/auth-form.test.js +131 -0
  148. package/dist/solid/auth-status.d.ts +21 -0
  149. package/dist/solid/auth-status.d.ts.map +1 -0
  150. package/dist/solid/auth-status.js +58 -0
  151. package/dist/solid/auth-status.test.d.ts +2 -0
  152. package/dist/solid/auth-status.test.d.ts.map +1 -0
  153. package/dist/solid/auth-status.test.js +60 -0
  154. package/dist/solid/index.d.ts +5 -0
  155. package/dist/solid/index.d.ts.map +1 -0
  156. package/dist/solid/index.js +10 -0
  157. package/dist/solid/knock-inbox.d.ts +61 -0
  158. package/dist/solid/knock-inbox.d.ts.map +1 -0
  159. package/dist/solid/knock-inbox.js +123 -0
  160. package/dist/solid/knock-inbox.test.d.ts +2 -0
  161. package/dist/solid/knock-inbox.test.d.ts.map +1 -0
  162. package/dist/solid/knock-inbox.test.js +153 -0
  163. package/dist/solid/reauth-flow.d.ts +26 -0
  164. package/dist/solid/reauth-flow.d.ts.map +1 -0
  165. package/dist/solid/reauth-flow.js +34 -0
  166. package/dist/solid/reauth-flow.test.d.ts +2 -0
  167. package/dist/solid/reauth-flow.test.d.ts.map +1 -0
  168. package/dist/solid/reauth-flow.test.js +91 -0
  169. package/dist/state/endpoint-control.d.ts +22 -0
  170. package/dist/state/endpoint-control.d.ts.map +1 -0
  171. package/dist/state/endpoint-control.js +34 -0
  172. package/dist/state/endpoint-control.test.d.ts +2 -0
  173. package/dist/state/endpoint-control.test.d.ts.map +1 -0
  174. package/dist/state/endpoint-control.test.js +63 -0
  175. package/dist/state/friend-directory.d.ts +37 -0
  176. package/dist/state/friend-directory.d.ts.map +1 -0
  177. package/dist/state/friend-directory.js +44 -0
  178. package/dist/state/friend-directory.test.d.ts +2 -0
  179. package/dist/state/friend-directory.test.d.ts.map +1 -0
  180. package/dist/state/friend-directory.test.js +63 -0
  181. package/dist/state/index.d.ts +6 -0
  182. package/dist/state/index.d.ts.map +1 -0
  183. package/dist/state/index.js +3 -0
  184. package/dist/state/peer-names.d.ts +7 -0
  185. package/dist/state/peer-names.d.ts.map +1 -0
  186. package/dist/state/peer-names.js +21 -0
  187. package/dist/state/peer-names.test.d.ts +2 -0
  188. package/dist/state/peer-names.test.d.ts.map +1 -0
  189. package/dist/state/peer-names.test.js +29 -0
  190. package/dist/testing/idb-harness.d.ts +9 -0
  191. package/dist/testing/idb-harness.d.ts.map +1 -0
  192. package/dist/testing/idb-harness.js +19 -0
  193. package/dist/testing/idb-harness.test.d.ts +2 -0
  194. package/dist/testing/idb-harness.test.d.ts.map +1 -0
  195. package/dist/testing/idb-harness.test.js +18 -0
  196. package/dist/testing/identity-fixtures.d.ts +10 -0
  197. package/dist/testing/identity-fixtures.d.ts.map +1 -0
  198. package/dist/testing/identity-fixtures.js +21 -0
  199. package/dist/testing/identity-fixtures.test.d.ts +2 -0
  200. package/dist/testing/identity-fixtures.test.d.ts.map +1 -0
  201. package/dist/testing/identity-fixtures.test.js +28 -0
  202. package/dist/testing/index.d.ts +7 -0
  203. package/dist/testing/index.d.ts.map +1 -0
  204. package/dist/testing/index.js +11 -0
  205. package/dist/testing/knock-fixtures.d.ts +13 -0
  206. package/dist/testing/knock-fixtures.d.ts.map +1 -0
  207. package/dist/testing/knock-fixtures.js +45 -0
  208. package/dist/testing/knock-fixtures.test.d.ts +2 -0
  209. package/dist/testing/knock-fixtures.test.d.ts.map +1 -0
  210. package/dist/testing/knock-fixtures.test.js +65 -0
  211. package/dist/testing/protocol-fixtures.d.ts +7 -0
  212. package/dist/testing/protocol-fixtures.d.ts.map +1 -0
  213. package/dist/testing/protocol-fixtures.js +28 -0
  214. package/dist/testing/protocol-fixtures.test.d.ts +2 -0
  215. package/dist/testing/protocol-fixtures.test.d.ts.map +1 -0
  216. package/dist/testing/protocol-fixtures.test.js +19 -0
  217. package/dist/testing/state-fixtures.d.ts +33 -0
  218. package/dist/testing/state-fixtures.d.ts.map +1 -0
  219. package/dist/testing/state-fixtures.js +67 -0
  220. package/dist/testing/state-fixtures.test.d.ts +2 -0
  221. package/dist/testing/state-fixtures.test.d.ts.map +1 -0
  222. package/dist/testing/state-fixtures.test.js +68 -0
  223. package/dist/testing/webauthn-mocks.d.ts +27 -0
  224. package/dist/testing/webauthn-mocks.d.ts.map +1 -0
  225. package/dist/testing/webauthn-mocks.js +78 -0
  226. package/dist/testing/webauthn-mocks.test.d.ts +2 -0
  227. package/dist/testing/webauthn-mocks.test.d.ts.map +1 -0
  228. package/dist/testing/webauthn-mocks.test.js +40 -0
  229. package/dist/webauthn/ceremony.d.ts +76 -0
  230. package/dist/webauthn/ceremony.d.ts.map +1 -0
  231. package/dist/webauthn/ceremony.js +136 -0
  232. package/dist/webauthn/ceremony.test.d.ts +2 -0
  233. package/dist/webauthn/ceremony.test.d.ts.map +1 -0
  234. package/dist/webauthn/ceremony.test.js +245 -0
  235. package/dist/webauthn/codec.d.ts +39 -0
  236. package/dist/webauthn/codec.d.ts.map +1 -0
  237. package/dist/webauthn/codec.js +118 -0
  238. package/dist/webauthn/codec.test.d.ts +2 -0
  239. package/dist/webauthn/codec.test.d.ts.map +1 -0
  240. package/dist/webauthn/codec.test.js +126 -0
  241. package/dist/webauthn/index.d.ts +7 -0
  242. package/dist/webauthn/index.d.ts.map +1 -0
  243. package/dist/webauthn/index.js +3 -0
  244. package/dist/webauthn/transport.d.ts +58 -0
  245. package/dist/webauthn/transport.d.ts.map +1 -0
  246. package/dist/webauthn/transport.js +6 -0
  247. package/dist/webauthn/types.d.ts +78 -0
  248. package/dist/webauthn/types.d.ts.map +1 -0
  249. package/dist/webauthn/types.js +29 -0
  250. package/package.json +83 -0
@@ -0,0 +1,271 @@
1
+ import type { PeerTarget } from "../../share/peer-addr.js";
2
+ export type { PeerTarget };
3
+ /** lifecycle stage of an in-progress remote addition, persisted so a
4
+ * closed tab/modal can resume where it left off. */
5
+ export type PendingRemoteStage = "testing" | "connected" | "failed" | "knock_pending" | "knock_accepted" | "knock_rejected";
6
+ /** a persisted in-progress remote addition. field names are snake_case:
7
+ * these records live in app storage (idb) and are adopted in place. */
8
+ export interface PendingRemote {
9
+ id: string;
10
+ /** p2p node id / endpoint json, or the http base url. */
11
+ peer_addr: string;
12
+ /** app-defined transport tag (e.g. "wasm", "app", "http"). */
13
+ transport: string;
14
+ stage: PendingRemoteStage;
15
+ server_name: string | null;
16
+ server_description: string | null;
17
+ server_version: string | null;
18
+ server_image_data: string | null;
19
+ server_image_type: string | null;
20
+ knock_username: string | null;
21
+ knock_message: string | null;
22
+ error_message: string | null;
23
+ }
24
+ /** what a peer's public hello endpoint reports about itself. snake_case
25
+ * matches the wire shape. */
26
+ export interface PeerServerInfo {
27
+ name: string;
28
+ description?: string | null;
29
+ version: string;
30
+ image_url?: string | null;
31
+ image_blob_id?: string | null;
32
+ requires_auth: boolean;
33
+ knocking_enabled?: boolean | null;
34
+ passkey_p2p_enabled?: boolean | null;
35
+ }
36
+ /** a saved remote, as returned by the app's remote store. */
37
+ export interface SavedRemote {
38
+ remote_id: string;
39
+ name: string;
40
+ base_url?: string;
41
+ peer_addr?: string;
42
+ }
43
+ export type AddPeerState = {
44
+ step: "url";
45
+ subStep: "input" | "knock_form";
46
+ error: string | null;
47
+ pendingRemotes: PendingRemote[];
48
+ serverInfo: PeerServerInfo | null;
49
+ peerAddr: string | null;
50
+ } | {
51
+ step: "testing";
52
+ progress: string | null;
53
+ peerAddr: string | null;
54
+ url: string;
55
+ } | {
56
+ step: "auth";
57
+ error: string | null;
58
+ serverInfo: PeerServerInfo | null;
59
+ peerAddr: string | null;
60
+ url: string;
61
+ } | {
62
+ step: "knock_sent";
63
+ } | {
64
+ step: "complete";
65
+ remote: SavedRemote;
66
+ };
67
+ /** outcome of a connection probe (hello + whoami against the target). */
68
+ export type ConnectionOutcome = {
69
+ kind: "already_authed";
70
+ serverInfo: PeerServerInfo;
71
+ } | {
72
+ kind: "needs_knock";
73
+ serverInfo: PeerServerInfo;
74
+ error?: string;
75
+ } | {
76
+ kind: "needs_auth";
77
+ serverInfo: PeerServerInfo;
78
+ } | {
79
+ kind: "failed";
80
+ error: string;
81
+ };
82
+ /** outcome of a knock-status re-check for a pending knock. */
83
+ export type KnockStatusOutcome = {
84
+ kind: "accepted_authed";
85
+ serverInfo: PeerServerInfo | null;
86
+ } | {
87
+ kind: "accepted_needs_auth";
88
+ serverInfo: PeerServerInfo | null;
89
+ } | {
90
+ kind: "denied";
91
+ } | {
92
+ kind: "pending";
93
+ } | {
94
+ kind: "unreachable";
95
+ };
96
+ export type AddPeerEvent = {
97
+ type: "MODAL_OPEN";
98
+ initialInput?: string;
99
+ } | {
100
+ type: "MODAL_CLOSE";
101
+ } | {
102
+ type: "INPUT_CHANGE";
103
+ input: string;
104
+ } | {
105
+ type: "QR_SCAN";
106
+ input: string;
107
+ } | {
108
+ type: "SUBMIT_URL";
109
+ input: string;
110
+ } | {
111
+ type: "SUBMIT_KNOCK";
112
+ username: string;
113
+ message: string;
114
+ } | {
115
+ type: "CANCEL_KNOCK";
116
+ } | {
117
+ type: "USE_INVITE_CODE";
118
+ } | {
119
+ type: "PASSKEY_SIGNIN";
120
+ } | {
121
+ type: "SUBMIT_AUTH";
122
+ mode: "login" | "register";
123
+ username: string;
124
+ inviteCode?: string;
125
+ } | {
126
+ type: "PASSKEY_AUTH";
127
+ mode: "login" | "register";
128
+ username?: string;
129
+ inviteCode?: string;
130
+ } | {
131
+ type: "BACK";
132
+ } | {
133
+ type: "RETRY_PENDING";
134
+ pending: PendingRemote;
135
+ } | {
136
+ type: "DELETE_PENDING";
137
+ pending: PendingRemote;
138
+ } | {
139
+ type: "PENDING_LOADED";
140
+ records: PendingRemote[];
141
+ } | {
142
+ type: "DUPLICATE_RESULT";
143
+ duplicateName: string | null;
144
+ } | {
145
+ type: "CONNECTION_RESULT";
146
+ outcome: ConnectionOutcome;
147
+ } | {
148
+ type: "KNOCK_SENT_RESULT";
149
+ ok: boolean;
150
+ error?: string;
151
+ } | {
152
+ type: "KNOCK_STATUS_RESULT";
153
+ outcome: KnockStatusOutcome;
154
+ } | {
155
+ type: "AUTH_RESULT";
156
+ ok: boolean;
157
+ error?: string;
158
+ } | {
159
+ type: "REMOTE_CREATED";
160
+ ok: boolean;
161
+ remote?: SavedRemote;
162
+ error?: string;
163
+ } | {
164
+ type: "COMPLETE_PEER_ADDR";
165
+ peerAddr: string;
166
+ } | {
167
+ type: "TIMER_FIRED";
168
+ id: string;
169
+ };
170
+ export type AddPeerEffect = {
171
+ type: "LOAD_PENDING_REMOTES";
172
+ } | {
173
+ type: "CHECK_DUPLICATE";
174
+ target: PeerTarget;
175
+ } | {
176
+ type: "UPSERT_PENDING";
177
+ peerAddr: string;
178
+ patch: Partial<Omit<PendingRemote, "id" | "peer_addr">>;
179
+ } | {
180
+ type: "DELETE_PENDING";
181
+ id: string;
182
+ } | {
183
+ type: "DELETE_PENDING_BY_ADDR";
184
+ peerAddr: string;
185
+ } | {
186
+ type: "CHECK_CONNECTION";
187
+ target: PeerTarget;
188
+ } | {
189
+ type: "SEND_KNOCK";
190
+ peerAddr: string;
191
+ username: string;
192
+ message: string;
193
+ } | {
194
+ type: "CHECK_KNOCK_STATUS";
195
+ pending: PendingRemote;
196
+ } | {
197
+ type: "AUTH_HTTP";
198
+ url: string;
199
+ mode: "login" | "register";
200
+ username: string;
201
+ inviteCode?: string;
202
+ } | {
203
+ type: "AUTH_REDEEM_INVITE";
204
+ peerAddr: string;
205
+ username: string;
206
+ inviteCode: string;
207
+ } | {
208
+ type: "AUTH_PASSKEY_REGISTER";
209
+ peerAddr: string;
210
+ username?: string;
211
+ inviteCode: string;
212
+ } | {
213
+ type: "AUTH_PASSKEY_LOGIN";
214
+ target: PeerTarget;
215
+ username?: string;
216
+ } | {
217
+ type: "CREATE_REMOTE";
218
+ peerAddr: string | null;
219
+ url: string;
220
+ } | {
221
+ type: "CANCEL_IN_FLIGHT";
222
+ } | {
223
+ type: "SCHEDULE_TIMER";
224
+ id: string;
225
+ ms: number;
226
+ } | {
227
+ type: "CLEAR_QUERY_PARAM";
228
+ } | {
229
+ type: "CALL_ON_SUCCESS";
230
+ remote: SavedRemote;
231
+ } | {
232
+ type: "CALL_ON_CLOSE";
233
+ };
234
+ /** everything the bundled effect runner needs from the app. each dep is a
235
+ * thin binding over the app's own storage/client layer; the machine
236
+ * itself never calls these - only `runEffect` does. */
237
+ export interface AddPeerFlowDeps {
238
+ getAllRemotes(): Promise<SavedRemote[]>;
239
+ getAllPendingRemotes(): Promise<PendingRemote[]>;
240
+ getPendingRemoteByPeerAddr(peerAddr: string): Promise<PendingRemote | null>;
241
+ createPendingRemote(record: Omit<PendingRemote, "id">): Promise<PendingRemote>;
242
+ updatePendingRemote(id: string, patch: Partial<Omit<PendingRemote, "id">>): Promise<void>;
243
+ deletePendingRemote(id: string): Promise<void>;
244
+ deletePendingRemoteByPeerAddr(peerAddr: string): Promise<void>;
245
+ createRemote(input: {
246
+ base_url?: string;
247
+ peer_addr?: string;
248
+ }): Promise<SavedRemote>;
249
+ getServerInfo(target: PeerTarget): Promise<PeerServerInfo | null>;
250
+ whoami(target: PeerTarget): Promise<boolean>;
251
+ sendKnock(peerAddr: string, username: string, message: string): Promise<void>;
252
+ checkKnockStatus(peerAddr: string): Promise<"accepted" | "rejected" | "pending" | null>;
253
+ authenticateHttp(url: string, data: {
254
+ mode: "login" | "register";
255
+ username: string;
256
+ inviteCode?: string;
257
+ }): Promise<void>;
258
+ redeemInvite(peerAddr: string, username: string, inviteCode: string): Promise<void>;
259
+ registerWithPasskey(peerAddr: string, username: string | undefined, inviteCode: string): Promise<void>;
260
+ loginWithPasskey(target: PeerTarget, username?: string): Promise<void>;
261
+ /** app-defined transport tag recorded on pending remotes ("wasm", "app", "http"). */
262
+ transportFor(target: PeerTarget): string;
263
+ /** default scheme for scheme-less http input. default "https". */
264
+ defaultScheme?: "http" | "https";
265
+ clearQueryParam?(): void;
266
+ onSuccess?(remote: SavedRemote): void;
267
+ onClose?(): void;
268
+ /** timer scheduling seam - override in tests. defaults to setTimeout. */
269
+ scheduleTimer?(id: string, ms: number, fire: () => void): () => void;
270
+ }
271
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/flows/add-peer/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ // shapes for the add-peer flow: the headless state machine that drives
2
+ // "add a remote peer" ui across apps (address entry -> connection test ->
3
+ // knock request or auth -> saved remote).
4
+ //
5
+ // the machine (see machine.ts) is pure and synchronous: `send(event)`
6
+ // returns effect descriptors; an adapter executes the effects (async io,
7
+ // timers, aborts) and feeds their outcomes back in as events. nothing in
8
+ // this subpath imports a framework - solid/pixi/react shells all drive
9
+ // the same machine.
10
+ export {};
@@ -0,0 +1,6 @@
1
+ export { COMPLETE_DISMISS_MS, DISMISS_TIMER_ID, initialContext, projectState, transition, } from "./add-peer/machine.js";
2
+ export type { AddPeerContext, TransitionResult } from "./add-peer/machine.js";
3
+ export { createAddPeerFlow } from "./add-peer/runner.js";
4
+ export type { AddPeerFlow } from "./add-peer/runner.js";
5
+ export type { AddPeerEffect, AddPeerEvent, AddPeerFlowDeps, AddPeerState, ConnectionOutcome, KnockStatusOutcome, PeerServerInfo, PeerTarget, PendingRemote, PendingRemoteStage, SavedRemote, } from "./add-peer/types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/flows/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,UAAU,GACX,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,WAAW,GACZ,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,6 @@
1
+ // framework-free flow state machines. each flow is a pure machine
2
+ // (send(event) -> effects) plus a bundled effect runner over injected
3
+ // deps; solid/pixi/react shells all drive the same machine. the ./solid
4
+ // subpath ships optional rendered components over these.
5
+ export { COMPLETE_DISMISS_MS, DISMISS_TIMER_ID, initialContext, projectState, transition, } from "./add-peer/machine.js";
6
+ export { createAddPeerFlow } from "./add-peer/runner.js";
@@ -0,0 +1,51 @@
1
+ import type { IdentityStore, P2PIdentity } from "./types.js";
2
+ /**
3
+ * an idb database/store/key triple describing where an identity record
4
+ * lives. every consuming app parameterizes its own database name (and,
5
+ * when reading another app's database as a fallback source, that app's
6
+ * database name too) - see resolve.ts for the cross-app fallback story.
7
+ */
8
+ export interface IdentitySource {
9
+ /** indexeddb database name. */
10
+ databaseName: string;
11
+ /** object store name within that database. */
12
+ storeName: string;
13
+ /** record key within the store; defaults to "p2p_identity". */
14
+ key?: string;
15
+ }
16
+ /**
17
+ * check whether a database exists without creating it.
18
+ *
19
+ * strategy:
20
+ * 1. use indexedDB.databases() when available (chrome/firefox 126+)
21
+ * 2. fall back to a versionless open; abort the versionchange transaction
22
+ * if upgrade fires (meaning the db did not exist), then delete the
23
+ * empty database that open unavoidably created before we could abort.
24
+ */
25
+ export declare function databaseExists(databaseName: string): Promise<boolean>;
26
+ /**
27
+ * open a database read-only-safe: returns null if it does not already
28
+ * exist, never creating or upgrading it in the process.
29
+ */
30
+ export declare function openExistingDatabase(databaseName: string): Promise<IDBDatabase | null>;
31
+ /** check whether a given identity source's database and store both exist,
32
+ * without creating or upgrading anything. */
33
+ export declare function identitySourceAvailable(source: IdentitySource): Promise<boolean>;
34
+ /**
35
+ * read an identity record from a source, without creating or upgrading its
36
+ * database. returns null if the database, store, or record is absent.
37
+ */
38
+ export declare function readIdentityFrom(source: IdentitySource): Promise<P2PIdentity | null>;
39
+ /**
40
+ * write an identity record to a source. the database and store must already
41
+ * exist (this never creates or upgrades a database it doesn't own).
42
+ */
43
+ export declare function writeIdentityTo(source: IdentitySource, identity: P2PIdentity): Promise<void>;
44
+ /**
45
+ * an identity store backed by its own dedicated idb database/store, created
46
+ * on demand. suitable as an app's local, always-available identity store -
47
+ * the store the cross-app resolution in resolve.ts falls back to when no
48
+ * other source has an identity yet.
49
+ */
50
+ export declare function createIdbIdentityStore(source: IdentitySource): IdentityStore;
51
+ //# sourceMappingURL=idbStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idbStore.d.ts","sourceRoot":"","sources":["../../src/identity/idbStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK7D;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU3E;AA6BD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAiB7B;AAED;8CAC8C;AAC9C,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,CAAC,CAMlB;AA6BD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAS7B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CAaf;AAwCD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,CAsB5E"}
@@ -0,0 +1,183 @@
1
+ /** record key used within an identity object store, unless overridden. */
2
+ const DEFAULT_KEY = "p2p_identity";
3
+ /**
4
+ * check whether a database exists without creating it.
5
+ *
6
+ * strategy:
7
+ * 1. use indexedDB.databases() when available (chrome/firefox 126+)
8
+ * 2. fall back to a versionless open; abort the versionchange transaction
9
+ * if upgrade fires (meaning the db did not exist), then delete the
10
+ * empty database that open unavoidably created before we could abort.
11
+ */
12
+ export async function databaseExists(databaseName) {
13
+ if (typeof indexedDB.databases === "function") {
14
+ try {
15
+ const dbs = await indexedDB.databases();
16
+ return dbs.some((d) => d.name === databaseName);
17
+ }
18
+ catch {
19
+ // fall through to open-based detection
20
+ }
21
+ }
22
+ return openBasedExistenceCheck(databaseName);
23
+ }
24
+ function openBasedExistenceCheck(databaseName) {
25
+ return new Promise((resolve) => {
26
+ const req = indexedDB.open(databaseName);
27
+ let existed = true;
28
+ req.onupgradeneeded = (event) => {
29
+ // upgradeneeded on a version-0 open means the db is being created now
30
+ existed = false;
31
+ event.target.transaction?.abort();
32
+ };
33
+ req.onsuccess = () => {
34
+ req.result.close();
35
+ if (!existed) {
36
+ // clean up the empty database that was created before we could abort
37
+ indexedDB.deleteDatabase(databaseName);
38
+ }
39
+ resolve(existed);
40
+ };
41
+ req.onerror = () => {
42
+ // the abort above causes an error - if existed is false this is expected
43
+ resolve(existed);
44
+ };
45
+ });
46
+ }
47
+ /**
48
+ * open a database read-only-safe: returns null if it does not already
49
+ * exist, never creating or upgrading it in the process.
50
+ */
51
+ export async function openExistingDatabase(databaseName) {
52
+ if (!(await databaseExists(databaseName)))
53
+ return null;
54
+ return new Promise((resolve) => {
55
+ const req = indexedDB.open(databaseName);
56
+ req.onupgradeneeded = (event) => {
57
+ // should not fire - databaseExists() just confirmed the db is there.
58
+ // abort defensively so we never create it out from under a concurrent
59
+ // deletion.
60
+ event.target.transaction?.abort();
61
+ resolve(null);
62
+ };
63
+ req.onsuccess = () => resolve(req.result);
64
+ req.onerror = () => resolve(null);
65
+ });
66
+ }
67
+ /** check whether a given identity source's database and store both exist,
68
+ * without creating or upgrading anything. */
69
+ export async function identitySourceAvailable(source) {
70
+ const db = await openExistingDatabase(source.databaseName);
71
+ if (!db)
72
+ return false;
73
+ const has = db.objectStoreNames.contains(source.storeName);
74
+ db.close();
75
+ return has;
76
+ }
77
+ function getRecord(db, storeName, key) {
78
+ return new Promise((resolve, reject) => {
79
+ const tx = db.transaction(storeName, "readonly");
80
+ const req = tx.objectStore(storeName).get(key);
81
+ req.onsuccess = () => resolve(req.result ?? null);
82
+ req.onerror = () => reject(req.error);
83
+ });
84
+ }
85
+ function putRecord(db, storeName, key, value) {
86
+ return new Promise((resolve, reject) => {
87
+ const tx = db.transaction(storeName, "readwrite");
88
+ const req = tx.objectStore(storeName).put(value, key);
89
+ req.onsuccess = () => resolve();
90
+ req.onerror = () => reject(req.error);
91
+ });
92
+ }
93
+ /**
94
+ * read an identity record from a source, without creating or upgrading its
95
+ * database. returns null if the database, store, or record is absent.
96
+ */
97
+ export async function readIdentityFrom(source) {
98
+ const db = await openExistingDatabase(source.databaseName);
99
+ if (!db)
100
+ return null;
101
+ try {
102
+ if (!db.objectStoreNames.contains(source.storeName))
103
+ return null;
104
+ return await getRecord(db, source.storeName, source.key ?? DEFAULT_KEY);
105
+ }
106
+ finally {
107
+ db.close();
108
+ }
109
+ }
110
+ /**
111
+ * write an identity record to a source. the database and store must already
112
+ * exist (this never creates or upgrades a database it doesn't own).
113
+ */
114
+ export async function writeIdentityTo(source, identity) {
115
+ const db = await openExistingDatabase(source.databaseName);
116
+ if (!db || !db.objectStoreNames.contains(source.storeName)) {
117
+ db?.close();
118
+ throw new Error(`identity source "${source.databaseName}" (store "${source.storeName}") does not exist`);
119
+ }
120
+ try {
121
+ await putRecord(db, source.storeName, source.key ?? DEFAULT_KEY, identity);
122
+ }
123
+ finally {
124
+ db.close();
125
+ }
126
+ }
127
+ async function openAtCurrentVersion(databaseName) {
128
+ return new Promise((resolve, reject) => {
129
+ const req = indexedDB.open(databaseName);
130
+ req.onsuccess = () => resolve(req.result);
131
+ req.onerror = () => reject(req.error);
132
+ });
133
+ }
134
+ function openWithStoreCreated(databaseName, version, storeName) {
135
+ return new Promise((resolve, reject) => {
136
+ const req = indexedDB.open(databaseName, version);
137
+ req.onupgradeneeded = () => {
138
+ const db = req.result;
139
+ if (!db.objectStoreNames.contains(storeName)) {
140
+ db.createObjectStore(storeName);
141
+ }
142
+ };
143
+ req.onsuccess = () => resolve(req.result);
144
+ req.onerror = () => reject(req.error);
145
+ });
146
+ }
147
+ async function openOrCreateDatabase(databaseName, storeName) {
148
+ const db = await openAtCurrentVersion(databaseName);
149
+ if (db.objectStoreNames.contains(storeName))
150
+ return db;
151
+ const nextVersion = db.version + 1;
152
+ db.close();
153
+ return openWithStoreCreated(databaseName, nextVersion, storeName);
154
+ }
155
+ /**
156
+ * an identity store backed by its own dedicated idb database/store, created
157
+ * on demand. suitable as an app's local, always-available identity store -
158
+ * the store the cross-app resolution in resolve.ts falls back to when no
159
+ * other source has an identity yet.
160
+ */
161
+ export function createIdbIdentityStore(source) {
162
+ const key = source.key ?? DEFAULT_KEY;
163
+ return {
164
+ async get() {
165
+ const db = await openOrCreateDatabase(source.databaseName, source.storeName);
166
+ try {
167
+ return await getRecord(db, source.storeName, key);
168
+ }
169
+ finally {
170
+ db.close();
171
+ }
172
+ },
173
+ async set(identity) {
174
+ const db = await openOrCreateDatabase(source.databaseName, source.storeName);
175
+ try {
176
+ await putRecord(db, source.storeName, key, identity);
177
+ }
178
+ finally {
179
+ db.close();
180
+ }
181
+ },
182
+ };
183
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=idbStore.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idbStore.test.d.ts","sourceRoot":"","sources":["../../src/identity/idbStore.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { IDBFactory } from "fake-indexeddb";
2
+ import { beforeEach, describe, expect, it } from "vitest";
3
+ import { createIdbIdentityStore, databaseExists, identitySourceAvailable, openExistingDatabase, readIdentityFrom, writeIdentityTo, } from "./idbStore.js";
4
+ function makeIdentity(nodeId = "node-abc") {
5
+ return {
6
+ secret_key: new Uint8Array([1, 2, 3]),
7
+ node_id: nodeId,
8
+ created_at: 1000,
9
+ };
10
+ }
11
+ beforeEach(() => {
12
+ // fresh, isolated indexedDB per test
13
+ globalThis.indexedDB = new IDBFactory();
14
+ });
15
+ describe("createIdbIdentityStore", () => {
16
+ it("returns null when nothing has been stored yet", async () => {
17
+ const store = createIdbIdentityStore({
18
+ databaseName: "app-db",
19
+ storeName: "identity",
20
+ });
21
+ expect(await store.get()).toBeNull();
22
+ });
23
+ it("creates the database and store lazily, round-tripping an identity", async () => {
24
+ const store = createIdbIdentityStore({
25
+ databaseName: "app-db",
26
+ storeName: "identity",
27
+ });
28
+ const identity = makeIdentity("node-1");
29
+ await store.set(identity);
30
+ const result = await store.get();
31
+ expect(result).toEqual(identity);
32
+ });
33
+ it("supports a custom record key", async () => {
34
+ const store = createIdbIdentityStore({
35
+ databaseName: "app-db",
36
+ storeName: "identity",
37
+ key: "custom-key",
38
+ });
39
+ const identity = makeIdentity("node-2");
40
+ await store.set(identity);
41
+ expect(await readIdentityFrom({
42
+ databaseName: "app-db",
43
+ storeName: "identity",
44
+ key: "custom-key",
45
+ })).toEqual(identity);
46
+ });
47
+ it("reuses the same database across separate store instances", async () => {
48
+ const first = createIdbIdentityStore({ databaseName: "shared-db", storeName: "identity" });
49
+ await first.set(makeIdentity("node-shared"));
50
+ const second = createIdbIdentityStore({ databaseName: "shared-db", storeName: "identity" });
51
+ expect(await second.get()).toEqual(makeIdentity("node-shared"));
52
+ });
53
+ });
54
+ describe("databaseExists / openExistingDatabase", () => {
55
+ it("reports false for a database that was never created", async () => {
56
+ expect(await databaseExists("nope")).toBe(false);
57
+ expect(await openExistingDatabase("nope")).toBeNull();
58
+ });
59
+ it("reports true once a database has actually been created", async () => {
60
+ const store = createIdbIdentityStore({ databaseName: "real-db", storeName: "identity" });
61
+ await store.set(makeIdentity());
62
+ expect(await databaseExists("real-db")).toBe(true);
63
+ const db = await openExistingDatabase("real-db");
64
+ expect(db).not.toBeNull();
65
+ db?.close();
66
+ });
67
+ it("never leaves behind a database after a failed existence check", async () => {
68
+ await databaseExists("probed-only");
69
+ expect(await databaseExists("probed-only")).toBe(false);
70
+ });
71
+ });
72
+ describe("identitySourceAvailable / readIdentityFrom / writeIdentityTo", () => {
73
+ it("is unavailable when the database does not exist", async () => {
74
+ const source = { databaseName: "missing-db", storeName: "identity" };
75
+ expect(await identitySourceAvailable(source)).toBe(false);
76
+ expect(await readIdentityFrom(source)).toBeNull();
77
+ });
78
+ it("is unavailable when the database exists but lacks the store", async () => {
79
+ const store = createIdbIdentityStore({ databaseName: "other-db", storeName: "other-store" });
80
+ await store.set(makeIdentity());
81
+ const source = { databaseName: "other-db", storeName: "identity" };
82
+ expect(await identitySourceAvailable(source)).toBe(false);
83
+ expect(await readIdentityFrom(source)).toBeNull();
84
+ });
85
+ it("reads an identity once the database and store exist", async () => {
86
+ const store = createIdbIdentityStore({ databaseName: "seeded-db", storeName: "identity" });
87
+ const identity = makeIdentity("node-seeded");
88
+ await store.set(identity);
89
+ const source = { databaseName: "seeded-db", storeName: "identity" };
90
+ expect(await identitySourceAvailable(source)).toBe(true);
91
+ expect(await readIdentityFrom(source)).toEqual(identity);
92
+ });
93
+ it("writeIdentityTo refuses to create a missing database", async () => {
94
+ await expect(writeIdentityTo({ databaseName: "missing-db", storeName: "identity" }, makeIdentity())).rejects.toThrow(/does not exist/);
95
+ });
96
+ it("writeIdentityTo updates an existing source in place", async () => {
97
+ const store = createIdbIdentityStore({ databaseName: "writable-db", storeName: "identity" });
98
+ await store.set(makeIdentity("node-original"));
99
+ const source = { databaseName: "writable-db", storeName: "identity" };
100
+ await writeIdentityTo(source, makeIdentity("node-updated"));
101
+ expect((await readIdentityFrom(source))?.node_id).toBe("node-updated");
102
+ });
103
+ });
@@ -0,0 +1,8 @@
1
+ export type { IdentityStore, P2PIdentity } from "./types.js";
2
+ export type { IdentitySource } from "./idbStore.js";
3
+ export { createIdbIdentityStore, databaseExists, identitySourceAvailable, openExistingDatabase, readIdentityFrom, writeIdentityTo, } from "./idbStore.js";
4
+ export type { ResolveIdentityOptions } from "./resolve.js";
5
+ export { persistIdentity, resolveIdentity } from "./resolve.js";
6
+ export type { AcquireNodeLeadershipOpts } from "./webLocks.js";
7
+ export { acquireNodeLeadership, LOCK_NAME } from "./webLocks.js";
8
+ //# sourceMappingURL=index.d.ts.map