@hashtree/core 0.1.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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bep52.d.ts +179 -0
  3. package/dist/bep52.d.ts.map +1 -0
  4. package/dist/bep52.js +384 -0
  5. package/dist/bep52.js.map +1 -0
  6. package/dist/builder.d.ts +137 -0
  7. package/dist/builder.d.ts.map +1 -0
  8. package/dist/builder.js +281 -0
  9. package/dist/builder.js.map +1 -0
  10. package/dist/codec.d.ts +37 -0
  11. package/dist/codec.d.ts.map +1 -0
  12. package/dist/codec.js +109 -0
  13. package/dist/codec.js.map +1 -0
  14. package/dist/crypto.d.ts +92 -0
  15. package/dist/crypto.d.ts.map +1 -0
  16. package/dist/crypto.js +212 -0
  17. package/dist/crypto.js.map +1 -0
  18. package/dist/encrypted.d.ts +114 -0
  19. package/dist/encrypted.d.ts.map +1 -0
  20. package/dist/encrypted.js +446 -0
  21. package/dist/encrypted.js.map +1 -0
  22. package/dist/hash.d.ts +14 -0
  23. package/dist/hash.d.ts.map +1 -0
  24. package/dist/hash.js +27 -0
  25. package/dist/hash.js.map +1 -0
  26. package/dist/hashtree.d.ts +237 -0
  27. package/dist/hashtree.d.ts.map +1 -0
  28. package/dist/hashtree.js +557 -0
  29. package/dist/hashtree.js.map +1 -0
  30. package/dist/index.d.ts +27 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +44 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/nhash.d.ts +94 -0
  35. package/dist/nhash.d.ts.map +1 -0
  36. package/dist/nhash.js +293 -0
  37. package/dist/nhash.js.map +1 -0
  38. package/dist/resolver/index.d.ts +5 -0
  39. package/dist/resolver/index.d.ts.map +1 -0
  40. package/dist/resolver/index.js +5 -0
  41. package/dist/resolver/index.js.map +1 -0
  42. package/dist/resolver/nostr.d.ts +82 -0
  43. package/dist/resolver/nostr.d.ts.map +1 -0
  44. package/dist/resolver/nostr.js +868 -0
  45. package/dist/resolver/nostr.js.map +1 -0
  46. package/dist/store/blossom.d.ts +100 -0
  47. package/dist/store/blossom.d.ts.map +1 -0
  48. package/dist/store/blossom.js +355 -0
  49. package/dist/store/blossom.js.map +1 -0
  50. package/dist/store/dexie.d.ts +44 -0
  51. package/dist/store/dexie.d.ts.map +1 -0
  52. package/dist/store/dexie.js +196 -0
  53. package/dist/store/dexie.js.map +1 -0
  54. package/dist/store/fallback.d.ts +40 -0
  55. package/dist/store/fallback.d.ts.map +1 -0
  56. package/dist/store/fallback.js +71 -0
  57. package/dist/store/fallback.js.map +1 -0
  58. package/dist/store/index.d.ts +6 -0
  59. package/dist/store/index.d.ts.map +1 -0
  60. package/dist/store/index.js +6 -0
  61. package/dist/store/index.js.map +1 -0
  62. package/dist/store/memory.d.ts +29 -0
  63. package/dist/store/memory.d.ts.map +1 -0
  64. package/dist/store/memory.js +66 -0
  65. package/dist/store/memory.js.map +1 -0
  66. package/dist/store/opfs.d.ts +56 -0
  67. package/dist/store/opfs.d.ts.map +1 -0
  68. package/dist/store/opfs.js +200 -0
  69. package/dist/store/opfs.js.map +1 -0
  70. package/dist/streaming.d.ts +74 -0
  71. package/dist/streaming.d.ts.map +1 -0
  72. package/dist/streaming.js +199 -0
  73. package/dist/streaming.js.map +1 -0
  74. package/dist/tree/create.d.ts +35 -0
  75. package/dist/tree/create.d.ts.map +1 -0
  76. package/dist/tree/create.js +90 -0
  77. package/dist/tree/create.js.map +1 -0
  78. package/dist/tree/edit.d.ts +28 -0
  79. package/dist/tree/edit.d.ts.map +1 -0
  80. package/dist/tree/edit.js +115 -0
  81. package/dist/tree/edit.js.map +1 -0
  82. package/dist/tree/editEncrypted.d.ts +46 -0
  83. package/dist/tree/editEncrypted.d.ts.map +1 -0
  84. package/dist/tree/editEncrypted.js +225 -0
  85. package/dist/tree/editEncrypted.js.map +1 -0
  86. package/dist/tree/index.d.ts +7 -0
  87. package/dist/tree/index.d.ts.map +1 -0
  88. package/dist/tree/index.js +7 -0
  89. package/dist/tree/index.js.map +1 -0
  90. package/dist/tree/read.d.ts +75 -0
  91. package/dist/tree/read.d.ts.map +1 -0
  92. package/dist/tree/read.js +389 -0
  93. package/dist/tree/read.js.map +1 -0
  94. package/dist/tree/writeAt.d.ts +44 -0
  95. package/dist/tree/writeAt.d.ts.map +1 -0
  96. package/dist/tree/writeAt.js +282 -0
  97. package/dist/tree/writeAt.js.map +1 -0
  98. package/dist/types.d.ts +274 -0
  99. package/dist/types.d.ts.map +1 -0
  100. package/dist/types.js +47 -0
  101. package/dist/types.js.map +1 -0
  102. package/dist/verify.d.ts +12 -0
  103. package/dist/verify.d.ts.map +1 -0
  104. package/dist/verify.js +32 -0
  105. package/dist/verify.js.map +1 -0
  106. package/dist/visibility.d.ts +50 -0
  107. package/dist/visibility.d.ts.map +1 -0
  108. package/dist/visibility.js +111 -0
  109. package/dist/visibility.js.map +1 -0
  110. package/dist/webrtc/index.d.ts +4 -0
  111. package/dist/webrtc/index.d.ts.map +1 -0
  112. package/dist/webrtc/index.js +4 -0
  113. package/dist/webrtc/index.js.map +1 -0
  114. package/dist/webrtc/lruCache.d.ts +20 -0
  115. package/dist/webrtc/lruCache.d.ts.map +1 -0
  116. package/dist/webrtc/lruCache.js +59 -0
  117. package/dist/webrtc/lruCache.js.map +1 -0
  118. package/dist/webrtc/peer.d.ts +122 -0
  119. package/dist/webrtc/peer.d.ts.map +1 -0
  120. package/dist/webrtc/peer.js +583 -0
  121. package/dist/webrtc/peer.js.map +1 -0
  122. package/dist/webrtc/protocol.d.ts +76 -0
  123. package/dist/webrtc/protocol.d.ts.map +1 -0
  124. package/dist/webrtc/protocol.js +167 -0
  125. package/dist/webrtc/protocol.js.map +1 -0
  126. package/dist/webrtc/store.d.ts +190 -0
  127. package/dist/webrtc/store.d.ts.map +1 -0
  128. package/dist/webrtc/store.js +1043 -0
  129. package/dist/webrtc/store.js.map +1 -0
  130. package/dist/webrtc/types.d.ts +196 -0
  131. package/dist/webrtc/types.d.ts.map +1 -0
  132. package/dist/webrtc/types.js +46 -0
  133. package/dist/webrtc/types.js.map +1 -0
  134. package/dist/worker/protocol.d.ts +493 -0
  135. package/dist/worker/protocol.d.ts.map +1 -0
  136. package/dist/worker/protocol.js +15 -0
  137. package/dist/worker/protocol.js.map +1 -0
  138. package/package.json +59 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * OPFS (Origin Private File System) content-addressed store
