shogun-core 3.3.1 → 3.3.2

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 (147) hide show
  1. package/dist/ship/examples/ephemeral-cli.js +234 -0
  2. package/dist/ship/examples/identity-cli.js +503 -0
  3. package/dist/ship/examples/messenger-cli.js +745 -0
  4. package/dist/ship/examples/stealth-cli.js +433 -0
  5. package/dist/ship/examples/storage-cli.js +615 -0
  6. package/dist/ship/examples/vault-cli.js +444 -0
  7. package/dist/ship/examples/wallet-cli.js +767 -0
  8. package/dist/ship/implementation/SHIP_00.js +478 -0
  9. package/dist/ship/implementation/SHIP_01.js +433 -0
  10. package/dist/ship/implementation/SHIP_02.js +1366 -0
  11. package/dist/ship/implementation/SHIP_03.js +855 -0
  12. package/dist/ship/implementation/SHIP_04.js +589 -0
  13. package/dist/ship/implementation/SHIP_05.js +1064 -0
  14. package/dist/ship/implementation/SHIP_06.js +350 -0
  15. package/dist/ship/implementation/SHIP_07.js +635 -0
  16. package/dist/ship/index.js +17 -0
  17. package/dist/ship/interfaces/ISHIP_00.js +135 -0
  18. package/dist/ship/interfaces/ISHIP_01.js +128 -0
  19. package/dist/ship/interfaces/ISHIP_02.js +57 -0
  20. package/dist/ship/interfaces/ISHIP_03.js +61 -0
  21. package/dist/ship/interfaces/ISHIP_04.js +62 -0
  22. package/dist/ship/interfaces/ISHIP_05.js +59 -0
  23. package/dist/ship/interfaces/ISHIP_06.js +144 -0
  24. package/dist/ship/interfaces/ISHIP_07.js +194 -0
  25. package/dist/types/ship/examples/ephemeral-cli.d.ts +13 -0
  26. package/dist/types/ship/examples/identity-cli.d.ts +40 -0
  27. package/dist/types/ship/examples/messenger-cli.d.ts +37 -0
  28. package/dist/types/ship/examples/stealth-cli.d.ts +31 -0
  29. package/dist/types/ship/examples/storage-cli.d.ts +48 -0
  30. package/dist/types/ship/examples/vault-cli.d.ts +13 -0
  31. package/dist/types/ship/examples/wallet-cli.d.ts +131 -0
  32. package/dist/types/ship/implementation/SHIP_00.d.ts +113 -0
  33. package/dist/types/ship/implementation/SHIP_01.d.ts +80 -0
  34. package/dist/types/ship/implementation/SHIP_02.d.ts +297 -0
  35. package/dist/types/ship/implementation/SHIP_03.d.ts +127 -0
  36. package/dist/types/ship/implementation/SHIP_04.d.ts +76 -0
  37. package/dist/types/ship/implementation/SHIP_05.d.ts +70 -0
  38. package/dist/types/ship/implementation/SHIP_06.d.ts +66 -0
  39. package/dist/types/ship/implementation/SHIP_07.d.ts +101 -0
  40. package/dist/types/ship/index.d.ts +14 -0
  41. package/dist/types/ship/interfaces/ISHIP_00.d.ts +410 -0
  42. package/dist/types/ship/interfaces/ISHIP_01.d.ts +343 -0
  43. package/dist/types/ship/interfaces/ISHIP_02.d.ts +470 -0
  44. package/dist/types/ship/interfaces/ISHIP_03.d.ts +295 -0
  45. package/dist/types/ship/interfaces/ISHIP_04.d.ts +245 -0
  46. package/dist/types/ship/interfaces/ISHIP_05.d.ts +234 -0
  47. package/dist/types/ship/interfaces/ISHIP_06.d.ts +370 -0
  48. package/dist/types/ship/interfaces/ISHIP_07.d.ts +522 -0
  49. package/package.json +1 -1
  50. /package/dist/{config → src/config}/simplified-config.js +0 -0
  51. /package/dist/{core.js → src/core.js} +0 -0
  52. /package/dist/{examples → src/examples}/api-test.js +0 -0
  53. /package/dist/{examples → src/examples}/simple-api-test.js +0 -0
  54. /package/dist/{gundb → src/gundb}/api.js +0 -0
  55. /package/dist/{gundb → src/gundb}/crypto.js +0 -0
  56. /package/dist/{gundb → src/gundb}/db.js +0 -0
  57. /package/dist/{gundb → src/gundb}/derive.js +0 -0
  58. /package/dist/{gundb → src/gundb}/errors.js +0 -0
  59. /package/dist/{gundb → src/gundb}/index.js +0 -0
  60. /package/dist/{gundb → src/gundb}/rxjs.js +0 -0
  61. /package/dist/{gundb → src/gundb}/types.js +0 -0
  62. /package/dist/{index.js → src/index.js} +0 -0
  63. /package/dist/{interfaces → src/interfaces}/common.js +0 -0
  64. /package/dist/{interfaces → src/interfaces}/events.js +0 -0
  65. /package/dist/{interfaces → src/interfaces}/plugin.js +0 -0
  66. /package/dist/{interfaces → src/interfaces}/shogun.js +0 -0
  67. /package/dist/{managers → src/managers}/AuthManager.js +0 -0
  68. /package/dist/{managers → src/managers}/CoreInitializer.js +0 -0
  69. /package/dist/{managers → src/managers}/EventManager.js +0 -0
  70. /package/dist/{managers → src/managers}/PluginManager.js +0 -0
  71. /package/dist/{migration-test.js → src/migration-test.js} +0 -0
  72. /package/dist/{plugins → src/plugins}/base.js +0 -0
  73. /package/dist/{plugins → src/plugins}/index.js +0 -0
  74. /package/dist/{plugins → src/plugins}/nostr/index.js +0 -0
  75. /package/dist/{plugins → src/plugins}/nostr/nostrConnector.js +0 -0
  76. /package/dist/{plugins → src/plugins}/nostr/nostrConnectorPlugin.js +0 -0
  77. /package/dist/{plugins → src/plugins}/nostr/nostrSigner.js +0 -0
  78. /package/dist/{plugins → src/plugins}/nostr/types.js +0 -0
  79. /package/dist/{plugins → src/plugins}/oauth/index.js +0 -0
  80. /package/dist/{plugins → src/plugins}/oauth/oauthConnector.js +0 -0
  81. /package/dist/{plugins → src/plugins}/oauth/oauthPlugin.js +0 -0
  82. /package/dist/{plugins → src/plugins}/oauth/types.js +0 -0
  83. /package/dist/{plugins → src/plugins}/web3/index.js +0 -0
  84. /package/dist/{plugins → src/plugins}/web3/types.js +0 -0
  85. /package/dist/{plugins → src/plugins}/web3/web3Connector.js +0 -0
  86. /package/dist/{plugins → src/plugins}/web3/web3ConnectorPlugin.js +0 -0
  87. /package/dist/{plugins → src/plugins}/web3/web3Signer.js +0 -0
  88. /package/dist/{plugins → src/plugins}/webauthn/index.js +0 -0
  89. /package/dist/{plugins → src/plugins}/webauthn/types.js +0 -0
  90. /package/dist/{plugins → src/plugins}/webauthn/webauthn.js +0 -0
  91. /package/dist/{plugins → src/plugins}/webauthn/webauthnPlugin.js +0 -0
  92. /package/dist/{plugins → src/plugins}/webauthn/webauthnSigner.js +0 -0
  93. /package/dist/{storage → src/storage}/storage.js +0 -0
  94. /package/dist/{types → src/types}/events.js +0 -0
  95. /package/dist/{types → src/types}/shogun.js +0 -0
  96. /package/dist/{utils → src/utils}/errorHandler.js +0 -0
  97. /package/dist/{utils → src/utils}/eventEmitter.js +0 -0
  98. /package/dist/{utils → src/utils}/validation.js +0 -0
  99. /package/dist/types/{config → src/config}/simplified-config.d.ts +0 -0
  100. /package/dist/types/{core.d.ts → src/core.d.ts} +0 -0
  101. /package/dist/types/{examples → src/examples}/api-test.d.ts +0 -0
  102. /package/dist/types/{examples → src/examples}/simple-api-test.d.ts +0 -0
  103. /package/dist/types/{gundb → src/gundb}/api.d.ts +0 -0
  104. /package/dist/types/{gundb → src/gundb}/crypto.d.ts +0 -0
  105. /package/dist/types/{gundb → src/gundb}/db.d.ts +0 -0
  106. /package/dist/types/{gundb → src/gundb}/derive.d.ts +0 -0
  107. /package/dist/types/{gundb → src/gundb}/errors.d.ts +0 -0
  108. /package/dist/types/{gundb → src/gundb}/index.d.ts +0 -0
  109. /package/dist/types/{gundb → src/gundb}/rxjs.d.ts +0 -0
  110. /package/dist/types/{gundb → src/gundb}/types.d.ts +0 -0
  111. /package/dist/types/{index.d.ts → src/index.d.ts} +0 -0
  112. /package/dist/types/{interfaces → src/interfaces}/common.d.ts +0 -0
  113. /package/dist/types/{interfaces → src/interfaces}/events.d.ts +0 -0
  114. /package/dist/types/{interfaces → src/interfaces}/plugin.d.ts +0 -0
  115. /package/dist/types/{interfaces → src/interfaces}/shogun.d.ts +0 -0
  116. /package/dist/types/{managers → src/managers}/AuthManager.d.ts +0 -0
  117. /package/dist/types/{managers → src/managers}/CoreInitializer.d.ts +0 -0
  118. /package/dist/types/{managers → src/managers}/EventManager.d.ts +0 -0
  119. /package/dist/types/{managers → src/managers}/PluginManager.d.ts +0 -0
  120. /package/dist/types/{migration-test.d.ts → src/migration-test.d.ts} +0 -0
  121. /package/dist/types/{plugins → src/plugins}/base.d.ts +0 -0
  122. /package/dist/types/{plugins → src/plugins}/index.d.ts +0 -0
  123. /package/dist/types/{plugins → src/plugins}/nostr/index.d.ts +0 -0
  124. /package/dist/types/{plugins → src/plugins}/nostr/nostrConnector.d.ts +0 -0
  125. /package/dist/types/{plugins → src/plugins}/nostr/nostrConnectorPlugin.d.ts +0 -0
  126. /package/dist/types/{plugins → src/plugins}/nostr/nostrSigner.d.ts +0 -0
  127. /package/dist/types/{plugins → src/plugins}/nostr/types.d.ts +0 -0
  128. /package/dist/types/{plugins → src/plugins}/oauth/index.d.ts +0 -0
  129. /package/dist/types/{plugins → src/plugins}/oauth/oauthConnector.d.ts +0 -0
  130. /package/dist/types/{plugins → src/plugins}/oauth/oauthPlugin.d.ts +0 -0
  131. /package/dist/types/{plugins → src/plugins}/oauth/types.d.ts +0 -0
  132. /package/dist/types/{plugins → src/plugins}/web3/index.d.ts +0 -0
  133. /package/dist/types/{plugins → src/plugins}/web3/types.d.ts +0 -0
  134. /package/dist/types/{plugins → src/plugins}/web3/web3Connector.d.ts +0 -0
  135. /package/dist/types/{plugins → src/plugins}/web3/web3ConnectorPlugin.d.ts +0 -0
  136. /package/dist/types/{plugins → src/plugins}/web3/web3Signer.d.ts +0 -0
  137. /package/dist/types/{plugins → src/plugins}/webauthn/index.d.ts +0 -0
  138. /package/dist/types/{plugins → src/plugins}/webauthn/types.d.ts +0 -0
  139. /package/dist/types/{plugins → src/plugins}/webauthn/webauthn.d.ts +0 -0
  140. /package/dist/types/{plugins → src/plugins}/webauthn/webauthnPlugin.d.ts +0 -0
  141. /package/dist/types/{plugins → src/plugins}/webauthn/webauthnSigner.d.ts +0 -0
  142. /package/dist/types/{storage → src/storage}/storage.d.ts +0 -0
  143. /package/dist/types/{types → src/types}/events.d.ts +0 -0
  144. /package/dist/types/{types → src/types}/shogun.d.ts +0 -0
  145. /package/dist/types/{utils → src/utils}/errorHandler.d.ts +0 -0
  146. /package/dist/types/{utils → src/utils}/eventEmitter.d.ts +0 -0
  147. /package/dist/types/{utils → src/utils}/validation.d.ts +0 -0
