@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,21 @@
1
+ // deterministic identity fixture builder for tests that need
2
+ // valid-shaped identity records without real crypto or a running node.
3
+ /** creates a test identity with deterministic fields. `nodeId` defaults to
4
+ * a repeating pattern for easy recognition in test output; `secretKey` is
5
+ * a short byte sequence sufficient for shape validation but not usable for
6
+ * real crypto. */
7
+ export function makeIdentity(nodeId = "ab".repeat(32), createdAt = 1000) {
8
+ return {
9
+ secret_key: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
10
+ node_id: nodeId,
11
+ created_at: createdAt,
12
+ };
13
+ }
14
+ /** creates a batch of distinct test identities, each with a unique node id
15
+ * derived from the given prefix and an incrementing counter. */
16
+ export function makeIdentities(count, nodeIdPrefix = "node") {
17
+ return Array.from({ length: count }, (_, i) => {
18
+ const nodeId = `${nodeIdPrefix}-${i.toString().padStart(2, "0")}`.padEnd(64, "0");
19
+ return makeIdentity(nodeId, 1000 + i);
20
+ });
21
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=identity-fixtures.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-fixtures.test.d.ts","sourceRoot":"","sources":["../../src/testing/identity-fixtures.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { makeIdentities, makeIdentity } from "./identity-fixtures.js";
3
+ describe("makeIdentity", () => {
4
+ it("produces a valid-shaped identity with default values", () => {
5
+ const identity = makeIdentity();
6
+ expect(identity.node_id).toHaveLength(64);
7
+ expect(identity.secret_key).toBeInstanceOf(Uint8Array);
8
+ expect(identity.created_at).toBe(1000);
9
+ });
10
+ it("accepts custom node id and timestamp", () => {
11
+ const identity = makeIdentity("custom-node-id".padEnd(64, "x"), 5000);
12
+ expect(identity.node_id).toBe("custom-node-id".padEnd(64, "x"));
13
+ expect(identity.created_at).toBe(5000);
14
+ });
15
+ });
16
+ describe("makeIdentities", () => {
17
+ it("creates a batch of distinct identities", () => {
18
+ const identities = makeIdentities(3);
19
+ expect(identities).toHaveLength(3);
20
+ const nodeIds = identities.map((i) => i.node_id);
21
+ expect(new Set(nodeIds).size).toBe(3);
22
+ });
23
+ it("uses the given prefix in node ids", () => {
24
+ const identities = makeIdentities(2, "test");
25
+ expect(identities[0].node_id).toMatch(/^test-00/);
26
+ expect(identities[1].node_id).toMatch(/^test-01/);
27
+ });
28
+ });
@@ -0,0 +1,7 @@
1
+ export { makeIdentity, makeIdentities } from "./identity-fixtures.js";
2
+ export { createFakeWebauthnTransport, FAKE_CEREMONY_DEPS, FAKE_CREDENTIAL, FAKE_REGISTRATION_CHALLENGE, FAKE_AUTHENTICATION_CHALLENGE, resetFakeCeremonyDeps, } from "./webauthn-mocks.js";
3
+ export { makeKnockScope, makeKnockRecord, makeKnockDecision, makeKnockRecords, } from "./knock-fixtures.js";
4
+ export { makeFriendDirectoryEntry, makeFriendDirectoryEntries, makeSavedRemote, makePendingRemote, createFakeEndpointAdapter, type SavedRemote, type PendingRemote, } from "./state-fixtures.js";
5
+ export { loadProtocolFixtures, loadProtocolFixture } from "./protocol-fixtures.js";
6
+ export { setupFakeIndexedDB, teardownFakeIndexedDB } from "./idb-harness.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,eAAe,EACf,2BAA2B,EAC3B,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,11 @@
1
+ // exported test utilities: doubles and fixtures consuming apps use in
2
+ // their own suites instead of re-rolling mocks. no restriction on what
3
+ // this subpath imports at runtime (it is only ever imported by test
4
+ // files), but its exports stay framework-free - none of these doubles
5
+ // need solid-js.
6
+ export { makeIdentity, makeIdentities } from "./identity-fixtures.js";
7
+ export { createFakeWebauthnTransport, FAKE_CEREMONY_DEPS, FAKE_CREDENTIAL, FAKE_REGISTRATION_CHALLENGE, FAKE_AUTHENTICATION_CHALLENGE, resetFakeCeremonyDeps, } from "./webauthn-mocks.js";
8
+ export { makeKnockScope, makeKnockRecord, makeKnockDecision, makeKnockRecords, } from "./knock-fixtures.js";
9
+ export { makeFriendDirectoryEntry, makeFriendDirectoryEntries, makeSavedRemote, makePendingRemote, createFakeEndpointAdapter, } from "./state-fixtures.js";
10
+ export { loadProtocolFixtures, loadProtocolFixture } from "./protocol-fixtures.js";
11
+ export { setupFakeIndexedDB, teardownFakeIndexedDB } from "./idb-harness.js";
@@ -0,0 +1,13 @@
1
+ import type { KnockDecision, KnockRecord, KnockScope } from "../knock/types.js";
2
+ /** creates a deterministic knock scope for the given kind. */
3
+ export declare function makeKnockScope(kind: "account" | "browse" | "resource", resourceId?: string): KnockScope;
4
+ /** creates a knock record with default test values. all fields are
5
+ * overridable via the optional `overrides` argument. */
6
+ export declare function makeKnockRecord(overrides?: Partial<KnockRecord>): KnockRecord;
7
+ /** creates a knock decision for test scenarios where the full audit log
8
+ * matters. */
9
+ export declare function makeKnockDecision(outcome: "accepted" | "denied", byNodeId?: string, at?: number, grantedRole?: string): KnockDecision;
10
+ /** creates a batch of knock records with distinct node ids and optional
11
+ * shared overrides. */
12
+ export declare function makeKnockRecords(count: number, overrides?: Partial<KnockRecord>): KnockRecord[];
13
+ //# sourceMappingURL=knock-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knock-fixtures.d.ts","sourceRoot":"","sources":["../../src/testing/knock-fixtures.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EAEb,WAAW,EACX,UAAU,EAEX,MAAM,mBAAmB,CAAC;AAE3B,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CAKvG;AAED;yDACyD;AACzD,wBAAgB,eAAe,CAC7B,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GACnC,WAAW,CAeb;AAED;eACe;AACf,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,GAAG,QAAQ,EAC9B,QAAQ,SAAkB,EAC1B,EAAE,SAAa,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,aAAa,CAOf;AAED;wBACwB;AACxB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GACnC,WAAW,EAAE,CAKf"}
@@ -0,0 +1,45 @@
1
+ // fixture builders for knock records and scopes - deterministic test data
2
+ // for exercising knock logic without a live transport or real store.
3
+ /** creates a deterministic knock scope for the given kind. */
4
+ export function makeKnockScope(kind, resourceId) {
5
+ if (kind === "resource") {
6
+ return { kind: "resource", resourceId: resourceId ?? "doc-1" };
7
+ }
8
+ return { kind };
9
+ }
10
+ /** creates a knock record with default test values. all fields are
11
+ * overridable via the optional `overrides` argument. */
12
+ export function makeKnockRecord(overrides = {}) {
13
+ const direction = overrides.direction ?? "outbound";
14
+ const scope = overrides.scope ?? { kind: "browse" };
15
+ return {
16
+ id: `knock-${Math.random().toString(36).slice(2, 9)}`,
17
+ nodeId: "ab".repeat(32),
18
+ direction,
19
+ scope,
20
+ message: "",
21
+ status: "pending",
22
+ createdAt: Date.now(),
23
+ decisions: [],
24
+ grantedResourceIds: [],
25
+ ...overrides,
26
+ };
27
+ }
28
+ /** creates a knock decision for test scenarios where the full audit log
29
+ * matters. */
30
+ export function makeKnockDecision(outcome, byNodeId = "cd".repeat(32), at = Date.now(), grantedRole) {
31
+ return {
32
+ byNodeId,
33
+ outcome,
34
+ at,
35
+ ...(grantedRole && { grantedRole }),
36
+ };
37
+ }
38
+ /** creates a batch of knock records with distinct node ids and optional
39
+ * shared overrides. */
40
+ export function makeKnockRecords(count, overrides = {}) {
41
+ return Array.from({ length: count }, (_, i) => {
42
+ const nodeId = `node-${i.toString().padStart(2, "0")}`.padEnd(64, "0");
43
+ return makeKnockRecord({ ...overrides, nodeId });
44
+ });
45
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=knock-fixtures.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knock-fixtures.test.d.ts","sourceRoot":"","sources":["../../src/testing/knock-fixtures.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,65 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { makeKnockDecision, makeKnockRecord, makeKnockRecords, makeKnockScope, } from "./knock-fixtures.js";
3
+ describe("makeKnockScope", () => {
4
+ it("creates account and browse scopes without extra fields", () => {
5
+ expect(makeKnockScope("account")).toEqual({ kind: "account" });
6
+ expect(makeKnockScope("browse")).toEqual({ kind: "browse" });
7
+ });
8
+ it("creates resource scopes with a resource id", () => {
9
+ expect(makeKnockScope("resource", "doc-1")).toEqual({
10
+ kind: "resource",
11
+ resourceId: "doc-1",
12
+ });
13
+ });
14
+ it("defaults resource id when omitted", () => {
15
+ const scope = makeKnockScope("resource");
16
+ expect(scope.kind).toBe("resource");
17
+ expect(scope.kind === "resource" && scope.resourceId).toBe("doc-1");
18
+ });
19
+ });
20
+ describe("makeKnockRecord", () => {
21
+ it("produces a valid knock record with default values", () => {
22
+ const record = makeKnockRecord();
23
+ expect(record.id).toMatch(/^knock-/);
24
+ expect(record.nodeId).toHaveLength(64);
25
+ expect(record.status).toBe("pending");
26
+ expect(record.decisions).toEqual([]);
27
+ });
28
+ it("accepts overrides for any field", () => {
29
+ const record = makeKnockRecord({
30
+ nodeId: "custom".padEnd(64, "0"),
31
+ status: "accepted",
32
+ message: "let me in",
33
+ });
34
+ expect(record.nodeId).toBe("custom".padEnd(64, "0"));
35
+ expect(record.status).toBe("accepted");
36
+ expect(record.message).toBe("let me in");
37
+ });
38
+ });
39
+ describe("makeKnockDecision", () => {
40
+ it("creates an accepted decision with optional role", () => {
41
+ const decision = makeKnockDecision("accepted", "node-id", 5000, "member");
42
+ expect(decision.outcome).toBe("accepted");
43
+ expect(decision.byNodeId).toBe("node-id");
44
+ expect(decision.at).toBe(5000);
45
+ expect(decision.grantedRole).toBe("member");
46
+ });
47
+ it("omits grantedRole when not provided", () => {
48
+ const decision = makeKnockDecision("denied");
49
+ expect(decision.outcome).toBe("denied");
50
+ expect("grantedRole" in decision).toBe(false);
51
+ });
52
+ });
53
+ describe("makeKnockRecords", () => {
54
+ it("creates a batch of distinct knock records", () => {
55
+ const records = makeKnockRecords(3);
56
+ expect(records).toHaveLength(3);
57
+ const nodeIds = records.map((r) => r.nodeId);
58
+ expect(new Set(nodeIds).size).toBe(3);
59
+ });
60
+ it("applies shared overrides to all records", () => {
61
+ const records = makeKnockRecords(2, { direction: "inbound", message: "hi" });
62
+ expect(records.every((r) => r.direction === "inbound")).toBe(true);
63
+ expect(records.every((r) => r.message === "hi")).toBe(true);
64
+ });
65
+ });
@@ -0,0 +1,7 @@
1
+ /** loads all json fixtures from `rust/fixtures/protocol/` and returns them
2
+ * as parsed objects, keyed by filename (without the `.json` extension). */
3
+ export declare function loadProtocolFixtures(): Record<string, unknown>;
4
+ /** loads a single protocol fixture by name (without the `.json`
5
+ * extension), throwing if the file doesn't exist. */
6
+ export declare function loadProtocolFixture(name: string): unknown;
7
+ //# sourceMappingURL=protocol-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-fixtures.d.ts","sourceRoot":"","sources":["../../src/testing/protocol-fixtures.ts"],"names":[],"mappings":"AAOA;4EAC4E;AAC5E,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAc9D;AAED;sDACsD;AACtD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKzD"}
@@ -0,0 +1,28 @@
1
+ // helper to load and parse rust-generated protocol fixtures - ensures the
2
+ // ts codec stays in sync with the rust side's serialized message shapes.
3
+ import { readFileSync, readdirSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ /** loads all json fixtures from `rust/fixtures/protocol/` and returns them
7
+ * as parsed objects, keyed by filename (without the `.json` extension). */
8
+ export function loadProtocolFixtures() {
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ const fixturesDir = join(here, "..", "..", "..", "rust", "fixtures", "protocol");
11
+ const files = readdirSync(fixturesDir)
12
+ .filter((name) => name.endsWith(".json"))
13
+ .sort();
14
+ const fixtures = {};
15
+ for (const file of files) {
16
+ const key = file.replace(".json", "");
17
+ fixtures[key] = JSON.parse(readFileSync(join(fixturesDir, file), "utf8"));
18
+ }
19
+ return fixtures;
20
+ }
21
+ /** loads a single protocol fixture by name (without the `.json`
22
+ * extension), throwing if the file doesn't exist. */
23
+ export function loadProtocolFixture(name) {
24
+ const here = dirname(fileURLToPath(import.meta.url));
25
+ const fixturesDir = join(here, "..", "..", "..", "rust", "fixtures", "protocol");
26
+ const path = join(fixturesDir, `${name}.json`);
27
+ return JSON.parse(readFileSync(path, "utf8"));
28
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=protocol-fixtures.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-fixtures.test.d.ts","sourceRoot":"","sources":["../../src/testing/protocol-fixtures.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { loadProtocolFixture, loadProtocolFixtures } from "./protocol-fixtures.js";
3
+ describe("loadProtocolFixtures", () => {
4
+ it("loads all fixtures as a keyed record", () => {
5
+ const fixtures = loadProtocolFixtures();
6
+ expect(Object.keys(fixtures).length).toBeGreaterThan(0);
7
+ expect(fixtures["hello"]).toBeDefined();
8
+ });
9
+ });
10
+ describe("loadProtocolFixture", () => {
11
+ it("loads a single fixture by name", () => {
12
+ const fixture = loadProtocolFixture("hello");
13
+ expect(fixture).toBeDefined();
14
+ expect(typeof fixture).toBe("object");
15
+ });
16
+ it("throws when the fixture does not exist", () => {
17
+ expect(() => loadProtocolFixture("nonexistent")).toThrow();
18
+ });
19
+ });
@@ -0,0 +1,33 @@
1
+ import type { EndpointAdapter, EndpointState } from "../state/endpoint-control.js";
2
+ import type { FriendDirectoryEntry } from "../state/friend-directory.js";
3
+ /** saved remote, as apps persist them after a successful connection. */
4
+ export interface SavedRemote {
5
+ remote_id: string;
6
+ name: string;
7
+ base_url?: string;
8
+ peer_addr: string;
9
+ }
10
+ /** pending remote record (staged during add-peer flow, not yet promoted to
11
+ * a saved remote). */
12
+ export interface PendingRemote {
13
+ id: string;
14
+ peer_addr: string;
15
+ stage: string;
16
+ error_message?: string;
17
+ server_name?: string;
18
+ transport?: "wasm" | "http";
19
+ }
20
+ /** creates a friend directory entry with deterministic defaults. */
21
+ export declare function makeFriendDirectoryEntry(overrides?: Partial<FriendDirectoryEntry>): FriendDirectoryEntry;
22
+ /** creates a batch of distinct friend directory entries with unique
23
+ * usernames and node ids. */
24
+ export declare function makeFriendDirectoryEntries(count: number): FriendDirectoryEntry[];
25
+ /** creates a saved remote with deterministic defaults. */
26
+ export declare function makeSavedRemote(overrides?: Partial<SavedRemote>): SavedRemote;
27
+ /** creates a pending remote record with deterministic defaults. */
28
+ export declare function makePendingRemote(overrides?: Partial<PendingRemote>): PendingRemote;
29
+ /** creates a scriptable `EndpointAdapter` double - every method is a
30
+ * `vi.fn()`, and state-change listeners are managed in-memory so tests
31
+ * can drive state transitions by hand. */
32
+ export declare function createFakeEndpointAdapter(initial?: EndpointState): EndpointAdapter;
33
+ //# sourceMappingURL=state-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-fixtures.d.ts","sourceRoot":"","sources":["../../src/testing/state-fixtures.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEzE,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;uBACuB;AACvB,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED,oEAAoE;AACpE,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,oBAAoB,CAMtB;AAED;8BAC8B;AAC9B,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAShF;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAOjF;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,aAAa,CAOvF;AAED;;2CAE2C;AAC3C,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,aAAwB,GAAG,eAAe,CAqB5F"}
@@ -0,0 +1,67 @@
1
+ // fixture builders for state-module types: friend directory entries,
2
+ // pending/saved remotes, and endpoint adapters - deterministic test data
3
+ // for exercising peer-directory and remote-management logic.
4
+ import { vi } from "vitest";
5
+ /** creates a friend directory entry with deterministic defaults. */
6
+ export function makeFriendDirectoryEntry(overrides = {}) {
7
+ return {
8
+ username: "alice",
9
+ nodeIds: [{ nodeId: "ab".repeat(32), profileDocId: "doc-1" }],
10
+ ...overrides,
11
+ };
12
+ }
13
+ /** creates a batch of distinct friend directory entries with unique
14
+ * usernames and node ids. */
15
+ export function makeFriendDirectoryEntries(count) {
16
+ return Array.from({ length: count }, (_, i) => {
17
+ const username = `user-${i}`;
18
+ const nodeId = `node-${i.toString().padStart(2, "0")}`.padEnd(64, "0");
19
+ return makeFriendDirectoryEntry({
20
+ username,
21
+ nodeIds: [{ nodeId, profileDocId: `doc-${i}` }],
22
+ });
23
+ });
24
+ }
25
+ /** creates a saved remote with deterministic defaults. */
26
+ export function makeSavedRemote(overrides = {}) {
27
+ return {
28
+ remote_id: `r-${Math.random().toString(36).slice(2, 7)}`,
29
+ name: "hub",
30
+ peer_addr: "cd".repeat(32),
31
+ ...overrides,
32
+ };
33
+ }
34
+ /** creates a pending remote record with deterministic defaults. */
35
+ export function makePendingRemote(overrides = {}) {
36
+ return {
37
+ id: `p-${Math.random().toString(36).slice(2, 7)}`,
38
+ peer_addr: "ef".repeat(32),
39
+ stage: "testing",
40
+ ...overrides,
41
+ };
42
+ }
43
+ /** creates a scriptable `EndpointAdapter` double - every method is a
44
+ * `vi.fn()`, and state-change listeners are managed in-memory so tests
45
+ * can drive state transitions by hand. */
46
+ export function createFakeEndpointAdapter(initial = "online") {
47
+ let state = initial;
48
+ const handlers = new Set();
49
+ const adapter = {
50
+ stop: vi.fn(() => {
51
+ state = "off";
52
+ for (const h of handlers)
53
+ h(state);
54
+ }),
55
+ restart: vi.fn(async () => {
56
+ state = "online";
57
+ for (const h of handlers)
58
+ h(state);
59
+ }),
60
+ getEndpointState: () => state,
61
+ onEndpointStateChange: (handler) => {
62
+ handlers.add(handler);
63
+ return () => handlers.delete(handler);
64
+ },
65
+ };
66
+ return adapter;
67
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state-fixtures.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-fixtures.test.d.ts","sourceRoot":"","sources":["../../src/testing/state-fixtures.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createFakeEndpointAdapter, makeFriendDirectoryEntries, makeFriendDirectoryEntry, makePendingRemote, makeSavedRemote, } from "./state-fixtures.js";
3
+ describe("makeFriendDirectoryEntry", () => {
4
+ it("produces a valid entry with default values", () => {
5
+ const entry = makeFriendDirectoryEntry();
6
+ expect(entry.username).toBe("alice");
7
+ expect(entry.nodeIds).toHaveLength(1);
8
+ expect(entry.nodeIds[0].nodeId).toHaveLength(64);
9
+ });
10
+ it("accepts overrides", () => {
11
+ const entry = makeFriendDirectoryEntry({ username: "bob", alias: "bobby" });
12
+ expect(entry.username).toBe("bob");
13
+ expect(entry.alias).toBe("bobby");
14
+ });
15
+ });
16
+ describe("makeFriendDirectoryEntries", () => {
17
+ it("creates a batch of distinct entries", () => {
18
+ const entries = makeFriendDirectoryEntries(3);
19
+ expect(entries).toHaveLength(3);
20
+ const usernames = entries.map((e) => e.username);
21
+ expect(new Set(usernames).size).toBe(3);
22
+ });
23
+ });
24
+ describe("makeSavedRemote", () => {
25
+ it("produces a valid saved remote", () => {
26
+ const remote = makeSavedRemote();
27
+ expect(remote.remote_id).toMatch(/^r-/);
28
+ expect(remote.name).toBe("hub");
29
+ expect(remote.peer_addr).toHaveLength(64);
30
+ });
31
+ it("accepts overrides", () => {
32
+ const remote = makeSavedRemote({ name: "custom", base_url: "https://test" });
33
+ expect(remote.name).toBe("custom");
34
+ expect(remote.base_url).toBe("https://test");
35
+ });
36
+ });
37
+ describe("makePendingRemote", () => {
38
+ it("produces a valid pending remote", () => {
39
+ const pending = makePendingRemote();
40
+ expect(pending.id).toMatch(/^p-/);
41
+ expect(pending.stage).toBe("testing");
42
+ });
43
+ it("accepts overrides", () => {
44
+ const pending = makePendingRemote({ stage: "failed", error_message: "timeout" });
45
+ expect(pending.stage).toBe("failed");
46
+ expect(pending.error_message).toBe("timeout");
47
+ });
48
+ });
49
+ describe("createFakeEndpointAdapter", () => {
50
+ it("starts in the given initial state", () => {
51
+ const adapter = createFakeEndpointAdapter("off");
52
+ expect(adapter.getEndpointState()).toBe("off");
53
+ });
54
+ it("transitions state on stop and restart", async () => {
55
+ const adapter = createFakeEndpointAdapter("online");
56
+ adapter.stop();
57
+ expect(adapter.getEndpointState()).toBe("off");
58
+ await adapter.restart();
59
+ expect(adapter.getEndpointState()).toBe("online");
60
+ });
61
+ it("notifies listeners on state changes", () => {
62
+ const adapter = createFakeEndpointAdapter("online");
63
+ const seen = [];
64
+ adapter.onEndpointStateChange((state) => seen.push(state));
65
+ adapter.stop();
66
+ expect(seen).toEqual(["off"]);
67
+ });
68
+ });
@@ -0,0 +1,27 @@
1
+ import type { PasskeyCeremonyDeps } from "../webauthn/ceremony.js";
2
+ import type { WebauthnTransport } from "../webauthn/transport.js";
3
+ import type { AuthenticationChallenge, RegistrationChallenge } from "../webauthn/types.js";
4
+ /** a canned registration challenge for tests that don't care about the
5
+ * exact shape, just that a challenge flows through the ceremony. */
6
+ export declare const FAKE_REGISTRATION_CHALLENGE: RegistrationChallenge;
7
+ /** a canned authentication challenge. */
8
+ export declare const FAKE_AUTHENTICATION_CHALLENGE: AuthenticationChallenge;
9
+ /** a canned credential returned by `FAKE_CEREMONY_DEPS.createCredential` /
10
+ * `.getCredential` - enough structure to satisfy the ceremony's
11
+ * serialization logic without touching real browser APIs. */
12
+ export declare const FAKE_CREDENTIAL: PublicKeyCredential;
13
+ /** a scriptable `WebauthnTransport` double. every method is a `vi.fn()`
14
+ * defaulting to successful outcomes; override per-call via
15
+ * `mockResolvedValueOnce` / `mockRejectedValueOnce` to simulate specific
16
+ * failure cases. */
17
+ export declare function createFakeWebauthnTransport(overrides?: Partial<WebauthnTransport>): WebauthnTransport;
18
+ /** fake browser ceremony deps (credential creation/retrieval + origin) that
19
+ * return canned values without touching `navigator.credentials`. every
20
+ * method is a `vi.fn()` so tests can assert on calls or override
21
+ * behavior. */
22
+ export declare const FAKE_CEREMONY_DEPS: PasskeyCeremonyDeps;
23
+ /** resets all `vi.fn()` call counts/mocks on the fake ceremony deps to a
24
+ * clean state. call this in `beforeEach` / `afterEach` when reusing the
25
+ * same deps object across multiple tests. */
26
+ export declare function resetFakeCeremonyDeps(): void;
27
+ //# sourceMappingURL=webauthn-mocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn-mocks.d.ts","sourceRoot":"","sources":["../../src/testing/webauthn-mocks.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAOV,iBAAiB,EAElB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,uBAAuB,EAGvB,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAE9B;qEACqE;AACrE,eAAO,MAAM,2BAA2B,EAAE,qBAOzC,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,6BAA6B,EAAE,uBAE3C,CAAC;AAEF;;8DAE8D;AAC9D,eAAO,MAAM,eAAe,EAIZ,mBAAmB,CAAC;AAEpC;;;qBAGqB;AACrB,wBAAgB,2BAA2B,CACzC,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACzC,iBAAiB,CAuCnB;AAED;;;gBAGgB;AAChB,eAAO,MAAM,kBAAkB,EAI1B,mBAAmB,CAAC;AAEzB;;8CAE8C;AAC9C,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C"}
@@ -0,0 +1,78 @@
1
+ // scriptable test doubles for webauthn ceremony flows: a fake transport
2
+ // whose responses a test can drive call-by-call, and fake browser deps
3
+ // (credential creation/retrieval) that return canned values without
4
+ // touching `navigator.credentials`.
5
+ //
6
+ // every method is a `vi.fn()` so consuming tests can assert on call
7
+ // counts/arguments or override a single call's behavior
8
+ // (`mockResolvedValueOnce`, `mockRejectedValueOnce`, ...) without
9
+ // replacing the whole double.
10
+ import { vi } from "vitest";
11
+ /** a canned registration challenge for tests that don't care about the
12
+ * exact shape, just that a challenge flows through the ceremony. */
13
+ export const FAKE_REGISTRATION_CHALLENGE = {
14
+ publicKey: {
15
+ rp: { name: "freqhole" },
16
+ user: { id: "AA", name: "alice", displayName: "Alice" },
17
+ challenge: "AQ",
18
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
19
+ },
20
+ };
21
+ /** a canned authentication challenge. */
22
+ export const FAKE_AUTHENTICATION_CHALLENGE = {
23
+ publicKey: { challenge: "AQ" },
24
+ };
25
+ /** a canned credential returned by `FAKE_CEREMONY_DEPS.createCredential` /
26
+ * `.getCredential` - enough structure to satisfy the ceremony's
27
+ * serialization logic without touching real browser APIs. */
28
+ export const FAKE_CREDENTIAL = {
29
+ id: "cred-id",
30
+ rawId: new Uint8Array([1]).buffer,
31
+ response: {},
32
+ };
33
+ /** a scriptable `WebauthnTransport` double. every method is a `vi.fn()`
34
+ * defaulting to successful outcomes; override per-call via
35
+ * `mockResolvedValueOnce` / `mockRejectedValueOnce` to simulate specific
36
+ * failure cases. */
37
+ export function createFakeWebauthnTransport(overrides = {}) {
38
+ return {
39
+ registerStart: vi.fn(async (_req) => ({
40
+ success: true,
41
+ data: { nonce: "nonce-1", challenge: FAKE_REGISTRATION_CHALLENGE },
42
+ })),
43
+ registerFinish: vi.fn(async (_req) => ({
44
+ success: true,
45
+ data: { userId: "user-1", username: "alice" },
46
+ })),
47
+ loginStart: vi.fn(async (_req) => ({
48
+ success: true,
49
+ data: { nonce: "nonce-2", challenge: FAKE_AUTHENTICATION_CHALLENGE },
50
+ })),
51
+ loginFinish: vi.fn(async (_req) => ({
52
+ success: true,
53
+ data: { userId: "user-1", username: "alice" },
54
+ })),
55
+ whoami: vi.fn(async () => ({ authenticated: false })),
56
+ ...overrides,
57
+ };
58
+ }
59
+ /** fake browser ceremony deps (credential creation/retrieval + origin) that
60
+ * return canned values without touching `navigator.credentials`. every
61
+ * method is a `vi.fn()` so tests can assert on calls or override
62
+ * behavior. */
63
+ export const FAKE_CEREMONY_DEPS = {
64
+ origin: "https://example.test",
65
+ createCredential: vi.fn(async () => FAKE_CREDENTIAL),
66
+ getCredential: vi.fn(async () => FAKE_CREDENTIAL),
67
+ };
68
+ /** resets all `vi.fn()` call counts/mocks on the fake ceremony deps to a
69
+ * clean state. call this in `beforeEach` / `afterEach` when reusing the
70
+ * same deps object across multiple tests. */
71
+ export function resetFakeCeremonyDeps() {
72
+ if (typeof FAKE_CEREMONY_DEPS.createCredential === "function" && "mockClear" in FAKE_CEREMONY_DEPS.createCredential) {
73
+ FAKE_CEREMONY_DEPS.createCredential.mockClear();
74
+ }
75
+ if (typeof FAKE_CEREMONY_DEPS.getCredential === "function" && "mockClear" in FAKE_CEREMONY_DEPS.getCredential) {
76
+ FAKE_CEREMONY_DEPS.getCredential.mockClear();
77
+ }
78
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=webauthn-mocks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn-mocks.test.d.ts","sourceRoot":"","sources":["../../src/testing/webauthn-mocks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { createFakeWebauthnTransport, FAKE_CEREMONY_DEPS, resetFakeCeremonyDeps, } from "./webauthn-mocks.js";
3
+ describe("createFakeWebauthnTransport", () => {
4
+ it("defaults to successful outcomes for all methods", async () => {
5
+ const transport = createFakeWebauthnTransport();
6
+ const startResult = await transport.registerStart({ username: "alice", origin: "https://test" });
7
+ expect(startResult.success).toBe(true);
8
+ expect(transport.registerStart).toHaveBeenCalledOnce();
9
+ });
10
+ it("allows overriding individual methods", async () => {
11
+ const transport = createFakeWebauthnTransport({
12
+ registerStart: vi.fn(async () => ({
13
+ success: false,
14
+ error: "username taken",
15
+ })),
16
+ });
17
+ const result = await transport.registerStart({ username: "alice", origin: "https://test" });
18
+ expect(result.success).toBe(false);
19
+ expect(result.success === false && result.error).toBe("username taken");
20
+ });
21
+ });
22
+ describe("FAKE_CEREMONY_DEPS", () => {
23
+ beforeEach(() => {
24
+ resetFakeCeremonyDeps();
25
+ });
26
+ it("provides canned credential responses", async () => {
27
+ if (!FAKE_CEREMONY_DEPS.createCredential)
28
+ throw new Error("no createCredential");
29
+ const cred = await FAKE_CEREMONY_DEPS.createCredential({});
30
+ expect(cred?.id).toBe("cred-id");
31
+ });
32
+ it("can be reset between tests", () => {
33
+ if (!FAKE_CEREMONY_DEPS.createCredential)
34
+ throw new Error("no createCredential");
35
+ void FAKE_CEREMONY_DEPS.createCredential({});
36
+ expect(FAKE_CEREMONY_DEPS.createCredential).toHaveBeenCalledOnce();
37
+ resetFakeCeremonyDeps();
38
+ expect(FAKE_CEREMONY_DEPS.createCredential).not.toHaveBeenCalled();
39
+ });
40
+ });