3
+ * Persistent browser storage using the File System Access API
4
+ *
5
+ * Files are stored as: <dirName>/<first2chars>/<hash>.bin
6
+ * This provides sharding to avoid too many files in one directory.
7
+ */
8
+ import { toHex, fromHex } from '../types.js';
9
+ const DEFAULT_DIR_NAME = 'hashtree';
10
+ export class OpfsStore {
11
+ dirName;
12
+ rootDir = null;
13
+ storeDir = null;
14
+ initPromise = null;
15
+ constructor(options = DEFAULT_DIR_NAME) {
16
+ if (typeof options === 'string') {
17
+ this.dirName = options;
18
+ }
19
+ else {
20
+ this.dirName = options.dirName ?? DEFAULT_DIR_NAME;
21
+ }
22
+ }
23
+ /**
24
+ * Get or create the store directory (with deduplication)
25
+ */
26
+ async getStoreDir() {
27
+ if (this.storeDir)
28
+ return this.storeDir;
29
+ // Deduplicate initialization
30
+ if (this.initPromise)
31
+ return this.initPromise;
32
+ this.initPromise = (async () => {
33
+ this.rootDir = await navigator.storage.getDirectory();
34
+ this.storeDir = await this.rootDir.getDirectoryHandle(this.dirName, { create: true });
35
+ return this.storeDir;
36
+ })();
37
+ return this.initPromise;
38
+ }
39
+ /**
40
+ * Get shard directory for a hash (first 2 hex chars)
41
+ */
42
+ async getShardDir(hashHex, create) {
43
+ const storeDir = await this.getStoreDir();
44
+ const shard = hashHex.slice(0, 2);
45
+ try {
46
+ return await storeDir.getDirectoryHandle(shard, { create });
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ async put(hash, data) {
53
+ const hashHex = toHex(hash);
54
+ try {
55
+ const shardDir = await this.getShardDir(hashHex, true);
56
+ if (!shardDir)
57
+ return false;
58
+ // Check if file already exists
59
+ try {
60
+ await shardDir.getFileHandle(`${hashHex}.bin`, { create: false });
61
+ return false; // Already exists
62
+ }
63
+ catch {
64
+ // File doesn't exist, continue to create
65
+ }
66
+ const handle = await shardDir.getFileHandle(`${hashHex}.bin`, { create: true });
67
+ const writable = await handle.createWritable();
68
+ await writable.write(data);
69
+ await writable.close();
70
+ return true;
71
+ }
72
+ catch (err) {
73
+ console.error(`OPFS write failed for ${hashHex}:`, err);
74
+ return false;
75
+ }
76
+ }
77
+ async get(hash) {
78
+ if (!hash)
79
+ return null;
80
+ const hashHex = toHex(hash);
81
+ const shardDir = await this.getShardDir(hashHex, false);
82
+ if (!shardDir)
83
+ return null;
84
+ try {
85
+ const handle = await shardDir.getFileHandle(`${hashHex}.bin`, { create: false });
86
+ const file = await handle.getFile();
87
+ const buffer = await file.arrayBuffer();
88
+ return new Uint8Array(buffer);
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ async has(hash) {
95
+ const hashHex = toHex(hash);
96
+ const shardDir = await this.getShardDir(hashHex, false);
97
+ if (!shardDir)
98
+ return false;
99
+ try {
100
+ await shardDir.getFileHandle(`${hashHex}.bin`, { create: false });
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
107
+ async delete(hash) {
108
+ const hashHex = toHex(hash);
109
+ const shardDir = await this.getShardDir(hashHex, false);
110
+ if (!shardDir)
111
+ return false;
112
+ try {
113
+ await shardDir.removeEntry(`${hashHex}.bin`);
114
+ return true;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ /**
121
+ * Get all stored hashes
122
+ */
123
+ async keys() {
124
+ const storeDir = await this.getStoreDir();
125
+ const hashes = [];
126
+ // @ts-ignore - entries() exists on FileSystemDirectoryHandle
127
+ for await (const [, shardHandle] of storeDir.entries()) {
128
+ if (shardHandle.kind !== 'directory')
129
+ continue;
130
+ // @ts-ignore
131
+ for await (const [fileName] of shardHandle.entries()) {
132
+ if (fileName.endsWith('.bin')) {
133
+ const hashHex = fileName.slice(0, -4);
134
+ hashes.push(fromHex(hashHex));
135
+ }
136
+ }
137
+ }
138
+ return hashes;
139
+ }
140
+ /**
141
+ * Clear all data
142
+ */
143
+ async clear() {
144
+ const storeDir = await this.getStoreDir();
145
+ // @ts-ignore
146
+ for await (const [name, handle] of storeDir.entries()) {
147
+ if (handle.kind === 'directory') {
148
+ await storeDir.removeEntry(name, { recursive: true });
149
+ }
150
+ }
151
+ }
152
+ /**
153
+ * Get count of stored items
154
+ */
155
+ async count() {
156
+ const keys = await this.keys();
157
+ return keys.length;
158
+ }
159
+ /**
160
+ * Get total bytes stored
161
+ */
162
+ async totalBytes() {
163
+ const storeDir = await this.getStoreDir();
164
+ let total = 0;
165
+ // @ts-ignore
166
+ for await (const [, shardHandle] of storeDir.entries()) {
167
+ if (shardHandle.kind !== 'directory')
168
+ continue;
169
+ // @ts-ignore
170
+ for await (const [, fileHandle] of shardHandle.entries()) {
171
+ if (fileHandle.kind === 'file') {
172
+ const file = await fileHandle.getFile();
173
+ total += file.size;
174
+ }
175
+ }
176
+ }
177
+ return total;
178
+ }
179
+ /**
180
+ * Close - cleanup any resources
181
+ */
182
+ async close() {
183
+ this.rootDir = null;
184
+ this.storeDir = null;
185
+ this.initPromise = null;
186
+ }
187
+ /**
188
+ * Delete the entire store directory
189
+ */
190
+ static async deleteStore(dirName = DEFAULT_DIR_NAME) {
191
+ const root = await navigator.storage.getDirectory();
192
+ try {
193
+ await root.removeEntry(dirName, { recursive: true });
194
+ }
195
+ catch {
196
+ // Directory might not exist
197
+ }
198
+ }
199
+ }
200
+ //# sourceMappingURL=opfs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opfs.js","sourceRoot":"","sources":["../../src/store/opfs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * StreamWriter - supports incremental file appends
3
+ *
4
+ * Created via HashTree.createStream()
5
+ *
6
+ * All chunks are CHK encrypted by default (same as putFile).
7
+ * Use createStream({ unencrypted: true }) for unencrypted streaming.
8
+ */
9
+ import { Store, Hash, CID } from './types.js';
10
+ import { type EncryptionKey } from './crypto.js';
11
+ import { type Chunker } from './builder.js';
12
+ export type { Chunker } from './builder.js';
13
+ export interface StreamWriterConfig {
14
+ store: Store;
15
+ chunkSize?: number;
16
+ chunker?: Chunker;
17
+ unencrypted?: boolean;
18
+ }
19
+ export declare class StreamWriter {
20
+ private store;
21
+ private chunker;
22
+ private unencrypted;
23
+ private buffer;
24
+ private bufferOffset;
25
+ private chunks;
26
+ private totalSize;
27
+ constructor(config: StreamWriterConfig);
28
+ /** @deprecated Use config object instead */
29
+ constructor(store: Store, chunkSize: number, maxLinks: number, unencrypted?: boolean);
30
+ /** Get current target chunk size */
31
+ private currentChunkSize;
32
+ /**
33
+ * Append data to the stream
34
+ */
35
+ append(data: Uint8Array): Promise<void>;
36
+ /**
37
+ * Flush current buffer as a chunk (encrypted or plaintext based on mode)
38
+ */
39
+ private flushChunk;
40
+ /**
41
+ * Get current root CID without finalizing
42
+ * Useful for checkpoints (e.g., live streaming)
43
+ * Returns CID with key for encrypted streams, CID without key for public streams
44
+ */
45
+ currentRoot(): Promise<CID | null>;
46
+ /**
47
+ * Finalize the stream and return root CID
48
+ * For encrypted streams: returns { hash, size, key }
49
+ * For public streams: returns { hash, size } (key is undefined)
50
+ */
51
+ finalize(): Promise<{
52
+ hash: Hash;
53
+ size: number;
54
+ key?: EncryptionKey;
55
+ }>;
56
+ /**
57
+ * Build flat tree from chunks
58
+ */
59
+ private buildTreeFromChunks;
60
+ /**
61
+ * Get stats
62
+ */
63
+ get stats(): {
64
+ chunks: number;
65
+ buffered: number;
66
+ totalSize: number;
67
+ };
68
+ /**
69
+ * Clear internal state to release memory
70
+ * Call after finalize() if you want to free memory immediately
71
+ */
72
+ clear(): void;
73
+ }
74
+ //# sourceMappingURL=streaming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../src/streaming.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAiC,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,KAAK,OAAO,EAAgB,MAAM,cAAc,CAAC;AAC1D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAU;IAG7B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,YAAY,CAAa;IAGjC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAa;gBAElB,MAAM,EAAE,kBAAkB;IACtC,4CAA4C;gBAChC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO;IAuBpF,oCAAoC;IACpC,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B7C;;OAEG;YACW,UAAU;IAuBxB;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IA2BxC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IAuB5E;;OAEG;YACW,mBAAmB;IA0BjC;;OAEG;IACH,IAAI,KAAK,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAMnE;IAED;;;OAGG;IACH,KAAK,IAAI,IAAI;CAMd"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * StreamWriter - supports incremental file appends
3
+ *
4
+ * Created via HashTree.createStream()
5
+ *
6
+ * All chunks are CHK encrypted by default (same as putFile).
7
+ * Use createStream({ unencrypted: true }) for unencrypted streaming.
8
+ */
9
+ import { LinkType, cid } from './types.js';
10
+ import { encodeAndHash } from './codec.js';
11
+ import { sha256 } from './hash.js';
12
+ import { encryptChk } from './crypto.js';
13
+ import { fixedChunker } from './builder.js';
14
+ export class StreamWriter {
15
+ store;
16
+ chunker;
17
+ unencrypted;
18
+ // Current partial chunk being built
19
+ buffer;
20
+ bufferOffset = 0;
21
+ // Completed chunks (with encryption keys for tree building when encrypted)
22
+ chunks = [];
23
+ totalSize = 0;
24
+ constructor(storeOrConfig, chunkSize, _maxLinks, unencrypted) {
25
+ if ('store' in storeOrConfig && typeof storeOrConfig === 'object' && !('get' in storeOrConfig)) {
26
+ // New config-based constructor
27
+ const config = storeOrConfig;
28
+ this.store = config.store;
29
+ this.chunker = config.chunker ?? fixedChunker(config.chunkSize ?? 2 * 1024 * 1024);
30
+ this.unencrypted = config.unencrypted ?? false;
31
+ }
32
+ else {
33
+ // Legacy positional constructor (maxLinks ignored)
34
+ this.store = storeOrConfig;
35
+ this.chunker = fixedChunker(chunkSize);
36
+ this.unencrypted = unencrypted ?? false;
37
+ }
38
+ // Initialize buffer with first chunk size
39
+ this.buffer = new Uint8Array(this.chunker(0));
40
+ }
41
+ /** Get current target chunk size */
42
+ currentChunkSize() {
43
+ return this.chunker(this.chunks.length);
44
+ }
45
+ /**
46
+ * Append data to the stream
47
+ */
48
+ async append(data) {
49
+ let offset = 0;
50
+ while (offset < data.length) {
51
+ const targetSize = this.currentChunkSize();
52
+ // Resize buffer if needed for new chunk size
53
+ if (this.buffer.length !== targetSize) {
54
+ const newBuffer = new Uint8Array(targetSize);
55
+ if (this.bufferOffset > 0) {
56
+ newBuffer.set(this.buffer.subarray(0, this.bufferOffset));
57
+ }
58
+ this.buffer = newBuffer;
59
+ }
60
+ const space = targetSize - this.bufferOffset;
61
+ const toWrite = Math.min(space, data.length - offset);
62
+ this.buffer.set(data.subarray(offset, offset + toWrite), this.bufferOffset);
63
+ this.bufferOffset += toWrite;
64
+ offset += toWrite;
65
+ // Flush full chunk
66
+ if (this.bufferOffset === targetSize) {
67
+ await this.flushChunk();
68
+ }
69
+ }
70
+ this.totalSize += data.length;
71
+ }
72
+ /**
73
+ * Flush current buffer as a chunk (encrypted or plaintext based on mode)
74
+ */
75
+ async flushChunk() {
76
+ if (this.bufferOffset === 0)
77
+ return;
78
+ const chunkSize = this.bufferOffset;
79
+ // slice() to create independent copy - allows buffer reuse after await
80
+ const chunk = this.buffer.slice(0, chunkSize);
81
+ if (this.unencrypted) {
82
+ // Public mode: store plaintext
83
+ const hash = await sha256(chunk);
84
+ await this.store.put(hash, chunk);
85
+ this.chunks.push({ hash, size: chunkSize, type: LinkType.Blob });
86
+ }
87
+ else {
88
+ // Encrypted mode: CHK encrypt the chunk
89
+ const { ciphertext, key } = await encryptChk(chunk);
90
+ const hash = await sha256(ciphertext);
91
+ await this.store.put(hash, ciphertext);
92
+ this.chunks.push({ hash, size: chunkSize, key, type: LinkType.Blob });
93
+ }
94
+ this.bufferOffset = 0;
95
+ }
96
+ /**
97
+ * Get current root CID without finalizing
98
+ * Useful for checkpoints (e.g., live streaming)
99
+ * Returns CID with key for encrypted streams, CID without key for public streams
100
+ */
101
+ async currentRoot() {
102
+ if (this.chunks.length === 0 && this.bufferOffset === 0) {
103
+ return null;
104
+ }
105
+ // Temporarily store buffer without modifying state
106
+ const tempChunks = [...this.chunks];
107
+ if (this.bufferOffset > 0) {
108
+ const chunk = this.buffer.slice(0, this.bufferOffset);
109
+ if (this.unencrypted) {
110
+ const hash = await sha256(chunk);
111
+ await this.store.put(hash, chunk);
112
+ tempChunks.push({ hash, size: chunk.length, type: LinkType.Blob });
113
+ }
114
+ else {
115
+ // Store PLAINTEXT size in link.size for correct range seeking
116
+ const plaintextSize = chunk.length;
117
+ const { ciphertext, key } = await encryptChk(chunk);
118
+ const hash = await sha256(ciphertext);
119
+ await this.store.put(hash, ciphertext);
120
+ tempChunks.push({ hash, size: plaintextSize, key, type: LinkType.Blob });
121
+ }
122
+ }
123
+ return this.buildTreeFromChunks(tempChunks, this.totalSize);
124
+ }
125
+ /**
126
+ * Finalize the stream and return root CID
127
+ * For encrypted streams: returns { hash, size, key }
128
+ * For public streams: returns { hash, size } (key is undefined)
129
+ */
130
+ async finalize() {
131
+ // Flush remaining buffer
132
+ await this.flushChunk();
133
+ if (this.chunks.length === 0) {
134
+ // Empty stream
135
+ if (this.unencrypted) {
136
+ const emptyData = new Uint8Array(0);
137
+ const hash = await sha256(emptyData);
138
+ await this.store.put(hash, emptyData);
139
+ return { hash, size: 0 };
140
+ }
141
+ else {
142
+ const { ciphertext, key } = await encryptChk(new Uint8Array(0));
143
+ const hash = await sha256(ciphertext);
144
+ await this.store.put(hash, ciphertext);
145
+ return { hash, size: 0, key };
146
+ }
147
+ }
148
+ const result = await this.buildTreeFromChunks(this.chunks, this.totalSize);
149
+ return { hash: result.hash, size: this.totalSize, key: result.key };
150
+ }
151
+ /**
152
+ * Build flat tree from chunks
153
+ */
154
+ async buildTreeFromChunks(chunks, _totalSize) {
155
+ // Single chunk - return its hash (and key if encrypted)
156
+ if (chunks.length === 1) {
157
+ return cid(chunks[0].hash, chunks[0].key);
158
+ }
159
+ // Create single flat node with all links
160
+ const node = {
161
+ type: LinkType.File,
162
+ links: chunks,
163
+ };
164
+ const { data, hash: nodeHash } = await encodeAndHash(node);
165
+ if (this.unencrypted) {
166
+ // Public mode: store plaintext tree node
167
+ await this.store.put(nodeHash, data);
168
+ return { hash: nodeHash };
169
+ }
170
+ else {
171
+ // Encrypted mode: CHK encrypt the tree node
172
+ const { ciphertext, key } = await encryptChk(data);
173
+ const hash = await sha256(ciphertext);
174
+ await this.store.put(hash, ciphertext);
175
+ return cid(hash, key);
176
+ }
177
+ }
178
+ /**
179
+ * Get stats
180
+ */
181
+ get stats() {
182
+ return {
183
+ chunks: this.chunks.length,
184
+ buffered: this.bufferOffset,
185
+ totalSize: this.totalSize,
186
+ };
187
+ }
188
+ /**
189
+ * Clear internal state to release memory
190
+ * Call after finalize() if you want to free memory immediately
191
+ */
192
+ clear() {
193
+ this.chunks = [];
194
+ this.buffer = new Uint8Array(0);
195
+ this.bufferOffset = 0;
196
+ this.totalSize = 0;
197
+ }
198
+ }
199
+ //# sourceMappingURL=streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../src/streaming.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Tree creation operations
3
+ */
4
+ import { Store, Hash, Link, LinkType, CID } from '../types.js';
5
+ export interface CreateConfig {
6
+ store: Store;
7
+ chunkSize: number;
8
+ }
9
+ export interface DirEntry {
10
+ name: string;
11
+ cid: CID;
12
+ size: number;
13
+ type: LinkType;
14
+ meta?: Record<string, unknown>;
15
+ }
16
+ /**
17
+ * Store a blob directly (small data)
18
+ */
19
+ export declare function putBlob(store: Store, data: Uint8Array): Promise<Hash>;
20
+ /**
21
+ * Store a file, chunking if necessary
22
+ */
23
+ export declare function putFile(config: CreateConfig, data: Uint8Array): Promise<{
24
+ hash: Hash;
25
+ size: number;
26
+ }>;
27
+ /**
28
+ * Build a directory from entries
29
+ *
30
+ * Directories are encoded as MessagePack blobs. If the encoded blob exceeds
31
+ * chunkSize, it's chunked by bytes like files using putFile.
32
+ */
33
+ export declare function putDirectory(config: CreateConfig, entries: DirEntry[]): Promise<Hash>;
34
+ export declare function buildTree(config: CreateConfig, links: Link[], totalSize?: number): Promise<Hash>;
35
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/tree/create.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAY,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAIzE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3E;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA4BvC;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,QAAQ,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,IAAI,EAAE,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Tree creation operations
3
+ */
4
+ import { LinkType } from '../types.js';
5
+ import { sha256 } from '../hash.js';
6
+ import { encodeAndHash } from '../codec.js';
7
+ /**
8
+ * Store a blob directly (small data)
9
+ */
10
+ export async function putBlob(store, data) {
11
+ const hash = await sha256(data);
12
+ await store.put(hash, data);
13
+ return hash;
14
+ }
15
+ /**
16
+ * Store a file, chunking if necessary
17
+ */
18
+ export async function putFile(config, data) {
19
+ const { store, chunkSize } = config;
20
+ const size = data.length;
21
+ if (data.length <= chunkSize) {
22
+ const hash = await putBlob(store, data);
23
+ return { hash, size };
24
+ }
25
+ // Process chunks sequentially to avoid memory spikes
26
+ // (For parallel processing of large files, use StreamWriter instead)
27
+ const links = [];
28
+ let offset = 0;
29
+ while (offset < data.length) {
30
+ const end = Math.min(offset + chunkSize, data.length);
31
+ // Use subarray to avoid copying
32
+ const chunk = data.subarray(offset, end);
33
+ const hash = await putBlob(store, chunk);
34
+ links.push({
35
+ hash,
36
+ size: chunk.length,
37
+ type: LinkType.Blob,
38
+ });
39
+ offset = end;
40
+ }
41
+ const rootHash = await buildTree(config, links, size);
42
+ return { hash: rootHash, size };
43
+ }
44
+ /**
45
+ * Build a directory from entries
46
+ *
47
+ * Directories are encoded as MessagePack blobs. If the encoded blob exceeds
48
+ * chunkSize, it's chunked by bytes like files using putFile.
49
+ */
50
+ export async function putDirectory(config, entries) {
51
+ const { store, chunkSize } = config;
52
+ const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
53
+ const links = sorted.map(e => ({
54
+ hash: e.cid.hash,
55
+ key: e.cid.key,
56
+ name: e.name,
57
+ size: e.size,
58
+ type: e.type,
59
+ meta: e.meta,
60
+ }));
61
+ const node = {
62
+ type: LinkType.Dir,
63
+ links,
64
+ };
65
+ const { data, hash } = await encodeAndHash(node);
66
+ // Small directory - store directly
67
+ if (data.length <= chunkSize) {
68
+ await store.put(hash, data);
69
+ return hash;
70
+ }
71
+ // Large directory - reuse putFile for chunking
72
+ const { hash: rootHash } = await putFile(config, data);
73
+ return rootHash;
74
+ }
75
+ export async function buildTree(config, links, totalSize) {
76
+ const { store } = config;
77
+ // Single chunk that matches total size - return it directly
78
+ if (links.length === 1 && links[0].size === totalSize) {
79
+ return links[0].hash;
80
+ }
81
+ // Create single flat node with all links
82
+ const node = {
83
+ type: LinkType.File,
84
+ links,
85
+ };
86
+ const { data, hash } = await encodeAndHash(node);
87
+ await store.put(hash, data);
88
+ return hash;
89
+ }
90
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/tree/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Tree editing operations
3
+ */
4
+ import { Hash, CID, LinkType } from '../types.js';
5
+ import { type CreateConfig } from './create.js';
6
+ export interface EditConfig extends CreateConfig {
7
+ }
8
+ /**
9
+ * Add or update an entry in a directory
10
+ * @returns New root hash
11
+ */
12
+ export declare function setEntry(config: EditConfig, rootHash: Hash, path: string[], name: string, entryCid: CID, size: number, type?: LinkType, meta?: Record<string, unknown>): Promise<Hash>;
13
+ /**
14
+ * Remove an entry from a directory
15
+ * @returns New root hash
16
+ */
17
+ export declare function removeEntry(config: EditConfig, rootHash: Hash, path: string[], name: string): Promise<Hash>;
18
+ /**
19
+ * Rename an entry in a directory
20
+ * @returns New root hash
21
+ */
22
+ export declare function renameEntry(config: EditConfig, rootHash: Hash, path: string[], oldName: string, newName: string): Promise<Hash>;
23
+ /**
24
+ * Move an entry to a different directory
25
+ * @returns New root hash
26
+ */
27
+ export declare function moveEntry(config: EditConfig, rootHash: Hash, sourcePath: string[], name: string, targetPath: string[]): Promise<Hash>;
28
+ //# sourceMappingURL=edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/tree/edit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAS,IAAI,EAAE,GAAG,EAAO,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7E,MAAM,WAAW,UAAW,SAAQ,YAAY;CAAG;AAEnD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,IAAI,EACd,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,GAAG,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,QAAwB,EAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,IAAI,EACd,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,IAAI,EACd,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,IAAI,EACd,UAAU,EAAE,MAAM,EAAE,EACpB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CA4Bf"}