@unrdf/kgc-runtime 26.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/IMPLEMENTATION_SUMMARY.json +150 -0
  2. package/PLUGIN_SYSTEM_SUMMARY.json +149 -0
  3. package/README.md +98 -0
  4. package/TRANSACTION_IMPLEMENTATION.json +119 -0
  5. package/capability-map.md +93 -0
  6. package/docs/api-stability.md +269 -0
  7. package/docs/extensions/plugin-development.md +382 -0
  8. package/package.json +40 -0
  9. package/plugins/registry.json +35 -0
  10. package/src/admission-gate.mjs +414 -0
  11. package/src/api-version.mjs +373 -0
  12. package/src/atomic-admission.mjs +310 -0
  13. package/src/bounds.mjs +289 -0
  14. package/src/bulkhead-manager.mjs +280 -0
  15. package/src/capsule.mjs +524 -0
  16. package/src/crdt.mjs +361 -0
  17. package/src/enhanced-bounds.mjs +614 -0
  18. package/src/executor.mjs +73 -0
  19. package/src/freeze-restore.mjs +521 -0
  20. package/src/index.mjs +62 -0
  21. package/src/materialized-views.mjs +371 -0
  22. package/src/merge.mjs +472 -0
  23. package/src/plugin-isolation.mjs +392 -0
  24. package/src/plugin-manager.mjs +441 -0
  25. package/src/projections-api.mjs +336 -0
  26. package/src/projections-cli.mjs +238 -0
  27. package/src/projections-docs.mjs +300 -0
  28. package/src/projections-ide.mjs +278 -0
  29. package/src/receipt.mjs +340 -0
  30. package/src/rollback.mjs +258 -0
  31. package/src/saga-orchestrator.mjs +355 -0
  32. package/src/schemas.mjs +1330 -0
  33. package/src/storage-optimization.mjs +359 -0
  34. package/src/tool-registry.mjs +272 -0
  35. package/src/transaction.mjs +466 -0
  36. package/src/validators.mjs +485 -0
  37. package/src/work-item.mjs +449 -0
  38. package/templates/plugin-template/README.md +58 -0
  39. package/templates/plugin-template/index.mjs +162 -0
  40. package/templates/plugin-template/plugin.json +19 -0
  41. package/test/admission-gate.test.mjs +583 -0
  42. package/test/api-version.test.mjs +74 -0
  43. package/test/atomic-admission.test.mjs +155 -0
  44. package/test/bounds.test.mjs +341 -0
  45. package/test/bulkhead-manager.test.mjs +236 -0
  46. package/test/capsule.test.mjs +625 -0
  47. package/test/crdt.test.mjs +215 -0
  48. package/test/enhanced-bounds.test.mjs +487 -0
  49. package/test/freeze-restore.test.mjs +472 -0
  50. package/test/materialized-views.test.mjs +243 -0
  51. package/test/merge.test.mjs +665 -0
  52. package/test/plugin-isolation.test.mjs +109 -0
  53. package/test/plugin-manager.test.mjs +208 -0
  54. package/test/projections-api.test.mjs +293 -0
  55. package/test/projections-cli.test.mjs +204 -0
  56. package/test/projections-docs.test.mjs +173 -0
  57. package/test/projections-ide.test.mjs +230 -0
  58. package/test/receipt.test.mjs +295 -0
  59. package/test/rollback.test.mjs +132 -0
  60. package/test/saga-orchestrator.test.mjs +279 -0
  61. package/test/schemas.test.mjs +716 -0
  62. package/test/storage-optimization.test.mjs +503 -0
  63. package/test/tool-registry.test.mjs +341 -0
  64. package/test/transaction.test.mjs +189 -0
  65. package/test/validators.test.mjs +463 -0
  66. package/test/work-item.test.mjs +548 -0
  67. package/test/work-item.test.mjs.bak +548 -0
  68. package/var/kgc/test-atomic-log.json +519 -0
  69. package/var/kgc/test-cascading-log.json +145 -0
  70. package/vitest.config.mjs +18 -0