@@ -0,0 +1,635 @@
1
+ "use strict";
2
+ /**
3
+ * SHIP-07: Secure Vault Implementation
4
+ *
5
+ * Vault crittografato decentralizzato che dipende da SHIP-00 per l'identità.
6
+ *
7
+ * Dipendenze:
8
+ * - SHIP-00 (Identity & Authentication) - per gestione utenti e chiavi
9
+ * - GunDB - per storage decentralizzato P2P
10
+ * - SEA - per crittografia AES-256-GCM
11
+ *
12
+ * Ispirato a: https://github.com/draeder/gunsafe
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SHIP_07 = void 0;
16
+ // ============================================================================
17
+ // IMPLEMENTATION
18
+ // ============================================================================
19
+ /**
20
+ * SHIP-07 Reference Implementation
21
+ *
22
+ * Questa implementazione dipende da ISHIP_00 per tutte le operazioni di identità.
23
+ * Si concentra esclusivamente sulla logica del vault crittografato.
24
+ */
25
+ class SHIP_07 {
26
+ /**
27
+ * Constructor
28
+ * @param identity ISHIP_00 instance for identity operations
29
+ * @param vaultNodeName Optional custom vault node name
30
+ */
31
+ constructor(identity, vaultNodeName) {
32
+ this.initialized = false;
33
+ // Gun nodes
34
+ this.vaultNode = null;
35
+ this.recordsNode = null;
36
+ this.metadataNode = null;
37
+ if (!identity.isLoggedIn()) {
38
+ throw new Error("User must be authenticated via SHIP-00 before using SHIP-07");
39
+ }
40
+ this.identity = identity;
41
+ this.vaultNodeName = vaultNodeName || SHIP_07.DEFAULT_NODE_NAME;
42
+ console.log("✅ SHIP-07 initialized");
43
+ }
44
+ /**
45
+ * Get identity provider
46
+ */
47
+ getIdentity() {
48
+ return this.identity;
49
+ }
50
+ // ========================================================================
51
+ // INITIALIZATION
52
+ // ========================================================================
53
+ /**
54
+ * Initialize vault
55
+ */
56
+ async initialize() {
57
+ if (this.initialized) {
58
+ console.warn("⚠️ Vault already initialized");
59
+ return;
60
+ }
61
+ try {
62
+ // Get Gun instance from identity
63
+ const shogun = this.identity.getShogun();
64
+ if (!shogun || !shogun.db) {
65
+ throw new Error("Cannot access ShogunCore from identity");
66
+ }
67
+ const gun = shogun.db.gun;
68
+ if (!gun) {
69
+ throw new Error("Cannot access GunDB");
70
+ }
71
+ // Get user node
72
+ const userNode = gun.user();
73
+ if (!userNode || !userNode.is) {
74
+ throw new Error("User not authenticated in Gun");
75
+ }
76
+ // Setup vault nodes
77
+ this.vaultNode = userNode.get(this.vaultNodeName);
78
+ this.recordsNode = this.vaultNode.get("records");
79
+ this.metadataNode = this.vaultNode.get("metadata");
80
+ // Initialize metadata
81
+ const existingMetadata = await this.metadataNode.then();
82
+ if (!existingMetadata || !existingMetadata.version) {
83
+ await this.metadataNode.put({
84
+ version: SHIP_07.VAULT_VERSION,
85
+ created: Date.now().toString(),
86
+ recordCount: "0",
87
+ }).then();
88
+ console.log("📦 Vault metadata initialized");
89
+ }
90
+ else {
91
+ console.log("📦 Existing vault found");
92
+ }
93
+ this.initialized = true;
94
+ console.log("✅ Vault initialized successfully");
95
+ }
96
+ catch (error) {
97
+ console.error("❌ Error initializing vault:", error);
98
+ throw error;
99
+ }
100
+ }
101
+ /**
102
+ * Check if vault is initialized
103
+ */
104
+ isInitialized() {
105
+ return this.initialized;
106
+ }
107
+ // ========================================================================
108
+ // CRUD OPERATIONS
109
+ // ========================================================================
110
+ /**
111
+ * Store encrypted record in vault
112
+ */
113
+ async put(name, data, metadata) {
114
+ if (!this.initialized) {
115
+ return { success: false, error: "Vault not initialized" };
116
+ }
117
+ if (!name || name.trim() === "") {
118
+ return { success: false, error: "Record name cannot be empty" };
119
+ }
120
+ try {
121
+ // Get crypto and key pair
122
+ const shogun = this.identity.getShogun();
123
+ const crypto = shogun?.db?.crypto;
124
+ const pair = this.identity.getKeyPair();
125
+ if (!crypto || !pair) {
126
+ return { success: false, error: "Cannot access encryption" };
127
+ }
128
+ // Encrypt data
129
+ const dataString = JSON.stringify(data);
130
+ const encryptedData = await crypto.encrypt(dataString, pair.epriv);
131
+ // Encrypt metadata if provided
132
+ let encryptedMetadata;
133
+ if (metadata) {
134
+ const metadataString = JSON.stringify(metadata);
135
+ encryptedMetadata = await crypto.encrypt(metadataString, pair.epriv);
136
+ }
137
+ // Create encrypted record
138
+ const record = {
139
+ data: encryptedData,
140
+ created: Date.now().toString(),
141
+ updated: Date.now().toString(),
142
+ deleted: false,
143
+ metadata: encryptedMetadata,
144
+ };
145
+ // Store in vault
146
+ await this.recordsNode.get(name).put(record).then();
147
+ // Update vault metadata
148
+ await this.updateRecordCount();
149
+ console.log(`✅ Record stored: ${name}`);
150
+ return {
151
+ success: true,
152
+ recordName: name,
153
+ };
154
+ }
155
+ catch (error) {
156
+ console.error("❌ Error storing record:", error);
157
+ return {
158
+ success: false,
159
+ error: error.message,
160
+ };
161
+ }
162
+ }
163
+ /**
164
+ * Retrieve and decrypt record from vault
165
+ */
166
+ async get(name, options) {
167
+ if (!this.initialized) {
168
+ console.error("❌ Vault not initialized");
169
+ return null;
170
+ }
171
+ try {
172
+ // Retrieve encrypted record
173
+ const encryptedRecord = await this.recordsNode.get(name).then();
174
+ if (!encryptedRecord || !encryptedRecord.data) {
175
+ return null;
176
+ }
177
+ // Check if deleted (unless includeDeleted)
178
+ if (encryptedRecord.deleted && !options?.includeDeleted) {
179
+ return null;
180
+ }
181
+ // Get crypto and key pair
182
+ const shogun = this.identity.getShogun();
183
+ const crypto = shogun?.db?.crypto;
184
+ const pair = this.identity.getKeyPair();
185
+ if (!crypto || !pair) {
186
+ console.error("❌ Cannot access encryption");
187
+ return null;
188
+ }
189
+ // Decrypt data
190
+ const decryptedDataString = await crypto.decrypt(encryptedRecord.data, pair.epriv);
191
+ // Try to parse JSON, if it fails, use the string as-is
192
+ let decryptedData;
193
+ try {
194
+ decryptedData = JSON.parse(decryptedDataString);
195
+ }
196
+ catch (parseError) {
197
+ // If JSON.parse fails, the data is likely a plain string
198
+ // This can happen if the data was already a string value
199
+ decryptedData = decryptedDataString;
200
+ }
201
+ // Decrypt metadata if present
202
+ let decryptedMetadata;
203
+ if (encryptedRecord.metadata) {
204
+ try {
205
+ const metadataString = await crypto.decrypt(encryptedRecord.metadata, pair.epriv);
206
+ decryptedMetadata = JSON.parse(metadataString);
207
+ }
208
+ catch (error) {
209
+ // Metadata decryption failed, continue without it
210
+ console.warn("⚠️ Could not decrypt metadata");
211
+ }
212
+ }
213
+ // Create vault record
214
+ const vaultRecord = {
215
+ name,
216
+ data: decryptedData,
217
+ created: parseInt(encryptedRecord.created),
218
+ updated: parseInt(encryptedRecord.updated),
219
+ deleted: encryptedRecord.deleted,
220
+ metadata: decryptedMetadata,
221
+ };
222
+ return vaultRecord;
223
+ }
224
+ catch (error) {
225
+ console.error("❌ Error retrieving record:", error);
226
+ return null;
227
+ }
228
+ }
229
+ /**
230
+ * Delete record from vault (soft delete)
231
+ */
232
+ async delete(name) {
233
+ if (!this.initialized) {
234
+ return { success: false, error: "Vault not initialized" };
235
+ }
236
+ try {
237
+ if (name) {
238
+ // Delete specific record (soft delete)
239
+ const existingRecord = await this.recordsNode.get(name).then();
240
+ if (!existingRecord) {
241
+ return { success: false, error: `Record ${name} not found` };
242
+ }
243
+ // Mark as deleted
244
+ await this.recordsNode.get(name).get("deleted").put(true).then();
245
+ await this.recordsNode.get(name).get("updated").put(Date.now().toString()).then();
246
+ console.log(`🗑️ Record soft-deleted: ${name}`);
247
+ return {
248
+ success: true,
249
+ recordName: name,
250
+ };
251
+ }
252
+ else {
253
+ // Delete all records (soft delete)
254
+ const allRecords = await this.list({ includeDeleted: false });
255
+ let deletedCount = 0;
256
+ for (const recordName of allRecords) {
257
+ const result = await this.delete(recordName);
258
+ if (result.success) {
259
+ deletedCount++;
260
+ }
261
+ }
262
+ console.log(`🗑️ ${deletedCount} records soft-deleted`);
263
+ return {
264
+ success: true,
265
+ recordCount: deletedCount,
266
+ };
267
+ }
268
+ }
269
+ catch (error) {
270
+ console.error("❌ Error deleting record:", error);
271
+ return {
272
+ success: false,
273
+ error: error.message,
274
+ };
275
+ }
276
+ }
277
+ /**
278
+ * List all record names in vault
279
+ */
280
+ async list(options) {
281
+ if (!this.initialized) {
282
+ console.error("❌ Vault not initialized");
283
+ return [];
284
+ }
285
+ try {
286
+ return new Promise((resolve) => {
287
+ const recordNames = [];
288
+ this.recordsNode.map().once(async (record, key) => {
289
+ // Skip metadata
290
+ if (!record || typeof record !== "object" || key === "_") {
291
+ return;
292
+ }
293
+ // Skip deleted records (unless includeDeleted)
294
+ if (record.deleted && !options?.includeDeleted) {
295
+ return;
296
+ }
297
+ // Apply filters if provided
298
+ if (options?.filterByType || options?.filterByTag) {
299
+ try {
300
+ // Need to decrypt metadata to filter
301
+ const shogun = this.identity.getShogun();
302
+ const crypto = shogun?.db?.crypto;
303
+ const pair = this.identity.getKeyPair();
304
+ if (crypto && pair && record.metadata) {
305
+ const metadataString = await crypto.decrypt(record.metadata, pair.epriv);
306
+ const metadata = JSON.parse(metadataString);
307
+ // Filter by type
308
+ if (options.filterByType && metadata.type !== options.filterByType) {
309
+ return;
310
+ }
311
+ // Filter by tag
312
+ if (options.filterByTag) {
313
+ if (!metadata.tags || !metadata.tags.includes(options.filterByTag)) {
314
+ return;
315
+ }
316
+ }
317
+ }
318
+ }
319
+ catch (error) {
320
+ // Decryption failed, skip
321
+ return;
322
+ }
323
+ }
324
+ recordNames.push(key);
325
+ });
326
+ // Wait for Gun to return all records
327
+ setTimeout(() => {
328
+ // Sort if requested
329
+ if (options?.sortBy) {
330
+ // For now, just sort by name
331
+ recordNames.sort();
332
+ if (options.sortDesc) {
333
+ recordNames.reverse();
334
+ }
335
+ }
336
+ resolve(recordNames);
337
+ }, 1000);
338
+ });
339
+ }
340
+ catch (error) {
341
+ console.error("❌ Error listing records:", error);
342
+ return [];
343
+ }
344
+ }
345
+ /**
346
+ * Check if record exists
347
+ */
348
+ async exists(name) {
349
+ const record = await this.get(name);
350
+ return record !== null;
351
+ }
352
+ /**
353
+ * Update existing record
354
+ */
355
+ async update(name, data) {
356
+ if (!this.initialized) {
357
+ return { success: false, error: "Vault not initialized" };
358
+ }
359
+ try {
360
+ // Check if record exists
361
+ const existingRecord = await this.get(name);
362
+ if (!existingRecord) {
363
+ return { success: false, error: `Record ${name} not found` };
364
+ }
365
+ // Keep existing metadata
366
+ return await this.put(name, data, existingRecord.metadata);
367
+ }
368
+ catch (error) {
369
+ console.error("❌ Error updating record:", error);
370
+ return {
371
+ success: false,
372
+ error: error.message,
373
+ };
374
+ }
375
+ }
376
+ // ========================================================================
377
+ // BACKUP & RESTORE
378
+ // ========================================================================
379
+ /**
380
+ * Export entire vault (encrypted)
381
+ */
382
+ async export(password, options) {
383
+ if (!this.initialized) {
384
+ throw new Error("Vault not initialized");
385
+ }
386
+ try {
387
+ // Get all records
388
+ const recordNames = await this.list({
389
+ includeDeleted: options?.includeDeleted || false,
390
+ filterByTag: options?.filterByTag,
391
+ filterByType: options?.filterByType,
392
+ });
393
+ const records = {};
394
+ for (const name of recordNames) {
395
+ const record = await this.get(name, {
396
+ includeDeleted: options?.includeDeleted || false,
397
+ });
398
+ if (record) {
399
+ records[name] = record;
400
+ }
401
+ }
402
+ // Create export data
403
+ const exportData = {
404
+ version: SHIP_07.VAULT_VERSION,
405
+ exportedAt: Date.now(),
406
+ exportedBy: this.identity.getCurrentUser()?.pub,
407
+ recordCount: Object.keys(records).length,
408
+ records,
409
+ };
410
+ // Serialize to JSON
411
+ const jsonString = options?.pretty
412
+ ? JSON.stringify(exportData, null, 2)
413
+ : JSON.stringify(exportData);
414
+ // Optionally encrypt with password
415
+ if (password) {
416
+ const crypto = this.identity.shogun?.db?.crypto;
417
+ if (!crypto) {
418
+ throw new Error("Cannot access crypto");
419
+ }
420
+ const encryptedJson = await crypto.encrypt(jsonString, password);
421
+ const base64 = Buffer.from(encryptedJson).toString("base64");
422
+ console.log(`✅ Vault exported (encrypted, ${base64.length} chars)`);
423
+ return base64;
424
+ }
425
+ // Otherwise, return as base64
426
+ const base64 = Buffer.from(jsonString).toString("base64");
427
+ console.log(`✅ Vault exported (${base64.length} chars)`);
428
+ return base64;
429
+ }
430
+ catch (error) {
431
+ console.error("❌ Error exporting vault:", error);
432
+ throw error;
433
+ }
434
+ }
435
+ /**
436
+ * Import vault from backup
437
+ */
438
+ async import(backupData, password, options) {
439
+ if (!this.initialized) {
440
+ return { success: false, error: "Vault not initialized" };
441
+ }
442
+ try {
443
+ // Decode base64
444
+ let jsonString = Buffer.from(backupData, "base64").toString("utf-8");
445
+ // Decrypt if password provided
446
+ if (password) {
447
+ const crypto = this.identity.shogun?.db?.crypto;
448
+ if (!crypto) {
449
+ return { success: false, error: "Cannot access crypto" };
450
+ }
451
+ jsonString = await crypto.decrypt(jsonString, password);
452
+ }
453
+ // Parse JSON
454
+ const importData = JSON.parse(jsonString);
455
+ // Validate version
456
+ if (importData.version !== SHIP_07.VAULT_VERSION) {
457
+ console.warn(`⚠️ Version mismatch: ${importData.version} vs ${SHIP_07.VAULT_VERSION}`);
458
+ }
459
+ // Import records
460
+ let importedCount = 0;
461
+ let skippedCount = 0;
462
+ for (const [name, record] of Object.entries(importData.records)) {
463
+ const vaultRecord = record;
464
+ // Skip deleted records if requested
465
+ if (options?.skipDeleted && vaultRecord.deleted) {
466
+ skippedCount++;
467
+ continue;
468
+ }
469
+ // Check if exists (if merge mode)
470
+ const exists = await this.exists(name);
471
+ if (exists) {
472
+ if (options?.overwrite) {
473
+ // Overwrite existing
474
+ await this.put(name, vaultRecord.data, vaultRecord.metadata);
475
+ importedCount++;
476
+ }
477
+ else if (!options?.merge) {
478
+ // Skip if not merge and not overwrite
479
+ skippedCount++;
480
+ continue;
481
+ }
482
+ else {
483
+ // Merge mode: skip existing
484
+ skippedCount++;
485
+ continue;
486
+ }
487
+ }
488
+ else {
489
+ // Import new record
490
+ await this.put(name, vaultRecord.data, vaultRecord.metadata);
491
+ importedCount++;
492
+ }
493
+ }
494
+ console.log(`✅ Vault imported: ${importedCount} records (${skippedCount} skipped)`);
495
+ return {
496
+ success: true,
497
+ recordCount: importedCount,
498
+ };
499
+ }
500
+ catch (error) {
501
+ console.error("❌ Error importing vault:", error);
502
+ return {
503
+ success: false,
504
+ error: error.message,
505
+ };
506
+ }
507
+ }
508
+ // ========================================================================
509
+ // UTILITIES
510
+ // ========================================================================
511
+ /**
512
+ * Get vault statistics
513
+ */
514
+ async getStats() {
515
+ if (!this.initialized) {
516
+ throw new Error("Vault not initialized");
517
+ }
518
+ try {
519
+ const allRecords = await this.list({ includeDeleted: true });
520
+ const activeRecords = await this.list({ includeDeleted: false });
521
+ // Get metadata
522
+ const metadata = await this.metadataNode.then();
523
+ const stats = {
524
+ totalRecords: allRecords.length,
525
+ activeRecords: activeRecords.length,
526
+ deletedRecords: allRecords.length - activeRecords.length,
527
+ totalSize: 0, // TODO: Calculate actual size
528
+ created: metadata?.created ? parseInt(metadata.created) : Date.now(),
529
+ lastModified: Date.now(),
530
+ recordsByType: {},
531
+ };
532
+ // Count by type
533
+ for (const name of activeRecords) {
534
+ const record = await this.get(name);
535
+ if (record && record.metadata?.type) {
536
+ const type = record.metadata.type;
537
+ stats.recordsByType[type] = (stats.recordsByType[type] || 0) + 1;
538
+ }
539
+ }
540
+ return stats;
541
+ }
542
+ catch (error) {
543
+ console.error("❌ Error getting stats:", error);
544
+ throw error;
545
+ }
546
+ }
547
+ /**
548
+ * Clear all records (soft delete all)
549
+ */
550
+ async clear() {
551
+ return await this.delete(); // Delete without name = delete all
552
+ }
553
+ /**
554
+ * Compact vault (remove deleted records permanently)
555
+ */
556
+ async compact() {
557
+ if (!this.initialized) {
558
+ return { success: false, error: "Vault not initialized" };
559
+ }
560
+ try {
561
+ // Get all deleted records
562
+ const allRecords = await this.list({ includeDeleted: true });
563
+ let compactedCount = 0;
564
+ for (const name of allRecords) {
565
+ const record = await this.get(name, { includeDeleted: true });
566
+ if (record && record.deleted) {
567
+ // Permanently remove
568
+ await this.recordsNode.get(name).put(null).then();
569
+ compactedCount++;
570
+ }
571
+ }
572
+ // Update metadata
573
+ await this.updateRecordCount();
574
+ console.log(`✅ Vault compacted: ${compactedCount} records permanently removed`);
575
+ return {
576
+ success: true,
577
+ recordCount: compactedCount,
578
+ };
579
+ }
580
+ catch (error) {
581
+ console.error("❌ Error compacting vault:", error);
582
+ return {
583
+ success: false,
584
+ error: error.message,
585
+ };
586
+ }
587
+ }
588
+ /**
589
+ * Search records by content
590
+ */
591
+ async search(query) {
592
+ if (!this.initialized) {
593
+ return [];
594
+ }
595
+ try {
596
+ const allRecords = await this.list({ includeDeleted: false });
597
+ const matches = [];
598
+ for (const name of allRecords) {
599
+ const record = await this.get(name);
600
+ if (record) {
601
+ // Search in data (converted to string)
602
+ const dataString = JSON.stringify(record.data).toLowerCase();
603
+ const queryLower = query.toLowerCase();
604
+ if (dataString.includes(queryLower) || name.toLowerCase().includes(queryLower)) {
605
+ matches.push(name);
606
+ }
607
+ }
608
+ }
609
+ return matches;
610
+ }
611
+ catch (error) {
612
+ console.error("❌ Error searching records:", error);
613
+ return [];
614
+ }
615
+ }
616
+ // ========================================================================
617
+ // PRIVATE HELPERS
618
+ // ========================================================================
619
+ /**
620
+ * Update record count in metadata
621
+ */
622
+ async updateRecordCount() {
623
+ try {
624
+ const activeRecords = await this.list({ includeDeleted: false });
625
+ await this.metadataNode.get("recordCount").put(activeRecords.length.toString()).then();
626
+ }
627
+ catch (error) {
628
+ console.error("❌ Error updating record count:", error);
629
+ }
630
+ }
631
+ }
632
+ exports.SHIP_07 = SHIP_07;
633
+ // Constants
634
+ SHIP_07.VAULT_VERSION = "1.0.0";
635
+ SHIP_07.DEFAULT_NODE_NAME = "vault";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SHIP_06 = exports.SHIP_05 = exports.SHIP_04 = exports.SHIP_03 = exports.SHIP_02 = exports.SHIP_01 = exports.SHIP_00 = void 0;
4
+ var SHIP_00_1 = require("../ship/implementation/SHIP_00");
5
+ Object.defineProperty(exports, "SHIP_00", { enumerable: true, get: function () { return SHIP_00_1.SHIP_00; } });
6
+ var SHIP_01_1 = require("../ship/implementation/SHIP_01");
7
+ Object.defineProperty(exports, "SHIP_01", { enumerable: true, get: function () { return SHIP_01_1.SHIP_01; } });
8
+ var SHIP_02_1 = require("../ship/implementation/SHIP_02");
9
+ Object.defineProperty(exports, "SHIP_02", { enumerable: true, get: function () { return SHIP_02_1.SHIP_02; } });
10
+ var SHIP_03_1 = require("../ship/implementation/SHIP_03");
11
+ Object.defineProperty(exports, "SHIP_03", { enumerable: true, get: function () { return SHIP_03_1.SHIP_03; } });
12
+ var SHIP_04_1 = require("../ship/implementation/SHIP_04");
13
+ Object.defineProperty(exports, "SHIP_04", { enumerable: true, get: function () { return SHIP_04_1.SHIP_04; } });
14
+ var SHIP_05_1 = require("../ship/implementation/SHIP_05");
15
+ Object.defineProperty(exports, "SHIP_05", { enumerable: true, get: function () { return SHIP_05_1.SHIP_05; } });
16
+ var SHIP_06_1 = require("../ship/implementation/SHIP_06");
17
+ Object.defineProperty(exports, "SHIP_06", { enumerable: true, get: function () { return SHIP_06_1.SHIP_06; } });