accounts 0.3.0 → 0.4.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 (168) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +97 -0
  4. package/dist/core/AccessKey.d.ts +55 -0
  5. package/dist/core/AccessKey.d.ts.map +1 -0
  6. package/dist/core/AccessKey.js +69 -0
  7. package/dist/core/AccessKey.js.map +1 -0
  8. package/dist/core/Account.d.ts +91 -0
  9. package/dist/core/Account.d.ts.map +1 -0
  10. package/dist/core/Account.js +64 -0
  11. package/dist/core/Account.js.map +1 -0
  12. package/dist/core/Adapter.d.ts +187 -0
  13. package/dist/core/Adapter.d.ts.map +1 -0
  14. package/dist/core/Adapter.js +7 -0
  15. package/dist/core/Adapter.js.map +1 -0
  16. package/dist/core/Ceremony.d.ts +109 -0
  17. package/dist/core/Ceremony.d.ts.map +1 -0
  18. package/dist/core/Ceremony.js +104 -0
  19. package/dist/core/Ceremony.js.map +1 -0
  20. package/dist/core/Client.d.ts +16 -0
  21. package/dist/core/Client.d.ts.map +1 -0
  22. package/dist/core/Client.js +18 -0
  23. package/dist/core/Client.js.map +1 -0
  24. package/dist/core/Dialog.d.ts +52 -0
  25. package/dist/core/Dialog.d.ts.map +1 -0
  26. package/dist/core/Dialog.js +342 -0
  27. package/dist/core/Dialog.js.map +1 -0
  28. package/dist/core/Expiry.d.ts +15 -0
  29. package/dist/core/Expiry.d.ts.map +1 -0
  30. package/dist/core/Expiry.js +29 -0
  31. package/dist/core/Expiry.js.map +1 -0
  32. package/dist/core/Messenger.d.ts +86 -0
  33. package/dist/core/Messenger.d.ts.map +1 -0
  34. package/dist/core/Messenger.js +127 -0
  35. package/dist/core/Messenger.js.map +1 -0
  36. package/dist/core/Provider.d.ts +69 -0
  37. package/dist/core/Provider.d.ts.map +1 -0
  38. package/dist/core/Provider.js +401 -0
  39. package/dist/core/Provider.js.map +1 -0
  40. package/dist/core/Remote.d.ts +114 -0
  41. package/dist/core/Remote.d.ts.map +1 -0
  42. package/dist/core/Remote.js +116 -0
  43. package/dist/core/Remote.js.map +1 -0
  44. package/dist/core/Schema.d.ts +805 -0
  45. package/dist/core/Schema.d.ts.map +1 -0
  46. package/dist/core/Schema.js +43 -0
  47. package/dist/core/Schema.js.map +1 -0
  48. package/dist/core/Storage.d.ts +42 -0
  49. package/dist/core/Storage.d.ts.map +1 -0
  50. package/dist/core/Storage.js +173 -0
  51. package/dist/core/Storage.js.map +1 -0
  52. package/dist/core/Store.d.ts +58 -0
  53. package/dist/core/Store.d.ts.map +1 -0
  54. package/dist/core/Store.js +58 -0
  55. package/dist/core/Store.js.map +1 -0
  56. package/dist/core/adapters/dangerous_secp256k1.d.ts +30 -0
  57. package/dist/core/adapters/dangerous_secp256k1.d.ts.map +1 -0
  58. package/dist/core/adapters/dangerous_secp256k1.js +39 -0
  59. package/dist/core/adapters/dangerous_secp256k1.js.map +1 -0
  60. package/dist/core/adapters/dialog.d.ts +31 -0
  61. package/dist/core/adapters/dialog.d.ts.map +1 -0
  62. package/dist/core/adapters/dialog.js +306 -0
  63. package/dist/core/adapters/dialog.js.map +1 -0
  64. package/dist/core/adapters/local.d.ts +33 -0
  65. package/dist/core/adapters/local.d.ts.map +1 -0
  66. package/dist/core/adapters/local.js +227 -0
  67. package/dist/core/adapters/local.js.map +1 -0
  68. package/dist/core/adapters/webAuthn.d.ts +36 -0
  69. package/dist/core/adapters/webAuthn.d.ts.map +1 -0
  70. package/dist/core/adapters/webAuthn.js +93 -0
  71. package/dist/core/adapters/webAuthn.js.map +1 -0
  72. package/dist/core/internal/withDedupe.d.ts +12 -0
  73. package/dist/core/internal/withDedupe.d.ts.map +1 -0
  74. package/dist/core/internal/withDedupe.js +12 -0
  75. package/dist/core/internal/withDedupe.js.map +1 -0
  76. package/dist/core/zod/request.d.ts +31 -0
  77. package/dist/core/zod/request.d.ts.map +1 -0
  78. package/dist/core/zod/request.js +41 -0
  79. package/dist/core/zod/request.js.map +1 -0
  80. package/dist/core/zod/rpc.d.ts +603 -0
  81. package/dist/core/zod/rpc.d.ts.map +1 -0
  82. package/dist/core/zod/rpc.js +293 -0
  83. package/dist/core/zod/rpc.js.map +1 -0
  84. package/dist/core/zod/utils.d.ts +18 -0
  85. package/dist/core/zod/utils.d.ts.map +1 -0
  86. package/dist/core/zod/utils.js +21 -0
  87. package/dist/core/zod/utils.js.map +1 -0
  88. package/dist/index.d.ts +15 -0
  89. package/dist/index.d.ts.map +1 -0
  90. package/dist/index.js +15 -0
  91. package/dist/index.js.map +1 -0
  92. package/dist/internal/types.d.ts +284 -0
  93. package/dist/internal/types.d.ts.map +1 -0
  94. package/dist/internal/types.js +2 -0
  95. package/dist/internal/types.js.map +1 -0
  96. package/dist/server/Handler.d.ts +257 -0
  97. package/dist/server/Handler.d.ts.map +1 -0
  98. package/dist/server/Handler.js +433 -0
  99. package/dist/server/Handler.js.map +1 -0
  100. package/dist/server/Kv.d.ts +16 -0
  101. package/dist/server/Kv.d.ts.map +1 -0
  102. package/dist/server/Kv.js +30 -0
  103. package/dist/server/Kv.js.map +1 -0
  104. package/dist/server/index.d.ts +3 -0
  105. package/dist/server/index.d.ts.map +1 -0
  106. package/dist/server/index.js +3 -0
  107. package/dist/server/index.js.map +1 -0
  108. package/dist/server/internal/requestListener.d.ts +124 -0
  109. package/dist/server/internal/requestListener.d.ts.map +1 -0
  110. package/dist/server/internal/requestListener.js +173 -0
  111. package/dist/server/internal/requestListener.js.map +1 -0
  112. package/dist/wagmi/Connector.d.ts +93 -0
  113. package/dist/wagmi/Connector.d.ts.map +1 -0
  114. package/dist/wagmi/Connector.js +238 -0
  115. package/dist/wagmi/Connector.js.map +1 -0
  116. package/dist/wagmi/index.d.ts +3 -0
  117. package/dist/wagmi/index.d.ts.map +1 -0
  118. package/dist/wagmi/index.js +3 -0
  119. package/dist/wagmi/index.js.map +1 -0
  120. package/package.json +56 -2
  121. package/src/core/AccessKey.test.ts +257 -0
  122. package/src/core/AccessKey.ts +123 -0
  123. package/src/core/Account.test.ts +309 -0
  124. package/src/core/Account.ts +152 -0
  125. package/src/core/Adapter.ts +238 -0
  126. package/src/core/Ceremony.browser.test.ts +239 -0
  127. package/src/core/Ceremony.test.ts +151 -0
  128. package/src/core/Ceremony.ts +203 -0
  129. package/src/core/Client.ts +36 -0
  130. package/src/core/Dialog.browser.test.ts +309 -0
  131. package/src/core/Dialog.test-d.ts +19 -0
  132. package/src/core/Dialog.ts +442 -0
  133. package/src/core/Expiry.ts +34 -0
  134. package/src/core/Messenger.ts +206 -0
  135. package/src/core/Provider.browser.test.ts +774 -0
  136. package/src/core/Provider.connect.browser.test.ts +415 -0
  137. package/src/core/Provider.test-d.ts +53 -0
  138. package/src/core/Provider.test.ts +1566 -0
  139. package/src/core/Provider.ts +559 -0
  140. package/src/core/Remote.ts +262 -0
  141. package/src/core/Schema.test-d.ts +211 -0
  142. package/src/core/Schema.ts +143 -0
  143. package/src/core/Storage.ts +213 -0
  144. package/src/core/Store.test.ts +287 -0
  145. package/src/core/Store.ts +129 -0
  146. package/src/core/adapters/dangerous_secp256k1.ts +53 -0
  147. package/src/core/adapters/dialog.ts +379 -0
  148. package/src/core/adapters/local.test.ts +97 -0
  149. package/src/core/adapters/local.ts +277 -0
  150. package/src/core/adapters/webAuthn.ts +129 -0
  151. package/src/core/internal/withDedupe.test.ts +116 -0
  152. package/src/core/internal/withDedupe.ts +20 -0
  153. package/src/core/mppx.test.ts +83 -0
  154. package/src/core/zod/request.test.ts +121 -0
  155. package/src/core/zod/request.ts +70 -0
  156. package/src/core/zod/rpc.ts +374 -0
  157. package/src/core/zod/utils.test.ts +69 -0
  158. package/src/core/zod/utils.ts +40 -0
  159. package/src/index.ts +14 -0
  160. package/src/internal/types.ts +378 -0
  161. package/src/server/Handler.test.ts +1014 -0
  162. package/src/server/Handler.ts +605 -0
  163. package/src/server/Kv.ts +46 -0
  164. package/src/server/index.ts +2 -0
  165. package/src/server/internal/requestListener.ts +273 -0
  166. package/src/tsconfig.json +9 -0
  167. package/src/wagmi/Connector.ts +287 -0
  168. package/src/wagmi/index.ts +2 -0
