@snaha/swarm-id 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +431 -0
  2. package/dist/chunk/bmt.d.ts +17 -0
  3. package/dist/chunk/bmt.d.ts.map +1 -0
  4. package/dist/chunk/cac.d.ts +18 -0
  5. package/dist/chunk/cac.d.ts.map +1 -0
  6. package/dist/chunk/constants.d.ts +10 -0
  7. package/dist/chunk/constants.d.ts.map +1 -0
  8. package/dist/chunk/encrypted-cac.d.ts +48 -0
  9. package/dist/chunk/encrypted-cac.d.ts.map +1 -0
  10. package/dist/chunk/encryption.d.ts +86 -0
  11. package/dist/chunk/encryption.d.ts.map +1 -0
  12. package/dist/chunk/index.d.ts +6 -0
  13. package/dist/chunk/index.d.ts.map +1 -0
  14. package/dist/index.d.ts +46 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/proxy/act/act.d.ts +78 -0
  17. package/dist/proxy/act/act.d.ts.map +1 -0
  18. package/dist/proxy/act/crypto.d.ts +44 -0
  19. package/dist/proxy/act/crypto.d.ts.map +1 -0
  20. package/dist/proxy/act/grantee-list.d.ts +82 -0
  21. package/dist/proxy/act/grantee-list.d.ts.map +1 -0
  22. package/dist/proxy/act/history.d.ts +183 -0
  23. package/dist/proxy/act/history.d.ts.map +1 -0
  24. package/dist/proxy/act/index.d.ts +104 -0
  25. package/dist/proxy/act/index.d.ts.map +1 -0
  26. package/dist/proxy/chunking-encrypted.d.ts +14 -0
  27. package/dist/proxy/chunking-encrypted.d.ts.map +1 -0
  28. package/dist/proxy/chunking.d.ts +15 -0
  29. package/dist/proxy/chunking.d.ts.map +1 -0
  30. package/dist/proxy/download-data.d.ts +16 -0
  31. package/dist/proxy/download-data.d.ts.map +1 -0
  32. package/dist/proxy/feed-manifest.d.ts +62 -0
  33. package/dist/proxy/feed-manifest.d.ts.map +1 -0
  34. package/dist/proxy/feeds/epochs/async-finder.d.ts +77 -0
  35. package/dist/proxy/feeds/epochs/async-finder.d.ts.map +1 -0
  36. package/dist/proxy/feeds/epochs/epoch.d.ts +88 -0
  37. package/dist/proxy/feeds/epochs/epoch.d.ts.map +1 -0
  38. package/dist/proxy/feeds/epochs/finder.d.ts +67 -0
  39. package/dist/proxy/feeds/epochs/finder.d.ts.map +1 -0
  40. package/dist/proxy/feeds/epochs/index.d.ts +35 -0
  41. package/dist/proxy/feeds/epochs/index.d.ts.map +1 -0
  42. package/dist/proxy/feeds/epochs/test-utils.d.ts +93 -0
  43. package/dist/proxy/feeds/epochs/test-utils.d.ts.map +1 -0
  44. package/dist/proxy/feeds/epochs/types.d.ts +109 -0
  45. package/dist/proxy/feeds/epochs/types.d.ts.map +1 -0
  46. package/dist/proxy/feeds/epochs/updater.d.ts +68 -0
  47. package/dist/proxy/feeds/epochs/updater.d.ts.map +1 -0
  48. package/dist/proxy/feeds/epochs/utils.d.ts +22 -0
  49. package/dist/proxy/feeds/epochs/utils.d.ts.map +1 -0
  50. package/dist/proxy/feeds/index.d.ts +5 -0
  51. package/dist/proxy/feeds/index.d.ts.map +1 -0
  52. package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
  53. package/dist/proxy/feeds/sequence/async-finder.d.ts.map +1 -0
  54. package/dist/proxy/feeds/sequence/finder.d.ts +17 -0
  55. package/dist/proxy/feeds/sequence/finder.d.ts.map +1 -0
  56. package/dist/proxy/feeds/sequence/index.d.ts +23 -0
  57. package/dist/proxy/feeds/sequence/index.d.ts.map +1 -0
  58. package/dist/proxy/feeds/sequence/types.d.ts +80 -0
  59. package/dist/proxy/feeds/sequence/types.d.ts.map +1 -0
  60. package/dist/proxy/feeds/sequence/updater.d.ts +26 -0
  61. package/dist/proxy/feeds/sequence/updater.d.ts.map +1 -0
  62. package/dist/proxy/index.d.ts +6 -0
  63. package/dist/proxy/index.d.ts.map +1 -0
  64. package/dist/proxy/manifest-builder.d.ts +183 -0
  65. package/dist/proxy/manifest-builder.d.ts.map +1 -0
  66. package/dist/proxy/mantaray-encrypted.d.ts +27 -0
  67. package/dist/proxy/mantaray-encrypted.d.ts.map +1 -0
  68. package/dist/proxy/mantaray.d.ts +26 -0
  69. package/dist/proxy/mantaray.d.ts.map +1 -0
  70. package/dist/proxy/types.d.ts +29 -0
  71. package/dist/proxy/types.d.ts.map +1 -0
  72. package/dist/proxy/upload-data.d.ts +17 -0
  73. package/dist/proxy/upload-data.d.ts.map +1 -0
  74. package/dist/proxy/upload-encrypted-data.d.ts +103 -0
  75. package/dist/proxy/upload-encrypted-data.d.ts.map +1 -0
  76. package/dist/schemas.d.ts +240 -0
  77. package/dist/schemas.d.ts.map +1 -0
  78. package/dist/storage/debounced-uploader.d.ts +62 -0
  79. package/dist/storage/debounced-uploader.d.ts.map +1 -0
  80. package/dist/storage/utilization-store.d.ts +108 -0
  81. package/dist/storage/utilization-store.d.ts.map +1 -0
  82. package/dist/swarm-id-auth.d.ts +74 -0
  83. package/dist/swarm-id-auth.d.ts.map +1 -0
  84. package/dist/swarm-id-auth.js +2 -0
  85. package/dist/swarm-id-auth.js.map +1 -0
  86. package/dist/swarm-id-client.d.ts +878 -0
  87. package/dist/swarm-id-client.d.ts.map +1 -0
  88. package/dist/swarm-id-client.js +2 -0
  89. package/dist/swarm-id-client.js.map +1 -0
  90. package/dist/swarm-id-proxy.d.ts +236 -0
  91. package/dist/swarm-id-proxy.d.ts.map +1 -0
  92. package/dist/swarm-id-proxy.js +2 -0
  93. package/dist/swarm-id-proxy.js.map +1 -0
  94. package/dist/swarm-id.esm.js +2 -0
  95. package/dist/swarm-id.esm.js.map +1 -0
  96. package/dist/swarm-id.umd.js +2 -0
  97. package/dist/swarm-id.umd.js.map +1 -0
  98. package/dist/sync/index.d.ts +9 -0
  99. package/dist/sync/index.d.ts.map +1 -0
  100. package/dist/sync/key-derivation.d.ts +25 -0
  101. package/dist/sync/key-derivation.d.ts.map +1 -0
  102. package/dist/sync/restore-account.d.ts +28 -0
  103. package/dist/sync/restore-account.d.ts.map +1 -0
  104. package/dist/sync/serialization.d.ts +16 -0
  105. package/dist/sync/serialization.d.ts.map +1 -0
  106. package/dist/sync/store-interfaces.d.ts +53 -0
  107. package/dist/sync/store-interfaces.d.ts.map +1 -0
  108. package/dist/sync/sync-account.d.ts +44 -0
  109. package/dist/sync/sync-account.d.ts.map +1 -0
  110. package/dist/sync/types.d.ts +13 -0
  111. package/dist/sync/types.d.ts.map +1 -0
  112. package/dist/test-fixtures.d.ts +17 -0
  113. package/dist/test-fixtures.d.ts.map +1 -0
  114. package/dist/types-BD_VkNn0.js +2 -0
  115. package/dist/types-BD_VkNn0.js.map +1 -0
  116. package/dist/types-lJCaT-50.js +2 -0
  117. package/dist/types-lJCaT-50.js.map +1 -0
  118. package/dist/types.d.ts +2157 -0
  119. package/dist/types.d.ts.map +1 -0
  120. package/dist/utils/account-payload.d.ts +94 -0
  121. package/dist/utils/account-payload.d.ts.map +1 -0
  122. package/dist/utils/account-state-snapshot.d.ts +38 -0
  123. package/dist/utils/account-state-snapshot.d.ts.map +1 -0
  124. package/dist/utils/backup-encryption.d.ts +127 -0
  125. package/dist/utils/backup-encryption.d.ts.map +1 -0
  126. package/dist/utils/batch-utilization.d.ts +432 -0
  127. package/dist/utils/batch-utilization.d.ts.map +1 -0
  128. package/dist/utils/constants.d.ts +11 -0
  129. package/dist/utils/constants.d.ts.map +1 -0
  130. package/dist/utils/hex.d.ts +17 -0
  131. package/dist/utils/hex.d.ts.map +1 -0
  132. package/dist/utils/key-derivation.d.ts +92 -0
  133. package/dist/utils/key-derivation.d.ts.map +1 -0
  134. package/dist/utils/storage-managers.d.ts +65 -0
  135. package/dist/utils/storage-managers.d.ts.map +1 -0
  136. package/dist/utils/swarm-id-export.d.ts +24 -0
  137. package/dist/utils/swarm-id-export.d.ts.map +1 -0
  138. package/dist/utils/ttl.d.ts +49 -0
  139. package/dist/utils/ttl.d.ts.map +1 -0
  140. package/dist/utils/url.d.ts +41 -0
  141. package/dist/utils/url.d.ts.map +1 -0
  142. package/dist/utils/versioned-storage.d.ts +131 -0
  143. package/dist/utils/versioned-storage.d.ts.map +1 -0
  144. package/package.json +78 -0
  145. package/src/chunk/bmt.test.ts +217 -0
  146. package/src/chunk/bmt.ts +57 -0
  147. package/src/chunk/cac.test.ts +214 -0
  148. package/src/chunk/cac.ts +65 -0
  149. package/src/chunk/constants.ts +18 -0
  150. package/src/chunk/encrypted-cac.test.ts +385 -0
  151. package/src/chunk/encrypted-cac.ts +131 -0
  152. package/src/chunk/encryption.test.ts +352 -0
  153. package/src/chunk/encryption.ts +300 -0
  154. package/src/chunk/index.ts +47 -0
  155. package/src/index.ts +430 -0
  156. package/src/proxy/act/act.test.ts +278 -0
  157. package/src/proxy/act/act.ts +158 -0
  158. package/src/proxy/act/bee-compat.test.ts +948 -0
  159. package/src/proxy/act/crypto.test.ts +436 -0
  160. package/src/proxy/act/crypto.ts +376 -0
  161. package/src/proxy/act/grantee-list.test.ts +393 -0
  162. package/src/proxy/act/grantee-list.ts +239 -0
  163. package/src/proxy/act/history.test.ts +360 -0
  164. package/src/proxy/act/history.ts +413 -0
  165. package/src/proxy/act/index.test.ts +748 -0
  166. package/src/proxy/act/index.ts +853 -0
  167. package/src/proxy/chunking-encrypted.ts +95 -0
  168. package/src/proxy/chunking.ts +65 -0
  169. package/src/proxy/download-data.ts +448 -0
  170. package/src/proxy/feed-manifest.ts +174 -0
  171. package/src/proxy/feeds/epochs/async-finder.ts +372 -0
  172. package/src/proxy/feeds/epochs/epoch.test.ts +249 -0
  173. package/src/proxy/feeds/epochs/epoch.ts +181 -0
  174. package/src/proxy/feeds/epochs/finder.ts +282 -0
  175. package/src/proxy/feeds/epochs/index.ts +73 -0
  176. package/src/proxy/feeds/epochs/integration.test.ts +1336 -0
  177. package/src/proxy/feeds/epochs/test-utils.ts +274 -0
  178. package/src/proxy/feeds/epochs/types.ts +128 -0
  179. package/src/proxy/feeds/epochs/updater.ts +192 -0
  180. package/src/proxy/feeds/epochs/utils.ts +62 -0
  181. package/src/proxy/feeds/index.ts +5 -0
  182. package/src/proxy/feeds/sequence/async-finder.ts +31 -0
  183. package/src/proxy/feeds/sequence/finder.ts +73 -0
  184. package/src/proxy/feeds/sequence/index.ts +54 -0
  185. package/src/proxy/feeds/sequence/integration.test.ts +966 -0
  186. package/src/proxy/feeds/sequence/types.ts +103 -0
  187. package/src/proxy/feeds/sequence/updater.ts +71 -0
  188. package/src/proxy/index.ts +5 -0
  189. package/src/proxy/manifest-builder.test.ts +427 -0
  190. package/src/proxy/manifest-builder.ts +679 -0
  191. package/src/proxy/mantaray-encrypted.ts +78 -0
  192. package/src/proxy/mantaray.ts +104 -0
  193. package/src/proxy/types.ts +32 -0
  194. package/src/proxy/upload-data.ts +189 -0
  195. package/src/proxy/upload-encrypted-data.ts +658 -0
  196. package/src/schemas.ts +299 -0
  197. package/src/storage/debounced-uploader.ts +192 -0
  198. package/src/storage/utilization-store.ts +397 -0
  199. package/src/swarm-id-client.test.ts +99 -0
  200. package/src/swarm-id-client.ts +3095 -0
  201. package/src/swarm-id-proxy.ts +3891 -0
  202. package/src/sync/index.ts +28 -0
  203. package/src/sync/restore-account.ts +90 -0
  204. package/src/sync/serialization.ts +39 -0
  205. package/src/sync/store-interfaces.ts +62 -0
  206. package/src/sync/sync-account.test.ts +302 -0
  207. package/src/sync/sync-account.ts +396 -0
  208. package/src/sync/types.ts +11 -0
  209. package/src/test-fixtures.ts +109 -0
  210. package/src/types.ts +1651 -0
  211. package/src/utils/account-state-snapshot.test.ts +595 -0
  212. package/src/utils/account-state-snapshot.ts +94 -0
  213. package/src/utils/backup-encryption.test.ts +442 -0
  214. package/src/utils/backup-encryption.ts +352 -0
  215. package/src/utils/batch-utilization.ts +1309 -0
  216. package/src/utils/constants.ts +20 -0
  217. package/src/utils/hex.ts +27 -0
  218. package/src/utils/key-derivation.ts +197 -0
  219. package/src/utils/storage-managers.ts +365 -0
  220. package/src/utils/ttl.ts +129 -0
  221. package/src/utils/url.test.ts +136 -0
  222. package/src/utils/url.ts +71 -0
  223. package/src/utils/versioned-storage.ts +323 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * TTL (Time To Live) calculation and formatting utilities for postage stamps
