@savestate/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +9 -0
  2. package/dist/cli.js +42 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/config.d.ts.map +1 -1
  5. package/dist/commands/config.js +40 -9
  6. package/dist/commands/config.js.map +1 -1
  7. package/dist/commands/login.d.ts +13 -0
  8. package/dist/commands/login.d.ts.map +1 -0
  9. package/dist/commands/login.js +111 -0
  10. package/dist/commands/login.js.map +1 -0
  11. package/dist/commands/migrate.d.ts +21 -0
  12. package/dist/commands/migrate.d.ts.map +1 -0
  13. package/dist/commands/migrate.js +218 -0
  14. package/dist/commands/migrate.js.map +1 -0
  15. package/dist/commands/schedule.d.ts +14 -0
  16. package/dist/commands/schedule.d.ts.map +1 -0
  17. package/dist/commands/schedule.js +294 -0
  18. package/dist/commands/schedule.js.map +1 -0
  19. package/dist/commands/snapshot.d.ts +1 -0
  20. package/dist/commands/snapshot.d.ts.map +1 -1
  21. package/dist/commands/snapshot.js +14 -4
  22. package/dist/commands/snapshot.js.map +1 -1
  23. package/dist/incremental.d.ts +137 -0
  24. package/dist/incremental.d.ts.map +1 -0
  25. package/dist/incremental.js +289 -0
  26. package/dist/incremental.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/restore.d.ts.map +1 -1
  32. package/dist/restore.js +7 -2
  33. package/dist/restore.js.map +1 -1
  34. package/dist/snapshot.d.ts +17 -1
  35. package/dist/snapshot.d.ts.map +1 -1
  36. package/dist/snapshot.js +82 -17
  37. package/dist/snapshot.js.map +1 -1
  38. package/dist/storage/index.d.ts +2 -0
  39. package/dist/storage/index.d.ts.map +1 -1
  40. package/dist/storage/index.js +1 -0
  41. package/dist/storage/index.js.map +1 -1
  42. package/dist/storage/resolve.d.ts.map +1 -1
  43. package/dist/storage/resolve.js +19 -4
  44. package/dist/storage/resolve.js.map +1 -1
  45. package/dist/storage/s3.d.ts +64 -0
  46. package/dist/storage/s3.d.ts.map +1 -0
  47. package/dist/storage/s3.js +360 -0
  48. package/dist/storage/s3.js.map +1 -0
  49. package/package.json +4 -1