@@ -0,0 +1,521 @@
1
+ /**
2
+ * KGC Runtime Freeze-Restore - Universal Checkpointing and Reconstruction
3
+ *
4
+ * Provides nanosecond-precision state snapshots with BLAKE3 integrity verification.
5
+ * Supports both Git-backed and filesystem-based storage strategies.
6
+ *
7
+ * @module freeze-restore
8
+ */
9
+
10
+ import { blake3 } from 'hash-wasm';
11
+ import { promises as fs } from 'fs';
12
+ import * as path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { dirname } from 'path';
15
+ import {
16
+ compressFile,
17
+ readCompressed,
18
+ computeDelta,
19
+ applyDelta,
20
+ } from './storage-optimization.mjs';
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = dirname(__filename);
24
+
25
+ /** @typedef {Object} SnapshotManifest
26
+ * @property {string} timestamp_ns - Nanosecond precision timestamp (as string for BigInt)
27
+ * @property {string} o_hash - BLAKE3 hash of canonical state
28
+ * @property {number} file_count - Number of files in snapshot
29
+ * @property {number} total_bytes - Total bytes in snapshot
30
+ * @property {string} created_at - ISO 8601 timestamp
31
+ * @property {boolean} [compressed] - Whether snapshot is compressed
32
+ * @property {number} [original_size] - Original size before compression
33
+ * @property {number} [compressed_size] - Size after compression
34
+ * @property {boolean} [incremental] - Whether snapshot is incremental
35
+ * @property {string} [base_snapshot] - Base snapshot timestamp for incremental
36
+ */
37
+
38
+ /** @typedef {Object} UniverseState
39
+ * @property {Object} data - Arbitrary universe state data
40
+ * @property {bigint} [timestamp] - Optional timestamp
41
+ */
42
+
43
+ /** @typedef {Object} Snapshot
44
+ * @property {SnapshotManifest} manifest - Snapshot metadata
45
+ * @property {string} path - Filesystem path to snapshot
46
+ * @property {UniverseState} state - Reconstructed state
47
+ */
48
+
49
+ /**
50
+ * Get default snapshot directory path
51
+ * @returns {string} Absolute path to snapshots directory
52
+ */
53
+ function getSnapshotDir() {
54
+ // Navigate up from src/ to package root, then to var/kgc/snapshots
55
+ const packageRoot = path.resolve(__dirname, '..');
56
+ return path.join(packageRoot, 'var', 'kgc', 'snapshots');
57
+ }
58
+
59
+ /**
60
+ * Normalize and canonicalize state for deterministic hashing
61
+ * @param {UniverseState} state - Universe state object
62
+ * @returns {string} Canonical JSON representation
63
+ */
64
+ function canonicalizeState(state) {
65
+ // Deep sort keys recursively for deterministic ordering
66
+ function sortKeys(obj) {
67
+ if (obj === null || typeof obj !== 'object') {
68
+ return obj;
69
+ }
70
+ if (Array.isArray(obj)) {
71
+ return obj.map(sortKeys);
72
+ }
73
+ const sorted = {};
74
+ const keys = Object.keys(obj).sort();
75
+ for (const key of keys) {
76
+ sorted[key] = sortKeys(obj[key]);
77
+ }
78
+ return sorted;
79
+ }
80
+
81
+ // Convert BigInt to string for JSON serialization
82
+ function replacer(key, value) {
83
+ if (typeof value === 'bigint') {
84
+ return value.toString() + 'n';
85
+ }
86
+ return value;
87
+ }
88
+
89
+ const normalized = sortKeys(state);
90
+ return JSON.stringify(normalized, replacer, 0); // No whitespace for determinism
91
+ }
92
+
93
+ /**
94
+ * Freeze universe to snapshot with BLAKE3 integrity hash
95
+ *
96
+ * Creates a point-in-time snapshot of universe state with:
97
+ * - Nanosecond-precision timestamp (BigInt)
98
+ * - BLAKE3 hash for integrity verification
99
+ * - Manifest with metadata
100
+ * - Compressed JSON serialization
101
+ *
102
+ * @param {UniverseState} O - Universe state to freeze
103
+ * @param {Object} [options] - Freeze options
104
+ * @param {string} [options.snapshotDir] - Custom snapshot directory
105
+ * @param {boolean} [options.useGit=false] - Use Git backbone if available
106
+ * @param {boolean} [options.compress=true] - Compress snapshot with gzip
107
+ * @param {boolean} [options.incremental=false] - Create incremental snapshot
108
+ * @returns {Promise<SnapshotManifest>} Snapshot manifest with hash and metadata
109
+ * @throws {TypeError} If O is not an object
110
+ * @throws {Error} If freeze operation fails
111
+ *
112
+ * @example
113
+ * const universe = { entities: [...], timestamp: 1234567890123456789n };
114
+ * const manifest = await freezeUniverse(universe);
115
+ * console.assert(manifest.o_hash, 'Has BLAKE3 hash');
116
+ * console.assert(manifest.timestamp_ns, 'Has nanosecond timestamp');
117
+ */
118
+ export async function freezeUniverse(O, options = {}) {
119
+ // Validation
120
+ if (!O || typeof O !== 'object') {
121
+ throw new TypeError('freezeUniverse: O must be an object');
122
+ }
123
+
124
+ try {
125
+ // 1. Generate nanosecond timestamp
126
+ const timestamp_ns = process.hrtime.bigint();
127
+
128
+ // 2. Canonicalize state for deterministic hashing
129
+ const canonical_state = canonicalizeState(O);
130
+
131
+ // 3. Compute BLAKE3 hash
132
+ const o_hash = await blake3(canonical_state);
133
+
134
+ // 4. Create snapshot directory
135
+ const snapshotDir = options.snapshotDir || getSnapshotDir();
136
+ const snapshotPath = path.join(snapshotDir, timestamp_ns.toString());
137
+ await fs.mkdir(snapshotPath, { recursive: true });
138
+
139
+ // 5. Handle incremental snapshots
140
+ let stateToWrite = canonical_state;
141
+ let isIncremental = false;
142
+ let baseSnapshot = null;
143
+
144
+ if (options.incremental) {
145
+ // Get previous snapshot for delta computation
146
+ const snapshots = await getSnapshotList({ snapshotDir });
147
+ if (snapshots.length > 0) {
148
+ const prevSnapshot = snapshots[0];
149
+ const prevStatePath = path.join(
150
+ prevSnapshot.path,
151
+ prevSnapshot.manifest.compressed ? 'state.json.gz' : 'state.json'
152
+ );
153
+
154
+ try {
155
+ let prevState;
156
+ if (prevSnapshot.manifest.compressed) {
157
+ prevState = await readCompressed(prevStatePath);
158
+ } else {
159
+ prevState = await fs.readFile(prevStatePath, 'utf-8');
160
+ }
161
+
162
+ const prevObj = JSON.parse(prevState);
163
+ const currObj = JSON.parse(canonical_state);
164
+ const delta = computeDelta(prevObj, currObj);
165
+
166
+ // Only use delta if it's significantly smaller
167
+ const deltaStr = JSON.stringify(delta);
168
+ if (deltaStr.length < canonical_state.length * 0.5) {
169
+ stateToWrite = deltaStr;
170
+ isIncremental = true;
171
+ baseSnapshot = prevSnapshot.manifest.timestamp_ns;
172
+ }
173
+ } catch {
174
+ // Fall back to full snapshot if delta fails
175
+ }
176
+ }
177
+ }
178
+
179
+ // 6. Write state file
180
+ const statePath = path.join(snapshotPath, 'state.json');
181
+ await fs.writeFile(statePath, stateToWrite, 'utf-8');
182
+
183
+ // 7. Compress if requested (default: true)
184
+ const shouldCompress = options.compress !== false;
185
+ let finalSize = 0;
186
+ let originalSize = 0;
187
+ let compressed = false;
188
+
189
+ if (shouldCompress) {
190
+ const compressedPath = statePath + '.gz';
191
+ const compressionResult = await compressFile(statePath, compressedPath);
192
+ originalSize = compressionResult.original_size;
193
+ finalSize = compressionResult.compressed_size;
194
+ compressed = true;
195
+
196
+ // Remove uncompressed file
197
+ await fs.unlink(statePath);
198
+ } else {
199
+ const stats = await fs.stat(statePath);
200
+ finalSize = stats.size;
201
+ originalSize = stats.size;
202
+ }
203
+
204
+ // 8. Create manifest
205
+ const manifest = {
206
+ timestamp_ns: timestamp_ns.toString(),
207
+ o_hash,
208
+ file_count: 1,
209
+ total_bytes: finalSize,
210
+ created_at: new Date().toISOString(),
211
+ compressed,
212
+ ...(compressed && { original_size: originalSize, compressed_size: finalSize }),
213
+ ...(isIncremental && { incremental: true, base_snapshot: baseSnapshot }),
214
+ };
215
+
216
+ // 9. Write manifest
217
+ const manifestPath = path.join(snapshotPath, 'manifest.json');
218
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
219
+
220
+ // 10. Optional: Git commit if requested
221
+ if (options.useGit) {
222
+ try {
223
+ const gitDir = path.resolve(snapshotDir, '../../..');
224
+ const gitExists = await fs.access(path.join(gitDir, '.git'))
225
+ .then(() => true)
226
+ .catch(() => false);
227
+
228
+ if (gitExists) {
229
+ // Git operations would go here - simplified for now
230
+ // This would use isomorphic-git to commit the snapshot
231
+ }
232
+ } catch (gitError) {
233
+ // Git operations are optional - continue without them
234
+ if (typeof console !== 'undefined' && console.warn) {
235
+ console.warn(`[KGC Freeze] Git commit skipped: ${gitError.message}`);
236
+ }
237
+ }
238
+ }
239
+
240
+ return manifest;
241
+ } catch (error) {
242
+ throw new Error(`Failed to freeze universe: ${error.message}`);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Verify snapshot integrity by recomputing hash
248
+ *
249
+ * Validates that snapshot data matches stored hash, ensuring:
250
+ * - Data integrity (no corruption)
251
+ * - Authenticity (matches original freeze)
252
+ * - Completeness (all files present)
253
+ *
254
+ * @param {SnapshotManifest|string} snapshot - Snapshot manifest or timestamp string
255
+ * @param {Object} [options] - Verification options
256
+ * @param {string} [options.snapshotDir] - Custom snapshot directory
257
+ * @returns {Promise<boolean>} True if snapshot is valid
258
+ * @throws {TypeError} If snapshot parameter is invalid
259
+ * @throws {Error} If snapshot not found
260
+ *
261
+ * @example
262
+ * const valid = await verifyFreeze(manifest);
263
+ * console.assert(valid === true, 'Snapshot is valid');
264
+ */
265
+ export async function verifyFreeze(snapshot, options = {}) {
266
+ // Handle both manifest objects and timestamp strings
267
+ let timestamp_ns;
268
+ let expected_hash;
269
+
270
+ if (typeof snapshot === 'string') {
271
+ timestamp_ns = snapshot;
272
+ // Load manifest to get hash
273
+ const snapshotDir = options.snapshotDir || getSnapshotDir();
274
+ const manifestPath = path.join(snapshotDir, timestamp_ns, 'manifest.json');
275
+
276
+ try {
277
+ const manifestData = await fs.readFile(manifestPath, 'utf-8');
278
+ const manifest = JSON.parse(manifestData);
279
+ expected_hash = manifest.o_hash;
280
+ } catch (error) {
281
+ throw new Error(`Snapshot not found: ${timestamp_ns}`);
282
+ }
283
+ } else if (snapshot && typeof snapshot === 'object') {
284
+ if (!snapshot.timestamp_ns || !snapshot.o_hash) {
285
+ throw new TypeError('verifyFreeze: snapshot must have timestamp_ns and o_hash');
286
+ }
287
+ timestamp_ns = snapshot.timestamp_ns;
288
+ expected_hash = snapshot.o_hash;
289
+ } else {
290
+ throw new TypeError('verifyFreeze: snapshot must be manifest object or timestamp string');
291
+ }
292
+
293
+ try {
294
+ // 1. Load manifest to check compression
295
+ const snapshotDir = options.snapshotDir || getSnapshotDir();
296
+ const manifestPath = path.join(snapshotDir, timestamp_ns, 'manifest.json');
297
+ let manifest;
298
+ try {
299
+ const manifestData = await fs.readFile(manifestPath, 'utf-8');
300
+ manifest = JSON.parse(manifestData);
301
+ } catch {
302
+ manifest = { compressed: false };
303
+ }
304
+
305
+ // 2. Load state file (handle compression)
306
+ const isCompressed = manifest.compressed === true;
307
+ const stateFileName = isCompressed ? 'state.json.gz' : 'state.json';
308
+ const statePath = path.join(snapshotDir, timestamp_ns, stateFileName);
309
+
310
+ let stateData;
311
+ if (isCompressed) {
312
+ stateData = await readCompressed(statePath);
313
+ } else {
314
+ stateData = await fs.readFile(statePath, 'utf-8');
315
+ }
316
+
317
+ // 3. Recompute hash
318
+ const recomputed_hash = await blake3(stateData);
319
+
320
+ // 4. Compare hashes
321
+ return recomputed_hash === expected_hash;
322
+ } catch (error) {
323
+ throw new Error(`Failed to verify snapshot: ${error.message}`);
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Reconstruct universe state from snapshot at specific time
329
+ *
330
+ * Loads and validates snapshot, returning reconstructed state:
331
+ * - Finds snapshot at or before target time
332
+ * - Validates manifest integrity
333
+ * - Deserializes state
334
+ * - Returns fully reconstructed object
335
+ *
336
+ * @param {bigint|string} t_ns - Target time in nanoseconds (BigInt or string)
337
+ * @param {Object} [options] - Reconstruction options
338
+ * @param {string} [options.snapshotDir] - Custom snapshot directory
339
+ * @param {boolean} [options.exact=false] - Require exact timestamp match
340
+ * @returns {Promise<UniverseState>} Reconstructed universe state
341
+ * @throws {TypeError} If t_ns is invalid
342
+ * @throws {Error} If no snapshot found before target time
343
+ *
344
+ * @example
345
+ * const targetTime = 1234567890123456789n;
346
+ * const reconstructed = await reconstructTo(targetTime);
347
+ * console.log('State at', targetTime, ':', reconstructed);
348
+ */
349
+ export async function reconstructTo(t_ns, options = {}) {
350
+ // Normalize to BigInt
351
+ let targetTime;
352
+ if (typeof t_ns === 'bigint') {
353
+ targetTime = t_ns;
354
+ } else if (typeof t_ns === 'string') {
355
+ targetTime = BigInt(t_ns);
356
+ } else {
357
+ throw new TypeError('reconstructTo: t_ns must be BigInt or string');
358
+ }
359
+
360
+ if (targetTime < 0n) {
361
+ throw new RangeError('reconstructTo: t_ns must be non-negative');
362
+ }
363
+
364
+ try {
365
+ // 1. Get list of all snapshots
366
+ const snapshots = await getSnapshotList(options);
367
+
368
+ if (snapshots.length === 0) {
369
+ throw new Error('No snapshots available for reconstruction');
370
+ }
371
+
372
+ // 2. Find best snapshot (at or before target time)
373
+ let bestSnapshot = null;
374
+ let bestTime = -1n;
375
+
376
+ for (const snapshot of snapshots) {
377
+ const snapshotTime = BigInt(snapshot.manifest.timestamp_ns);
378
+
379
+ if (options.exact) {
380
+ // Exact match required
381
+ if (snapshotTime === targetTime) {
382
+ bestSnapshot = snapshot;
383
+ bestTime = snapshotTime;
384
+ break;
385
+ }
386
+ } else {
387
+ // Find closest snapshot at or before target
388
+ if (snapshotTime <= targetTime && snapshotTime > bestTime) {
389
+ bestSnapshot = snapshot;
390
+ bestTime = snapshotTime;
391
+ }
392
+ }
393
+ }
394
+
395
+ if (!bestSnapshot) {
396
+ const matchType = options.exact ? 'exact' : 'at or before';
397
+ throw new Error(`No snapshot found ${matchType} time ${targetTime}`);
398
+ }
399
+
400
+ // 3. Verify snapshot integrity
401
+ const isValid = await verifyFreeze(bestSnapshot.manifest, options);
402
+ if (!isValid) {
403
+ throw new Error(`Snapshot integrity check failed for ${bestSnapshot.manifest.timestamp_ns}`);
404
+ }
405
+
406
+ // 4. Load and deserialize state (handle compression and incremental)
407
+ const snapshotDir = options.snapshotDir || getSnapshotDir();
408
+ const isCompressed = bestSnapshot.manifest.compressed === true;
409
+ const stateFileName = isCompressed ? 'state.json.gz' : 'state.json';
410
+ const statePath = path.join(
411
+ snapshotDir,
412
+ bestSnapshot.manifest.timestamp_ns,
413
+ stateFileName
414
+ );
415
+
416
+ let stateData;
417
+ if (isCompressed) {
418
+ stateData = await readCompressed(statePath);
419
+ } else {
420
+ stateData = await fs.readFile(statePath, 'utf-8');
421
+ }
422
+
423
+ // 5. Handle incremental snapshots
424
+ let finalState;
425
+ if (bestSnapshot.manifest.incremental && bestSnapshot.manifest.base_snapshot) {
426
+ // Reconstruct from base + delta
427
+ const delta = JSON.parse(stateData);
428
+ const baseState = await reconstructTo(
429
+ BigInt(bestSnapshot.manifest.base_snapshot),
430
+ options
431
+ );
432
+ finalState = applyDelta(baseState, delta);
433
+ } else {
434
+ // Revive BigInt values
435
+ finalState = JSON.parse(stateData, (key, value) => {
436
+ if (typeof value === 'string' && value.endsWith('n')) {
437
+ try {
438
+ return BigInt(value.slice(0, -1));
439
+ } catch {
440
+ return value;
441
+ }
442
+ }
443
+ return value;
444
+ });
445
+ }
446
+
447
+ return finalState;
448
+ } catch (error) {
449
+ throw new Error(`Failed to reconstruct state: ${error.message}`);
450
+ }
451
+ }
452
+
453
+ /**
454
+ * Get list of all snapshots sorted by timestamp
455
+ *
456
+ * Scans snapshot directory and returns sorted list of available snapshots
457
+ * with their manifests loaded and validated.
458
+ *
459
+ * @param {Object} [options] - List options
460
+ * @param {string} [options.snapshotDir] - Custom snapshot directory
461
+ * @param {boolean} [options.ascending=false] - Sort oldest first (default: newest first)
462
+ * @returns {Promise<Snapshot[]>} Array of snapshots sorted by time
463
+ *
464
+ * @example
465
+ * const snapshots = await getSnapshotList();
466
+ * console.log(`Found ${snapshots.length} snapshots`);
467
+ * console.log('Latest:', snapshots[0].manifest.timestamp_ns);
468
+ */
469
+ export async function getSnapshotList(options = {}) {
470
+ const snapshotDir = options.snapshotDir || getSnapshotDir();
471
+
472
+ try {
473
+ // Ensure directory exists
474
+ await fs.mkdir(snapshotDir, { recursive: true });
475
+
476
+ // Read directory entries
477
+ const entries = await fs.readdir(snapshotDir, { withFileTypes: true });
478
+
479
+ // Filter for directories (each snapshot is a directory)
480
+ const snapshotDirs = entries
481
+ .filter(entry => entry.isDirectory())
482
+ .map(entry => entry.name);
483
+
484
+ // Load manifests
485
+ const snapshots = [];
486
+ for (const dirName of snapshotDirs) {
487
+ try {
488
+ const manifestPath = path.join(snapshotDir, dirName, 'manifest.json');
489
+ const manifestData = await fs.readFile(manifestPath, 'utf-8');
490
+ const manifest = JSON.parse(manifestData);
491
+
492
+ snapshots.push({
493
+ manifest,
494
+ path: path.join(snapshotDir, dirName),
495
+ });
496
+ } catch (error) {
497
+ // Skip invalid snapshots
498
+ if (typeof console !== 'undefined' && console.warn) {
499
+ console.warn(`[KGC Snapshots] Skipped invalid snapshot ${dirName}: ${error.message}`);
500
+ }
501
+ }
502
+ }
503
+
504
+ // Sort by timestamp (newest first by default)
505
+ snapshots.sort((a, b) => {
506
+ const timeA = BigInt(a.manifest.timestamp_ns);
507
+ const timeB = BigInt(b.manifest.timestamp_ns);
508
+
509
+ if (options.ascending) {
510
+ return timeA < timeB ? -1 : timeA > timeB ? 1 : 0;
511
+ } else {
512
+ return timeB < timeA ? -1 : timeB > timeA ? 1 : 0;
513
+ }
514
+ });
515
+
516
+ return snapshots;
517
+ } catch (error) {
518
+ // If directory doesn't exist or other error, return empty array
519
+ return [];
520
+ }
521
+ }
package/src/index.mjs ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * KGC Runtime - Main Entry Point
3
+ * Exports admission gate, receipt chain system, work item executor, merge functionality,
4
+ * custom validators, and enhanced bounds enforcement
5
+ */
6
+
7
+ export { AdmissionGate } from './admission-gate.mjs';
8
+ export { WorkItemExecutor, WORK_ITEM_STATES } from './work-item.mjs';
9
+ export {
10
+ shardMerge,
11
+ mergeCapsules,
12
+ ConflictDetector,
13
+ ConflictResolver,
14
+ } from './merge.mjs';
15
+ export { EnhancedBoundsChecker } from './enhanced-bounds.mjs';
16
+ export {
17
+ validateReceiptChainIntegrity,
18
+ validateTemporalConsistency,
19
+ validateArtifactHash,
20
+ validateDependencyDAG,
21
+ validateAsyncPolicy,
22
+ validateTimeRange,
23
+ ReceiptChainSchema,
24
+ TemporallyOrderedSchema,
25
+ ArtifactSchema,
26
+ WorkItemDependencySchema,
27
+ RunCapsuleTimeRangeSchema,
28
+ createAsyncPolicySchema,
29
+ detectCycle,
30
+ combineValidators,
31
+ createValidationResult,
32
+ } from './validators.mjs';
33
+ export {
34
+ generateReceipt,
35
+ verifyReceiptHash,
36
+ verifyReceiptChain,
37
+ ReceiptStore,
38
+ } from './receipt.mjs';
39
+ export {
40
+ RunCapsule,
41
+ storeCapsule,
42
+ replayCapsule,
43
+ listCapsules,
44
+ } from './capsule.mjs';
45
+ export {
46
+ PluginManager,
47
+ PLUGIN_STATES,
48
+ createPluginManager,
49
+ } from './plugin-manager.mjs';
50
+ export {
51
+ PluginIsolation,
52
+ createPluginIsolation,
53
+ createPublicAPI,
54
+ } from './plugin-isolation.mjs';
55
+ export {
56
+ APIVersionManager,
57
+ CURRENT_API_VERSION,
58
+ API_STATUS,
59
+ getVersionManager,
60
+ isPluginCompatible,
61
+ validatePluginVersion,
62
+ } from './api-version.mjs';