@@ -0,0 +1,109 @@
1
+ import type { Hex } from 'viem';
2
+ import { Authentication, Registration } from 'webauthx/server';
3
+ import * as Storage from './Storage.js';
4
+ /** Pluggable strategy for WebAuthn registration and authentication ceremonies. */
5
+ export type Ceremony = {
6
+ /** Get credential creation options for `navigator.credentials.create()`. */
7
+ getRegistrationOptions: (params: getRegistrationOptions.Parameters) => Promise<getRegistrationOptions.ReturnType>;
8
+ /** Verify a registration response and extract the public key. */
9
+ verifyRegistration: (credential: Registration.Credential, options?: verifyRegistration.Options | undefined) => Promise<verifyRegistration.ReturnType>;
10
+ /** Get credential request options for `navigator.credentials.get()`. */
11
+ getAuthenticationOptions: (params?: getAuthenticationOptions.Parameters | undefined) => Promise<getAuthenticationOptions.ReturnType>;
12
+ /** Verify an authentication response and extract the public key. */
13
+ verifyAuthentication: (response: Authentication.Response) => Promise<verifyAuthentication.ReturnType>;
14
+ };
15
+ export declare namespace getRegistrationOptions {
16
+ type Parameters = {
17
+ /** Credential IDs to exclude (prevents re-registering existing credentials). */
18
+ excludeCredentialIds?: readonly string[] | undefined;
19
+ /** Credential display name (e.g. `"alice"`). */
20
+ name: string;
21
+ /** Opaque user identifier. Encoded as `user.id` in the WebAuthn creation options. */
22
+ userId?: string | undefined;
23
+ };
24
+ type ReturnType = {
25
+ options: Registration.Options;
26
+ };
27
+ }
28
+ export declare namespace verifyRegistration {
29
+ type Options = {
30
+ /** Display name for the credential (e.g. user's email). */
31
+ name?: string | undefined;
32
+ };
33
+ type ReturnType = {
34
+ /** The registered credential's ID. */
35
+ credentialId: string;
36
+ /** The credential's public key (uncompressed P256, hex-encoded). */
37
+ publicKey: Hex;
38
+ };
39
+ }
40
+ export declare namespace getAuthenticationOptions {
41
+ type Parameters = {
42
+ /** Credential IDs to allow (restricts which credentials can be used). */
43
+ allowCredentialIds?: readonly string[] | undefined;
44
+ /** Challenge to use. */
45
+ challenge?: `0x${string}` | undefined;
46
+ /** Credential ID to restrict authentication to a specific credential. */
47
+ credentialId?: string | undefined;
48
+ /** Mediation hint for passkey autofill / conditional UI. */
49
+ mediation?: 'conditional' | 'optional' | 'required' | 'silent' | undefined;
50
+ };
51
+ type ReturnType = {
52
+ options: Authentication.Options;
53
+ };
54
+ }
55
+ export declare namespace verifyAuthentication {
56
+ type ReturnType = {
57
+ /** The authenticated credential's ID. */
58
+ credentialId: string;
59
+ /** The credential's public key (uncompressed P256, hex-encoded). */
60
+ publicKey: Hex;
61
+ /** User identifier from the authenticator's `userHandle` (discoverable/conditional flows). */
62
+ userId?: string | undefined;
63
+ };
64
+ }
65
+ /** Creates a {@link Ceremony} from a custom implementation. */
66
+ export declare function from(ceremony: Ceremony): Ceremony;
67
+ /**
68
+ * Creates a pure client-side ceremony for development and prototyping.
69
+ *
70
+ * Generates challenges and verifies responses locally using `webauthx/server`.
71
+ * Stores credentials in memory. No external server needed.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * import { Ceremony } from 'accounts'
76
+ *
77
+ * const ceremony = Ceremony.local()
78
+ * ```
79
+ */
80
+ export declare function local(options?: local.Options): Ceremony;
81
+ export declare namespace local {
82
+ type Options = {
83
+ /** Relying Party ID (e.g. `"example.com"`). @default location.hostname */
84
+ rpId?: string | undefined;
85
+ /** Storage adapter for credential persistence. @default Storage.idb() in browser, Storage.memory() otherwise. */
86
+ storage?: Storage.Storage | undefined;
87
+ };
88
+ }
89
+ /**
90
+ * Creates a server-backed ceremony that delegates to a remote {@link Handler.webauthn} endpoint.
91
+ *
92
+ * All challenge generation, verification, and credential storage happen server-side.
93
+ * The client uses `fetch()` to communicate with 4 POST endpoints derived from the base URL.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import { Ceremony } from 'accounts'
98
+ *
99
+ * const ceremony = Ceremony.server({ url: 'https://example.com/webauthn' })
100
+ * ```
101
+ */
102
+ export declare function server(options: server.Options): Ceremony;
103
+ export declare namespace server {
104
+ type Options = {
105
+ /** Base URL of the WebAuthn handler (e.g. `"https://example.com/webauthn"`). */
106
+ url: string;
107
+ };
108
+ }
109
+ //# sourceMappingURL=Ceremony.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Ceremony.d.ts","sourceRoot":"","sources":["../../src/core/Ceremony.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { Bytes } from 'ox';
2
+ import { Authentication, Registration } from 'webauthx/server';
3
+ import * as Storage from './Storage.js';
4
+ /** Creates a {@link Ceremony} from a custom implementation. */
5
+ export function from(ceremony) {
6
+ return ceremony;
7
+ }
8
+ /**
9
+ * Creates a pure client-side ceremony for development and prototyping.
10
+ *
11
+ * Generates challenges and verifies responses locally using `webauthx/server`.
12
+ * Stores credentials in memory. No external server needed.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { Ceremony } from 'accounts'
17
+ *
18
+ * const ceremony = Ceremony.local()
19
+ * ```
20
+ */
21
+ export function local(options = {}) {
22
+ const rpId = options.rpId ?? (typeof location !== 'undefined' ? location.hostname : 'localhost');
23
+ const storage = options.storage ?? (typeof window !== 'undefined' ? Storage.idb() : Storage.memory());
24
+ const storageKey = 'credentials';
25
+ return from({
26
+ async getRegistrationOptions(parameters) {
27
+ const { excludeCredentialIds, name, userId } = parameters;
28
+ const { options } = Registration.getOptions({
29
+ excludeCredentialIds: excludeCredentialIds,
30
+ name,
31
+ rp: { id: rpId, name: rpId },
32
+ user: userId ? { id: Bytes.fromString(userId), name } : undefined,
33
+ });
34
+ return { options };
35
+ },
36
+ async verifyRegistration(credential) {
37
+ const publicKey = credential.publicKey;
38
+ const credentials = (await storage.getItem(storageKey)) ?? {};
39
+ credentials[credential.id] = publicKey;
40
+ await storage.setItem(storageKey, credentials);
41
+ return { credentialId: credential.id, publicKey };
42
+ },
43
+ async getAuthenticationOptions(parameters = {}) {
44
+ const { allowCredentialIds, challenge, credentialId } = parameters;
45
+ const { options } = Authentication.getOptions({
46
+ challenge,
47
+ credentialId: allowCredentialIds ?? credentialId,
48
+ rpId,
49
+ });
50
+ return { options };
51
+ },
52
+ async verifyAuthentication(response) {
53
+ const credentials = (await storage.getItem(storageKey)) ?? {};
54
+ const publicKey = credentials[response.id];
55
+ if (!publicKey)
56
+ throw new Error(`Unknown credential: ${response.id}`);
57
+ return { credentialId: response.id, publicKey };
58
+ },
59
+ });
60
+ }
61
+ /**
62
+ * Creates a server-backed ceremony that delegates to a remote {@link Handler.webauthn} endpoint.
63
+ *
64
+ * All challenge generation, verification, and credential storage happen server-side.
65
+ * The client uses `fetch()` to communicate with 4 POST endpoints derived from the base URL.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { Ceremony } from 'accounts'
70
+ *
71
+ * const ceremony = Ceremony.server({ url: 'https://example.com/webauthn' })
72
+ * ```
73
+ */
74
+ export function server(options) {
75
+ const { url } = options;
76
+ async function request(path, body) {
77
+ const response = await fetch(`${url}${path}`, {
78
+ method: 'POST',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify(body),
81
+ });
82
+ const json = await response.json();
83
+ if (!response.ok)
84
+ throw new Error(json.error ?? 'Request failed');
85
+ return json;
86
+ }
87
+ return from({
88
+ async getRegistrationOptions(parameters) {
89
+ const { excludeCredentialIds, name, userId } = parameters;
90
+ return request('/register/options', { excludeCredentialIds, name, userId });
91
+ },
92
+ async verifyRegistration(credential) {
93
+ return request('/register', credential);
94
+ },
95
+ async getAuthenticationOptions(parameters = {}) {
96
+ const { allowCredentialIds, challenge, credentialId, mediation } = parameters;
97
+ return request('/login/options', { allowCredentialIds, challenge, credentialId, mediation });
98
+ },
99
+ async verifyAuthentication(response) {
100
+ return request('/login', response);
101
+ },
102
+ });
103
+ }
104
+ //# sourceMappingURL=Ceremony.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Ceremony.js","sourceRoot":"","sources":["../../src/core/Ceremony.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type Chain, type Client, type Transport } from 'viem';
2
+ import type { tempo } from 'viem/chains';
3
+ import type * as Store from './Store.js';
4
+ /** Resolves a viem Client for a given chain ID (cached). */
5
+ export declare function fromChainId(chainId: number | undefined, options: fromChainId.Options): Client<Transport, typeof tempo>;
6
+ export declare namespace fromChainId {
7
+ type Options = {
8
+ /** Supported chains. */
9
+ chains: readonly [Chain, ...Chain[]];
10
+ /** Fee payer service URL. When set, the transport routes fee-payer RPC calls to this URL. */
11
+ feePayer?: string | undefined;
12
+ /** Reactive state store. */
13
+ store: Store.Store;
14
+ };
15
+ }
16
+ //# sourceMappingURL=Client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Client.d.ts","sourceRoot":"","sources":["../../src/core/Client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAgB,KAAK,MAAM,EAAQ,KAAK,SAAS,EAAE,MAAM,MAAM,CAAA;AAClF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAIxC,4DAA4D;AAC5D,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,WAAW,CAAC,OAAO,GAC3B,MAAM,CAAC,SAAS,EAAE,OAAO,KAAK,CAAC,CAYjC;AAED,MAAM,CAAC,OAAO,WAAW,WAAW,CAAC;IACnC,KAAK,OAAO,GAAG;QACb,wBAAwB;QACxB,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAA;QACpC,6FAA6F;QAC7F,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC7B,4BAA4B;QAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;KACnB,CAAA;CACF"}
@@ -0,0 +1,18 @@
1
+ import { createClient, http } from 'viem';
2
+ import { withFeePayer } from 'viem/tempo';
3
+ const clients = new Map();
4
+ /** Resolves a viem Client for a given chain ID (cached). */
5
+ export function fromChainId(chainId, options) {
6
+ const { chains, feePayer, store } = options;
7
+ const id = chainId ?? store.getState().chainId;
8
+ const key = `${id}:${feePayer ?? ''}`;
9
+ let client = clients.get(key);
10
+ if (!client) {
11
+ const chain = chains.find((c) => c.id === id) ?? chains[0];
12
+ const transport = feePayer ? withFeePayer(http(), http(feePayer)) : http();
13
+ client = createClient({ chain, transport, pollingInterval: 1000 });
14
+ clients.set(key, client);
15
+ }
16
+ return client;
17
+ }
18
+ //# sourceMappingURL=Client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Client.js","sourceRoot":"","sources":["../../src/core/Client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,YAAY,EAAe,IAAI,EAAkB,MAAM,MAAM,CAAA;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAIzC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;AAEzC,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CACzB,OAA2B,EAC3B,OAA4B;IAE5B,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAC3C,MAAM,EAAE,GAAG,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAA;IAC9C,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,QAAQ,IAAI,EAAE,EAAE,CAAA;IACrC,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAE,CAAA;QAC3D,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC1E,MAAM,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAA;QAClE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IAC1B,CAAC;IACD,OAAO,MAAe,CAAA;AACxB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import type * as Store from './Store.js';
2
+ /** Dialog interface — manages the iframe/popup lifecycle for cross-origin auth. */
3
+ export type Dialog = SetupFn & Meta;
4
+ /** Static metadata attached to a dialog function. */
5
+ export type Meta = {
6
+ /** Identifier for the dialog type (e.g. `'iframe'`, `'popup'`). */
7
+ name?: string | undefined;
8
+ };
9
+ export type Instance = {
10
+ /** Close the dialog (hide iframe / close popup). */
11
+ close: () => void;
12
+ /** Destroy the dialog (remove DOM elements, clean up). */
13
+ destroy: () => void;
14
+ /** Open the dialog (show iframe / open popup). */
15
+ open: () => void;
16
+ /** Sync the pending request queue to the remote auth app. */
17
+ syncRequests: (requests: readonly Store.QueuedRequest[]) => Promise<void>;
18
+ };
19
+ /** The setup function a dialog must implement. */
20
+ export type SetupFn = (parameters: SetupFn.Parameters) => Instance;
21
+ export declare namespace SetupFn {
22
+ type Parameters = {
23
+ /** URL of the Tempo Auth app. */
24
+ host: string;
25
+ /** Reactive state store. */
26
+ store: Store.Store;
27
+ };
28
+ }
29
+ export declare const defaultSize: {
30
+ height: number;
31
+ width: number;
32
+ };
33
+ /** Creates a dialog from metadata and a setup function. */
34
+ export declare function define(meta: Meta, fn: SetupFn): Dialog;
35
+ /** Detects Safari (which does not support WebAuthn in cross-origin iframes). */
36
+ export declare function isSafari(): boolean;
37
+ /** Creates an iframe dialog that embeds the auth app in a `<dialog>` element. */
38
+ export declare function iframe(): Dialog;
39
+ /** Opens the auth app in a new browser window. */
40
+ export declare function popup(options?: popup.Options): Dialog;
41
+ export declare namespace popup {
42
+ type Options = {
43
+ /** Popup window dimensions. @default `{ width: 360, height: 440 }` */
44
+ size?: {
45
+ width: number;
46
+ height: number;
47
+ } | undefined;
48
+ };
49
+ }
50
+ /** Returns a no-op dialog for SSR environments. */
51
+ export declare function noop(): Dialog;
52
+ //# sourceMappingURL=Dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/core/Dialog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAExC,mFAAmF;AACnF,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;AAEnC,qDAAqD;AACrD,MAAM,MAAM,IAAI,GAAG;IACjB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,oDAAoD;IACpD,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,kDAAkD;IAClD,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,6DAA6D;IAC7D,YAAY,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,CAAC,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1E,CAAA;AAED,kDAAkD;AAClD,MAAM,MAAM,OAAO,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAA;AAElE,MAAM,CAAC,OAAO,WAAW,OAAO,CAAC;IAC/B,KAAK,UAAU,GAAG;QAChB,iCAAiC;QACjC,IAAI,EAAE,MAAM,CAAA;QACZ,4BAA4B;QAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;KACnB,CAAA;CACF;AAED,eAAO,MAAM,WAAW;;;CAA8B,CAAA;AAEtD,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,GAAG,MAAM,CAItD;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,IAAI,OAAO,CAIlC;AAED,iFAAiF;AACjF,wBAAgB,MAAM,IAAI,MAAM,CA6M/B;AAED,kDAAkD;AAClD,wBAAgB,KAAK,CAAC,OAAO,GAAE,KAAK,CAAC,OAAY,GAAG,MAAM,CAoFzD;AAED,MAAM,CAAC,OAAO,WAAW,KAAK,CAAC;IAC7B,KAAK,OAAO,GAAG;QACb,sEAAsE;QACtE,IAAI,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAA;KACrD,CAAA;CACF;AAED,mDAAmD;AACnD,wBAAgB,IAAI,IAAI,MAAM,CAO7B"}
@@ -0,0 +1,342 @@
1
+ import * as Messenger from './Messenger.js';
2
+ export const defaultSize = { height: 440, width: 360 };
3
+ /** Creates a dialog from metadata and a setup function. */
4
+ export function define(meta, fn) {
5
+ const { name, ...rest } = meta;
6
+ Object.defineProperty(fn, 'name', { value: name, configurable: true });
7
+ return Object.assign(fn, rest);
8
+ }
9
+ /** Detects Safari (which does not support WebAuthn in cross-origin iframes). */
10
+ export function isSafari() {
11
+ if (typeof navigator === 'undefined')
12
+ return false;
13
+ const ua = navigator.userAgent.toLowerCase();
14
+ return ua.includes('safari') && !ua.includes('chrome');
15
+ }
16
+ /** Creates an iframe dialog that embeds the auth app in a `<dialog>` element. */
17
+ export function iframe() {
18
+ if (typeof window === 'undefined')
19
+ return noop();
20
+ return define({ name: 'iframe' }, (parameters) => {
21
+ const { host, store } = parameters;
22
+ const fallback = popup()(parameters);
23
+ let open = false;
24
+ const referrer = getReferrer();
25
+ const hostUrl = new URL(host);
26
+ hostUrl.searchParams.set('chainId', String(store.getState().chainId));
27
+ hostUrl.searchParams.set('mode', 'iframe');
28
+ if (referrer.icon) {
29
+ if (typeof referrer.icon === 'string')
30
+ hostUrl.searchParams.set('icon', referrer.icon);
31
+ else {
32
+ hostUrl.searchParams.set('icon', referrer.icon.light);
33
+ hostUrl.searchParams.set('iconDark', referrer.icon.dark);
34
+ }
35
+ }
36
+ const root = document.createElement('dialog');
37
+ root.dataset.tempoWallet = '';
38
+ root.setAttribute('role', 'dialog');
39
+ root.setAttribute('aria-closed', 'true');
40
+ root.setAttribute('aria-label', 'Tempo Auth');
41
+ root.setAttribute('hidden', 'until-found');
42
+ Object.assign(root.style, {
43
+ background: 'transparent',
44
+ border: '0',
45
+ outline: '0',
46
+ padding: '0',
47
+ position: 'fixed',
48
+ });
49
+ document.body.appendChild(root);
50
+ const frame = document.createElement('iframe');
51
+ frame.dataset.testid = 'tempo-wallet';
52
+ frame.setAttribute('allow', [
53
+ `publickey-credentials-get ${hostUrl.origin}`,
54
+ `publickey-credentials-create ${hostUrl.origin}`,
55
+ 'clipboard-write',
56
+ ].join('; '));
57
+ frame.setAttribute('allowtransparency', 'true');
58
+ frame.setAttribute('tabindex', '0');
59
+ frame.setAttribute('title', 'Tempo Auth');
60
+ frame.src = hostUrl.toString();
61
+ Object.assign(frame.style, {
62
+ backgroundColor: 'transparent',
63
+ border: '0',
64
+ colorScheme: 'light dark',
65
+ height: '100%',
66
+ left: '0',
67
+ position: 'fixed',
68
+ top: '0',
69
+ width: '100%',
70
+ });
71
+ const style = document.createElement('style');
72
+ style.innerHTML = `
73
+ dialog[data-tempo-wallet]::backdrop {
74
+ background: transparent!important;
75
+ }
76
+ `;
77
+ root.appendChild(style);
78
+ root.appendChild(frame);
79
+ const messenger = Messenger.bridge({
80
+ from: Messenger.fromWindow(window, { targetOrigin: hostUrl.origin }),
81
+ to: Messenger.fromWindow(frame.contentWindow, {
82
+ targetOrigin: hostUrl.origin,
83
+ }),
84
+ waitForReady: true,
85
+ });
86
+ messenger.on('rpc-response', (response) => handleResponse(store, response));
87
+ let savedOverflow = '';
88
+ let opener = null;
89
+ const onBlur = () => handleBlur(store);
90
+ // 1Password extension adds `inert` attribute to `dialog` rendering it unusable.
91
+ const inertObserver = new MutationObserver((mutations) => {
92
+ for (const mutation of mutations) {
93
+ if (mutation.type !== 'attributes')
94
+ continue;
95
+ if (mutation.attributeName !== 'inert')
96
+ continue;
97
+ root.removeAttribute('inert');
98
+ }
99
+ });
100
+ inertObserver.observe(root, { attributeOldValue: true, attributes: true });
101
+ // dialog/page interactivity (no visibility change)
102
+ let dialogActive = false;
103
+ const activatePage = () => {
104
+ if (!dialogActive)
105
+ return;
106
+ dialogActive = false;
107
+ root.removeEventListener('cancel', onBlur);
108
+ root.removeEventListener('click', onBlur);
109
+ root.style.pointerEvents = 'none';
110
+ opener?.focus();
111
+ opener = null;
112
+ document.body.style.overflow = savedOverflow;
113
+ };
114
+ const activateDialog = () => {
115
+ if (dialogActive)
116
+ return;
117
+ dialogActive = true;
118
+ root.addEventListener('cancel', onBlur);
119
+ root.addEventListener('click', onBlur);
120
+ frame.focus();
121
+ root.style.pointerEvents = 'auto';
122
+ savedOverflow = document.body.style.overflow;
123
+ document.body.style.overflow = 'hidden';
124
+ };
125
+ // dialog visibility
126
+ let visible = false;
127
+ const showDialog = () => {
128
+ if (visible)
129
+ return;
130
+ visible = true;
131
+ if (document.activeElement instanceof HTMLElement)
132
+ opener = document.activeElement;
133
+ root.removeAttribute('hidden');
134
+ root.removeAttribute('aria-closed');
135
+ root.showModal();
136
+ };
137
+ const hideDialog = () => {
138
+ if (!visible)
139
+ return;
140
+ visible = false;
141
+ root.setAttribute('hidden', 'true');
142
+ root.setAttribute('aria-closed', 'true');
143
+ root.close();
144
+ // 1Password extension sometimes adds `inert` to dialog siblings
145
+ // and does not clean up when dialog closes.
146
+ for (const sibling of root.parentNode ? Array.from(root.parentNode.children) : []) {
147
+ if (sibling === root)
148
+ continue;
149
+ if (!sibling.hasAttribute('inert'))
150
+ continue;
151
+ sibling.removeAttribute('inert');
152
+ }
153
+ };
154
+ return {
155
+ close() {
156
+ fallback.close();
157
+ open = false;
158
+ hideDialog();
159
+ activatePage();
160
+ },
161
+ destroy() {
162
+ fallback.close();
163
+ open = false;
164
+ activatePage();
165
+ hideDialog();
166
+ fallback.destroy();
167
+ messenger.destroy();
168
+ root.remove();
169
+ inertObserver.disconnect();
170
+ },
171
+ open() {
172
+ if (open)
173
+ return;
174
+ open = true;
175
+ showDialog();
176
+ activateDialog();
177
+ },
178
+ async syncRequests(requests) {
179
+ // Safari does not support WebAuthn credential creation in iframes.
180
+ // Fall back to popup dialog.
181
+ const unsupported = isSafari() &&
182
+ requests.some((x) => ['wallet_connect', 'eth_requestAccounts'].includes(x.request.method));
183
+ if (unsupported) {
184
+ fallback.syncRequests(requests);
185
+ }
186
+ else {
187
+ const requiresConfirm = requests.some((x) => x.status === 'pending');
188
+ if (!open && requiresConfirm)
189
+ this.open();
190
+ messenger.send('rpc-requests', {
191
+ account: getAccount(store),
192
+ chainId: store.getState().chainId,
193
+ requests,
194
+ });
195
+ }
196
+ },
197
+ };
198
+ });
199
+ }
200
+ /** Opens the auth app in a new browser window. */
201
+ export function popup(options = {}) {
202
+ if (typeof window === 'undefined')
203
+ return noop();
204
+ const { size = defaultSize } = options;
205
+ return define({ name: 'popup' }, (parameters) => {
206
+ const { host, store } = parameters;
207
+ let win = null;
208
+ function onBlur() {
209
+ if (win)
210
+ handleBlur(store);
211
+ }
212
+ const offDetectClosed = (() => {
213
+ const timer = setInterval(() => {
214
+ if (win?.closed)
215
+ handleBlur(store);
216
+ }, 100);
217
+ return () => clearInterval(timer);
218
+ })();
219
+ let messenger;
220
+ return {
221
+ close() {
222
+ if (!win)
223
+ return;
224
+ win.close();
225
+ win = null;
226
+ },
227
+ destroy() {
228
+ this.close();
229
+ window.removeEventListener('focus', onBlur);
230
+ messenger?.destroy();
231
+ offDetectClosed();
232
+ },
233
+ open() {
234
+ const referrer = getReferrer();
235
+ const hostUrl = new URL(host);
236
+ hostUrl.searchParams.set('chainId', String(store.getState().chainId));
237
+ hostUrl.searchParams.set('mode', 'popup');
238
+ if (referrer.icon) {
239
+ if (typeof referrer.icon === 'string')
240
+ hostUrl.searchParams.set('icon', referrer.icon);
241
+ else {
242
+ hostUrl.searchParams.set('icon', referrer.icon.light);
243
+ hostUrl.searchParams.set('iconDark', referrer.icon.dark);
244
+ }
245
+ }
246
+ const left = (window.innerWidth - size.width) / 2 + window.screenX;
247
+ const top = window.screenY + 100;
248
+ win = window.open(hostUrl.toString(), '_blank', `width=${size.width},height=${size.height},left=${left},top=${top}`);
249
+ if (!win)
250
+ throw new Error('Failed to open popup');
251
+ messenger = Messenger.bridge({
252
+ from: Messenger.fromWindow(window, { targetOrigin: hostUrl.origin }),
253
+ to: Messenger.fromWindow(win, { targetOrigin: hostUrl.origin }),
254
+ waitForReady: true,
255
+ });
256
+ messenger.on('rpc-response', (response) => handleResponse(store, response));
257
+ window.removeEventListener('focus', onBlur);
258
+ window.addEventListener('focus', onBlur);
259
+ },
260
+ async syncRequests(requests) {
261
+ const requiresConfirm = requests.some((x) => x.status === 'pending');
262
+ if (requiresConfirm) {
263
+ if (!win || win.closed)
264
+ this.open();
265
+ win?.focus();
266
+ }
267
+ messenger?.send('rpc-requests', {
268
+ account: getAccount(store),
269
+ chainId: store.getState().chainId,
270
+ requests,
271
+ });
272
+ },
273
+ };
274
+ });
275
+ }
276
+ /** Returns a no-op dialog for SSR environments. */
277
+ export function noop() {
278
+ return define({ name: 'noop' }, () => ({
279
+ open() { },
280
+ close() { },
281
+ destroy() { },
282
+ async syncRequests() { },
283
+ }));
284
+ }
285
+ /** Updates the store with an RPC response from the remote auth app. */
286
+ function handleResponse(store, response) {
287
+ store.setState((x) => ({
288
+ ...x,
289
+ requestQueue: x.requestQueue.map((queued) => {
290
+ if (queued.request.id !== response.id)
291
+ return queued;
292
+ if (response.error)
293
+ return {
294
+ request: queued.request,
295
+ error: response.error,
296
+ status: 'error',
297
+ };
298
+ return {
299
+ request: queued.request,
300
+ result: response.result,
301
+ status: 'success',
302
+ };
303
+ }),
304
+ }));
305
+ }
306
+ /** Marks all pending requests as rejected (user closed the dialog). */
307
+ function handleBlur(store) {
308
+ store.setState((x) => ({
309
+ ...x,
310
+ requestQueue: x.requestQueue.map((queued) => queued.status === 'pending'
311
+ ? {
312
+ request: queued.request,
313
+ error: { code: 4001, message: 'User rejected the request.' },
314
+ status: 'error',
315
+ }
316
+ : queued),
317
+ }));
318
+ }
319
+ /** Returns the active account from the store, or `undefined` if none. */
320
+ function getAccount(store) {
321
+ const { accounts, activeAccount } = store.getState();
322
+ const account = accounts[activeAccount];
323
+ if (!account)
324
+ return undefined;
325
+ return { address: account.address };
326
+ }
327
+ /**
328
+ * Extracts referrer metadata from the host page.
329
+ * Must be called in the host page context (where `document` is accessible).
330
+ */
331
+ function getReferrer() {
332
+ const icon = (() => {
333
+ const dark = document.querySelector('link[rel~="icon"][media="(prefers-color-scheme: dark)"]');
334
+ const light = (document.querySelector('link[rel~="icon"][media="(prefers-color-scheme: light)"]') ?? document.querySelector('link[rel~="icon"]'));
335
+ if (dark?.href && light?.href && dark.href !== light.href)
336
+ return { dark: dark.href, light: light.href };
337
+ const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
338
+ return (isDark ? dark?.href : light?.href) ?? light?.href;
339
+ })();
340
+ return { icon, title: document.title };
341
+ }
342
+ //# sourceMappingURL=Dialog.js.map