@@ -0,0 +1,289 @@
1
+ /**
2
+ * SaveState Incremental Snapshots
3
+ *
4
+ * Instead of storing a full copy every time, incremental snapshots
5
+ * only capture what changed since the parent snapshot.
6
+ *
7
+ * How it works:
8
+ * 1. Hash every file in the current snapshot (SHA-256)
9
+ * 2. Load the parent snapshot's content hashes
10
+ * 3. Compare: identify added, modified, removed files
11
+ * 4. Store only the delta (changed + added files)
12
+ * 5. On restore: reconstruct by applying deltas from base → current
13
+ *
14
+ * The delta manifest tracks the relationship:
15
+ * ```
16
+ * base (full) → delta-1 → delta-2 → delta-3 (current)
17
+ * ```
18
+ *
19
+ * Every Nth snapshot (configurable, default 10) forces a full snapshot
20
+ * to keep the chain short and restore fast.
21
+ */
22
+ import { createHash } from 'node:crypto';
23
+ import { unpackFromArchive } from './format.js';
24
+ import { decrypt } from './encryption.js';
25
+ import { findEntry } from './index-file.js';
26
+ // ─── Configuration ───────────────────────────────────────────
27
+ /** Max chain depth before forcing a full snapshot */
28
+ export const MAX_CHAIN_DEPTH = 10;
29
+ /** If more than this fraction of files changed, just do a full snapshot */
30
+ export const FULL_SNAPSHOT_THRESHOLD = 0.7;
31
+ // ─── Content Hashing ─────────────────────────────────────────
32
+ /**
33
+ * Compute SHA-256 hashes for every file in a packed snapshot.
34
+ */
35
+ export function computeContentHashes(files) {
36
+ const hashes = {};
37
+ for (const [path, data] of files) {
38
+ hashes[path] = createHash('sha256').update(data).digest('hex');
39
+ }
40
+ // Root hash = hash of all sorted path:hash pairs
41
+ const sortedPairs = Object.entries(hashes)
42
+ .sort(([a], [b]) => a.localeCompare(b))
43
+ .map(([p, h]) => `${p}:${h}`)
44
+ .join('\n');
45
+ const rootHash = createHash('sha256').update(sortedPairs).digest('hex');
46
+ return {
47
+ files: hashes,
48
+ count: Object.keys(hashes).length,
49
+ rootHash,
50
+ };
51
+ }
52
+ // ─── Delta Computation ───────────────────────────────────────
53
+ /**
54
+ * Compute the delta between the current snapshot files and a parent's content hashes.
55
+ *
56
+ * @param currentFiles - The full file map of the current snapshot
57
+ * @param parentHashes - Content hashes from the parent snapshot
58
+ * @param parentId - ID of the parent snapshot
59
+ * @param baseId - ID of the base (full) snapshot
60
+ * @param chainDepth - Current position in the chain
61
+ * @returns The computed delta and changed files
62
+ */
63
+ export function computeDelta(currentFiles, parentHashes, parentId, baseId, chainDepth) {
64
+ const currentHashes = computeContentHashes(currentFiles);
65
+ const changedFiles = new Map();
66
+ const entries = [];
67
+ let added = 0;
68
+ let modified = 0;
69
+ let removed = 0;
70
+ let unchanged = 0;
71
+ let changedBytes = 0;
72
+ let totalBytes = 0;
73
+ // Check each current file against parent
74
+ for (const [path, data] of currentFiles) {
75
+ totalBytes += data.length;
76
+ const currentHash = currentHashes.files[path];
77
+ const parentHash = parentHashes.files[path];
78
+ if (!parentHash) {
79
+ // New file
80
+ entries.push({ path, type: 'added', hash: currentHash, size: data.length });
81
+ changedFiles.set(path, data);
82
+ changedBytes += data.length;
83
+ added++;
84
+ }
85
+ else if (currentHash !== parentHash) {
86
+ // Modified file
87
+ entries.push({ path, type: 'modified', hash: currentHash, size: data.length });
88
+ changedFiles.set(path, data);
89
+ changedBytes += data.length;
90
+ modified++;
91
+ }
92
+ else {
93
+ unchanged++;
94
+ }
95
+ }
96
+ // Check for removed files (in parent but not in current)
97
+ for (const path of Object.keys(parentHashes.files)) {
98
+ if (!currentFiles.has(path)) {
99
+ entries.push({ path, type: 'removed' });
100
+ removed++;
101
+ }
102
+ }
103
+ const totalFiles = currentFiles.size;
104
+ const changedCount = added + modified + removed;
105
+ const changeRatio = totalFiles > 0 ? changedCount / totalFiles : 0;
106
+ // Should we force a full snapshot?
107
+ const shouldForceFull = chainDepth >= MAX_CHAIN_DEPTH || changeRatio >= FULL_SNAPSHOT_THRESHOLD;
108
+ const delta = {
109
+ parentId,
110
+ baseId,
111
+ chainDepth,
112
+ resultHashes: currentHashes,
113
+ entries,
114
+ stats: {
115
+ added,
116
+ modified,
117
+ removed,
118
+ unchanged,
119
+ totalFiles,
120
+ bytesSaved: totalBytes - changedBytes,
121
+ },
122
+ };
123
+ return { delta, changedFiles, shouldForceFull };
124
+ }
125
+ // ─── Packing Incremental Archives ────────────────────────────
126
+ /**
127
+ * Pack an incremental snapshot (delta only).
128
+ * The archive contains:
129
+ * - manifest.json (with parent reference)
130
+ * - meta/delta-manifest.json (the delta details)
131
+ * - meta/snapshot-chain.json (chain info)
132
+ * - Only the changed/added files (same paths as full snapshot)
133
+ */
134
+ export function packDelta(snapshot, delta, changedFiles) {
135
+ const files = new Map();
136
+ // Always include manifest and meta
137
+ files.set('manifest.json', Buffer.from(JSON.stringify(snapshot.manifest, null, 2)));
138
+ files.set('meta/delta-manifest.json', Buffer.from(JSON.stringify(delta, null, 2)));
139
+ files.set('meta/platform.json', Buffer.from(JSON.stringify(snapshot.platform, null, 2)));
140
+ files.set('meta/snapshot-chain.json', Buffer.from(JSON.stringify(snapshot.chain, null, 2)));
141
+ files.set('meta/restore-hints.json', Buffer.from(JSON.stringify(snapshot.restoreHints, null, 2)));
142
+ // Include only changed/added files
143
+ for (const [path, data] of changedFiles) {
144
+ files.set(path, data);
145
+ }
146
+ return files;
147
+ }
148
+ // ─── Reconstruction ──────────────────────────────────────────
149
+ /**
150
+ * Reconstruct a full snapshot by walking the chain from base to current.
151
+ *
152
+ * Starting from the base (full) snapshot, applies each delta in order:
153
+ * 1. Load base snapshot → full file map
154
+ * 2. For each delta in the chain:
155
+ * a. Add/overwrite files from the delta
156
+ * b. Remove files marked as 'removed'
157
+ * 3. Return the reconstructed full file map
158
+ *
159
+ * @param snapshotId - The target snapshot to reconstruct
160
+ * @param storage - Storage backend
161
+ * @param passphrase - Decryption passphrase
162
+ * @returns Reconstructed full file map
163
+ */
164
+ export async function reconstructFromChain(snapshotId, storage, passphrase) {
165
+ // Build the chain: walk backwards from snapshotId to find the base
166
+ const chain = await buildChain(snapshotId, storage, passphrase);
167
+ if (chain.length === 0) {
168
+ throw new Error(`Could not build snapshot chain for ${snapshotId}`);
169
+ }
170
+ // chain[0] is the base (full) snapshot, chain[n-1] is the target
171
+ // Start with the base's full file map
172
+ const baseArchive = chain[0];
173
+ const files = new Map(baseArchive.files);
174
+ // Apply each delta in order
175
+ for (let i = 1; i < chain.length; i++) {
176
+ const { files: deltaFiles, delta } = chain[i];
177
+ if (!delta) {
178
+ // This is a full snapshot in the middle of the chain (shouldn't happen normally)
179
+ files.clear();
180
+ for (const [path, data] of deltaFiles) {
181
+ files.set(path, data);
182
+ }
183
+ continue;
184
+ }
185
+ // Apply additions and modifications
186
+ for (const [path, data] of deltaFiles) {
187
+ // Skip meta files — they're delta-specific
188
+ if (path === 'meta/delta-manifest.json')
189
+ continue;
190
+ files.set(path, data);
191
+ }
192
+ // Apply removals
193
+ for (const entry of delta.entries) {
194
+ if (entry.type === 'removed') {
195
+ files.delete(entry.path);
196
+ }
197
+ }
198
+ }
199
+ return files;
200
+ }
201
+ /**
202
+ * Build the full chain from base to the given snapshot.
203
+ * Returns an ordered array: [base, delta1, delta2, ..., target]
204
+ */
205
+ async function buildChain(snapshotId, storage, passphrase) {
206
+ const links = [];
207
+ let currentId = snapshotId;
208
+ const visited = new Set();
209
+ // Walk backwards collecting snapshots
210
+ while (currentId && !visited.has(currentId)) {
211
+ visited.add(currentId);
212
+ const entry = await findEntry(currentId);
213
+ if (!entry) {
214
+ throw new Error(`Snapshot not found in index: ${currentId}`);
215
+ }
216
+ const encrypted = await storage.get(entry.filename);
217
+ const archive = await decrypt(encrypted, passphrase);
218
+ const files = await unpackFromArchive(archive);
219
+ // Check if this is a delta or full snapshot
220
+ const deltaManifestBuf = files.get('meta/delta-manifest.json');
221
+ let delta;
222
+ if (deltaManifestBuf) {
223
+ delta = JSON.parse(deltaManifestBuf.toString('utf-8'));
224
+ links.unshift({ id: currentId, files, delta });
225
+ currentId = delta.parentId;
226
+ }
227
+ else {
228
+ // Full snapshot — this is the base
229
+ links.unshift({ id: currentId, files, delta: undefined });
230
+ currentId = undefined; // Stop walking
231
+ }
232
+ }
233
+ return links;
234
+ }
235
+ // ─── Helpers ─────────────────────────────────────────────────
236
+ /**
237
+ * Get content hashes for the latest snapshot (used as parent for incremental).
238
+ * Returns null if no previous snapshots exist.
239
+ */
240
+ export async function getParentHashes(storage, passphrase) {
241
+ const { getLatestEntry } = await import('./index-file.js');
242
+ const latest = await getLatestEntry();
243
+ if (!latest)
244
+ return null;
245
+ try {
246
+ const encrypted = await storage.get(latest.filename);
247
+ const archive = await decrypt(encrypted, passphrase);
248
+ const files = await unpackFromArchive(archive);
249
+ // Check if the latest is itself a delta
250
+ const deltaManifestBuf = files.get('meta/delta-manifest.json');
251
+ if (deltaManifestBuf) {
252
+ const delta = JSON.parse(deltaManifestBuf.toString('utf-8'));
253
+ return {
254
+ hashes: delta.resultHashes,
255
+ parentId: latest.id,
256
+ baseId: delta.baseId,
257
+ chainDepth: delta.chainDepth + 1,
258
+ };
259
+ }
260
+ // Full snapshot — compute hashes from the file map
261
+ const hashes = computeContentHashes(files);
262
+ return {
263
+ hashes,
264
+ parentId: latest.id,
265
+ baseId: latest.id,
266
+ chainDepth: 1,
267
+ };
268
+ }
269
+ catch {
270
+ // Can't load parent — force full snapshot
271
+ return null;
272
+ }
273
+ }
274
+ /**
275
+ * Check if a snapshot is incremental (has a delta manifest).
276
+ */
277
+ export function isIncremental(files) {
278
+ return files.has('meta/delta-manifest.json');
279
+ }
280
+ /**
281
+ * Get the delta manifest from an unpacked archive, if present.
282
+ */
283
+ export function getDeltaManifest(files) {
284
+ const buf = files.get('meta/delta-manifest.json');
285
+ if (!buf)
286
+ return null;
287
+ return JSON.parse(buf.toString('utf-8'));
288
+ }
289
+ //# sourceMappingURL=incremental.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental.js","sourceRoot":"","sources":["../src/incremental.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,8 @@ export { encrypt, decrypt, verify } from './encryption.js';
8
8
  export { packSnapshot, unpackSnapshot, computeChecksum, generateSnapshotId, snapshotFilename, SAF_EXTENSION, SAF_VERSION, } from './format.js';