3
+ */
4
+ /**
5
+ * Gnosis Chain block time in seconds
6
+ */
7
+ export declare const GNOSIS_BLOCK_TIME = 5;
8
+ /**
9
+ * Swarmscan API URL for price data
10
+ */
11
+ export declare const SWARMSCAN_STATS_URL = "https://api.swarmscan.io/v1/postage-stamps/stats";
12
+ /**
13
+ * Fetches current price from Swarmscan.
14
+ * @returns pricePerGBPerMonth in BZZ
15
+ */
16
+ export declare function fetchSwarmPrice(): Promise<number>;
17
+ /**
18
+ * Calculates TTL in seconds from stamp amount and Swarmscan price.
19
+ *
20
+ * @param amount - Stamp amount in PLUR (smallest BZZ unit)
21
+ * @param pricePerGBPerMonth - Price from Swarmscan (in BZZ)
22
+ * @returns TTL in seconds
23
+ */
24
+ export declare function calculateTTLSeconds(amount: bigint | number | string, pricePerGBPerMonth: number): number;
25
+ /**
26
+ * Formats a TTL value (in seconds) to a human-readable string.
27
+ * Returns "Xd Yh" format (e.g., "30d 14h").
28
+ *
29
+ * @param ttlSeconds - TTL in seconds
30
+ * @returns Human-readable TTL string, or "N/A" if undefined/invalid
31
+ */
32
+ export declare function formatTTL(ttlSeconds: number | undefined): string;
33
+ /**
34
+ * Fetches block timestamp from Gnosis RPC.
35
+ *
36
+ * @param rpcUrl - Gnosis RPC URL
37
+ * @param blockNumber - Block number to get timestamp for
38
+ * @returns Block timestamp in seconds (Unix timestamp)
39
+ */
40
+ export declare function getBlockTimestamp(rpcUrl: string, blockNumber: number): Promise<number>;
41
+ /**
42
+ * Calculates expiry timestamp for a postage stamp.
43
+ *
44
+ * @param blockTimestamp - Timestamp when stamp was created (from blockNumber)
45
+ * @param ttlSeconds - TTL in seconds
46
+ * @returns Expiry timestamp in seconds (Unix timestamp)
47
+ */
48
+ export declare function calculateExpiryTimestamp(blockTimestamp: number, ttlSeconds: number): number;
49
+ //# sourceMappingURL=ttl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../src/utils/ttl.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAkBlC;;GAEG;AACH,eAAO,MAAM,mBAAmB,qDACoB,CAAA;AAEpD;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAOvD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAChC,kBAAkB,EAAE,MAAM,GACzB,MAAM,CASR;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAShE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GACjB,MAAM,CAER"}
@@ -0,0 +1,41 @@
1
+ import type { AppMetadata } from "../types";
2
+ /**
3
+ * Configuration options for building the authentication URL.
4
+ */
5
+ export interface BuildAuthUrlOptions {
6
+ /**
7
+ * When true, shows the agent sign-up option on the connect page.
8
+ * Agents are automated services that can perform operations on behalf of users.
9
+ */
10
+ agent?: boolean;
11
+ /**
12
+ * Challenge string for storage partitioning detection. When present, the popup checks
13
+ * if it can read this challenge from localStorage to determine whether
14
+ * storage is partitioned.
15
+ */
16
+ challenge?: string;
17
+ }
18
+ /**
19
+ * Build the authentication URL for connecting to Swarm ID
20
+ *
21
+ * This function creates the same URL format as used by SwarmIdProxy.openAuthPopup()
22
+ * to ensure consistency across the library.
23
+ *
24
+ * @param baseUrl - The base URL where the authentication page is hosted
25
+ * @param origin - The origin of the parent application requesting authentication
26
+ * @param metadata - Optional application metadata to display during authentication
27
+ * @param options - Optional configuration for the auth URL
28
+ * @returns The complete authentication URL with hash parameters
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const url = buildAuthUrl(
33
+ * "https://swarm-id.example.com",
34
+ * "https://myapp.example.com",
35
+ * { name: "My App", description: "A decentralized application" }
36
+ * )
37
+ * // Returns: "https://swarm-id.example.com/connect#origin=https%3A%2F%2Fmyapp.example.com&appName=My+App&appDescription=A+decentralized+application"
38
+ * ```
39
+ */
40
+ export declare function buildAuthUrl(baseUrl: string, origin: string, metadata?: AppMetadata, options?: BuildAuthUrlOptions): string;
41
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAwBR"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Generic Versioned Storage Utility
3
+ *
4
+ * Provides a framework-agnostic way to store and retrieve versioned data
5
+ * with automatic migration support.
6
+ */
7
+ import { z } from "zod";
8
+ /**
9
+ * Versioned storage wrapper schema
10
+ * Used to check if data is in versioned format
11
+ */
12
+ export declare const VersionedStorageSchema: z.ZodObject<{
13
+ version: z.ZodNumber;
14
+ data: z.ZodUnknown;
15
+ }, z.core.$strip>;
16
+ export type VersionedStorage = z.infer<typeof VersionedStorageSchema>;
17
+ /**
18
+ * Storage adapter interface - allows different storage backends
19
+ */
20
+ export interface StorageAdapter {
21
+ getItem(key: string): string | undefined;
22
+ setItem(key: string, value: string): void;
23
+ removeItem(key: string): void;
24
+ }
25
+ /**
26
+ * Version parser function - handles migration from one version to another
27
+ */
28
+ export type VersionParser<T> = (data: unknown, version: number) => T[];
29
+ /**
30
+ * Serializer function - converts data to JSON-serializable format
31
+ */
32
+ export type Serializer<T> = (data: T) => Record<string, unknown>;
33
+ /**
34
+ * Listener function for storage change events
35
+ */
36
+ export type StorageChangeListener<T> = (data: T[]) => void;
37
+ /**
38
+ * Options for versioned storage
39
+ */
40
+ export interface VersionedStorageOptions<T> {
41
+ /** Storage key */
42
+ key: string;
43
+ /** Current version number */
44
+ currentVersion: number;
45
+ /** Storage adapter (e.g., localStorage) */
46
+ storage: StorageAdapter;
47
+ /** Version parsers - map of version to parser function */
48
+ parsers: Record<number, VersionParser<T>>;
49
+ /** Optional serializer for complex types */
50
+ serializer?: Serializer<T>;
51
+ /** Logger name for error messages */
52
+ loggerName?: string;
53
+ }
54
+ /**
55
+ * LocalStorage adapter for browser environments
56
+ */
57
+ export declare class LocalStorageAdapter implements StorageAdapter {
58
+ getItem(key: string): string | undefined;
59
+ setItem(key: string, value: string): void;
60
+ removeItem(key: string): void;
61
+ }
62
+ /**
63
+ * In-memory storage adapter (for testing or non-browser environments)
64
+ */
65
+ export declare class MemoryStorageAdapter implements StorageAdapter {
66
+ private storage;
67
+ getItem(key: string): string | undefined;
68
+ setItem(key: string, value: string): void;
69
+ removeItem(key: string): void;
70
+ clear(): void;
71
+ }
72
+ /**
73
+ * Generic versioned storage manager
74
+ */
75
+ export declare class VersionedStorageManager<T> {
76
+ private options;
77
+ private listeners;
78
+ private boundStorageHandler;
79
+ constructor(options: VersionedStorageOptions<T>);
80
+ /**
81
+ * Subscribe to storage change events from other windows/tabs
82
+ * The browser's storage event fires when localStorage changes in OTHER windows,
83
+ * making this useful for cross-window synchronization.
84
+ *
85
+ * @param listener - Callback function that receives the updated data
86
+ * @returns Unsubscribe function to remove the listener
87
+ */
88
+ subscribe(listener: StorageChangeListener<T>): () => void;
89
+ /**
90
+ * Set up the browser storage event listener
91
+ * Only listens for changes to this manager's specific key
92
+ */
93
+ private setupStorageEventListener;
94
+ /**
95
+ * Clean up the storage event listener when no more subscribers
96
+ */
97
+ private cleanupStorageEventListener;
98
+ /**
99
+ * Notify all listeners of data changes
100
+ */
101
+ private notifyListeners;
102
+ /**
103
+ * Load data from storage
104
+ */
105
+ load(): T[];
106
+ /**
107
+ * Parse versioned data and migrate if needed
108
+ */
109
+ private parse;
110
+ /**
111
+ * Save data to storage
112
+ */
113
+ save(data: T[]): void;
114
+ /**
115
+ * Clear data from storage
116
+ */
117
+ clear(): void;
118
+ }
119
+ /**
120
+ * Create a versioned storage manager with localStorage
121
+ */
122
+ export declare function createLocalStorageManager<T>(options: Omit<VersionedStorageOptions<T>, "storage">): VersionedStorageManager<T>;
123
+ /**
124
+ * Create a versioned storage manager with memory storage
125
+ */
126
+ export declare function createMemoryStorageManager<T>(options: Omit<VersionedStorageOptions<T>, "storage">): VersionedStorageManager<T>;
127
+ /**
128
+ * Create a simple Zod-based parser for a single version
129
+ */
130
+ export declare function createZodParser<T>(schema: z.ZodType<T[]>): VersionParser<T>;
131
+ //# sourceMappingURL=versioned-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-storage.d.ts","sourceRoot":"","sources":["../../src/utils/versioned-storage.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@snaha/swarm-id",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Swarm ID library for authentication and Bee API operations",
6
+ "main": "dist/swarm-id.esm.js",
7
+ "module": "dist/swarm-id.esm.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/swarm-id.esm.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "scripts": {
20
+ "build": "rollup -c",
21
+ "build:watch": "rollup -c -w",
22
+ "clean": "rm -rf dist",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "test:ui": "vitest --ui",
26
+ "test:coverage": "vitest run --coverage",
27
+ "lint": "eslint src/**/*.ts",
28
+ "lint:fix": "eslint src/**/*.ts --fix",
29
+ "format": "prettier --write \"src/**/*.ts\"",
30
+ "format:check": "prettier --check \"src/**/*.ts\"",
31
+ "typecheck": "tsc --noEmit",
32
+ "check:all": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test"
33
+ },
34
+ "keywords": [
35
+ "swarm",
36
+ "identity",
37
+ "authentication",
38
+ "bee",
39
+ "web3"
40
+ ],
41
+ "author": "snaha",
42
+ "license": "Apache-2.0",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/snaha/swarm-id"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "provenance": true
50
+ },
51
+ "dependencies": {
52
+ "@ethersphere/bee-js": "^11.1.1",
53
+ "cafe-utility": "33.3.2",
54
+ "zod": "4.1.13"
55
+ },
56
+ "devDependencies": {
57
+ "@rollup/plugin-commonjs": "29.0.0",
58
+ "@rollup/plugin-json": "6.1.0",
59
+ "@rollup/plugin-node-resolve": "16.0.3",
60
+ "@rollup/plugin-replace": "6.0.3",
61
+ "@rollup/plugin-terser": "0.4.4",
62
+ "@rollup/plugin-typescript": "12.3.0",
63
+ "@types/node": "24.10.1",
64
+ "@typescript-eslint/eslint-plugin": "8.48.1",
65
+ "@typescript-eslint/parser": "8.48.1",
66
+ "@vitest/ui": "^4.0.16",
67
+ "eslint": "9.39.1",
68
+ "prettier": "3.7.3",
69
+ "rollup": "4.53.3",
70
+ "tslib": "2.8.1",
71
+ "typescript": "5.8.3",
72
+ "vitest": "4.0.14"
73
+ },
74
+ "engines": {
75
+ "node": ">=22",
76
+ "pnpm": "10.x"
77
+ }
78
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Unit tests for BMT (Binary Merkle Tree) hashing
3
+ */
4
+
5
+ import { describe, it, expect } from "vitest"
6
+ import { Span } from "@ethersphere/bee-js"
7
+ import { calculateChunkAddress } from "./bmt"
8
+
9
+ // ============================================================================
10
+ // calculateChunkAddress Tests
11
+ // ============================================================================
12
+
13
+ describe("calculateChunkAddress", () => {
14
+ describe("basic functionality", () => {
15
+ it("should return 32-byte Reference", () => {
16
+ const span = Span.fromBigInt(BigInt(4))
17
+ const payload = new Uint8Array([1, 2, 3, 4])
18
+ const chunkContent = new Uint8Array(8 + payload.length)
19
+ chunkContent.set(span.toUint8Array())
20
+ chunkContent.set(payload, 8)
21
+
22
+ const address = calculateChunkAddress(chunkContent)
23
+
24
+ expect(address.toUint8Array().length).toBe(32)
25
+ })
26
+
27
+ it("should produce deterministic result (same content = same address)", () => {
28
+ const span = Span.fromBigInt(BigInt(5))
29
+ const payload = new Uint8Array([10, 20, 30, 40, 50])
30
+ const chunkContent = new Uint8Array(8 + payload.length)
31
+ chunkContent.set(span.toUint8Array())
32
+ chunkContent.set(payload, 8)
33
+
34
+ const address1 = calculateChunkAddress(chunkContent)
35
+ const address2 = calculateChunkAddress(chunkContent)
36
+
37
+ expect(address1.toUint8Array()).toEqual(address2.toUint8Array())
38
+ })
39
+
40
+ it("should produce different address for different content", () => {
41
+ const span = Span.fromBigInt(BigInt(4))
42
+
43
+ const payload1 = new Uint8Array([1, 2, 3, 4])
44
+ const chunkContent1 = new Uint8Array(8 + payload1.length)
45
+ chunkContent1.set(span.toUint8Array())
46
+ chunkContent1.set(payload1, 8)
47
+
48
+ const payload2 = new Uint8Array([5, 6, 7, 8])
49
+ const chunkContent2 = new Uint8Array(8 + payload2.length)
50
+ chunkContent2.set(span.toUint8Array())
51
+ chunkContent2.set(payload2, 8)
52
+
53
+ const address1 = calculateChunkAddress(chunkContent1)
54
+ const address2 = calculateChunkAddress(chunkContent2)
55
+
56
+ expect(address1.toUint8Array()).not.toEqual(address2.toUint8Array())
57
+ })
58
+ })
59
+
60
+ describe("payload size handling", () => {
61
+ it("should handle minimum payload (1 byte)", () => {
62
+ const span = Span.fromBigInt(BigInt(1))
63
+ const payload = new Uint8Array([42])
64
+ const chunkContent = new Uint8Array(8 + payload.length)
65
+ chunkContent.set(span.toUint8Array())
66
+ chunkContent.set(payload, 8)
67
+
68
+ const address = calculateChunkAddress(chunkContent)
69
+
70
+ expect(address.toUint8Array().length).toBe(32)
71
+ })
72
+
73
+ it("should handle maximum payload (4096 bytes)", () => {
74
+ const span = Span.fromBigInt(BigInt(4096))
75
+ const payload = new Uint8Array(4096)
76
+ for (let i = 0; i < 4096; i++) {
77
+ payload[i] = i % 256
78
+ }
79
+ const chunkContent = new Uint8Array(8 + payload.length)
80
+ chunkContent.set(span.toUint8Array())
81
+ chunkContent.set(payload, 8)
82
+
83
+ const address = calculateChunkAddress(chunkContent)
84
+
85
+ expect(address.toUint8Array().length).toBe(32)
86
+ })
87
+
88
+ it("should throw error on payload larger than 4096 bytes", () => {
89
+ const span = Span.fromBigInt(BigInt(4097))
90
+ const payload = new Uint8Array(4097)
91
+ const chunkContent = new Uint8Array(8 + payload.length)
92
+ chunkContent.set(span.toUint8Array())
93
+ chunkContent.set(payload, 8)
94
+
95
+ expect(() => calculateChunkAddress(chunkContent)).toThrow(
96
+ /payload size .* exceeds maximum chunk payload size/,
97
+ )
98
+ })
99
+ })
100
+
101
+ describe("zero-padding behavior", () => {
102
+ it("should produce consistent hash with short payloads (zero-padded internally)", () => {
103
+ // Two chunks with same payload should have same address
104
+ const span = Span.fromBigInt(BigInt(10))
105
+ const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
106
+
107
+ const chunkContent = new Uint8Array(8 + payload.length)
108
+ chunkContent.set(span.toUint8Array())
109
+ chunkContent.set(payload, 8)
110
+
111
+ const address1 = calculateChunkAddress(chunkContent)
112
+ const address2 = calculateChunkAddress(chunkContent)
113
+
114
+ expect(address1.toUint8Array()).toEqual(address2.toUint8Array())
115
+ })
116
+
117
+ it("should produce different hash for different payload lengths with same prefix", () => {
118
+ // Payload of length 4 vs length 5 should have different addresses
119
+ const payload4 = new Uint8Array([1, 2, 3, 4])
120
+ const payload5 = new Uint8Array([1, 2, 3, 4, 0])
121
+
122
+ const span4 = Span.fromBigInt(BigInt(4))
123
+ const chunkContent4 = new Uint8Array(8 + payload4.length)
124
+ chunkContent4.set(span4.toUint8Array())
125
+ chunkContent4.set(payload4, 8)
126
+
127
+ const span5 = Span.fromBigInt(BigInt(5))
128
+ const chunkContent5 = new Uint8Array(8 + payload5.length)
129
+ chunkContent5.set(span5.toUint8Array())
130
+ chunkContent5.set(payload5, 8)
131
+
132
+ const address4 = calculateChunkAddress(chunkContent4)
133
+ const address5 = calculateChunkAddress(chunkContent5)
134
+
135
+ // Even though payload5 ends with 0 (like zero-padding), different spans mean different addresses
136
+ expect(address4.toUint8Array()).not.toEqual(address5.toUint8Array())
137
+ })
138
+ })
139
+
140
+ describe("span variations", () => {
141
+ it("should produce different addresses for same payload but different span", () => {
142
+ const payload = new Uint8Array([1, 2, 3, 4])
143
+
144
+ const span1 = Span.fromBigInt(BigInt(4))
145
+ const chunkContent1 = new Uint8Array(8 + payload.length)
146
+ chunkContent1.set(span1.toUint8Array())
147
+ chunkContent1.set(payload, 8)
148
+
149
+ // Same payload but span indicates different length
150
+ const span2 = Span.fromBigInt(BigInt(3))
151
+ const chunkContent2 = new Uint8Array(8 + payload.length)
152
+ chunkContent2.set(span2.toUint8Array())
153
+ chunkContent2.set(payload, 8)
154
+
155
+ const address1 = calculateChunkAddress(chunkContent1)
156
+ const address2 = calculateChunkAddress(chunkContent2)
157
+
158
+ expect(address1.toUint8Array()).not.toEqual(address2.toUint8Array())
159
+ })
160
+ })
161
+
162
+ describe("edge cases", () => {
163
+ it("should handle chunk with zero-filled payload", () => {
164
+ const span = Span.fromBigInt(BigInt(32))
165
+ const payload = new Uint8Array(32) // all zeros
166
+ const chunkContent = new Uint8Array(8 + payload.length)
167
+ chunkContent.set(span.toUint8Array())
168
+ chunkContent.set(payload, 8)
169
+
170
+ const address = calculateChunkAddress(chunkContent)
171
+
172
+ expect(address.toUint8Array().length).toBe(32)
173
+ })
174
+
175
+ it("should handle chunk with all 0xFF payload", () => {
176
+ const span = Span.fromBigInt(BigInt(32))
177
+ const payload = new Uint8Array(32).fill(0xff)
178
+ const chunkContent = new Uint8Array(8 + payload.length)
179
+ chunkContent.set(span.toUint8Array())
180
+ chunkContent.set(payload, 8)
181
+
182
+ const address = calculateChunkAddress(chunkContent)
183
+
184
+ expect(address.toUint8Array().length).toBe(32)
185
+ })
186
+
187
+ it("should handle exactly 32 bytes payload (one BMT segment)", () => {
188
+ const span = Span.fromBigInt(BigInt(32))
189
+ const payload = new Uint8Array(32)
190
+ for (let i = 0; i < 32; i++) {
191
+ payload[i] = i
192
+ }
193
+ const chunkContent = new Uint8Array(8 + payload.length)
194
+ chunkContent.set(span.toUint8Array())
195
+ chunkContent.set(payload, 8)
196
+
197
+ const address = calculateChunkAddress(chunkContent)
198
+
199
+ expect(address.toUint8Array().length).toBe(32)
200
+ })
201
+
202
+ it("should handle exactly 64 bytes payload (two BMT segments)", () => {
203
+ const span = Span.fromBigInt(BigInt(64))
204
+ const payload = new Uint8Array(64)
205
+ for (let i = 0; i < 64; i++) {
206
+ payload[i] = i
207
+ }
208
+ const chunkContent = new Uint8Array(8 + payload.length)
209
+ chunkContent.set(span.toUint8Array())
210
+ chunkContent.set(payload, 8)
211
+
212
+ const address = calculateChunkAddress(chunkContent)
213
+
214
+ expect(address.toUint8Array().length).toBe(32)
215
+ })
216
+ })
217
+ })
@@ -0,0 +1,57 @@
1
+ import { Binary } from "cafe-utility"
2
+ import { Reference, Span } from "@ethersphere/bee-js"
3
+
4
+ const MAX_CHUNK_PAYLOAD_SIZE = 4096
5
+ const SEGMENT_SIZE = 32
6
+
7
+ /**
8
+ * Calculate a Binary Merkle Tree hash for a chunk
9
+ *
10
+ * The BMT chunk address is the hash of the 8 byte span and the root
11
+ * hash of a binary Merkle tree (BMT) built on the 32-byte segments
12
+ * of the underlying data.
13
+ *
14
+ * If the chunk content is less than 4k, the hash is calculated as
15
+ * if the chunk was padded with all zeros up to 4096 bytes.
16
+ *
17
+ * @param chunkContent Chunk data including span and payload as well
18
+ *
19
+ * @returns the keccak256 hash in a byte array
20
+ */
21
+ export function calculateChunkAddress(chunkContent: Uint8Array): Reference {
22
+ const span = chunkContent.slice(0, Span.LENGTH)
23
+ const payload = chunkContent.slice(Span.LENGTH)
24
+ const rootHash = calculateBmtRootHash(payload)
25
+ const chunkHash = Binary.keccak256(Binary.concatBytes(span, rootHash))
26
+
27
+ return new Reference(chunkHash)
28
+ }
29
+
30
+ function calculateBmtRootHash(payload: Uint8Array): Uint8Array {
31
+ if (payload.length > MAX_CHUNK_PAYLOAD_SIZE) {
32
+ throw new Error(
33
+ `payload size ${payload.length} exceeds maximum chunk payload size ${MAX_CHUNK_PAYLOAD_SIZE}`,
34
+ )
35
+ }
36
+ const input = new Uint8Array(MAX_CHUNK_PAYLOAD_SIZE)
37
+ input.set(payload)
38
+
39
+ // Build BMT by hashing pairs of segments level by level
40
+ let currentLevel = Binary.partition(input, SEGMENT_SIZE)
41
+
42
+ while (currentLevel.length > 1) {
43
+ const nextLevel: Uint8Array[] = []
44
+
45
+ for (let i = 0; i < currentLevel.length; i += 2) {
46
+ const left = currentLevel[i]
47
+ const right = currentLevel[i + 1]
48
+ const combined = Binary.concatBytes(left, right)
49
+ const hash = Binary.keccak256(combined)
50
+ nextLevel.push(hash)
51
+ }
52
+
53
+ currentLevel = nextLevel
54
+ }
55
+
56
+ return currentLevel[0]
57
+ }