@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,137 @@
1
+ /**
2
+ * Tree builder with chunking and fanout support
3
+ *
4
+ * - Large files are split into chunks
5
+ * - Large directories are split into sub-trees
6
+ * - Supports streaming appends
7
+ */
8
+ import { Store, Hash, Link, LinkType } from './types.js';
9
+ /**
10
+ * Default chunk size: 2MB (optimized for blossom uploads)
11
+ */
12
+ export declare const DEFAULT_CHUNK_SIZE: number;
13
+ /**
14
+ * Chunker function: returns chunk size for a given chunk index
15
+ * @param index - 0-based chunk index
16
+ * @returns chunk size in bytes
17
+ */
18
+ export type Chunker = (index: number) => number;
19
+ /**
20
+ * Create a fixed-size chunker
21
+ */
22
+ export declare function fixedChunker(size: number): Chunker;
23
+ /**
24
+ * Create a video-optimized chunker with smaller first chunk for fast playback start
25
+ * @param firstChunkSize - Size of first chunk (default: 256KB)
26
+ * @param regularChunkSize - Size of remaining chunks (default: 2MB)
27
+ */
28
+ export declare function videoChunker(firstChunkSize?: number, regularChunkSize?: number): Chunker;
29
+ export interface BuilderConfig {
30
+ store: Store;
31
+ /** Chunk size for splitting blobs (ignored if chunker is provided) */
32
+ chunkSize?: number;
33
+ /** Custom chunker function for variable chunk sizes */
34
+ chunker?: Chunker;
35
+ /** Hash chunks in parallel (default: true) */
36
+ parallel?: boolean;
37
+ }
38
+ export interface FileEntry {
39
+ name: string;
40
+ data: Uint8Array;
41
+ }
42
+ export interface DirEntry {
43
+ name: string;
44
+ hash: Hash;
45
+ size: number;
46
+ type: LinkType;
47
+ meta?: Record<string, unknown>;
48
+ }
49
+ /**
50
+ * TreeBuilder - builds content-addressed merkle trees
51
+ */
52
+ export declare class TreeBuilder {
53
+ private store;
54
+ private chunker;
55
+ private parallel;
56
+ constructor(config: BuilderConfig);
57
+ /**
58
+ * Store a blob directly (small data)
59
+ * Returns the content hash
60
+ */
61
+ putBlob(data: Uint8Array): Promise<Hash>;
62
+ /**
63
+ * Store a file, chunking if necessary
64
+ * Returns root hash and total size
65
+ */
66
+ putFile(data: Uint8Array): Promise<{
67
+ hash: Hash;
68
+ size: number;
69
+ }>;
70
+ /**
71
+ * Build a balanced tree from links (for chunked files)
72
+ * Handles fanout by creating intermediate nodes
73
+ */
74
+ private buildTree;
75
+ /**
76
+ * Build a directory from entries
77
+ * Entries can be files or subdirectories
78
+ *
79
+ * Directories are encoded as MessagePack blobs. If the encoded blob exceeds
80
+ * chunkSize, it's chunked by bytes like files using putFile.
81
+ *
82
+ * @param entries Directory entries
83
+ */
84
+ putDirectory(entries: DirEntry[]): Promise<Hash>;
85
+ /**
86
+ * Create a tree node
87
+ * @param nodeType - LinkType.File or LinkType.Dir
88
+ */
89
+ putTreeNode(nodeType: LinkType.File | LinkType.Dir, links: Link[]): Promise<Hash>;
90
+ }
91
+ /**
92
+ * StreamBuilder - supports incremental appends
93
+ */
94
+ export declare class StreamBuilder {
95
+ private store;
96
+ private chunker;
97
+ private buffer;
98
+ private bufferOffset;
99
+ private chunks;
100
+ private totalSize;
101
+ constructor(config: BuilderConfig);
102
+ /** Get current target chunk size */
103
+ private currentChunkSize;
104
+ /**
105
+ * Append data to the stream
106
+ */
107
+ append(data: Uint8Array): Promise<void>;
108
+ /**
109
+ * Flush current buffer as a chunk
110
+ */
111
+ private flushChunk;
112
+ /**
113
+ * Get current root hash without finalizing
114
+ * Useful for checkpoints
115
+ */
116
+ currentRoot(): Promise<Hash | null>;
117
+ /**
118
+ * Finalize the stream and return root hash
119
+ */
120
+ finalize(): Promise<{
121
+ hash: Hash;
122
+ size: number;
123
+ }>;
124
+ /**
125
+ * Build flat tree from chunks (for streaming files)
126
+ */
127
+ private buildTreeFromChunks;
128
+ /**
129
+ * Get stats
130
+ */
131
+ get stats(): {
132
+ chunks: number;
133
+ buffered: number;
134
+ totalSize: number;
135
+ };
136
+ }
137
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAY,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAInE;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAkB,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAEhD;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,cAAc,GAAE,MAAmB,EACnC,gBAAgB,GAAE,MAA2B,GAC5C,OAAO,CAET;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAU;gBAEd,MAAM,EAAE,aAAa;IAMjC;;;OAGG;IACG,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9C;;;OAGG;IACG,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IA8CtE;;;OAGG;YACW,SAAS;IAgBvB;;;;;;;;OAQG;IACG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BtD;;;OAGG;IACG,WAAW,CACf,QAAQ,EAAE,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,EACtC,KAAK,EAAE,IAAI,EAAE,GACZ,OAAO,CAAC,IAAI,CAAC;CAUjB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAU;IAGzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,YAAY,CAAa;IAGjC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAa;gBAElB,MAAM,EAAE,aAAa;IAOjC,oCAAoC;IACpC,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B7C;;OAEG;YACW,UAAU;IAWxB;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAiBzC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAevD;;OAEG;YACW,mBAAmB;IAejC;;OAEG;IACH,IAAI,KAAK,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAMnE;CACF"}
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Tree builder with chunking and fanout support
3
+ *
4
+ * - Large files are split into chunks
5
+ * - Large directories are split into sub-trees
6
+ * - Supports streaming appends
7
+ */
8
+ import { LinkType } from './types.js';
9
+ import { sha256 } from './hash.js';
10
+ import { encodeAndHash } from './codec.js';
11
+ /**
12
+ * Default chunk size: 2MB (optimized for blossom uploads)
13
+ */
14
+ export const DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024;
15
+ /**
16
+ * Create a fixed-size chunker
17
+ */
18
+ export function fixedChunker(size) {
19
+ return () => size;
20
+ }
21
+ /**
22
+ * Create a video-optimized chunker with smaller first chunk for fast playback start
23
+ * @param firstChunkSize - Size of first chunk (default: 256KB)
24
+ * @param regularChunkSize - Size of remaining chunks (default: 2MB)
25
+ */
26
+ export function videoChunker(firstChunkSize = 256 * 1024, regularChunkSize = DEFAULT_CHUNK_SIZE) {
27
+ return (index) => index === 0 ? firstChunkSize : regularChunkSize;
28
+ }
29
+ /**
30
+ * TreeBuilder - builds content-addressed merkle trees
31
+ */
32
+ export class TreeBuilder {
33
+ store;
34
+ chunker;
35
+ parallel;
36
+ constructor(config) {
37
+ this.store = config.store;
38
+ this.chunker = config.chunker ?? fixedChunker(config.chunkSize ?? DEFAULT_CHUNK_SIZE);
39
+ this.parallel = config.parallel ?? true;
40
+ }
41
+ /**
42
+ * Store a blob directly (small data)
43
+ * Returns the content hash
44
+ */
45
+ async putBlob(data) {
46
+ const hash = await sha256(data);
47
+ await this.store.put(hash, data);
48
+ return hash;
49
+ }
50
+ /**
51
+ * Store a file, chunking if necessary
52
+ * Returns root hash and total size
53
+ */
54
+ async putFile(data) {
55
+ const size = data.length;
56
+ const firstChunkSize = this.chunker(0);
57
+ // Small file - store as single blob
58
+ if (data.length <= firstChunkSize) {
59
+ const hash = await this.putBlob(data);
60
+ return { hash, size };
61
+ }
62
+ // Split into chunks using chunker
63
+ const chunkList = [];
64
+ const chunkSizes = [];
65
+ let offset = 0;
66
+ let chunkIndex = 0;
67
+ while (offset < data.length) {
68
+ const chunkSize = this.chunker(chunkIndex);
69
+ const end = Math.min(offset + chunkSize, data.length);
70
+ chunkList.push(data.slice(offset, end));
71
+ chunkSizes.push(end - offset);
72
+ offset = end;
73
+ chunkIndex++;
74
+ }
75
+ // Hash and store chunks (parallel or sequential)
76
+ let chunkHashes;
77
+ if (this.parallel) {
78
+ chunkHashes = await Promise.all(chunkList.map(chunk => this.putBlob(chunk)));
79
+ }
80
+ else {
81
+ chunkHashes = [];
82
+ for (const chunk of chunkList) {
83
+ chunkHashes.push(await this.putBlob(chunk));
84
+ }
85
+ }
86
+ // Build tree from chunks (leaf chunks are raw blobs)
87
+ const chunks = chunkHashes.map((hash, i) => ({
88
+ hash,
89
+ size: chunkSizes[i],
90
+ type: LinkType.Blob,
91
+ }));
92
+ const rootHash = await this.buildTree(chunks, size);
93
+ return { hash: rootHash, size };
94
+ }
95
+ /**
96
+ * Build a balanced tree from links (for chunked files)
97
+ * Handles fanout by creating intermediate nodes
98
+ */
99
+ async buildTree(links, totalSize) {
100
+ // Single link - return it directly
101
+ if (links.length === 1 && links[0].size === totalSize) {
102
+ return links[0].hash;
103
+ }
104
+ // Create single flat node with all links
105
+ const node = {
106
+ type: LinkType.File,
107
+ links,
108
+ };
109
+ const { data, hash } = await encodeAndHash(node);
110
+ await this.store.put(hash, data);
111
+ return hash;
112
+ }
113
+ /**
114
+ * Build a directory from entries
115
+ * Entries can be files or subdirectories
116
+ *
117
+ * Directories are encoded as MessagePack blobs. If the encoded blob exceeds
118
+ * chunkSize, it's chunked by bytes like files using putFile.
119
+ *
120
+ * @param entries Directory entries
121
+ */
122
+ async putDirectory(entries) {
123
+ // Sort entries by name for deterministic hashing
124
+ const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
125
+ const links = sorted.map(e => ({
126
+ hash: e.hash,
127
+ name: e.name,
128
+ size: e.size,
129
+ type: e.type,
130
+ meta: e.meta,
131
+ }));
132
+ const node = {
133
+ type: LinkType.Dir,
134
+ links,
135
+ };
136
+ const { data, hash } = await encodeAndHash(node);
137
+ // Small directory - store directly
138
+ if (data.length <= this.chunker(0)) {
139
+ await this.store.put(hash, data);
140
+ return hash;
141
+ }
142
+ // Large directory - reuse putFile for chunking
143
+ const { hash: rootHash } = await this.putFile(data);
144
+ return rootHash;
145
+ }
146
+ /**
147
+ * Create a tree node
148
+ * @param nodeType - LinkType.File or LinkType.Dir
149
+ */
150
+ async putTreeNode(nodeType, links) {
151
+ const node = {
152
+ type: nodeType,
153
+ links,
154
+ };
155
+ const { data, hash } = await encodeAndHash(node);
156
+ await this.store.put(hash, data);
157
+ return hash;
158
+ }
159
+ }
160
+ /**
161
+ * StreamBuilder - supports incremental appends
162
+ */
163
+ export class StreamBuilder {
164
+ store;
165
+ chunker;
166
+ // Current partial chunk being built
167
+ buffer;
168
+ bufferOffset = 0;
169
+ // Completed chunks
170
+ chunks = [];
171
+ totalSize = 0;
172
+ constructor(config) {
173
+ this.store = config.store;
174
+ this.chunker = config.chunker ?? fixedChunker(config.chunkSize ?? DEFAULT_CHUNK_SIZE);
175
+ // Initialize buffer with first chunk size
176
+ this.buffer = new Uint8Array(this.chunker(0));
177
+ }
178
+ /** Get current target chunk size */
179
+ currentChunkSize() {
180
+ return this.chunker(this.chunks.length);
181
+ }
182
+ /**
183
+ * Append data to the stream
184
+ */
185
+ async append(data) {
186
+ let offset = 0;
187
+ while (offset < data.length) {
188
+ const targetSize = this.currentChunkSize();
189
+ // Resize buffer if needed for new chunk size
190
+ if (this.buffer.length !== targetSize) {
191
+ const newBuffer = new Uint8Array(targetSize);
192
+ if (this.bufferOffset > 0) {
193
+ newBuffer.set(this.buffer.subarray(0, this.bufferOffset));
194
+ }
195
+ this.buffer = newBuffer;
196
+ }
197
+ const space = targetSize - this.bufferOffset;
198
+ const toWrite = Math.min(space, data.length - offset);
199
+ this.buffer.set(data.subarray(offset, offset + toWrite), this.bufferOffset);
200
+ this.bufferOffset += toWrite;
201
+ offset += toWrite;
202
+ // Flush full chunk
203
+ if (this.bufferOffset === targetSize) {
204
+ await this.flushChunk();
205
+ }
206
+ }
207
+ this.totalSize += data.length;
208
+ }
209
+ /**
210
+ * Flush current buffer as a chunk
211
+ */
212
+ async flushChunk() {
213
+ if (this.bufferOffset === 0)
214
+ return;
215
+ const chunk = this.buffer.slice(0, this.bufferOffset);
216
+ const hash = await sha256(chunk);
217
+ await this.store.put(hash, new Uint8Array(chunk));
218
+ this.chunks.push({ hash, size: chunk.length, type: LinkType.Blob });
219
+ this.bufferOffset = 0;
220
+ }
221
+ /**
222
+ * Get current root hash without finalizing
223
+ * Useful for checkpoints
224
+ */
225
+ async currentRoot() {
226
+ if (this.chunks.length === 0 && this.bufferOffset === 0) {
227
+ return null;
228
+ }
229
+ // Temporarily flush buffer
230
+ const tempChunks = [...this.chunks];
231
+ if (this.bufferOffset > 0) {
232
+ const chunk = this.buffer.slice(0, this.bufferOffset);
233
+ const hash = await sha256(chunk);
234
+ await this.store.put(hash, new Uint8Array(chunk));
235
+ tempChunks.push({ hash, size: chunk.length, type: LinkType.Blob });
236
+ }
237
+ return this.buildTreeFromChunks(tempChunks, this.totalSize);
238
+ }
239
+ /**
240
+ * Finalize the stream and return root hash
241
+ */
242
+ async finalize() {
243
+ // Flush remaining buffer
244
+ await this.flushChunk();
245
+ if (this.chunks.length === 0) {
246
+ // Empty stream - return hash of empty data
247
+ const emptyHash = await sha256(new Uint8Array(0));
248
+ await this.store.put(emptyHash, new Uint8Array(0));
249
+ return { hash: emptyHash, size: 0 };
250
+ }
251
+ const hash = await this.buildTreeFromChunks(this.chunks, this.totalSize);
252
+ return { hash, size: this.totalSize };
253
+ }
254
+ /**
255
+ * Build flat tree from chunks (for streaming files)
256
+ */
257
+ async buildTreeFromChunks(chunks, _totalSize) {
258
+ if (chunks.length === 1) {
259
+ return chunks[0].hash;
260
+ }
261
+ // Create single flat node with all links
262
+ const node = {
263
+ type: LinkType.File,
264
+ links: chunks,
265
+ };
266
+ const { data, hash } = await encodeAndHash(node);
267
+ await this.store.put(hash, data);
268
+ return hash;
269
+ }
270
+ /**
271
+ * Get stats
272
+ */
273
+ get stats() {
274
+ return {
275
+ chunks: this.chunks.length,
276
+ buffered: this.bufferOffset,
277
+ totalSize: this.totalSize,
278
+ };
279
+ }
280
+ }
281
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * MessagePack encoding/decoding for tree nodes
3
+ *
4
+ * Blobs are stored raw (not wrapped) for efficiency.
5
+ * Tree nodes are MessagePack-encoded.
6
+ *
7
+ * **Determinism:** We ensure deterministic output by:
8
+ * 1. Using fixed field order in the encoded map
9
+ * 2. Sorting metadata keys alphabetically before encoding
10
+ */
11
+ import { TreeNode, LinkType, Hash } from './types.js';
12
+ /**
13
+ * Encode a tree node to MessagePack
14
+ * Fields are ordered alphabetically for canonical encoding
15
+ */
16
+ export declare function encodeTreeNode(node: TreeNode): Uint8Array;
17
+ /**
18
+ * Try to decode MessagePack data as a tree node
19
+ * Returns null if data is not a valid tree node (i.e., it's a raw blob)
20
+ */
21
+ export declare function tryDecodeTreeNode(data: Uint8Array): TreeNode | null;
22
+ /**
23
+ * Decode MessagePack to a tree node (throws if not a tree node)
24
+ */
25
+ export declare function decodeTreeNode(data: Uint8Array): TreeNode;
26
+ /**
27
+ * Encode a tree node and compute its hash
28
+ */
29
+ export declare function encodeAndHash(node: TreeNode): Promise<{
30
+ data: Uint8Array;
31
+ hash: Hash;
32
+ }>;
33
+ /**
34
+ * Get the type of a chunk: File, Dir, or Blob
35
+ */
36
+ export declare function getNodeType(data: Uint8Array): LinkType;
37
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,QAAQ,EAAQ,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AA2C5D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAwBzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAuBnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAMzD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAI7F;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAGtD"}
package/dist/codec.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * MessagePack encoding/decoding for tree nodes
3
+ *
4
+ * Blobs are stored raw (not wrapped) for efficiency.
5
+ * Tree nodes are MessagePack-encoded.
6
+ *
7
+ * **Determinism:** We ensure deterministic output by:
8
+ * 1. Using fixed field order in the encoded map
9
+ * 2. Sorting metadata keys alphabetically before encoding
10
+ */
11
+ import { encode, decode } from '@msgpack/msgpack';
12
+ import { LinkType } from './types.js';
13
+ import { sha256 } from './hash.js';
14
+ /**
15
+ * Sort object keys alphabetically for deterministic encoding
16
+ */
17
+ function sortObjectKeys(obj) {
18
+ const sorted = {};
19
+ for (const key of Object.keys(obj).sort()) {
20
+ sorted[key] = obj[key];
21
+ }
22
+ return sorted;
23
+ }
24
+ /**
25
+ * Encode a tree node to MessagePack
26
+ * Fields are ordered alphabetically for canonical encoding
27
+ */
28
+ export function encodeTreeNode(node) {
29
+ // TreeNode fields in alphabetical order: l, t
30
+ const msgpack = {
31
+ l: node.links.map(link => {
32
+ // Link fields in alphabetical order: h, k?, m?, n?, s, t
33
+ // Build object with all fields in order, undefined values are omitted by msgpack
34
+ const l = {
35
+ h: link.hash,
36
+ k: link.key,
37
+ m: link.meta !== undefined ? sortObjectKeys(link.meta) : undefined,
38
+ n: link.name,
39
+ s: link.size,
40
+ t: link.type,
41
+ };
42
+ // Remove undefined fields to match skip_serializing_if behavior
43
+ if (l.k === undefined)
44
+ delete l.k;
45
+ if (l.m === undefined)
46
+ delete l.m;
47
+ if (l.n === undefined)
48
+ delete l.n;
49
+ return l;
50
+ }),
51
+ t: node.type,
52
+ };
53
+ return encode(msgpack);
54
+ }
55
+ /**
56
+ * Try to decode MessagePack data as a tree node
57
+ * Returns null if data is not a valid tree node (i.e., it's a raw blob)
58
+ */
59
+ export function tryDecodeTreeNode(data) {
60
+ try {
61
+ const msgpack = decode(data);
62
+ if (msgpack.t !== LinkType.File && msgpack.t !== LinkType.Dir) {
63
+ return null;
64
+ }
65
+ const node = {
66
+ type: msgpack.t,
67
+ links: msgpack.l.map(l => {
68
+ const link = { hash: l.h, size: l.s ?? 0, type: l.t ?? LinkType.Blob };
69
+ if (l.n !== undefined)
70
+ link.name = l.n;
71
+ if (l.k !== undefined)
72
+ link.key = l.k;
73
+ if (l.m !== undefined)
74
+ link.meta = l.m;
75
+ return link;
76
+ }),
77
+ };
78
+ return node;
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /**
85
+ * Decode MessagePack to a tree node (throws if not a tree node)
86
+ */
87
+ export function decodeTreeNode(data) {
88
+ const node = tryDecodeTreeNode(data);
89
+ if (!node) {
90
+ throw new Error('Data is not a valid tree node');
91
+ }
92
+ return node;
93
+ }
94
+ /**
95
+ * Encode a tree node and compute its hash
96
+ */
97
+ export async function encodeAndHash(node) {
98
+ const data = encodeTreeNode(node);
99
+ const hash = await sha256(data);
100
+ return { data, hash };
101
+ }
102
+ /**
103
+ * Get the type of a chunk: File, Dir, or Blob
104
+ */
105
+ export function getNodeType(data) {
106
+ const node = tryDecodeTreeNode(data);
107
+ return node?.type ?? LinkType.Blob;
108
+ }
109
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Encryption utilities for HashTree
3
+ *
4
+ * CHK (Content Hash Key) encryption for deterministic encryption:
5
+ * - key = SHA256(plaintext) - content hash becomes decryption key
6
+ * - encryption_key = HKDF(key, salt="hashtree-chk", info="encryption-key")
7
+ * - ciphertext = AES-256-GCM(encryption_key, zero_nonce, plaintext)
8
+ *
9
+ * Zero nonce is safe because CHK guarantees: same key = same content.
10
+ * This enables deduplication: same content → same ciphertext → same hash.
11
+ *
12
+ * Format: [ciphertext][16-byte auth tag] (no IV needed with CHK)
13
+ *
14
+ * Also includes legacy functions with random IV for backward compatibility:
15
+ * Format: [12-byte IV][ciphertext][16-byte auth tag]
16
+ */
17
+ /** 32-byte encryption key (256 bits) */
18
+ export type EncryptionKey = Uint8Array;
19
+ /** Generate a random 32-byte encryption key */
20
+ export declare function generateKey(): EncryptionKey;
21
+ /**
22
+ * Compute content hash (SHA256) - this becomes the decryption key for CHK
23
+ */
24
+ export declare function contentHash(data: Uint8Array): Promise<EncryptionKey>;
25
+ /**
26
+ * CHK encrypt: derive key from content, encrypt with zero nonce
27
+ *
28
+ * Returns: (ciphertext with auth tag, content_hash as decryption key)
29
+ *
30
+ * Zero nonce is safe because CHK guarantees: same key = same content.
31
+ * We never encrypt different content with the same key.
32
+ *
33
+ * The content_hash is both:
34
+ * - The decryption key (store securely, share with authorized users)
35
+ * - Enables dedup: same content → same ciphertext
36
+ *
37
+ * @param plaintext - Data to encrypt
38
+ * @returns Object with encrypted data and content hash (decryption key)
39
+ */
40
+ export declare function encryptChk(plaintext: Uint8Array): Promise<{
41
+ ciphertext: Uint8Array;
42
+ key: EncryptionKey;
43
+ }>;
44
+ /**
45
+ * CHK decrypt: derive key from content_hash, decrypt with zero nonce
46
+ *
47
+ * @param ciphertext - Encrypted data (includes auth tag)
48
+ * @param key - Content hash returned from encryptChk
49
+ * @returns Decrypted plaintext
50
+ * @throws Error if decryption fails (wrong key or tampered data)
51
+ */
52
+ export declare function decryptChk(ciphertext: Uint8Array, key: EncryptionKey): Promise<Uint8Array>;
53
+ /**
54
+ * Calculate encrypted size for CHK (no nonce prefix, just ciphertext + auth tag)
55
+ */
56
+ export declare function encryptedSizeChk(plaintextSize: number): number;
57
+ /**
58
+ * Encrypt data using AES-256-GCM with random IV
59
+ * @deprecated Use encryptChk for deterministic CHK encryption
60
+ *
61
+ * @param plaintext - Data to encrypt
62
+ * @param key - 32-byte encryption key
63
+ * @returns Encrypted data: [12-byte IV][ciphertext + 16-byte auth tag]
64
+ */
65
+ export declare function encrypt(plaintext: Uint8Array, key: EncryptionKey): Promise<Uint8Array>;
66
+ /**
67
+ * Decrypt data using AES-256-GCM
68
+ *
69
+ * @param encrypted - Encrypted data: [12-byte IV][ciphertext + auth tag]
70
+ * @param key - 32-byte encryption key
71
+ * @returns Decrypted plaintext
72
+ * @throws Error if decryption fails (wrong key or tampered data)
73
+ */
74
+ export declare function decrypt(encrypted: Uint8Array, key: EncryptionKey): Promise<Uint8Array>;
75
+ /**
76
+ * Check if data could be encrypted (based on minimum size).
77
+ * Note: This is a heuristic - actual encrypted data might be larger.
78
+ */
79
+ export declare function couldBeEncrypted(data: Uint8Array): boolean;
80
+ /**
81
+ * Calculate encrypted size for given plaintext size
82
+ */
83
+ export declare function encryptedSize(plaintextSize: number): number;
84
+ /**
85
+ * Calculate plaintext size from encrypted size
86
+ */
87
+ export declare function plaintextSize(encryptedSize: number): number;
88
+ /** Convert key to hex string */
89
+ export declare function keyToHex(key: EncryptionKey): string;
90
+ /** Convert hex string to key */
91
+ export declare function keyFromHex(hex: string): EncryptionKey;
92
+ //# sourceMappingURL=crypto.d.ts.map