9
9
  export { loadConfig, saveConfig, initializeProject, isInitialized, defaultConfig, SAVESTATE_DIR, GLOBAL_SAVESTATE_DIR, } from './config.js';
10
10
  export { createSnapshot, getLatestSnapshotId } from './snapshot.js';
11
+ export { computeContentHashes, computeDelta, packDelta, reconstructFromChain, getParentHashes, isIncremental, getDeltaManifest, MAX_CHAIN_DEPTH, FULL_SNAPSHOT_THRESHOLD, } from './incremental.js';
12
+ export type { ContentHashes, DeltaEntry, DeltaManifest, DeltaResult, } from './incremental.js';
11
13
  export { restoreSnapshot, validateSnapshot } from './restore.js';
12
14
  export { searchSnapshots, scoreMatch } from './search.js';
13
15
  export { LocalStorageBackend } from './storage/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,OAAO,EACP,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG3D,OAAO,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,aAAa,EACb,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGjE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,OAAO,EACP,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG3D,OAAO,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,aAAa,EACb,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGjE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@ export { packSnapshot, unpackSnapshot, computeChecksum, generateSnapshotId, snap
11
11
  export { loadConfig, saveConfig, initializeProject, isInitialized, defaultConfig, SAVESTATE_DIR, GLOBAL_SAVESTATE_DIR, } from './config.js';
