@korajs/core 0.1.3 → 0.3.1
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.
- package/dist/{chunk-ZMUY7AVV.js → chunk-KQYXWUFV.js} +57 -23
- package/dist/{chunk-ZMUY7AVV.js.map → chunk-KQYXWUFV.js.map} +1 -1
- package/dist/{events-D_kDPDC9.d.cts → events-7Fhdjxd2.d.cts} +3 -0
- package/dist/{events-D_kDPDC9.d.ts → events-7Fhdjxd2.d.ts} +3 -0
- package/dist/index.cjs +61 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +56 -22
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +5 -4
- package/dist/internal.d.ts +5 -4
- package/dist/internal.js +1 -1
- package/package.json +2 -2
|
@@ -347,32 +347,25 @@ function topologicalSort(operations) {
|
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
|
-
const
|
|
350
|
+
const heap = new MinHeap(compareByTimestamp);
|
|
351
351
|
for (const op of operations) {
|
|
352
352
|
if ((inDegree.get(op.id) ?? 0) === 0) {
|
|
353
|
-
|
|
353
|
+
heap.push(op);
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
|
-
queue.sort(compareByTimestamp);
|
|
357
356
|
const result = [];
|
|
358
|
-
while (
|
|
359
|
-
const current =
|
|
360
|
-
if (!current) break;
|
|
357
|
+
while (heap.size > 0) {
|
|
358
|
+
const current = heap.pop();
|
|
361
359
|
result.push(current);
|
|
362
360
|
const deps = dependents.get(current.id) ?? [];
|
|
363
|
-
const newlyReady = [];
|
|
364
361
|
for (const depId of deps) {
|
|
365
362
|
const deg = (inDegree.get(depId) ?? 0) - 1;
|
|
366
363
|
inDegree.set(depId, deg);
|
|
367
364
|
if (deg === 0) {
|
|
368
365
|
const op = opMap.get(depId);
|
|
369
|
-
if (op)
|
|
366
|
+
if (op) heap.push(op);
|
|
370
367
|
}
|
|
371
368
|
}
|
|
372
|
-
if (newlyReady.length > 0) {
|
|
373
|
-
newlyReady.sort(compareByTimestamp);
|
|
374
|
-
mergeIntoSorted(queue, newlyReady);
|
|
375
|
-
}
|
|
376
369
|
}
|
|
377
370
|
if (result.length !== operations.length) {
|
|
378
371
|
throw new OperationError(
|
|
@@ -388,18 +381,59 @@ function topologicalSort(operations) {
|
|
|
388
381
|
function compareByTimestamp(a, b) {
|
|
389
382
|
return HybridLogicalClock.compare(a.timestamp, b.timestamp);
|
|
390
383
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
384
|
+
var MinHeap = class {
|
|
385
|
+
data = [];
|
|
386
|
+
cmp;
|
|
387
|
+
constructor(comparator) {
|
|
388
|
+
this.cmp = comparator;
|
|
389
|
+
}
|
|
390
|
+
get size() {
|
|
391
|
+
return this.data.length;
|
|
392
|
+
}
|
|
393
|
+
push(item) {
|
|
394
|
+
this.data.push(item);
|
|
395
|
+
this.bubbleUp(this.data.length - 1);
|
|
396
|
+
}
|
|
397
|
+
pop() {
|
|
398
|
+
const top = this.data[0];
|
|
399
|
+
const last = this.data.pop();
|
|
400
|
+
if (this.data.length > 0) {
|
|
401
|
+
this.data[0] = last;
|
|
402
|
+
this.sinkDown(0);
|
|
403
|
+
}
|
|
404
|
+
return top;
|
|
405
|
+
}
|
|
406
|
+
bubbleUp(index) {
|
|
407
|
+
while (index > 0) {
|
|
408
|
+
const parentIndex = index - 1 >> 1;
|
|
409
|
+
if (this.cmp(this.data[index], this.data[parentIndex]) >= 0) break;
|
|
410
|
+
this.swap(index, parentIndex);
|
|
411
|
+
index = parentIndex;
|
|
398
412
|
}
|
|
399
|
-
target.splice(insertIndex, 0, item);
|
|
400
|
-
insertIndex++;
|
|
401
413
|
}
|
|
402
|
-
|
|
414
|
+
sinkDown(index) {
|
|
415
|
+
const length = this.data.length;
|
|
416
|
+
while (true) {
|
|
417
|
+
let smallest = index;
|
|
418
|
+
const left = 2 * index + 1;
|
|
419
|
+
const right = 2 * index + 2;
|
|
420
|
+
if (left < length && this.cmp(this.data[left], this.data[smallest]) < 0) {
|
|
421
|
+
smallest = left;
|
|
422
|
+
}
|
|
423
|
+
if (right < length && this.cmp(this.data[right], this.data[smallest]) < 0) {
|
|
424
|
+
smallest = right;
|
|
425
|
+
}
|
|
426
|
+
if (smallest === index) break;
|
|
427
|
+
this.swap(index, smallest);
|
|
428
|
+
index = smallest;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
swap(i, j) {
|
|
432
|
+
const tmp = this.data[i];
|
|
433
|
+
this.data[i] = this.data[j];
|
|
434
|
+
this.data[j] = tmp;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
403
437
|
|
|
404
438
|
export {
|
|
405
439
|
KoraError,
|
|
@@ -418,4 +452,4 @@ export {
|
|
|
418
452
|
isValidOperation,
|
|
419
453
|
topologicalSort
|
|
420
454
|
};
|
|
421
|
-
//# sourceMappingURL=chunk-
|
|
455
|
+
//# sourceMappingURL=chunk-KQYXWUFV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors/errors.ts","../src/clock/hlc.ts","../src/operations/content-hash.ts","../src/operations/operation.ts","../src/version-vector/topological-sort.ts"],"sourcesContent":["/**\n * Base error class for all Kora errors.\n * Every error includes a machine-readable code and optional context for debugging.\n */\nexport class KoraError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly code: string,\n\t\tpublic readonly context?: Record<string, unknown>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'KoraError'\n\t}\n}\n\n/**\n * Thrown when schema validation fails during defineSchema() or at app initialization.\n */\nexport class SchemaValidationError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'SCHEMA_VALIDATION', context)\n\t\tthis.name = 'SchemaValidationError'\n\t}\n}\n\n/**\n * Thrown when an operation is invalid or cannot be created.\n */\nexport class OperationError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'OPERATION_ERROR', context)\n\t\tthis.name = 'OperationError'\n\t}\n}\n\n/**\n * Thrown when a merge conflict cannot be automatically resolved.\n */\nexport class MergeConflictError extends KoraError {\n\tconstructor(\n\t\tpublic readonly operationA: { id: string; collection: string },\n\t\tpublic readonly operationB: { id: string; collection: string },\n\t\tpublic readonly field: string,\n\t) {\n\t\tsuper(\n\t\t\t`Merge conflict on field \"${field}\" in collection \"${operationA.collection}\"`,\n\t\t\t'MERGE_CONFLICT',\n\t\t\t{ operationA: operationA.id, operationB: operationB.id, field },\n\t\t)\n\t\tthis.name = 'MergeConflictError'\n\t}\n}\n\n/**\n * Thrown when a sync error occurs.\n */\nexport class SyncError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'SYNC_ERROR', context)\n\t\tthis.name = 'SyncError'\n\t}\n}\n\n/**\n * Thrown when a storage operation fails.\n */\nexport class StorageError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'STORAGE_ERROR', context)\n\t\tthis.name = 'StorageError'\n\t}\n}\n\n/**\n * Thrown when the HLC detects excessive clock drift.\n * Drift > 60s: warning. Drift > 5min: this error is thrown, refusing to generate timestamps.\n */\nexport class ClockDriftError extends KoraError {\n\tconstructor(\n\t\tpublic readonly currentHlcTime: number,\n\t\tpublic readonly physicalTime: number,\n\t) {\n\t\tconst driftSeconds = Math.round((currentHlcTime - physicalTime) / 1000)\n\t\tsuper(\n\t\t\t`Clock drift of ${driftSeconds}s detected. Physical time is behind HLC by more than 5 minutes. This indicates a severe clock issue.`,\n\t\t\t'CLOCK_DRIFT',\n\t\t\t{ currentHlcTime, physicalTime, driftSeconds },\n\t\t)\n\t\tthis.name = 'ClockDriftError'\n\t}\n}\n","import { ClockDriftError } from '../errors/errors'\nimport type { HLCTimestamp, TimeSource } from '../types'\n\n/** Default time source using the system clock */\nconst systemTimeSource: TimeSource = { now: () => Date.now() }\n\n/** Maximum allowed drift before warning (60 seconds) */\nconst DRIFT_WARN_MS = 60_000\n\n/** Maximum allowed drift before refusing to generate timestamps (5 minutes) */\nconst DRIFT_ERROR_MS = 5 * 60_000\n\n/**\n * Hybrid Logical Clock implementation based on Kulkarni et al.\n *\n * Provides a total order that respects causality without requiring synchronized clocks.\n * Each call to now() returns a timestamp strictly greater than the previous one.\n *\n * @example\n * ```typescript\n * const clock = new HybridLogicalClock('node-1')\n * const ts1 = clock.now()\n * const ts2 = clock.now()\n * // HybridLogicalClock.compare(ts1, ts2) < 0 (ts1 is earlier)\n * ```\n */\nexport class HybridLogicalClock {\n\tprivate wallTime = 0\n\tprivate logical = 0\n\n\tconstructor(\n\t\tprivate readonly nodeId: string,\n\t\tprivate readonly timeSource: TimeSource = systemTimeSource,\n\t\tprivate readonly onDriftWarning?: (driftMs: number) => void,\n\t) {}\n\n\t/**\n\t * Generate a new timestamp for a local event.\n\t * Guarantees monotonicity: each call returns a timestamp strictly greater than the previous.\n\t *\n\t * @throws {ClockDriftError} If physical time is more than 5 minutes behind the HLC wallTime\n\t */\n\tnow(): HLCTimestamp {\n\t\tconst physicalTime = this.timeSource.now()\n\t\tthis.checkDrift(physicalTime)\n\n\t\tif (physicalTime > this.wallTime) {\n\t\t\tthis.wallTime = physicalTime\n\t\t\tthis.logical = 0\n\t\t} else {\n\t\t\tthis.logical++\n\t\t}\n\n\t\treturn { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId }\n\t}\n\n\t/**\n\t * Update clock on receiving a remote timestamp.\n\t * Merges the remote clock state with the local state to maintain causal ordering.\n\t *\n\t * @throws {ClockDriftError} If physical time is more than 5 minutes behind the resulting wallTime\n\t */\n\treceive(remote: HLCTimestamp): HLCTimestamp {\n\t\tconst physicalTime = this.timeSource.now()\n\n\t\tif (physicalTime > this.wallTime && physicalTime > remote.wallTime) {\n\t\t\tthis.wallTime = physicalTime\n\t\t\tthis.logical = 0\n\t\t} else if (remote.wallTime > this.wallTime) {\n\t\t\tthis.wallTime = remote.wallTime\n\t\t\tthis.logical = remote.logical + 1\n\t\t} else if (this.wallTime === remote.wallTime) {\n\t\t\tthis.logical = Math.max(this.logical, remote.logical) + 1\n\t\t} else {\n\t\t\t// this.wallTime > remote.wallTime && this.wallTime >= physicalTime\n\t\t\tthis.logical++\n\t\t}\n\n\t\tthis.checkDrift(physicalTime)\n\n\t\treturn { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId }\n\t}\n\n\t/**\n\t * Compare two timestamps. Returns negative if a < b, positive if a > b, zero if equal.\n\t * Total order: wallTime first, then logical, then nodeId (lexicographic).\n\t */\n\tstatic compare(a: HLCTimestamp, b: HLCTimestamp): number {\n\t\tif (a.wallTime !== b.wallTime) return a.wallTime - b.wallTime\n\t\tif (a.logical !== b.logical) return a.logical - b.logical\n\t\tif (a.nodeId < b.nodeId) return -1\n\t\tif (a.nodeId > b.nodeId) return 1\n\t\treturn 0\n\t}\n\n\t/**\n\t * Serialize an HLC timestamp to a string that sorts lexicographically.\n\t * Format: zero-padded wallTime:logical:nodeId\n\t */\n\tstatic serialize(ts: HLCTimestamp): string {\n\t\tconst wall = ts.wallTime.toString().padStart(15, '0')\n\t\tconst log = ts.logical.toString().padStart(5, '0')\n\t\treturn `${wall}:${log}:${ts.nodeId}`\n\t}\n\n\t/**\n\t * Deserialize an HLC timestamp from its serialized string form.\n\t */\n\tstatic deserialize(s: string): HLCTimestamp {\n\t\tconst parts = s.split(':')\n\t\tif (parts.length < 3) {\n\t\t\tthrow new Error(`Invalid HLC timestamp string: \"${s}\"`)\n\t\t}\n\t\treturn {\n\t\t\twallTime: Number.parseInt(parts[0] ?? '0', 10),\n\t\t\tlogical: Number.parseInt(parts[1] ?? '0', 10),\n\t\t\t// nodeId may contain colons, so rejoin remaining parts\n\t\t\tnodeId: parts.slice(2).join(':'),\n\t\t}\n\t}\n\n\tprivate checkDrift(physicalTime: number): void {\n\t\tconst drift = this.wallTime - physicalTime\n\t\tif (drift > DRIFT_ERROR_MS) {\n\t\t\tthrow new ClockDriftError(this.wallTime, physicalTime)\n\t\t}\n\t\tif (drift > DRIFT_WARN_MS) {\n\t\t\tthis.onDriftWarning?.(drift)\n\t\t}\n\t}\n}\n","import type { OperationInput } from '../types'\n\n/**\n * Compute the content-addressed ID for an operation using SHA-256.\n * The same operation content always produces the same hash, ensuring deduplication.\n *\n * @param input - The operation input (without id/timestamp, which are assigned separately)\n * @param timestamp - The HLC timestamp serialized as a string\n * @returns A hex-encoded SHA-256 hash\n */\nexport async function computeOperationId(\n\tinput: OperationInput,\n\ttimestamp: string,\n): Promise<string> {\n\tconst canonical = canonicalize({\n\t\ttype: input.type,\n\t\tcollection: input.collection,\n\t\trecordId: input.recordId,\n\t\tdata: input.data,\n\t\ttimestamp,\n\t\tnodeId: input.nodeId,\n\t})\n\tconst encoded = new TextEncoder().encode(canonical)\n\tconst hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded)\n\treturn bufferToHex(hashBuffer)\n}\n\n/**\n * Deterministic JSON serialization with sorted keys.\n * Ensures identical objects always produce identical strings regardless of property insertion order.\n *\n * @param obj - The value to serialize\n * @returns A deterministic JSON string\n */\nexport function canonicalize(obj: unknown): string {\n\tif (obj === null || obj === undefined) {\n\t\treturn JSON.stringify(obj)\n\t}\n\n\tif (typeof obj !== 'object') {\n\t\treturn JSON.stringify(obj)\n\t}\n\n\tif (Array.isArray(obj)) {\n\t\tconst items = obj.map((item) => canonicalize(item))\n\t\treturn `[${items.join(',')}]`\n\t}\n\n\tconst keys = Object.keys(obj as Record<string, unknown>).sort()\n\tconst pairs = keys.map((key) => {\n\t\tconst value = (obj as Record<string, unknown>)[key]\n\t\treturn `${JSON.stringify(key)}:${canonicalize(value)}`\n\t})\n\treturn `{${pairs.join(',')}}`\n}\n\nfunction bufferToHex(buffer: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(buffer)\n\treturn Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n","import { HybridLogicalClock } from '../clock/hlc'\nimport { OperationError } from '../errors/errors'\nimport type { HLCTimestamp, Operation, OperationInput } from '../types'\nimport { computeOperationId } from './content-hash'\n\n/**\n * Creates an immutable, content-addressed Operation from the given parameters.\n * The operation is deep-frozen after creation — it cannot be modified.\n *\n * @param input - The operation parameters (without id, which is computed)\n * @param clock - The HLC clock to generate the timestamp\n * @returns A frozen Operation with a content-addressed id\n *\n * @example\n * ```typescript\n * const op = await createOperation({\n * nodeId: 'device-1',\n * type: 'insert',\n * collection: 'todos',\n * recordId: 'rec-1',\n * data: { title: 'Ship it' },\n * previousData: null,\n * sequenceNumber: 1,\n * causalDeps: [],\n * schemaVersion: 1,\n * }, clock)\n * ```\n */\nexport async function createOperation(\n\tinput: OperationInput,\n\tclock: HybridLogicalClock,\n): Promise<Operation> {\n\tvalidateOperationParams(input)\n\n\tconst timestamp = clock.now()\n\tconst serializedTs = HybridLogicalClock.serialize(timestamp)\n\tconst id = await computeOperationId(input, serializedTs)\n\n\tconst operation: Operation = {\n\t\tid,\n\t\tnodeId: input.nodeId,\n\t\ttype: input.type,\n\t\tcollection: input.collection,\n\t\trecordId: input.recordId,\n\t\tdata: input.data ? { ...input.data } : null,\n\t\tpreviousData: input.previousData ? { ...input.previousData } : null,\n\t\ttimestamp,\n\t\tsequenceNumber: input.sequenceNumber,\n\t\tcausalDeps: [...input.causalDeps],\n\t\tschemaVersion: input.schemaVersion,\n\t}\n\n\treturn deepFreeze(operation)\n}\n\n/**\n * Validates operation input parameters. Throws OperationError with\n * contextual information on validation failure.\n */\nexport function validateOperationParams(input: OperationInput): void {\n\tif (!input.nodeId || typeof input.nodeId !== 'string') {\n\t\tthrow new OperationError('nodeId is required and must be a non-empty string', {\n\t\t\treceived: input.nodeId,\n\t\t})\n\t}\n\n\tif (!input.type || !['insert', 'update', 'delete'].includes(input.type)) {\n\t\tthrow new OperationError('type must be \"insert\", \"update\", or \"delete\"', {\n\t\t\treceived: input.type,\n\t\t})\n\t}\n\n\tif (!input.collection || typeof input.collection !== 'string') {\n\t\tthrow new OperationError('collection is required and must be a non-empty string', {\n\t\t\treceived: input.collection,\n\t\t})\n\t}\n\n\tif (!input.recordId || typeof input.recordId !== 'string') {\n\t\tthrow new OperationError('recordId is required and must be a non-empty string', {\n\t\t\treceived: input.recordId,\n\t\t})\n\t}\n\n\tif (input.type === 'insert' && input.data === null) {\n\t\tthrow new OperationError('insert operations must include data', {\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t})\n\t}\n\n\tif (input.type === 'update' && input.data === null) {\n\t\tthrow new OperationError('update operations must include data with changed fields', {\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t})\n\t}\n\n\tif (input.type === 'update' && input.previousData === null) {\n\t\tthrow new OperationError(\n\t\t\t'update operations must include previousData for 3-way merge support',\n\t\t\t{\n\t\t\t\ttype: input.type,\n\t\t\t\tcollection: input.collection,\n\t\t\t},\n\t\t)\n\t}\n\n\tif (input.type === 'delete' && input.data !== null) {\n\t\tthrow new OperationError('delete operations must have null data', {\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t})\n\t}\n\n\tif (typeof input.sequenceNumber !== 'number' || input.sequenceNumber < 0) {\n\t\tthrow new OperationError('sequenceNumber must be a non-negative number', {\n\t\t\treceived: input.sequenceNumber,\n\t\t})\n\t}\n\n\tif (!Array.isArray(input.causalDeps)) {\n\t\tthrow new OperationError('causalDeps must be an array of operation IDs', {\n\t\t\treceived: typeof input.causalDeps,\n\t\t})\n\t}\n\n\tif (typeof input.schemaVersion !== 'number' || input.schemaVersion < 1) {\n\t\tthrow new OperationError('schemaVersion must be a positive number', {\n\t\t\treceived: input.schemaVersion,\n\t\t})\n\t}\n}\n\n/**\n * Verify the integrity of an operation by recomputing its content hash.\n * Returns true if the id matches the recomputed hash.\n */\nexport async function verifyOperationIntegrity(op: Operation): Promise<boolean> {\n\tconst input: OperationInput = {\n\t\tnodeId: op.nodeId,\n\t\ttype: op.type,\n\t\tcollection: op.collection,\n\t\trecordId: op.recordId,\n\t\tdata: op.data,\n\t\tpreviousData: op.previousData,\n\t\tsequenceNumber: op.sequenceNumber,\n\t\tcausalDeps: op.causalDeps,\n\t\tschemaVersion: op.schemaVersion,\n\t}\n\tconst serializedTs = HybridLogicalClock.serialize(op.timestamp)\n\tconst expectedId = await computeOperationId(input, serializedTs)\n\treturn op.id === expectedId\n}\n\n/**\n * Type guard for Operation interface.\n */\nexport function isValidOperation(value: unknown): value is Operation {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst op = value as Record<string, unknown>\n\treturn (\n\t\ttypeof op.id === 'string' &&\n\t\ttypeof op.nodeId === 'string' &&\n\t\t(op.type === 'insert' || op.type === 'update' || op.type === 'delete') &&\n\t\ttypeof op.collection === 'string' &&\n\t\ttypeof op.recordId === 'string' &&\n\t\ttypeof op.sequenceNumber === 'number' &&\n\t\tArray.isArray(op.causalDeps) &&\n\t\ttypeof op.schemaVersion === 'number' &&\n\t\ttypeof op.timestamp === 'object' &&\n\t\top.timestamp !== null\n\t)\n}\n\nfunction deepFreeze<T>(obj: T): T {\n\tif (typeof obj !== 'object' || obj === null) return obj\n\tObject.freeze(obj)\n\tfor (const value of Object.values(obj)) {\n\t\tif (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n\t\t\tdeepFreeze(value)\n\t\t}\n\t}\n\treturn obj\n}\n","import { HybridLogicalClock } from '../clock/hlc'\nimport { OperationError } from '../errors/errors'\nimport type { Operation } from '../types'\n\n/**\n * Topological sort of operations based on their causal dependency DAG.\n * Uses Kahn's algorithm with deterministic tie-breaking via HLC timestamp.\n * Time complexity: O(V + E) where V = operations, E = causal dependency edges.\n *\n * @param operations - The operations to sort\n * @returns Operations in causal order (dependencies before dependents)\n * @throws {OperationError} If a cycle is detected in the dependency graph\n */\nexport function topologicalSort(operations: Operation[]): Operation[] {\n\tif (operations.length <= 1) return [...operations]\n\n\t// Build adjacency list and in-degree map\n\tconst opMap = new Map<string, Operation>()\n\tfor (const op of operations) {\n\t\topMap.set(op.id, op)\n\t}\n\n\t// Only count edges where both ends are in the operation set\n\tconst inDegree = new Map<string, number>()\n\tconst dependents = new Map<string, string[]>()\n\n\tfor (const op of operations) {\n\t\tif (!inDegree.has(op.id)) {\n\t\t\tinDegree.set(op.id, 0)\n\t\t}\n\t\tif (!dependents.has(op.id)) {\n\t\t\tdependents.set(op.id, [])\n\t\t}\n\n\t\tfor (const depId of op.causalDeps) {\n\t\t\tif (opMap.has(depId)) {\n\t\t\t\t// depId -> op.id edge (depId must come before op.id)\n\t\t\t\tinDegree.set(op.id, (inDegree.get(op.id) ?? 0) + 1)\n\t\t\t\tconst deps = dependents.get(depId)\n\t\t\t\tif (deps) {\n\t\t\t\t\tdeps.push(op.id)\n\t\t\t\t} else {\n\t\t\t\t\tdependents.set(depId, [op.id])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Initialize queue with nodes that have no in-set dependencies\n\t// Use a sorted array for deterministic ordering (by HLC timestamp)\n\tconst queue: Operation[] = []\n\tfor (const op of operations) {\n\t\tif ((inDegree.get(op.id) ?? 0) === 0) {\n\t\t\tqueue.push(op)\n\t\t}\n\t}\n\tqueue.sort(compareByTimestamp)\n\n\tconst result: Operation[] = []\n\n\twhile (queue.length > 0) {\n\t\t// Take the earliest operation (deterministic tie-breaking by HLC)\n\t\tconst current = queue.shift()\n\t\tif (!current) break\n\t\tresult.push(current)\n\n\t\tconst deps = dependents.get(current.id) ?? []\n\t\tconst newlyReady: Operation[] = []\n\n\t\tfor (const depId of deps) {\n\t\t\tconst deg = (inDegree.get(depId) ?? 0) - 1\n\t\t\tinDegree.set(depId, deg)\n\t\t\tif (deg === 0) {\n\t\t\t\tconst op = opMap.get(depId)\n\t\t\t\tif (op) newlyReady.push(op)\n\t\t\t}\n\t\t}\n\n\t\t// Sort newly ready operations and merge into queue maintaining sort order\n\t\tif (newlyReady.length > 0) {\n\t\t\tnewlyReady.sort(compareByTimestamp)\n\t\t\tmergeIntoSorted(queue, newlyReady)\n\t\t}\n\t}\n\n\tif (result.length !== operations.length) {\n\t\tthrow new OperationError(\n\t\t\t`Cycle detected in operation dependency graph. Sorted ${result.length} of ${operations.length} operations.`,\n\t\t\t{\n\t\t\t\tsortedCount: result.length,\n\t\t\t\ttotalCount: operations.length,\n\t\t\t},\n\t\t)\n\t}\n\n\treturn result\n}\n\nfunction compareByTimestamp(a: Operation, b: Operation): number {\n\treturn HybridLogicalClock.compare(a.timestamp, b.timestamp)\n}\n\n/** Merge sorted `items` into an already-sorted `target` array, maintaining sort order. */\nfunction mergeIntoSorted(target: Operation[], items: Operation[]): void {\n\tlet insertIndex = 0\n\tfor (const item of items) {\n\t\twhile (insertIndex < target.length) {\n\t\t\tconst existing = target[insertIndex]\n\t\t\tif (existing && compareByTimestamp(item, existing) <= 0) break\n\t\t\tinsertIndex++\n\t\t}\n\t\ttarget.splice(insertIndex, 0, item)\n\t\tinsertIndex++\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors/errors.ts","../src/clock/hlc.ts","../src/operations/content-hash.ts","../src/operations/operation.ts","../src/version-vector/topological-sort.ts"],"sourcesContent":["/**\n * Base error class for all Kora errors.\n * Every error includes a machine-readable code and optional context for debugging.\n */\nexport class KoraError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly code: string,\n\t\tpublic readonly context?: Record<string, unknown>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'KoraError'\n\t}\n}\n\n/**\n * Thrown when schema validation fails during defineSchema() or at app initialization.\n */\nexport class SchemaValidationError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'SCHEMA_VALIDATION', context)\n\t\tthis.name = 'SchemaValidationError'\n\t}\n}\n\n/**\n * Thrown when an operation is invalid or cannot be created.\n */\nexport class OperationError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'OPERATION_ERROR', context)\n\t\tthis.name = 'OperationError'\n\t}\n}\n\n/**\n * Thrown when a merge conflict cannot be automatically resolved.\n */\nexport class MergeConflictError extends KoraError {\n\tconstructor(\n\t\tpublic readonly operationA: { id: string; collection: string },\n\t\tpublic readonly operationB: { id: string; collection: string },\n\t\tpublic readonly field: string,\n\t) {\n\t\tsuper(\n\t\t\t`Merge conflict on field \"${field}\" in collection \"${operationA.collection}\"`,\n\t\t\t'MERGE_CONFLICT',\n\t\t\t{ operationA: operationA.id, operationB: operationB.id, field },\n\t\t)\n\t\tthis.name = 'MergeConflictError'\n\t}\n}\n\n/**\n * Thrown when a sync error occurs.\n */\nexport class SyncError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'SYNC_ERROR', context)\n\t\tthis.name = 'SyncError'\n\t}\n}\n\n/**\n * Thrown when a storage operation fails.\n */\nexport class StorageError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'STORAGE_ERROR', context)\n\t\tthis.name = 'StorageError'\n\t}\n}\n\n/**\n * Thrown when the HLC detects excessive clock drift.\n * Drift > 60s: warning. Drift > 5min: this error is thrown, refusing to generate timestamps.\n */\nexport class ClockDriftError extends KoraError {\n\tconstructor(\n\t\tpublic readonly currentHlcTime: number,\n\t\tpublic readonly physicalTime: number,\n\t) {\n\t\tconst driftSeconds = Math.round((currentHlcTime - physicalTime) / 1000)\n\t\tsuper(\n\t\t\t`Clock drift of ${driftSeconds}s detected. Physical time is behind HLC by more than 5 minutes. This indicates a severe clock issue.`,\n\t\t\t'CLOCK_DRIFT',\n\t\t\t{ currentHlcTime, physicalTime, driftSeconds },\n\t\t)\n\t\tthis.name = 'ClockDriftError'\n\t}\n}\n","import { ClockDriftError } from '../errors/errors'\nimport type { HLCTimestamp, TimeSource } from '../types'\n\n/** Default time source using the system clock */\nconst systemTimeSource: TimeSource = { now: () => Date.now() }\n\n/** Maximum allowed drift before warning (60 seconds) */\nconst DRIFT_WARN_MS = 60_000\n\n/** Maximum allowed drift before refusing to generate timestamps (5 minutes) */\nconst DRIFT_ERROR_MS = 5 * 60_000\n\n/**\n * Hybrid Logical Clock implementation based on Kulkarni et al.\n *\n * Provides a total order that respects causality without requiring synchronized clocks.\n * Each call to now() returns a timestamp strictly greater than the previous one.\n *\n * @example\n * ```typescript\n * const clock = new HybridLogicalClock('node-1')\n * const ts1 = clock.now()\n * const ts2 = clock.now()\n * // HybridLogicalClock.compare(ts1, ts2) < 0 (ts1 is earlier)\n * ```\n */\nexport class HybridLogicalClock {\n\tprivate wallTime = 0\n\tprivate logical = 0\n\n\tconstructor(\n\t\tprivate readonly nodeId: string,\n\t\tprivate readonly timeSource: TimeSource = systemTimeSource,\n\t\tprivate readonly onDriftWarning?: (driftMs: number) => void,\n\t) {}\n\n\t/**\n\t * Generate a new timestamp for a local event.\n\t * Guarantees monotonicity: each call returns a timestamp strictly greater than the previous.\n\t *\n\t * @throws {ClockDriftError} If physical time is more than 5 minutes behind the HLC wallTime\n\t */\n\tnow(): HLCTimestamp {\n\t\tconst physicalTime = this.timeSource.now()\n\t\tthis.checkDrift(physicalTime)\n\n\t\tif (physicalTime > this.wallTime) {\n\t\t\tthis.wallTime = physicalTime\n\t\t\tthis.logical = 0\n\t\t} else {\n\t\t\tthis.logical++\n\t\t}\n\n\t\treturn { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId }\n\t}\n\n\t/**\n\t * Update clock on receiving a remote timestamp.\n\t * Merges the remote clock state with the local state to maintain causal ordering.\n\t *\n\t * @throws {ClockDriftError} If physical time is more than 5 minutes behind the resulting wallTime\n\t */\n\treceive(remote: HLCTimestamp): HLCTimestamp {\n\t\tconst physicalTime = this.timeSource.now()\n\n\t\tif (physicalTime > this.wallTime && physicalTime > remote.wallTime) {\n\t\t\tthis.wallTime = physicalTime\n\t\t\tthis.logical = 0\n\t\t} else if (remote.wallTime > this.wallTime) {\n\t\t\tthis.wallTime = remote.wallTime\n\t\t\tthis.logical = remote.logical + 1\n\t\t} else if (this.wallTime === remote.wallTime) {\n\t\t\tthis.logical = Math.max(this.logical, remote.logical) + 1\n\t\t} else {\n\t\t\t// this.wallTime > remote.wallTime && this.wallTime >= physicalTime\n\t\t\tthis.logical++\n\t\t}\n\n\t\tthis.checkDrift(physicalTime)\n\n\t\treturn { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId }\n\t}\n\n\t/**\n\t * Compare two timestamps. Returns negative if a < b, positive if a > b, zero if equal.\n\t * Total order: wallTime first, then logical, then nodeId (lexicographic).\n\t */\n\tstatic compare(a: HLCTimestamp, b: HLCTimestamp): number {\n\t\tif (a.wallTime !== b.wallTime) return a.wallTime - b.wallTime\n\t\tif (a.logical !== b.logical) return a.logical - b.logical\n\t\tif (a.nodeId < b.nodeId) return -1\n\t\tif (a.nodeId > b.nodeId) return 1\n\t\treturn 0\n\t}\n\n\t/**\n\t * Serialize an HLC timestamp to a string that sorts lexicographically.\n\t * Format: zero-padded wallTime:logical:nodeId\n\t */\n\tstatic serialize(ts: HLCTimestamp): string {\n\t\tconst wall = ts.wallTime.toString().padStart(15, '0')\n\t\tconst log = ts.logical.toString().padStart(5, '0')\n\t\treturn `${wall}:${log}:${ts.nodeId}`\n\t}\n\n\t/**\n\t * Deserialize an HLC timestamp from its serialized string form.\n\t */\n\tstatic deserialize(s: string): HLCTimestamp {\n\t\tconst parts = s.split(':')\n\t\tif (parts.length < 3) {\n\t\t\tthrow new Error(`Invalid HLC timestamp string: \"${s}\"`)\n\t\t}\n\t\treturn {\n\t\t\twallTime: Number.parseInt(parts[0] ?? '0', 10),\n\t\t\tlogical: Number.parseInt(parts[1] ?? '0', 10),\n\t\t\t// nodeId may contain colons, so rejoin remaining parts\n\t\t\tnodeId: parts.slice(2).join(':'),\n\t\t}\n\t}\n\n\tprivate checkDrift(physicalTime: number): void {\n\t\tconst drift = this.wallTime - physicalTime\n\t\tif (drift > DRIFT_ERROR_MS) {\n\t\t\tthrow new ClockDriftError(this.wallTime, physicalTime)\n\t\t}\n\t\tif (drift > DRIFT_WARN_MS) {\n\t\t\tthis.onDriftWarning?.(drift)\n\t\t}\n\t}\n}\n","import type { OperationInput } from '../types'\n\n/**\n * Compute the content-addressed ID for an operation using SHA-256.\n * The same operation content always produces the same hash, ensuring deduplication.\n *\n * @param input - The operation input (without id/timestamp, which are assigned separately)\n * @param timestamp - The HLC timestamp serialized as a string\n * @returns A hex-encoded SHA-256 hash\n */\nexport async function computeOperationId(\n\tinput: OperationInput,\n\ttimestamp: string,\n): Promise<string> {\n\tconst canonical = canonicalize({\n\t\ttype: input.type,\n\t\tcollection: input.collection,\n\t\trecordId: input.recordId,\n\t\tdata: input.data,\n\t\ttimestamp,\n\t\tnodeId: input.nodeId,\n\t})\n\tconst encoded = new TextEncoder().encode(canonical)\n\tconst hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded)\n\treturn bufferToHex(hashBuffer)\n}\n\n/**\n * Deterministic JSON serialization with sorted keys.\n * Ensures identical objects always produce identical strings regardless of property insertion order.\n *\n * @param obj - The value to serialize\n * @returns A deterministic JSON string\n */\nexport function canonicalize(obj: unknown): string {\n\tif (obj === null || obj === undefined) {\n\t\treturn JSON.stringify(obj)\n\t}\n\n\tif (typeof obj !== 'object') {\n\t\treturn JSON.stringify(obj)\n\t}\n\n\tif (Array.isArray(obj)) {\n\t\tconst items = obj.map((item) => canonicalize(item))\n\t\treturn `[${items.join(',')}]`\n\t}\n\n\tconst keys = Object.keys(obj as Record<string, unknown>).sort()\n\tconst pairs = keys.map((key) => {\n\t\tconst value = (obj as Record<string, unknown>)[key]\n\t\treturn `${JSON.stringify(key)}:${canonicalize(value)}`\n\t})\n\treturn `{${pairs.join(',')}}`\n}\n\nfunction bufferToHex(buffer: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(buffer)\n\treturn Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n","import { HybridLogicalClock } from '../clock/hlc'\nimport { OperationError } from '../errors/errors'\nimport type { HLCTimestamp, Operation, OperationInput } from '../types'\nimport { computeOperationId } from './content-hash'\n\n/**\n * Creates an immutable, content-addressed Operation from the given parameters.\n * The operation is deep-frozen after creation — it cannot be modified.\n *\n * @param input - The operation parameters (without id, which is computed)\n * @param clock - The HLC clock to generate the timestamp\n * @returns A frozen Operation with a content-addressed id\n *\n * @example\n * ```typescript\n * const op = await createOperation({\n * nodeId: 'device-1',\n * type: 'insert',\n * collection: 'todos',\n * recordId: 'rec-1',\n * data: { title: 'Ship it' },\n * previousData: null,\n * sequenceNumber: 1,\n * causalDeps: [],\n * schemaVersion: 1,\n * }, clock)\n * ```\n */\nexport async function createOperation(\n\tinput: OperationInput,\n\tclock: HybridLogicalClock,\n): Promise<Operation> {\n\tvalidateOperationParams(input)\n\n\tconst timestamp = clock.now()\n\tconst serializedTs = HybridLogicalClock.serialize(timestamp)\n\tconst id = await computeOperationId(input, serializedTs)\n\n\tconst operation: Operation = {\n\t\tid,\n\t\tnodeId: input.nodeId,\n\t\ttype: input.type,\n\t\tcollection: input.collection,\n\t\trecordId: input.recordId,\n\t\tdata: input.data ? { ...input.data } : null,\n\t\tpreviousData: input.previousData ? { ...input.previousData } : null,\n\t\ttimestamp,\n\t\tsequenceNumber: input.sequenceNumber,\n\t\tcausalDeps: [...input.causalDeps],\n\t\tschemaVersion: input.schemaVersion,\n\t}\n\n\treturn deepFreeze(operation)\n}\n\n/**\n * Validates operation input parameters. Throws OperationError with\n * contextual information on validation failure.\n */\nexport function validateOperationParams(input: OperationInput): void {\n\tif (!input.nodeId || typeof input.nodeId !== 'string') {\n\t\tthrow new OperationError('nodeId is required and must be a non-empty string', {\n\t\t\treceived: input.nodeId,\n\t\t})\n\t}\n\n\tif (!input.type || !['insert', 'update', 'delete'].includes(input.type)) {\n\t\tthrow new OperationError('type must be \"insert\", \"update\", or \"delete\"', {\n\t\t\treceived: input.type,\n\t\t})\n\t}\n\n\tif (!input.collection || typeof input.collection !== 'string') {\n\t\tthrow new OperationError('collection is required and must be a non-empty string', {\n\t\t\treceived: input.collection,\n\t\t})\n\t}\n\n\tif (!input.recordId || typeof input.recordId !== 'string') {\n\t\tthrow new OperationError('recordId is required and must be a non-empty string', {\n\t\t\treceived: input.recordId,\n\t\t})\n\t}\n\n\tif (input.type === 'insert' && input.data === null) {\n\t\tthrow new OperationError('insert operations must include data', {\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t})\n\t}\n\n\tif (input.type === 'update' && input.data === null) {\n\t\tthrow new OperationError('update operations must include data with changed fields', {\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t})\n\t}\n\n\tif (input.type === 'update' && input.previousData === null) {\n\t\tthrow new OperationError(\n\t\t\t'update operations must include previousData for 3-way merge support',\n\t\t\t{\n\t\t\t\ttype: input.type,\n\t\t\t\tcollection: input.collection,\n\t\t\t},\n\t\t)\n\t}\n\n\tif (input.type === 'delete' && input.data !== null) {\n\t\tthrow new OperationError('delete operations must have null data', {\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t})\n\t}\n\n\tif (typeof input.sequenceNumber !== 'number' || input.sequenceNumber < 0) {\n\t\tthrow new OperationError('sequenceNumber must be a non-negative number', {\n\t\t\treceived: input.sequenceNumber,\n\t\t})\n\t}\n\n\tif (!Array.isArray(input.causalDeps)) {\n\t\tthrow new OperationError('causalDeps must be an array of operation IDs', {\n\t\t\treceived: typeof input.causalDeps,\n\t\t})\n\t}\n\n\tif (typeof input.schemaVersion !== 'number' || input.schemaVersion < 1) {\n\t\tthrow new OperationError('schemaVersion must be a positive number', {\n\t\t\treceived: input.schemaVersion,\n\t\t})\n\t}\n}\n\n/**\n * Verify the integrity of an operation by recomputing its content hash.\n * Returns true if the id matches the recomputed hash.\n */\nexport async function verifyOperationIntegrity(op: Operation): Promise<boolean> {\n\tconst input: OperationInput = {\n\t\tnodeId: op.nodeId,\n\t\ttype: op.type,\n\t\tcollection: op.collection,\n\t\trecordId: op.recordId,\n\t\tdata: op.data,\n\t\tpreviousData: op.previousData,\n\t\tsequenceNumber: op.sequenceNumber,\n\t\tcausalDeps: op.causalDeps,\n\t\tschemaVersion: op.schemaVersion,\n\t}\n\tconst serializedTs = HybridLogicalClock.serialize(op.timestamp)\n\tconst expectedId = await computeOperationId(input, serializedTs)\n\treturn op.id === expectedId\n}\n\n/**\n * Type guard for Operation interface.\n */\nexport function isValidOperation(value: unknown): value is Operation {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst op = value as Record<string, unknown>\n\treturn (\n\t\ttypeof op.id === 'string' &&\n\t\ttypeof op.nodeId === 'string' &&\n\t\t(op.type === 'insert' || op.type === 'update' || op.type === 'delete') &&\n\t\ttypeof op.collection === 'string' &&\n\t\ttypeof op.recordId === 'string' &&\n\t\ttypeof op.sequenceNumber === 'number' &&\n\t\tArray.isArray(op.causalDeps) &&\n\t\ttypeof op.schemaVersion === 'number' &&\n\t\ttypeof op.timestamp === 'object' &&\n\t\top.timestamp !== null\n\t)\n}\n\nfunction deepFreeze<T>(obj: T): T {\n\tif (typeof obj !== 'object' || obj === null) return obj\n\tObject.freeze(obj)\n\tfor (const value of Object.values(obj)) {\n\t\tif (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n\t\t\tdeepFreeze(value)\n\t\t}\n\t}\n\treturn obj\n}\n","import { HybridLogicalClock } from '../clock/hlc'\nimport { OperationError } from '../errors/errors'\nimport type { Operation } from '../types'\n\n/**\n * Topological sort of operations based on their causal dependency DAG.\n * Uses Kahn's algorithm with a binary heap for O(V log V + E) performance.\n * Deterministic tie-breaking via HLC timestamp ensures identical output\n * regardless of input order.\n *\n * @param operations - The operations to sort\n * @returns Operations in causal order (dependencies before dependents)\n * @throws {OperationError} If a cycle is detected in the dependency graph\n */\nexport function topologicalSort(operations: Operation[]): Operation[] {\n\tif (operations.length <= 1) return [...operations]\n\n\t// Build adjacency list and in-degree map\n\tconst opMap = new Map<string, Operation>()\n\tfor (const op of operations) {\n\t\topMap.set(op.id, op)\n\t}\n\n\t// Only count edges where both ends are in the operation set\n\tconst inDegree = new Map<string, number>()\n\tconst dependents = new Map<string, string[]>()\n\n\tfor (const op of operations) {\n\t\tif (!inDegree.has(op.id)) {\n\t\t\tinDegree.set(op.id, 0)\n\t\t}\n\t\tif (!dependents.has(op.id)) {\n\t\t\tdependents.set(op.id, [])\n\t\t}\n\n\t\tfor (const depId of op.causalDeps) {\n\t\t\tif (opMap.has(depId)) {\n\t\t\t\t// depId -> op.id edge (depId must come before op.id)\n\t\t\t\tinDegree.set(op.id, (inDegree.get(op.id) ?? 0) + 1)\n\t\t\t\tconst deps = dependents.get(depId)\n\t\t\t\tif (deps) {\n\t\t\t\t\tdeps.push(op.id)\n\t\t\t\t} else {\n\t\t\t\t\tdependents.set(depId, [op.id])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Initialize min-heap with nodes that have no in-set dependencies\n\tconst heap = new MinHeap(compareByTimestamp)\n\tfor (const op of operations) {\n\t\tif ((inDegree.get(op.id) ?? 0) === 0) {\n\t\t\theap.push(op)\n\t\t}\n\t}\n\n\tconst result: Operation[] = []\n\n\twhile (heap.size > 0) {\n\t\t// Extract the earliest operation (deterministic tie-breaking by HLC)\n\t\tconst current = heap.pop()\n\t\tresult.push(current)\n\n\t\tconst deps = dependents.get(current.id) ?? []\n\n\t\tfor (const depId of deps) {\n\t\t\tconst deg = (inDegree.get(depId) ?? 0) - 1\n\t\t\tinDegree.set(depId, deg)\n\t\t\tif (deg === 0) {\n\t\t\t\tconst op = opMap.get(depId)\n\t\t\t\tif (op) heap.push(op)\n\t\t\t}\n\t\t}\n\t}\n\n\tif (result.length !== operations.length) {\n\t\tthrow new OperationError(\n\t\t\t`Cycle detected in operation dependency graph. Sorted ${result.length} of ${operations.length} operations.`,\n\t\t\t{\n\t\t\t\tsortedCount: result.length,\n\t\t\t\ttotalCount: operations.length,\n\t\t\t},\n\t\t)\n\t}\n\n\treturn result\n}\n\nfunction compareByTimestamp(a: Operation, b: Operation): number {\n\treturn HybridLogicalClock.compare(a.timestamp, b.timestamp)\n}\n\n/**\n * Binary min-heap for efficient priority queue operations.\n * push: O(log n), pop: O(log n) — replaces the O(n) sorted array approach.\n */\nclass MinHeap {\n\tprivate readonly data: Operation[] = []\n\tprivate readonly cmp: (a: Operation, b: Operation) => number\n\n\tconstructor(comparator: (a: Operation, b: Operation) => number) {\n\t\tthis.cmp = comparator\n\t}\n\n\tget size(): number {\n\t\treturn this.data.length\n\t}\n\n\tpush(item: Operation): void {\n\t\tthis.data.push(item)\n\t\tthis.bubbleUp(this.data.length - 1)\n\t}\n\n\tpop(): Operation {\n\t\tconst top = this.data[0] as Operation\n\t\tconst last = this.data.pop() as Operation\n\t\tif (this.data.length > 0) {\n\t\t\tthis.data[0] = last\n\t\t\tthis.sinkDown(0)\n\t\t}\n\t\treturn top\n\t}\n\n\tprivate bubbleUp(index: number): void {\n\t\twhile (index > 0) {\n\t\t\tconst parentIndex = (index - 1) >> 1\n\t\t\tif (this.cmp(this.data[index] as Operation, this.data[parentIndex] as Operation) >= 0) break\n\t\t\tthis.swap(index, parentIndex)\n\t\t\tindex = parentIndex\n\t\t}\n\t}\n\n\tprivate sinkDown(index: number): void {\n\t\tconst length = this.data.length\n\t\twhile (true) {\n\t\t\tlet smallest = index\n\t\t\tconst left = 2 * index + 1\n\t\t\tconst right = 2 * index + 2\n\n\t\t\tif (left < length && this.cmp(this.data[left] as Operation, this.data[smallest] as Operation) < 0) {\n\t\t\t\tsmallest = left\n\t\t\t}\n\t\t\tif (right < length && this.cmp(this.data[right] as Operation, this.data[smallest] as Operation) < 0) {\n\t\t\t\tsmallest = right\n\t\t\t}\n\n\t\t\tif (smallest === index) break\n\t\t\tthis.swap(index, smallest)\n\t\t\tindex = smallest\n\t\t}\n\t}\n\n\tprivate swap(i: number, j: number): void {\n\t\tconst tmp = this.data[i] as Operation\n\t\tthis.data[i] = this.data[j] as Operation\n\t\tthis.data[j] = tmp\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -598,6 +598,11 @@ function generateSQL(collectionName, collection, relations) {
|
|
|
598
598
|
statements.push(`CREATE TABLE IF NOT EXISTS ${collectionName} (
|
|
599
599
|
${columns.join(",\n ")}
|
|
600
600
|
)`);
|
|
601
|
+
for (const [fieldName, descriptor] of Object.entries(collection.fields)) {
|
|
602
|
+
const colDef = columnDefinition(fieldName, descriptor);
|
|
603
|
+
statements.push(`--kora:safe-alter
|
|
604
|
+
ALTER TABLE ${collectionName} ADD COLUMN ${colDef}`);
|
|
605
|
+
}
|
|
601
606
|
for (const indexField of collection.indexes) {
|
|
602
607
|
statements.push(
|
|
603
608
|
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${collectionName} (${indexField})`
|
|
@@ -1014,32 +1019,25 @@ function topologicalSort(operations) {
|
|
|
1014
1019
|
}
|
|
1015
1020
|
}
|
|
1016
1021
|
}
|
|
1017
|
-
const
|
|
1022
|
+
const heap = new MinHeap(compareByTimestamp);
|
|
1018
1023
|
for (const op of operations) {
|
|
1019
1024
|
if ((inDegree.get(op.id) ?? 0) === 0) {
|
|
1020
|
-
|
|
1025
|
+
heap.push(op);
|
|
1021
1026
|
}
|
|
1022
1027
|
}
|
|
1023
|
-
queue.sort(compareByTimestamp);
|
|
1024
1028
|
const result = [];
|
|
1025
|
-
while (
|
|
1026
|
-
const current =
|
|
1027
|
-
if (!current) break;
|
|
1029
|
+
while (heap.size > 0) {
|
|
1030
|
+
const current = heap.pop();
|
|
1028
1031
|
result.push(current);
|
|
1029
1032
|
const deps = dependents.get(current.id) ?? [];
|
|
1030
|
-
const newlyReady = [];
|
|
1031
1033
|
for (const depId of deps) {
|
|
1032
1034
|
const deg = (inDegree.get(depId) ?? 0) - 1;
|
|
1033
1035
|
inDegree.set(depId, deg);
|
|
1034
1036
|
if (deg === 0) {
|
|
1035
1037
|
const op = opMap.get(depId);
|
|
1036
|
-
if (op)
|
|
1038
|
+
if (op) heap.push(op);
|
|
1037
1039
|
}
|
|
1038
1040
|
}
|
|
1039
|
-
if (newlyReady.length > 0) {
|
|
1040
|
-
newlyReady.sort(compareByTimestamp);
|
|
1041
|
-
mergeIntoSorted(queue, newlyReady);
|
|
1042
|
-
}
|
|
1043
1041
|
}
|
|
1044
1042
|
if (result.length !== operations.length) {
|
|
1045
1043
|
throw new OperationError(
|
|
@@ -1055,18 +1053,59 @@ function topologicalSort(operations) {
|
|
|
1055
1053
|
function compareByTimestamp(a, b) {
|
|
1056
1054
|
return HybridLogicalClock.compare(a.timestamp, b.timestamp);
|
|
1057
1055
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1056
|
+
var MinHeap = class {
|
|
1057
|
+
data = [];
|
|
1058
|
+
cmp;
|
|
1059
|
+
constructor(comparator) {
|
|
1060
|
+
this.cmp = comparator;
|
|
1061
|
+
}
|
|
1062
|
+
get size() {
|
|
1063
|
+
return this.data.length;
|
|
1064
|
+
}
|
|
1065
|
+
push(item) {
|
|
1066
|
+
this.data.push(item);
|
|
1067
|
+
this.bubbleUp(this.data.length - 1);
|
|
1068
|
+
}
|
|
1069
|
+
pop() {
|
|
1070
|
+
const top = this.data[0];
|
|
1071
|
+
const last = this.data.pop();
|
|
1072
|
+
if (this.data.length > 0) {
|
|
1073
|
+
this.data[0] = last;
|
|
1074
|
+
this.sinkDown(0);
|
|
1075
|
+
}
|
|
1076
|
+
return top;
|
|
1077
|
+
}
|
|
1078
|
+
bubbleUp(index) {
|
|
1079
|
+
while (index > 0) {
|
|
1080
|
+
const parentIndex = index - 1 >> 1;
|
|
1081
|
+
if (this.cmp(this.data[index], this.data[parentIndex]) >= 0) break;
|
|
1082
|
+
this.swap(index, parentIndex);
|
|
1083
|
+
index = parentIndex;
|
|
1065
1084
|
}
|
|
1066
|
-
target.splice(insertIndex, 0, item);
|
|
1067
|
-
insertIndex++;
|
|
1068
1085
|
}
|
|
1069
|
-
|
|
1086
|
+
sinkDown(index) {
|
|
1087
|
+
const length = this.data.length;
|
|
1088
|
+
while (true) {
|
|
1089
|
+
let smallest = index;
|
|
1090
|
+
const left = 2 * index + 1;
|
|
1091
|
+
const right = 2 * index + 2;
|
|
1092
|
+
if (left < length && this.cmp(this.data[left], this.data[smallest]) < 0) {
|
|
1093
|
+
smallest = left;
|
|
1094
|
+
}
|
|
1095
|
+
if (right < length && this.cmp(this.data[right], this.data[smallest]) < 0) {
|
|
1096
|
+
smallest = right;
|
|
1097
|
+
}
|
|
1098
|
+
if (smallest === index) break;
|
|
1099
|
+
this.swap(index, smallest);
|
|
1100
|
+
index = smallest;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
swap(i, j) {
|
|
1104
|
+
const tmp = this.data[i];
|
|
1105
|
+
this.data[i] = this.data[j];
|
|
1106
|
+
this.data[j] = tmp;
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1070
1109
|
|
|
1071
1110
|
// src/version-vector/version-vector.ts
|
|
1072
1111
|
function createVersionVector() {
|