⚡ Zero-dependency plcbundle library exclusively for Bun
1/**
2 * Type definitions for plcbundle library
3 *
4 * This module contains all TypeScript type definitions used throughout
5 * the plcbundle library.
6 *
7 * @module
8 */
9
10/**
11 * Metadata for a single bundle in the repository.
12 *
13 * Contains information about the bundle's contents, hashes for verification,
14 * and temporal boundaries.
15 */
16export interface BundleMetadata {
17 /** Sequential number identifying this bundle (e.g., 1, 2, 3...) */
18 bundle_number: number;
19
20 /** ISO 8601 timestamp of the first operation in this bundle */
21 start_time: string;
22
23 /** ISO 8601 timestamp of the last operation in this bundle */
24 end_time: string;
25
26 /** Total number of PLC operations contained in this bundle */
27 operation_count: number;
28
29 /** Number of unique DIDs referenced in this bundle */
30 did_count: number;
31
32 /** Chain hash linking this bundle to its predecessor */
33 hash: string;
34
35 /** SHA-256 hash of the uncompressed JSONL content */
36 content_hash: string;
37
38 /** Chain hash of the previous bundle (empty string for genesis bundle) */
39 parent: string;
40
41 /** SHA-256 hash of the compressed .jsonl.zst file */
42 compressed_hash: string;
43
44 /** Size of the compressed bundle file in bytes */
45 compressed_size: number;
46
47 /** Size of the uncompressed JSONL content in bytes */
48 uncompressed_size: number;
49
50 /** Cursor for fetching subsequent operations (end_time of previous bundle) */
51 cursor: string;
52
53 /** ISO 8601 timestamp when this bundle was created */
54 created_at: string;
55}
56
57/**
58 * Index file containing metadata for all bundles in a repository.
59 *
60 * This is the main entry point for discovering available bundles.
61 * Located at `plc_bundles.json` in the repository root.
62 */
63export interface BundleIndex {
64 /** Version of the index format (currently "1.0") */
65 version: string;
66
67 /** Bundle number of the most recent bundle */
68 last_bundle: number;
69
70 /** ISO 8601 timestamp when the index was last updated */
71 updated_at: string;
72
73 /** Total size of all compressed bundles in bytes */
74 total_size_bytes: number;
75
76 /** Array of metadata for each bundle, sorted by bundle_number */
77 bundles: BundleMetadata[];
78}
79
80/**
81 * A single PLC operation as stored in bundles.
82 *
83 * Operations represent changes to DID documents in the PLC directory.
84 */
85export interface Operation {
86 /** Decentralized Identifier (DID) this operation applies to */
87 did: string;
88
89 /** Content Identifier (CID) of this operation */
90 cid: string;
91
92 /** The actual operation data containing DID document changes */
93 operation: any;
94
95 /** ISO 8601 timestamp when this operation was created */
96 createdAt: string;
97
98 /** Additional fields that may be present in operations */
99 [key: string]: any;
100}
101
102/**
103 * Callback function called for each operation during processing.
104 *
105 * @param op - The operation being processed
106 * @param position - Zero-based position of the operation within its bundle
107 * @param bundleNum - The bundle number being processed
108 * @param line - The raw JSONL line (for getting size without re-serializing)
109 */
110export type ProcessCallback = (
111 op: Operation,
112 position: number,
113 bundleNum: number,
114 line: string
115) => void | Promise<void>;
116
117/**
118 * Statistics collected during bundle processing.
119 *
120 * Tracks the number of operations and bytes processed.
121 */
122export interface ProcessStats {
123 /** Total number of operations processed */
124 totalOps: number;
125
126 /** Number of operations that matched criteria (if applicable) */
127 matchCount: number;
128
129 /** Total bytes of operation data processed */
130 totalBytes: number;
131
132 /** Bytes of data for matched operations (if applicable) */
133 matchedBytes: number;
134}
135
136/**
137 * Unified options for processing bundles.
138 *
139 * Supports both callback functions (single-threaded) and module paths (multi-threaded).
140 */
141export interface ProcessOptions {
142 /**
143 * Number of worker threads (default: 1).
144 * If > 1, requires `module` instead of passing callback directly.
145 */
146 threads?: number;
147
148 /**
149 * Path to module exporting a `detect` or default function.
150 * Required when threads > 1. The module should export:
151 * ```ts
152 * export function detect({ op }) { return [...labels]; }
153 * ```
154 */
155 module?: string;
156
157 /**
158 * Suppress all console output from the detect script (default: false).
159 *
160 * When enabled, console.log, console.error, etc. from the detect function
161 * are silenced. Progress reporting and final statistics are still shown.
162 *
163 * @example
164 * ```ts
165 * await bundle.processBundles(1, 10, {
166 * module: './noisy-detect.ts',
167 * silent: true // Suppress debug output
168 * });
169 * ```
170 */
171 silent?: boolean;
172
173 /**
174 * Output matches immediately without buffering or sorting (default: false).
175 *
176 * When enabled, matches are output as soon as they're found rather than
177 * being collected, sorted, and output at the end. Useful for:
178 * - Real-time streaming output
179 * - Reducing memory usage with large result sets
180 * - Early access to results
181 *
182 * Note: With flush enabled, matches may be out of chronological order
183 * when using multiple threads.
184 *
185 * @example
186 * ```ts
187 * await bundle.processBundles(1, 100, {
188 * module: './detect.ts',
189 * threads: 4,
190 * flush: true,
191 * onMatch: (match) => {
192 * console.log(`Found: ${match.bundle}/${match.position}`);
193 * }
194 * });
195 * ```
196 */
197 flush?: boolean;
198
199 /**
200 * Progress callback invoked periodically during processing.
201 *
202 * Called approximately every 10,000 operations to report current
203 * processing statistics.
204 *
205 * @param stats - Current processing statistics
206 *
207 * @example
208 * ```ts
209 * await bundle.processBundles(1, 100, callback, {
210 * onProgress: (stats) => {
211 * console.log(`${stats.totalOps} operations processed`);
212 * }
213 * });
214 * ```
215 */
216 onProgress?: (stats: ProcessStats) => void;
217
218 /**
219 * Match callback invoked for each match when flush mode is enabled.
220 *
221 * Only called when `flush: true`. Receives match objects as they're
222 * found, without waiting for sorting. Ignored in non-flush mode.
223 *
224 * @param match - The match object with bundle, position, cid, size, and labels
225 *
226 * @example
227 * ```ts
228 * await bundle.processBundles(1, 100, {
229 * module: './detect.ts',
230 * flush: true,
231 * onMatch: (match) => {
232 * // Output immediately
233 * console.log(`${match.bundle},${match.position},${match.labels}`);
234 * }
235 * });
236 * ```
237 */
238 onMatch?: (match: any) => void;
239}
240
241/**
242 * Options for cloning bundles from a remote repository.
243 *
244 * Controls download behavior, verification, and progress reporting.
245 */
246export interface CloneOptions {
247 /** Number of parallel download threads (default: 4) */
248 threads?: number;
249
250 /**
251 * Bundle selection specification.
252 *
253 * Can be:
254 * - A single bundle number: `"42"`
255 * - A range: `"1-100"`
256 * - Undefined to clone all available bundles
257 */
258 bundles?: string;
259
260 /** Whether to verify SHA-256 hashes of downloaded bundles (default: true) */
261 verify?: boolean;
262
263 /**
264 * Function to check if cloning should stop (for graceful shutdown).
265 *
266 * @returns `true` if cloning should stop, `false` to continue
267 */
268 shouldStop?: () => boolean;
269
270 /**
271 * Callback invoked to report download progress.
272 *
273 * @param stats - Current download statistics
274 */
275 onProgress?: (stats: CloneStats) => void;
276}
277
278/**
279 * Statistics collected during bundle cloning.
280 *
281 * Tracks download progress, including successes, skips, and failures.
282 */
283export interface CloneStats {
284 /** Total number of bundles to download */
285 totalBundles: number;
286
287 /** Number of bundles successfully downloaded in this session */
288 downloadedBundles: number;
289
290 /** Number of bundles skipped (already existed and verified) */
291 skippedBundles: number;
292
293 /** Number of bundles that failed to download */
294 failedBundles: number;
295
296 /** Total bytes to download across all bundles */
297 totalBytes: number;
298
299 /** Bytes downloaded so far (including skipped bundles) */
300 downloadedBytes: number;
301}
302
303/**
304 * Result of chain verification.
305 *
306 * Contains overall validity status and detailed information about
307 * any issues found in the bundle chain.
308 */
309export interface ChainVerificationResult {
310 /** Whether the entire chain is valid */
311 valid: boolean;
312
313 /** Total number of bundles verified */
314 totalBundles: number;
315
316 /** Number of bundles that passed verification */
317 validBundles: number;
318
319 /** Number of bundles that failed verification */
320 invalidBundles: number;
321
322 /** Detailed errors for each invalid bundle */
323 errors: Array<{
324 bundleNum: number;
325 errors: string[];
326 }>;
327}