12
12
  // Snapshot
13
13
  export { createSnapshot, getLatestSnapshotId } from './snapshot.js';
14
+ // Incremental
15
+ export { computeContentHashes, computeDelta, packDelta, reconstructFromChain, getParentHashes, isIncremental, getDeltaManifest, MAX_CHAIN_DEPTH, FULL_SNAPSHOT_THRESHOLD, } from './incremental.js';
14
16
  // Restore
15
17
  export { restoreSnapshot, validateSnapshot } from './restore.js';
16
18
  // Search
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE3D,SAAS;AACT,OAAO,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,SAAS;AACT,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,aAAa,EACb,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpE,UAAU;AACV,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjE,SAAS;AACT,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,UAAU;AACV,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,WAAW;AACX,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE3D,SAAS;AACT,OAAO,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,SAAS;AACT,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,aAAa,EACb,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpE,cAAc;AACd,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAQ1B,UAAU;AACV,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjE,SAAS;AACT,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,UAAU;AACV,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,WAAW;AACX,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../src/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKpE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,CAAC,UAAU,GAAG,QAAQ,GAAG,eAAe,CAAC,EAAE,CAAC;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACA,OAAO,CAAC,aAAa,CAAC,CAwExB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC,CAYD"}
1
+ {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../src/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMpE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,CAAC,UAAU,GAAG,QAAQ,GAAG,eAAe,CAAC,EAAE,CAAC;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACA,OAAO,CAAC,aAAa,CAAC,CA8ExB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC,CAYD"}
package/dist/restore.js CHANGED
@@ -14,6 +14,7 @@
14
14
  import { unpackFromArchive, unpackSnapshot, computeChecksum, snapshotFilename } from './format.js';
15
15
  import { decrypt } from './encryption.js';
16
16
  import { findEntry, getLatestEntry } from './index-file.js';
17
+ import { isIncremental, reconstructFromChain } from './incremental.js';
17
18
  /**
18
19
  * Restore from a snapshot.
19
20
  *
@@ -67,9 +68,13 @@ export async function restoreSnapshot(snapshotId, adapter, storage, passphrase,
67
68
  throw err;
68
69
  }
69
70
  // Step 4: Unpack SAF
70
- const fileMap = await unpackFromArchive(archive);
71
+ let fileMap = await unpackFromArchive(archive);
72
+ // Step 4b: If incremental, reconstruct full state from chain
73
+ if (isIncremental(fileMap)) {
74
+ fileMap = await reconstructFromChain(resolvedId, storage, passphrase);
75
+ }
71
76
  const snapshot = unpackSnapshot(fileMap);
72
- // Verify integrity
77
+ // Verify integrity (for full snapshots)
73
78
  const expectedChecksum = snapshot.manifest.checksum;
74
79
  if (expectedChecksum) {
75
80
  const actualChecksum = computeChecksum(archive);
@@ -1 +1 @@
1
- {"version":3,"file":"restore.js","sourceRoot":"","sources":["../src/restore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../src/restore.ts"],"names":[],"mappings":"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"}
@@ -21,20 +21,36 @@ export interface CreateSnapshotResult {
21
21
  archiveSize: number;
22
22
  encryptedSize: number;
23
23
  fileCount: number;
24
+ /** Whether this is an incremental (delta) snapshot */
25
+ incremental: boolean;
26
+ /** Delta stats (only present for incremental snapshots) */
27
+ delta?: {
28
+ added: number;
29
+ modified: number;
30
+ removed: number;
31
+ unchanged: number;
32
+ bytesSaved: number;
33
+ chainDepth: number;
34
+ };
24
35
  }
