@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.
- package/README.md +431 -0
- package/dist/chunk/bmt.d.ts +17 -0
- package/dist/chunk/bmt.d.ts.map +1 -0
- package/dist/chunk/cac.d.ts +18 -0
- package/dist/chunk/cac.d.ts.map +1 -0
- package/dist/chunk/constants.d.ts +10 -0
- package/dist/chunk/constants.d.ts.map +1 -0
- package/dist/chunk/encrypted-cac.d.ts +48 -0
- package/dist/chunk/encrypted-cac.d.ts.map +1 -0
- package/dist/chunk/encryption.d.ts +86 -0
- package/dist/chunk/encryption.d.ts.map +1 -0
- package/dist/chunk/index.d.ts +6 -0
- package/dist/chunk/index.d.ts.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/proxy/act/act.d.ts +78 -0
- package/dist/proxy/act/act.d.ts.map +1 -0
- package/dist/proxy/act/crypto.d.ts +44 -0
- package/dist/proxy/act/crypto.d.ts.map +1 -0
- package/dist/proxy/act/grantee-list.d.ts +82 -0
- package/dist/proxy/act/grantee-list.d.ts.map +1 -0
- package/dist/proxy/act/history.d.ts +183 -0
- package/dist/proxy/act/history.d.ts.map +1 -0
- package/dist/proxy/act/index.d.ts +104 -0
- package/dist/proxy/act/index.d.ts.map +1 -0
- package/dist/proxy/chunking-encrypted.d.ts +14 -0
- package/dist/proxy/chunking-encrypted.d.ts.map +1 -0
- package/dist/proxy/chunking.d.ts +15 -0
- package/dist/proxy/chunking.d.ts.map +1 -0
- package/dist/proxy/download-data.d.ts +16 -0
- package/dist/proxy/download-data.d.ts.map +1 -0
- package/dist/proxy/feed-manifest.d.ts +62 -0
- package/dist/proxy/feed-manifest.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts +77 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts +88 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/finder.d.ts +67 -0
- package/dist/proxy/feeds/epochs/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/index.d.ts +35 -0
- package/dist/proxy/feeds/epochs/index.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts +93 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/types.d.ts +109 -0
- package/dist/proxy/feeds/epochs/types.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/updater.d.ts +68 -0
- package/dist/proxy/feeds/epochs/updater.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/utils.d.ts +22 -0
- package/dist/proxy/feeds/epochs/utils.d.ts.map +1 -0
- package/dist/proxy/feeds/index.d.ts +5 -0
- package/dist/proxy/feeds/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/finder.d.ts +17 -0
- package/dist/proxy/feeds/sequence/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/index.d.ts +23 -0
- package/dist/proxy/feeds/sequence/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/types.d.ts +80 -0
- package/dist/proxy/feeds/sequence/types.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/updater.d.ts +26 -0
- package/dist/proxy/feeds/sequence/updater.d.ts.map +1 -0
- package/dist/proxy/index.d.ts +6 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/manifest-builder.d.ts +183 -0
- package/dist/proxy/manifest-builder.d.ts.map +1 -0
- package/dist/proxy/mantaray-encrypted.d.ts +27 -0
- package/dist/proxy/mantaray-encrypted.d.ts.map +1 -0
- package/dist/proxy/mantaray.d.ts +26 -0
- package/dist/proxy/mantaray.d.ts.map +1 -0
- package/dist/proxy/types.d.ts +29 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/upload-data.d.ts +17 -0
- package/dist/proxy/upload-data.d.ts.map +1 -0
- package/dist/proxy/upload-encrypted-data.d.ts +103 -0
- package/dist/proxy/upload-encrypted-data.d.ts.map +1 -0
- package/dist/schemas.d.ts +240 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/storage/debounced-uploader.d.ts +62 -0
- package/dist/storage/debounced-uploader.d.ts.map +1 -0
- package/dist/storage/utilization-store.d.ts +108 -0
- package/dist/storage/utilization-store.d.ts.map +1 -0
- package/dist/swarm-id-auth.d.ts +74 -0
- package/dist/swarm-id-auth.d.ts.map +1 -0
- package/dist/swarm-id-auth.js +2 -0
- package/dist/swarm-id-auth.js.map +1 -0
- package/dist/swarm-id-client.d.ts +878 -0
- package/dist/swarm-id-client.d.ts.map +1 -0
- package/dist/swarm-id-client.js +2 -0
- package/dist/swarm-id-client.js.map +1 -0
- package/dist/swarm-id-proxy.d.ts +236 -0
- package/dist/swarm-id-proxy.d.ts.map +1 -0
- package/dist/swarm-id-proxy.js +2 -0
- package/dist/swarm-id-proxy.js.map +1 -0
- package/dist/swarm-id.esm.js +2 -0
- package/dist/swarm-id.esm.js.map +1 -0
- package/dist/swarm-id.umd.js +2 -0
- package/dist/swarm-id.umd.js.map +1 -0
- package/dist/sync/index.d.ts +9 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/key-derivation.d.ts +25 -0
- package/dist/sync/key-derivation.d.ts.map +1 -0
- package/dist/sync/restore-account.d.ts +28 -0
- package/dist/sync/restore-account.d.ts.map +1 -0
- package/dist/sync/serialization.d.ts +16 -0
- package/dist/sync/serialization.d.ts.map +1 -0
- package/dist/sync/store-interfaces.d.ts +53 -0
- package/dist/sync/store-interfaces.d.ts.map +1 -0
- package/dist/sync/sync-account.d.ts +44 -0
- package/dist/sync/sync-account.d.ts.map +1 -0
- package/dist/sync/types.d.ts +13 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/test-fixtures.d.ts +17 -0
- package/dist/test-fixtures.d.ts.map +1 -0
- package/dist/types-BD_VkNn0.js +2 -0
- package/dist/types-BD_VkNn0.js.map +1 -0
- package/dist/types-lJCaT-50.js +2 -0
- package/dist/types-lJCaT-50.js.map +1 -0
- package/dist/types.d.ts +2157 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/account-payload.d.ts +94 -0
- package/dist/utils/account-payload.d.ts.map +1 -0
- package/dist/utils/account-state-snapshot.d.ts +38 -0
- package/dist/utils/account-state-snapshot.d.ts.map +1 -0
- package/dist/utils/backup-encryption.d.ts +127 -0
- package/dist/utils/backup-encryption.d.ts.map +1 -0
- package/dist/utils/batch-utilization.d.ts +432 -0
- package/dist/utils/batch-utilization.d.ts.map +1 -0
- package/dist/utils/constants.d.ts +11 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/hex.d.ts +17 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/key-derivation.d.ts +92 -0
- package/dist/utils/key-derivation.d.ts.map +1 -0
- package/dist/utils/storage-managers.d.ts +65 -0
- package/dist/utils/storage-managers.d.ts.map +1 -0
- package/dist/utils/swarm-id-export.d.ts +24 -0
- package/dist/utils/swarm-id-export.d.ts.map +1 -0
- package/dist/utils/ttl.d.ts +49 -0
- package/dist/utils/ttl.d.ts.map +1 -0
- package/dist/utils/url.d.ts +41 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/versioned-storage.d.ts +131 -0
- package/dist/utils/versioned-storage.d.ts.map +1 -0
- package/package.json +78 -0
- package/src/chunk/bmt.test.ts +217 -0
- package/src/chunk/bmt.ts +57 -0
- package/src/chunk/cac.test.ts +214 -0
- package/src/chunk/cac.ts +65 -0
- package/src/chunk/constants.ts +18 -0
- package/src/chunk/encrypted-cac.test.ts +385 -0
- package/src/chunk/encrypted-cac.ts +131 -0
- package/src/chunk/encryption.test.ts +352 -0
- package/src/chunk/encryption.ts +300 -0
- package/src/chunk/index.ts +47 -0
- package/src/index.ts +430 -0
- package/src/proxy/act/act.test.ts +278 -0
- package/src/proxy/act/act.ts +158 -0
- package/src/proxy/act/bee-compat.test.ts +948 -0
- package/src/proxy/act/crypto.test.ts +436 -0
- package/src/proxy/act/crypto.ts +376 -0
- package/src/proxy/act/grantee-list.test.ts +393 -0
- package/src/proxy/act/grantee-list.ts +239 -0
- package/src/proxy/act/history.test.ts +360 -0
- package/src/proxy/act/history.ts +413 -0
- package/src/proxy/act/index.test.ts +748 -0
- package/src/proxy/act/index.ts +853 -0
- package/src/proxy/chunking-encrypted.ts +95 -0
- package/src/proxy/chunking.ts +65 -0
- package/src/proxy/download-data.ts +448 -0
- package/src/proxy/feed-manifest.ts +174 -0
- package/src/proxy/feeds/epochs/async-finder.ts +372 -0
- package/src/proxy/feeds/epochs/epoch.test.ts +249 -0
- package/src/proxy/feeds/epochs/epoch.ts +181 -0
- package/src/proxy/feeds/epochs/finder.ts +282 -0
- package/src/proxy/feeds/epochs/index.ts +73 -0
- package/src/proxy/feeds/epochs/integration.test.ts +1336 -0
- package/src/proxy/feeds/epochs/test-utils.ts +274 -0
- package/src/proxy/feeds/epochs/types.ts +128 -0
- package/src/proxy/feeds/epochs/updater.ts +192 -0
- package/src/proxy/feeds/epochs/utils.ts +62 -0
- package/src/proxy/feeds/index.ts +5 -0
- package/src/proxy/feeds/sequence/async-finder.ts +31 -0
- package/src/proxy/feeds/sequence/finder.ts +73 -0
- package/src/proxy/feeds/sequence/index.ts +54 -0
- package/src/proxy/feeds/sequence/integration.test.ts +966 -0
- package/src/proxy/feeds/sequence/types.ts +103 -0
- package/src/proxy/feeds/sequence/updater.ts +71 -0
- package/src/proxy/index.ts +5 -0
- package/src/proxy/manifest-builder.test.ts +427 -0
- package/src/proxy/manifest-builder.ts +679 -0
- package/src/proxy/mantaray-encrypted.ts +78 -0
- package/src/proxy/mantaray.ts +104 -0
- package/src/proxy/types.ts +32 -0
- package/src/proxy/upload-data.ts +189 -0
- package/src/proxy/upload-encrypted-data.ts +658 -0
- package/src/schemas.ts +299 -0
- package/src/storage/debounced-uploader.ts +192 -0
- package/src/storage/utilization-store.ts +397 -0
- package/src/swarm-id-client.test.ts +99 -0
- package/src/swarm-id-client.ts +3095 -0
- package/src/swarm-id-proxy.ts +3891 -0
- package/src/sync/index.ts +28 -0
- package/src/sync/restore-account.ts +90 -0
- package/src/sync/serialization.ts +39 -0
- package/src/sync/store-interfaces.ts +62 -0
- package/src/sync/sync-account.test.ts +302 -0
- package/src/sync/sync-account.ts +396 -0
- package/src/sync/types.ts +11 -0
- package/src/test-fixtures.ts +109 -0
- package/src/types.ts +1651 -0
- package/src/utils/account-state-snapshot.test.ts +595 -0
- package/src/utils/account-state-snapshot.ts +94 -0
- package/src/utils/backup-encryption.test.ts +442 -0
- package/src/utils/backup-encryption.ts +352 -0
- package/src/utils/batch-utilization.ts +1309 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/hex.ts +27 -0
- package/src/utils/key-derivation.ts +197 -0
- package/src/utils/storage-managers.ts +365 -0
- package/src/utils/ttl.ts +129 -0
- package/src/utils/url.test.ts +136 -0
- package/src/utils/url.ts +71 -0
- package/src/utils/versioned-storage.ts +323 -0
package/src/schemas.ts
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity Schemas with Zod
|
|
3
|
+
*
|
|
4
|
+
* Defines Zod schemas for storage entities with transforms to convert
|
|
5
|
+
* serialized primitives to bee-js runtime types. Types are derived using
|
|
6
|
+
* z.infer to guarantee schema/type consistency.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { z } from "zod"
|
|
10
|
+
import {
|
|
11
|
+
EthAddress,
|
|
12
|
+
BatchId as BeeBatchId,
|
|
13
|
+
Bytes,
|
|
14
|
+
PrivateKey as BeePrivateKey,
|
|
15
|
+
} from "@ethersphere/bee-js"
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Network Settings Constants
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_BEE_NODE_URL = "https://api.gateway.ethswarm.org/"
|
|
22
|
+
export const DEFAULT_GNOSIS_RPC_URL = "https://xdai.fairdatasociety.org/"
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Base Validation Schemas (hex strings, no transforms)
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
const hexString = (length: number) =>
|
|
29
|
+
z.string().regex(new RegExp(`^[0-9a-fA-F]{${length}}$`), {
|
|
30
|
+
message: `Must be a ${length}-character hex string`,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// Support both regular (32-byte = 64 hex chars) and encrypted (64-byte = 128 hex chars) references
|
|
34
|
+
export const ReferenceSchema = z
|
|
35
|
+
.string()
|
|
36
|
+
.regex(/^[0-9a-fA-F]{64}$|^[0-9a-fA-F]{128}$/, {
|
|
37
|
+
message:
|
|
38
|
+
"Reference must be 64 hex chars (32 bytes) or 128 hex chars (64 bytes for encrypted)",
|
|
39
|
+
})
|
|
40
|
+
export const BatchIdSchema = hexString(64) // 32 bytes
|
|
41
|
+
export const AddressSchema = hexString(40) // 20 bytes
|
|
42
|
+
export const PrivateKeySchema = hexString(64) // 32 bytes
|
|
43
|
+
export const EncryptionKeySchema = hexString(64) // 32 bytes symmetric key
|
|
44
|
+
export const IdentifierSchema = hexString(64) // 32 bytes
|
|
45
|
+
export const SignatureSchema = hexString(130) // 65 bytes
|
|
46
|
+
export const TimestampSchema = z.preprocess(
|
|
47
|
+
(val) => (typeof val === "bigint" ? val.toString() : val),
|
|
48
|
+
z.union([z.number(), z.string()]),
|
|
49
|
+
)
|
|
50
|
+
export const FeedIndexSchema = z.preprocess(
|
|
51
|
+
(val) => (typeof val === "bigint" ? val.toString() : val),
|
|
52
|
+
z.union([z.number(), z.string()]),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
export type Reference = z.infer<typeof ReferenceSchema>
|
|
56
|
+
export type BatchId = z.infer<typeof BatchIdSchema>
|
|
57
|
+
export type Address = z.infer<typeof AddressSchema>
|
|
58
|
+
export type PrivateKey = z.infer<typeof PrivateKeySchema>
|
|
59
|
+
export type Identifier = z.infer<typeof IdentifierSchema>
|
|
60
|
+
export type Signature = z.infer<typeof SignatureSchema>
|
|
61
|
+
export type Timestamp = z.infer<typeof TimestampSchema>
|
|
62
|
+
export type FeedIndex = z.infer<typeof FeedIndexSchema>
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Primitive → bee-js Type Transforms (internal, for entity schemas)
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Schema for EthAddress - validates 40-char hex string, transforms to EthAddress
|
|
70
|
+
*/
|
|
71
|
+
const StoredEthAddress = z
|
|
72
|
+
.string()
|
|
73
|
+
.length(40)
|
|
74
|
+
.transform((s) => new EthAddress(s))
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Schema for BatchId - validates 64-char hex string, transforms to BatchId
|
|
78
|
+
*/
|
|
79
|
+
const StoredBatchId = z
|
|
80
|
+
.string()
|
|
81
|
+
.length(64)
|
|
82
|
+
.transform((s) => new BeeBatchId(s))
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Schema for PrivateKey - validates 64-char hex string, transforms to PrivateKey
|
|
86
|
+
*/
|
|
87
|
+
const StoredPrivateKey = z
|
|
88
|
+
.string()
|
|
89
|
+
.length(64)
|
|
90
|
+
.transform((s) => new BeePrivateKey(s))
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Schema for Bytes - validates number array, transforms to Bytes
|
|
94
|
+
*/
|
|
95
|
+
const StoredBytes = z
|
|
96
|
+
.array(z.number())
|
|
97
|
+
.transform((arr) => new Bytes(new Uint8Array(arr)))
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Account Schemas
|
|
101
|
+
// ============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Passkey Account Schema V1
|
|
105
|
+
*/
|
|
106
|
+
export const PasskeyAccountSchemaV1 = z.object({
|
|
107
|
+
id: StoredEthAddress,
|
|
108
|
+
name: z.string(),
|
|
109
|
+
createdAt: z.number(),
|
|
110
|
+
type: z.literal("passkey"),
|
|
111
|
+
credentialId: z.string(),
|
|
112
|
+
swarmEncryptionKey: z.string().length(64), // NEW: derived encryption key for Swarm data (64-char hex)
|
|
113
|
+
defaultPostageStampBatchID: StoredBatchId.optional(), // NEW: account default stamp
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Ethereum Account Schema V1
|
|
118
|
+
*/
|
|
119
|
+
export const EthereumAccountSchemaV1 = z.object({
|
|
120
|
+
id: StoredEthAddress,
|
|
121
|
+
name: z.string(),
|
|
122
|
+
createdAt: z.number(),
|
|
123
|
+
type: z.literal("ethereum"),
|
|
124
|
+
ethereumAddress: StoredEthAddress,
|
|
125
|
+
encryptedMasterKey: StoredBytes,
|
|
126
|
+
encryptionSalt: StoredBytes,
|
|
127
|
+
encryptedSecretSeed: StoredBytes, // Encrypted secret seed for later retrieval
|
|
128
|
+
swarmEncryptionKey: z.string().length(64), // NEW: derived encryption key for Swarm data (64-char hex)
|
|
129
|
+
defaultPostageStampBatchID: StoredBatchId.optional(), // NEW: account default stamp
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Agent Account Schema V1
|
|
134
|
+
* For automated testing and programmatic use with BIP39 seed phrases
|
|
135
|
+
* Seed phrase is NOT stored - must be re-entered on each authentication (like passkey)
|
|
136
|
+
*/
|
|
137
|
+
export const AgentAccountSchemaV1 = z.object({
|
|
138
|
+
id: StoredEthAddress,
|
|
139
|
+
name: z.string(),
|
|
140
|
+
createdAt: z.number(),
|
|
141
|
+
type: z.literal("agent"),
|
|
142
|
+
swarmEncryptionKey: z.string().length(64), // derived encryption key for Swarm data (64-char hex)
|
|
143
|
+
defaultPostageStampBatchID: StoredBatchId.optional(),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Account Schema V1 (discriminated union)
|
|
148
|
+
*/
|
|
149
|
+
export const AccountSchemaV1 = z.discriminatedUnion("type", [
|
|
150
|
+
PasskeyAccountSchemaV1,
|
|
151
|
+
EthereumAccountSchemaV1,
|
|
152
|
+
AgentAccountSchemaV1,
|
|
153
|
+
])
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Identity Schema
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Identity Schema V1
|
|
161
|
+
*/
|
|
162
|
+
export const IdentitySchemaV1 = z.object({
|
|
163
|
+
id: AddressSchema,
|
|
164
|
+
accountId: StoredEthAddress,
|
|
165
|
+
name: z.string(),
|
|
166
|
+
defaultPostageStampBatchID: StoredBatchId.optional(),
|
|
167
|
+
createdAt: z.number(),
|
|
168
|
+
settings: z
|
|
169
|
+
.object({
|
|
170
|
+
appSessionDuration: z.number().optional(),
|
|
171
|
+
})
|
|
172
|
+
.optional(),
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// Connected App Schema
|
|
177
|
+
// ============================================================================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Connected App Schema V1 (no transforms needed - all primitives)
|
|
181
|
+
*/
|
|
182
|
+
export const ConnectedAppSchemaV1 = z.object({
|
|
183
|
+
appUrl: z.string(),
|
|
184
|
+
appName: z.string(),
|
|
185
|
+
lastConnectedAt: z.number(),
|
|
186
|
+
identityId: z.string(),
|
|
187
|
+
appIcon: z.string().optional(),
|
|
188
|
+
appDescription: z.string().optional(),
|
|
189
|
+
connectedUntil: z.number().optional(),
|
|
190
|
+
appSecret: z.string().optional(),
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// ============================================================================
|
|
194
|
+
// Postage Stamp Schema
|
|
195
|
+
// ============================================================================
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Postage Stamp Schema V1
|
|
199
|
+
*/
|
|
200
|
+
export const PostageStampSchemaV1 = z.object({
|
|
201
|
+
accountId: z.string().length(40), // CHANGED: was identityId
|
|
202
|
+
batchID: StoredBatchId,
|
|
203
|
+
signerKey: StoredPrivateKey,
|
|
204
|
+
utilization: z.number(),
|
|
205
|
+
usable: z.boolean(),
|
|
206
|
+
depth: z.number(),
|
|
207
|
+
amount: z.string().transform((val) => BigInt(val)),
|
|
208
|
+
bucketDepth: z.number(),
|
|
209
|
+
blockNumber: z.number(),
|
|
210
|
+
immutableFlag: z.boolean(),
|
|
211
|
+
exists: z.boolean(),
|
|
212
|
+
batchTTL: z.number().optional(),
|
|
213
|
+
createdAt: z.number(),
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// ============================================================================
|
|
217
|
+
// Sync State Snapshot Schemas
|
|
218
|
+
// ============================================================================
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Account Metadata Schema V1
|
|
222
|
+
*/
|
|
223
|
+
export const AccountMetadataSchemaV1 = z.object({
|
|
224
|
+
accountName: z.string(),
|
|
225
|
+
defaultPostageStampBatchID: z.string().length(64).optional(), // BatchId hex string
|
|
226
|
+
createdAt: z.number(),
|
|
227
|
+
lastModified: z.number(),
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const ACCOUNT_STATE_SNAPSHOT_VERSION = 1
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Unified account state snapshot schema used by both file export and Swarm sync.
|
|
234
|
+
* Contains minimal metadata instead of the full Account object.
|
|
235
|
+
*/
|
|
236
|
+
export const AccountStateSnapshotSchemaV1 = z.object({
|
|
237
|
+
version: z.literal(ACCOUNT_STATE_SNAPSHOT_VERSION),
|
|
238
|
+
timestamp: z.number(),
|
|
239
|
+
accountId: z.string().length(40),
|
|
240
|
+
metadata: AccountMetadataSchemaV1,
|
|
241
|
+
identities: z.array(IdentitySchemaV1),
|
|
242
|
+
connectedApps: z.array(ConnectedAppSchemaV1),
|
|
243
|
+
postageStamps: z.array(PostageStampSchemaV1),
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Derived Types (guaranteed to match current schema version)
|
|
248
|
+
// ============================================================================
|
|
249
|
+
|
|
250
|
+
export type PasskeyAccount = z.infer<typeof PasskeyAccountSchemaV1>
|
|
251
|
+
export type EthereumAccount = z.infer<typeof EthereumAccountSchemaV1>
|
|
252
|
+
export type AgentAccount = z.infer<typeof AgentAccountSchemaV1>
|
|
253
|
+
export type Account = z.infer<typeof AccountSchemaV1>
|
|
254
|
+
export type Identity = z.infer<typeof IdentitySchemaV1>
|
|
255
|
+
export type ConnectedApp = z.infer<typeof ConnectedAppSchemaV1>
|
|
256
|
+
export type PostageStamp = z.infer<typeof PostageStampSchemaV1>
|
|
257
|
+
export type AccountMetadata = z.infer<typeof AccountMetadataSchemaV1>
|
|
258
|
+
export type AccountStateSnapshot = z.infer<typeof AccountStateSnapshotSchemaV1>
|
|
259
|
+
|
|
260
|
+
// ============================================================================
|
|
261
|
+
// Network Settings Schema
|
|
262
|
+
// ============================================================================
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Network Settings Schema V1
|
|
266
|
+
* Stores user-configurable network endpoints (Bee node and Gnosis RPC)
|
|
267
|
+
*/
|
|
268
|
+
export const NetworkSettingsSchemaV1 = z.object({
|
|
269
|
+
beeNodeUrl: z.string().url(),
|
|
270
|
+
gnosisRpcUrl: z.string().url(),
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
export type NetworkSettings = z.infer<typeof NetworkSettingsSchemaV1>
|
|
274
|
+
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// BroadcastChannel Message Schemas
|
|
277
|
+
// ============================================================================
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Bucket update entry for cross-tab synchronization
|
|
281
|
+
*/
|
|
282
|
+
export const BucketUpdateSchema = z.object({
|
|
283
|
+
index: z.number().int().min(0).max(65535),
|
|
284
|
+
value: z.number().int().min(0),
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Utilization update message sent via BroadcastChannel
|
|
289
|
+
*/
|
|
290
|
+
export const UtilizationUpdateMessageSchema = z.object({
|
|
291
|
+
type: z.literal("utilization-updated"),
|
|
292
|
+
batchId: z.string().length(64),
|
|
293
|
+
buckets: z.array(BucketUpdateSchema),
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
export type BucketUpdate = z.infer<typeof BucketUpdateSchema>
|
|
297
|
+
export type UtilizationUpdateMessage = z.infer<
|
|
298
|
+
typeof UtilizationUpdateMessageSchema
|
|
299
|
+
>
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debounced Utilization Uploader
|
|
3
|
+
*
|
|
4
|
+
* Batches multiple utilization updates together to minimize upload frequency.
|
|
5
|
+
* Uses a per-batch debounce mechanism with configurable delay.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { DirtyChunkTracker } from "../utils/batch-utilization"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default debounce delay in milliseconds
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_DEBOUNCE_DELAY_MS = 1000
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Pending upload task for a batch
|
|
17
|
+
*/
|
|
18
|
+
interface PendingUpload {
|
|
19
|
+
/** Dirty chunk tracker accumulating changes */
|
|
20
|
+
tracker: DirtyChunkTracker
|
|
21
|
+
|
|
22
|
+
/** Timer ID for debounce */
|
|
23
|
+
timerId: ReturnType<typeof setTimeout> | undefined
|
|
24
|
+
|
|
25
|
+
/** Upload function to execute */
|
|
26
|
+
uploadFn: () => Promise<void>
|
|
27
|
+
|
|
28
|
+
/** Promise resolvers for awaiting completion */
|
|
29
|
+
promise: { resolve: () => void; reject: (error: Error) => void }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Debounced uploader for batch utilization data
|
|
34
|
+
*
|
|
35
|
+
* Batches multiple updates within a time window and uploads only once.
|
|
36
|
+
* Each batch has its own independent debounce timer.
|
|
37
|
+
*/
|
|
38
|
+
export class DebouncedUtilizationUploader {
|
|
39
|
+
private pendingUploads = new Map<string, PendingUpload>()
|
|
40
|
+
private defaultDelay: number
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a new debounced uploader
|
|
44
|
+
* @param delay - Debounce delay in milliseconds (default: 1000ms = 1s)
|
|
45
|
+
*/
|
|
46
|
+
constructor(delay = DEFAULT_DEBOUNCE_DELAY_MS) {
|
|
47
|
+
this.defaultDelay = delay
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Schedule an upload for a batch (debounced)
|
|
52
|
+
*
|
|
53
|
+
* If multiple updates occur within the debounce window, they are merged
|
|
54
|
+
* and only one upload is performed.
|
|
55
|
+
*
|
|
56
|
+
* @param batchId - Batch ID (hex string)
|
|
57
|
+
* @param tracker - Dirty chunk tracker with current changes
|
|
58
|
+
* @param uploadFn - Function to execute upload
|
|
59
|
+
* @param delay - Optional custom delay for this upload
|
|
60
|
+
* @returns Promise that resolves when upload completes
|
|
61
|
+
*/
|
|
62
|
+
scheduleUpload(
|
|
63
|
+
batchId: string,
|
|
64
|
+
tracker: DirtyChunkTracker,
|
|
65
|
+
uploadFn: () => Promise<void>,
|
|
66
|
+
delay?: number,
|
|
67
|
+
): Promise<void> {
|
|
68
|
+
const actualDelay = delay ?? this.defaultDelay
|
|
69
|
+
|
|
70
|
+
// Cancel existing timer if present
|
|
71
|
+
const existing = this.pendingUploads.get(batchId)
|
|
72
|
+
if (existing?.timerId) {
|
|
73
|
+
clearTimeout(existing.timerId)
|
|
74
|
+
// Reject old promise
|
|
75
|
+
existing.promise.reject(new Error("Upload cancelled by new request"))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Merge with existing tracker or create new
|
|
79
|
+
const mergedTracker = existing?.tracker ?? tracker
|
|
80
|
+
|
|
81
|
+
// If we're merging with an existing tracker, copy dirty chunks
|
|
82
|
+
if (existing && existing.tracker !== tracker) {
|
|
83
|
+
for (const chunkIndex of tracker.getDirtyChunks()) {
|
|
84
|
+
mergedTracker.markDirty(chunkIndex * 1024) // Mark any bucket in the chunk
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Create new promise
|
|
89
|
+
return new Promise<void>((resolve, reject) => {
|
|
90
|
+
// Schedule new upload
|
|
91
|
+
const timerId = setTimeout(async () => {
|
|
92
|
+
try {
|
|
93
|
+
await uploadFn()
|
|
94
|
+
resolve()
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(
|
|
97
|
+
`[DebouncedUploader] Upload failed for batch ${batchId}:`,
|
|
98
|
+
error,
|
|
99
|
+
)
|
|
100
|
+
reject(error instanceof Error ? error : new Error(String(error)))
|
|
101
|
+
} finally {
|
|
102
|
+
// Remove from pending
|
|
103
|
+
this.pendingUploads.delete(batchId)
|
|
104
|
+
}
|
|
105
|
+
}, actualDelay)
|
|
106
|
+
|
|
107
|
+
// Store pending upload
|
|
108
|
+
this.pendingUploads.set(batchId, {
|
|
109
|
+
tracker: mergedTracker,
|
|
110
|
+
timerId,
|
|
111
|
+
uploadFn,
|
|
112
|
+
promise: { resolve, reject },
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Flush pending upload for a batch immediately (cancel debounce)
|
|
119
|
+
* @param batchId - Batch ID to flush
|
|
120
|
+
*/
|
|
121
|
+
async flush(batchId: string): Promise<void> {
|
|
122
|
+
const pending = this.pendingUploads.get(batchId)
|
|
123
|
+
if (!pending) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Cancel timer
|
|
128
|
+
if (pending.timerId) {
|
|
129
|
+
clearTimeout(pending.timerId)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Execute immediately
|
|
133
|
+
try {
|
|
134
|
+
await pending.uploadFn()
|
|
135
|
+
} finally {
|
|
136
|
+
this.pendingUploads.delete(batchId)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Flush all pending uploads immediately
|
|
142
|
+
*/
|
|
143
|
+
async flushAll(): Promise<void> {
|
|
144
|
+
const batchIds = Array.from(this.pendingUploads.keys())
|
|
145
|
+
|
|
146
|
+
await Promise.all(batchIds.map((batchId) => this.flush(batchId)))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Cancel pending upload for a batch (discard changes)
|
|
151
|
+
* @param batchId - Batch ID to cancel
|
|
152
|
+
*/
|
|
153
|
+
cancel(batchId: string): void {
|
|
154
|
+
const pending = this.pendingUploads.get(batchId)
|
|
155
|
+
if (!pending) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (pending.timerId) {
|
|
160
|
+
clearTimeout(pending.timerId)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.pendingUploads.delete(batchId)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Cancel all pending uploads (discard all changes)
|
|
168
|
+
*/
|
|
169
|
+
cancelAll(): void {
|
|
170
|
+
for (const pending of this.pendingUploads.values()) {
|
|
171
|
+
if (pending.timerId) {
|
|
172
|
+
clearTimeout(pending.timerId)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.pendingUploads.clear()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get count of pending uploads
|
|
181
|
+
*/
|
|
182
|
+
getPendingCount(): number {
|
|
183
|
+
return this.pendingUploads.size
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if a batch has a pending upload
|
|
188
|
+
*/
|
|
189
|
+
hasPending(batchId: string): boolean {
|
|
190
|
+
return this.pendingUploads.has(batchId)
|
|
191
|
+
}
|
|
192
|
+
}
|