25
36
  /**
26
37
  * Create a new snapshot by extracting state from an adapter.
27
38
  *
39
+ * Automatically uses incremental snapshots when a parent exists,
40
+ * unless `full` is explicitly requested or the chain is too deep.
41
+ *
28
42
  * @param adapter - Platform adapter to extract state from
29
43
  * @param storage - Storage backend to write to
30
44
  * @param passphrase - Encryption passphrase
31
- * @param options - Snapshot options (label, tags, etc.)
45
+ * @param options - Snapshot options (label, tags, full, etc.)
32
46
  * @returns Details about the created snapshot
33
47
  */
34
48
  export declare function createSnapshot(adapter: Adapter, storage: StorageBackend, passphrase: string, options?: {
35
49
  label?: string;
36
50
  tags?: string[];
37
51
  parentId?: string;
52
+ /** Force a full snapshot (skip incremental) */
53
+ full?: boolean;
38
54
  }): Promise<CreateSnapshotResult>;
39
55
  /**
40
56
  * Get the latest snapshot ID from the index.
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAmB,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAYrF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,oBAAoB,CAAC,CAmE/B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIlE"}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAmB,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAmBrF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,WAAW,EAAE,OAAO,CAAC;IACrB,2DAA2D;IAC3D,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GACA,OAAO,CAAC,oBAAoB,CAAC,CAkI/B;AAYD;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIlE"}
package/dist/snapshot.js CHANGED
@@ -17,13 +17,17 @@
17
17
  import { generateSnapshotId, SAF_VERSION, packSnapshot, packToArchive, computeChecksum, snapshotFilename, } from './format.js';
18
18
  import { encrypt } from './encryption.js';
19
19
  import { addToIndex } from './index-file.js';
20
+ import { getParentHashes, computeDelta, packDelta, } from './incremental.js';
20
21
  /**
21
22
  * Create a new snapshot by extracting state from an adapter.
22
23
  *
24
+ * Automatically uses incremental snapshots when a parent exists,
25
+ * unless `full` is explicitly requested or the chain is too deep.
26
+ *
23
27
  * @param adapter - Platform adapter to extract state from
24
28
  * @param storage - Storage backend to write to
25
29
  * @param passphrase - Encryption passphrase
26
- * @param options - Snapshot options (label, tags, etc.)
30
+ * @param options - Snapshot options (label, tags, full, etc.)
27
31
  * @returns Details about the created snapshot
28
32
  */
29
33
  export async function createSnapshot(adapter, storage, passphrase, options) {
@@ -32,6 +36,31 @@ export async function createSnapshot(adapter, storage, passphrase, options) {
32
36
  // Step 2: Enrich manifest
33
37
  const snapshotId = generateSnapshotId();
34
38
  const now = new Date().toISOString();
39
+ // Step 3: Check for incremental opportunity
40
+ let parentInfo = null;
41
+ if (!options?.full) {
42
+ try {
43
+ parentInfo = await getParentHashes(storage, passphrase);
44
+ }
45
+ catch {
46
+ // Can't load parent — proceed with full snapshot
47
+ }
48
+ }
49
+ // Step 4: Pack the full snapshot to get file map
50
+ // (we need this regardless — for delta comparison or full storage)
51
+ const fullFileMap = packSnapshot(snapshot);
52
+ // Step 5: Determine if we should do incremental or full
53
+ let deltaManifest;
54
+ let deltaResult;
55
+ let isIncremental = false;
56
+ if (parentInfo) {
57
+ deltaResult = computeDelta(fullFileMap, parentInfo.hashes, parentInfo.parentId, parentInfo.baseId, parentInfo.chainDepth);
58
+ if (!deltaResult.shouldForceFull) {
59
+ isIncremental = true;
60
+ deltaManifest = deltaResult.delta;
61
+ }
62
+ }
63
+ // Step 6: Set manifest with parent reference
35
64
  snapshot.manifest = {
36
65
  ...snapshot.manifest,
37
66
  id: snapshotId,
@@ -41,31 +70,47 @@ export async function createSnapshot(adapter, storage, passphrase, options) {
41
70
  platform: adapter.platform,
42
71
  label: options?.label ?? snapshot.manifest.label,
43
72
  tags: options?.tags ?? snapshot.manifest.tags,
44
- parent: options?.parentId,
73
+ parent: isIncremental ? parentInfo.parentId : options?.parentId,
45
74
  };
46
- // Step 3: Update snapshot chain
75
+ // Step 7: Update snapshot chain
47
76
  snapshot.chain = {
48
77
  current: snapshotId,
49
- parent: options?.parentId,
50
- ancestors: options?.parentId
51
- ? [...(snapshot.chain?.ancestors ?? []), options.parentId]
52
- : [],
78
+ parent: isIncremental ? parentInfo.parentId : options?.parentId,
79
+ ancestors: isIncremental
80
+ ? [...(deltaManifest.chainDepth > 1
81
+ ? (await getChainAncestors(parentInfo.parentId))
82
+ : []), parentInfo.parentId]
83
+ : options?.parentId
84
+ ? [...(snapshot.chain?.ancestors ?? []), options.parentId]
85
+ : [],
53
86
  };
54
- // Step 4: First pass pack to get file map and compute checksum
55
- const fileMap = packSnapshot(snapshot);
56
- const firstArchive = packToArchive(fileMap);
87
+ // Step 8: Pack the archive (incremental or full)
88
+ let archiveFileMap;
89
+ if (isIncremental && deltaResult && deltaManifest) {
90
+ archiveFileMap = packDelta(snapshot, deltaManifest, deltaResult.changedFiles);
91
+ }
92
+ else {
93
+ archiveFileMap = packSnapshot(snapshot);
94
+ }
95
+ // Step 9: Compute checksum and finalize
96
+ const firstArchive = packToArchive(archiveFileMap);
57
97
  const checksum = computeChecksum(firstArchive);
58
- // Step 5: Update manifest with checksum and size, then re-pack
59
98
  snapshot.manifest.checksum = checksum;
60
99
  snapshot.manifest.size = firstArchive.length;
61
- const finalFileMap = packSnapshot(snapshot);
62
- const finalArchive = packToArchive(finalFileMap);
63
- // Step 6: Encrypt the archive
100
+ // Re-pack with updated manifest
101
+ if (isIncremental && deltaResult && deltaManifest) {
102
+ archiveFileMap.set('manifest.json', Buffer.from(JSON.stringify(snapshot.manifest, null, 2)));
103
+ }
104
+ else {
105
+ archiveFileMap = packSnapshot(snapshot);
106
+ }
107
+ const finalArchive = packToArchive(archiveFileMap);
108
+ // Step 10: Encrypt
64
109
  const encrypted = await encrypt(finalArchive, passphrase);
65
- // Step 7: Store the encrypted archive
110
+ // Step 11: Store
66
111
  const filename = snapshotFilename(snapshotId);
67
112
  await storage.put(filename, encrypted);
68
- // Step 8: Update local index
113
+ // Step 12: Update local index
69
114
  await addToIndex({
70
115
  id: snapshotId,
71
116
  timestamp: now,
@@ -81,9 +126,29 @@ export async function createSnapshot(adapter, storage, passphrase, options) {
81
126
  filename,
82
127
  archiveSize: finalArchive.length,
83
128
  encryptedSize: encrypted.length,
84
- fileCount: finalFileMap.size,
129
+ fileCount: archiveFileMap.size,
130
+ incremental: isIncremental,
131
+ delta: isIncremental && deltaManifest
132
+ ? {
133
+ added: deltaManifest.stats.added,
134
+ modified: deltaManifest.stats.modified,
135
+ removed: deltaManifest.stats.removed,
136
+ unchanged: deltaManifest.stats.unchanged,
137
+ bytesSaved: deltaManifest.stats.bytesSaved,
138
+ chainDepth: deltaManifest.chainDepth,
139
+ }
140
+ : undefined,
85
141
  };
86
142
  }
143
+ /**
144
+ * Helper: get the chain ancestors for a given snapshot ID.
145
+ */
146
+ async function getChainAncestors(snapshotId) {
147
+ const index = await import('./index-file.js');
148
+ // We don't need to decrypt the full chain — just track IDs from the index
149
+ // For now, return empty. The chain info is primarily in meta/snapshot-chain.json
150
+ return [];
151
+ }
87
152
  /**
88
153
  * Get the latest snapshot ID from the index.
89
154
  */
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAU7C;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB,EAChB,OAAuB,EACvB,UAAkB,EAClB,OAIC;IAED,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAEzC,0BAA0B;IAC1B,MAAM,UAAU,GAAG,kBAAkB,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,QAAQ,CAAC,QAAQ,GAAG;QAClB,GAAG,QAAQ,CAAC,QAAQ;QACpB,EAAE,EAAE,UAAU;QACd,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,GAAG;QACd,OAAO,EAAE,OAAO,CAAC,EAAE;QACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK;QAChD,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI;QAC7C,MAAM,EAAE,OAAO,EAAE,QAAQ;KAC1B,CAAC;IAEF,gCAAgC;IAChC,QAAQ,CAAC,KAAK,GAAG;QACf,OAAO,EAAE,UAAU;QACnB,MAAM,EAAE,OAAO,EAAE,QAAQ;QACzB,SAAS,EAAE,OAAO,EAAE,QAAQ;YAC1B,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC1D,CAAC,CAAC,EAAE;KACP,CAAC;IAEF,iEAAiE;IACjE,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;IAE/C,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC;IAE7C,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;IAEjD,8BAA8B;IAC9B,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAE1D,sCAAsC;IACtC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEvC,6BAA6B;IAC7B,MAAM,UAAU,CAAC;QACf,EAAE,EAAE,UAAU;QACd,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,EAAE;QACnB,KAAK,EAAE,OAAO,EAAE,KAAK;QACrB,IAAI,EAAE,OAAO,EAAE,IAAI;QACnB,QAAQ;QACR,IAAI,EAAE,SAAS,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,WAAW,EAAE,YAAY,CAAC,MAAM;QAChC,aAAa,EAAE,SAAS,CAAC,MAAM;QAC/B,SAAS,EAAE,YAAY,CAAC,IAAI;KAC7B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,MAAM,cAAc,EAAE,CAAC;IACrC,OAAO,KAAK,EAAE,EAAE,IAAI,IAAI,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@
2
2
  * Storage module re-exports
3
3
  */
4
4
  export { LocalStorageBackend } from './local.js';
5
+ export { S3Storage, S3Error } from './s3.js';
6
+ export type { S3StorageOptions } from './s3.js';
5
7
  export { resolveStorage } from './resolve.js';
6
8
  export type { StorageBackend } from './interface.js';
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
@@ -2,5 +2,6 @@
2
2
  * Storage module re-exports
3
3
  */
4
4
  export { LocalStorageBackend } from './local.js';
5
+ export { S3Storage, S3Error } from './s3.js';
5
6
  export { resolveStorage } from './resolve.js';
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/storage/resolve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGnE;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,cAAc,CAatE"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/storage/resolve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKnE;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,cAAc,CA6BtE"}