@korajs/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.
package/dist/index.js ADDED
@@ -0,0 +1,707 @@
1
+ import {
2
+ ClockDriftError,
3
+ HybridLogicalClock,
4
+ KoraError,
5
+ MergeConflictError,
6
+ OperationError,
7
+ SchemaValidationError,
8
+ StorageError,
9
+ SyncError,
10
+ createOperation,
11
+ isValidOperation,
12
+ topologicalSort,
13
+ verifyOperationIntegrity
14
+ } from "./chunk-ZMUY7AVV.js";
15
+
16
+ // src/types.ts
17
+ var MERGE_STRATEGIES = [
18
+ "auto-merge",
19
+ "lww",
20
+ "first-write-wins",
21
+ "server-decides",
22
+ "custom"
23
+ ];
24
+ var CONNECTION_QUALITIES = ["excellent", "good", "fair", "poor", "offline"];
25
+
26
+ // src/identifiers/uuid-v7.ts
27
+ var defaultRandom = globalThis.crypto;
28
+ function generateUUIDv7(timestamp = Date.now(), randomSource = defaultRandom) {
29
+ const bytes = new Uint8Array(16);
30
+ randomSource.getRandomValues(bytes);
31
+ const ms = Math.max(0, Math.floor(timestamp));
32
+ bytes[0] = ms / 2 ** 40 & 255;
33
+ bytes[1] = ms / 2 ** 32 & 255;
34
+ bytes[2] = ms / 2 ** 24 & 255;
35
+ bytes[3] = ms / 2 ** 16 & 255;
36
+ bytes[4] = ms / 2 ** 8 & 255;
37
+ bytes[5] = ms & 255;
38
+ bytes[6] = (bytes[6] ?? 0) & 15 | 112;
39
+ bytes[8] = (bytes[8] ?? 0) & 63 | 128;
40
+ return formatUUID(bytes);
41
+ }
42
+ function extractTimestamp(uuid) {
43
+ const hex = uuid.replace(/-/g, "");
44
+ const high = Number.parseInt(hex.slice(0, 8), 16);
45
+ const low = Number.parseInt(hex.slice(8, 12), 16);
46
+ return high * 2 ** 16 + low;
47
+ }
48
+ function isValidUUIDv7(uuid) {
49
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid)) {
50
+ return false;
51
+ }
52
+ const hex = uuid.replace(/-/g, "");
53
+ if (hex[12] !== "7") return false;
54
+ const variantNibble = Number.parseInt(hex[16] ?? "0", 16);
55
+ return variantNibble >= 8 && variantNibble <= 11;
56
+ }
57
+ function formatUUID(bytes) {
58
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
59
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
60
+ }
61
+
62
+ // src/schema/define.ts
63
+ var COLLECTION_NAME_RE = /^[a-z][a-z0-9_]*$/;
64
+ var FIELD_NAME_RE = /^[a-z][a-z0-9_]*$/;
65
+ var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "_created_at", "_updated_at", "_deleted"]);
66
+ function defineSchema(input) {
67
+ validateVersion(input.version);
68
+ const collections = {};
69
+ for (const [name, collectionInput] of Object.entries(input.collections)) {
70
+ validateCollectionName(name);
71
+ collections[name] = buildCollection(name, collectionInput);
72
+ }
73
+ if (Object.keys(collections).length === 0) {
74
+ throw new SchemaValidationError("Schema must define at least one collection");
75
+ }
76
+ const relations = {};
77
+ if (input.relations) {
78
+ for (const [name, relationInput] of Object.entries(input.relations)) {
79
+ validateRelation(name, relationInput, collections);
80
+ relations[name] = { ...relationInput };
81
+ }
82
+ }
83
+ return { version: input.version, collections, relations };
84
+ }
85
+ function validateVersion(version) {
86
+ if (typeof version !== "number" || !Number.isInteger(version) || version < 1) {
87
+ throw new SchemaValidationError("Schema version must be a positive integer", {
88
+ received: version
89
+ });
90
+ }
91
+ }
92
+ function validateCollectionName(name) {
93
+ if (!COLLECTION_NAME_RE.test(name)) {
94
+ throw new SchemaValidationError(
95
+ `Collection name "${name}" is invalid. Must be lowercase, start with a letter, and contain only letters, numbers, and underscores.`,
96
+ { collection: name }
97
+ );
98
+ }
99
+ }
100
+ function buildCollection(name, input) {
101
+ const fields = {};
102
+ if (!input.fields || Object.keys(input.fields).length === 0) {
103
+ throw new SchemaValidationError(`Collection "${name}" must define at least one field`, {
104
+ collection: name
105
+ });
106
+ }
107
+ for (const [fieldName, builder] of Object.entries(input.fields)) {
108
+ validateFieldName(name, fieldName);
109
+ fields[fieldName] = builder._build();
110
+ }
111
+ const indexes = input.indexes ?? [];
112
+ for (const indexField of indexes) {
113
+ if (!(indexField in fields)) {
114
+ throw new SchemaValidationError(
115
+ `Index field "${indexField}" does not exist in collection "${name}". Available fields: ${Object.keys(fields).join(", ")}`,
116
+ { collection: name, field: indexField }
117
+ );
118
+ }
119
+ }
120
+ const constraints = [];
121
+ if (input.constraints) {
122
+ for (const constraintInput of input.constraints) {
123
+ validateConstraint(name, constraintInput, fields);
124
+ constraints.push({ ...constraintInput });
125
+ }
126
+ }
127
+ const resolvers = {};
128
+ if (input.resolve) {
129
+ for (const [fieldName, resolver] of Object.entries(input.resolve)) {
130
+ if (!(fieldName in fields)) {
131
+ throw new SchemaValidationError(
132
+ `Resolver for field "${fieldName}" does not exist in collection "${name}". Available fields: ${Object.keys(fields).join(", ")}`,
133
+ { collection: name, field: fieldName }
134
+ );
135
+ }
136
+ if (typeof resolver !== "function") {
137
+ throw new SchemaValidationError(
138
+ `Resolver for field "${fieldName}" in collection "${name}" must be a function`,
139
+ { collection: name, field: fieldName }
140
+ );
141
+ }
142
+ resolvers[fieldName] = resolver;
143
+ }
144
+ }
145
+ return { fields, indexes, constraints, resolvers };
146
+ }
147
+ function validateFieldName(collection, fieldName) {
148
+ if (RESERVED_FIELDS.has(fieldName)) {
149
+ throw new SchemaValidationError(
150
+ `Field name "${fieldName}" is reserved in collection "${collection}". Reserved fields: ${[...RESERVED_FIELDS].join(", ")}`,
151
+ { collection, field: fieldName }
152
+ );
153
+ }
154
+ if (!FIELD_NAME_RE.test(fieldName)) {
155
+ throw new SchemaValidationError(
156
+ `Field name "${fieldName}" in collection "${collection}" is invalid. Must be lowercase, start with a letter, and contain only letters, numbers, and underscores.`,
157
+ { collection, field: fieldName }
158
+ );
159
+ }
160
+ }
161
+ function validateConstraint(collection, constraint, fields) {
162
+ for (const field of constraint.fields) {
163
+ if (!(field in fields)) {
164
+ throw new SchemaValidationError(
165
+ `Constraint references field "${field}" which does not exist in collection "${collection}". Available fields: ${Object.keys(fields).join(", ")}`,
166
+ { collection, field }
167
+ );
168
+ }
169
+ }
170
+ if (constraint.onConflict === "priority-field" && !constraint.priorityField) {
171
+ throw new SchemaValidationError(
172
+ `Constraint with "priority-field" onConflict strategy in collection "${collection}" requires a priorityField`,
173
+ { collection }
174
+ );
175
+ }
176
+ if (constraint.onConflict === "priority-field" && constraint.priorityField) {
177
+ if (!(constraint.priorityField in fields)) {
178
+ throw new SchemaValidationError(
179
+ `Constraint priorityField "${constraint.priorityField}" does not exist in collection "${collection}"`,
180
+ { collection, field: constraint.priorityField }
181
+ );
182
+ }
183
+ }
184
+ if (constraint.onConflict === "custom" && typeof constraint.resolve !== "function") {
185
+ throw new SchemaValidationError(
186
+ `Constraint with "custom" onConflict strategy in collection "${collection}" requires a resolve function`,
187
+ { collection }
188
+ );
189
+ }
190
+ }
191
+ function validateRelation(name, relation, collections) {
192
+ if (!(relation.from in collections)) {
193
+ throw new SchemaValidationError(
194
+ `Relation "${name}" references source collection "${relation.from}" which does not exist. Available collections: ${Object.keys(collections).join(", ")}`,
195
+ { relation: name, collection: relation.from }
196
+ );
197
+ }
198
+ if (!(relation.to in collections)) {
199
+ throw new SchemaValidationError(
200
+ `Relation "${name}" references target collection "${relation.to}" which does not exist. Available collections: ${Object.keys(collections).join(", ")}`,
201
+ { relation: name, collection: relation.to }
202
+ );
203
+ }
204
+ const fromCollection = collections[relation.from];
205
+ if (fromCollection && !(relation.field in fromCollection.fields)) {
206
+ throw new SchemaValidationError(
207
+ `Relation "${name}" references field "${relation.field}" which does not exist in collection "${relation.from}". Available fields: ${Object.keys(fromCollection.fields).join(", ")}`,
208
+ { relation: name, collection: relation.from, field: relation.field }
209
+ );
210
+ }
211
+ }
212
+
213
+ // src/schema/sql-gen.ts
214
+ function generateSQL(collectionName, collection, relations) {
215
+ const statements = [];
216
+ const columns = ["id TEXT PRIMARY KEY NOT NULL"];
217
+ const indexedFields = new Set(collection.indexes);
218
+ const fkFields = [];
219
+ for (const [fieldName, descriptor] of Object.entries(collection.fields)) {
220
+ let colDef = columnDefinition(fieldName, descriptor);
221
+ if (relations) {
222
+ for (const rel of Object.values(relations)) {
223
+ if (rel.from === collectionName && rel.field === fieldName) {
224
+ colDef += ` REFERENCES ${rel.to}(id)`;
225
+ fkFields.push(fieldName);
226
+ break;
227
+ }
228
+ }
229
+ }
230
+ columns.push(colDef);
231
+ }
232
+ columns.push("_created_at INTEGER NOT NULL");
233
+ columns.push("_updated_at INTEGER NOT NULL");
234
+ columns.push("_deleted INTEGER NOT NULL DEFAULT 0");
235
+ statements.push(`CREATE TABLE IF NOT EXISTS ${collectionName} (
236
+ ${columns.join(",\n ")}
237
+ )`);
238
+ for (const indexField of collection.indexes) {
239
+ statements.push(
240
+ `CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${collectionName} (${indexField})`
241
+ );
242
+ }
243
+ for (const fkField of fkFields) {
244
+ if (!indexedFields.has(fkField)) {
245
+ statements.push(
246
+ `CREATE INDEX IF NOT EXISTS idx_${collectionName}_${fkField} ON ${collectionName} (${fkField})`
247
+ );
248
+ }
249
+ }
250
+ statements.push(
251
+ `CREATE TABLE IF NOT EXISTS _kora_ops_${collectionName} (
252
+ id TEXT PRIMARY KEY NOT NULL,
253
+ node_id TEXT NOT NULL,
254
+ type TEXT NOT NULL,
255
+ record_id TEXT NOT NULL,
256
+ data TEXT,
257
+ previous_data TEXT,
258
+ timestamp TEXT NOT NULL,
259
+ sequence_number INTEGER NOT NULL,
260
+ causal_deps TEXT NOT NULL,
261
+ schema_version INTEGER NOT NULL
262
+ )`
263
+ );
264
+ return statements;
265
+ }
266
+ function generateFullDDL(schema) {
267
+ const statements = [];
268
+ statements.push(
269
+ "CREATE TABLE IF NOT EXISTS _kora_meta (\n key TEXT PRIMARY KEY NOT NULL,\n value TEXT NOT NULL\n)"
270
+ );
271
+ statements.push(
272
+ "CREATE TABLE IF NOT EXISTS _kora_version_vector (\n node_id TEXT PRIMARY KEY NOT NULL,\n sequence_number INTEGER NOT NULL\n)"
273
+ );
274
+ for (const [name, collection] of Object.entries(schema.collections)) {
275
+ statements.push(...generateSQL(name, collection, schema.relations));
276
+ }
277
+ return statements;
278
+ }
279
+ function columnDefinition(fieldName, descriptor) {
280
+ const sqlType = mapFieldType(descriptor);
281
+ const parts = [fieldName, sqlType];
282
+ if (descriptor.required && descriptor.defaultValue === void 0 && !descriptor.auto) {
283
+ parts.push("NOT NULL");
284
+ }
285
+ if (descriptor.defaultValue !== void 0) {
286
+ parts.push(`DEFAULT ${sqlDefault(descriptor.defaultValue)}`);
287
+ }
288
+ if (descriptor.kind === "enum" && descriptor.enumValues) {
289
+ const values = descriptor.enumValues.map((v) => `'${v}'`).join(", ");
290
+ parts.push(`CHECK (${fieldName} IN (${values}))`);
291
+ }
292
+ return parts.join(" ");
293
+ }
294
+ function mapFieldType(descriptor) {
295
+ switch (descriptor.kind) {
296
+ case "string":
297
+ return "TEXT";
298
+ case "number":
299
+ return "REAL";
300
+ case "boolean":
301
+ return "INTEGER";
302
+ case "enum":
303
+ return "TEXT";
304
+ case "timestamp":
305
+ return "INTEGER";
306
+ case "array":
307
+ return "TEXT";
308
+ // JSON-serialized
309
+ case "richtext":
310
+ return "BLOB";
311
+ }
312
+ }
313
+ function sqlDefault(value) {
314
+ if (value === null) return "NULL";
315
+ if (typeof value === "string") return `'${value}'`;
316
+ if (typeof value === "number") return String(value);
317
+ if (typeof value === "boolean") return value ? "1" : "0";
318
+ return `'${JSON.stringify(value)}'`;
319
+ }
320
+
321
+ // src/schema/types.ts
322
+ var FieldBuilder = class _FieldBuilder {
323
+ _kind;
324
+ _required;
325
+ _defaultValue;
326
+ _auto;
327
+ constructor(kind, required = true, defaultValue = void 0, auto = false) {
328
+ this._kind = kind;
329
+ this._required = required;
330
+ this._defaultValue = defaultValue;
331
+ this._auto = auto;
332
+ }
333
+ /** Mark this field as optional (not required on insert) */
334
+ optional() {
335
+ return new _FieldBuilder(this._kind, false, this._defaultValue, this._auto);
336
+ }
337
+ /** Set a default value for this field. Implicitly makes the field optional. */
338
+ default(value) {
339
+ return new _FieldBuilder(this._kind, false, value, this._auto);
340
+ }
341
+ /** Mark this field as auto-populated (e.g., createdAt timestamps). Developers cannot set auto fields. */
342
+ auto() {
343
+ return new _FieldBuilder(this._kind, false, void 0, true);
344
+ }
345
+ /** @internal Build the final FieldDescriptor. Used by defineSchema(). */
346
+ _build() {
347
+ return {
348
+ kind: this._kind,
349
+ required: this._required,
350
+ defaultValue: this._defaultValue,
351
+ auto: this._auto,
352
+ enumValues: null,
353
+ itemKind: null
354
+ };
355
+ }
356
+ };
357
+ var EnumFieldBuilder = class _EnumFieldBuilder extends FieldBuilder {
358
+ _enumValues;
359
+ constructor(values, required = true, defaultValue = void 0, auto = false) {
360
+ super("enum", required, defaultValue, auto);
361
+ this._enumValues = values;
362
+ }
363
+ optional() {
364
+ return new _EnumFieldBuilder(this._enumValues, false, this._defaultValue, this._auto);
365
+ }
366
+ default(value) {
367
+ return new _EnumFieldBuilder(this._enumValues, false, value, this._auto);
368
+ }
369
+ auto() {
370
+ return new _EnumFieldBuilder(this._enumValues, false, void 0, true);
371
+ }
372
+ _build() {
373
+ return {
374
+ kind: "enum",
375
+ required: this._required,
376
+ defaultValue: this._defaultValue,
377
+ auto: this._auto,
378
+ enumValues: this._enumValues,
379
+ itemKind: null
380
+ };
381
+ }
382
+ };
383
+ var ArrayFieldBuilder = class _ArrayFieldBuilder extends FieldBuilder {
384
+ _itemKind;
385
+ constructor(itemBuilder, required = true, defaultValue = void 0, auto = false) {
386
+ super("array", required, defaultValue, auto);
387
+ this._itemKind = itemBuilder._build().kind;
388
+ }
389
+ optional() {
390
+ return new _ArrayFieldBuilder(
391
+ new FieldBuilder(this._itemKind),
392
+ false,
393
+ this._defaultValue,
394
+ this._auto
395
+ );
396
+ }
397
+ default(value) {
398
+ return new _ArrayFieldBuilder(new FieldBuilder(this._itemKind), false, value, this._auto);
399
+ }
400
+ auto() {
401
+ return new _ArrayFieldBuilder(new FieldBuilder(this._itemKind), false, void 0, true);
402
+ }
403
+ _build() {
404
+ return {
405
+ kind: "array",
406
+ required: this._required,
407
+ defaultValue: this._defaultValue,
408
+ auto: this._auto,
409
+ enumValues: null,
410
+ itemKind: this._itemKind
411
+ };
412
+ }
413
+ };
414
+ var t = {
415
+ string() {
416
+ return new FieldBuilder("string", true, void 0, false);
417
+ },
418
+ number() {
419
+ return new FieldBuilder("number", true, void 0, false);
420
+ },
421
+ boolean() {
422
+ return new FieldBuilder("boolean", true, void 0, false);
423
+ },
424
+ timestamp() {
425
+ return new FieldBuilder("timestamp", true, void 0, false);
426
+ },
427
+ richtext() {
428
+ return new FieldBuilder("richtext", true, void 0, false);
429
+ },
430
+ enum(values) {
431
+ return new EnumFieldBuilder(values, true, void 0, false);
432
+ },
433
+ array(itemBuilder) {
434
+ return new ArrayFieldBuilder(itemBuilder, true, void 0, false);
435
+ }
436
+ };
437
+
438
+ // src/schema/validation.ts
439
+ function validateRecord(collection, collectionDef, data, operationType) {
440
+ if (operationType === "delete") {
441
+ return {};
442
+ }
443
+ const result = {};
444
+ const fieldNames = Object.keys(collectionDef.fields);
445
+ for (const key of Object.keys(data)) {
446
+ if (!(key in collectionDef.fields)) {
447
+ throw new SchemaValidationError(
448
+ `Unknown field "${key}" in collection "${collection}". Available fields: ${fieldNames.join(", ")}`,
449
+ { collection, field: key }
450
+ );
451
+ }
452
+ }
453
+ for (const [fieldName, descriptor] of Object.entries(collectionDef.fields)) {
454
+ const value = data[fieldName];
455
+ const hasValue = fieldName in data;
456
+ if (descriptor.auto && hasValue) {
457
+ throw new SchemaValidationError(
458
+ `Field "${fieldName}" in collection "${collection}" is auto-populated and cannot be set manually`,
459
+ { collection, field: fieldName }
460
+ );
461
+ }
462
+ if (operationType === "update") {
463
+ if (hasValue) {
464
+ if (value !== void 0 && value !== null) {
465
+ validateFieldValue(collection, fieldName, descriptor, value);
466
+ }
467
+ result[fieldName] = value;
468
+ }
469
+ continue;
470
+ }
471
+ if (descriptor.auto) {
472
+ continue;
473
+ }
474
+ if (!hasValue || value === void 0) {
475
+ if (descriptor.defaultValue !== void 0) {
476
+ result[fieldName] = typeof descriptor.defaultValue === "object" && descriptor.defaultValue !== null ? JSON.parse(JSON.stringify(descriptor.defaultValue)) : descriptor.defaultValue;
477
+ continue;
478
+ }
479
+ if (descriptor.required) {
480
+ throw new SchemaValidationError(
481
+ `Required field "${fieldName}" is missing in collection "${collection}"`,
482
+ { collection, field: fieldName }
483
+ );
484
+ }
485
+ continue;
486
+ }
487
+ validateFieldValue(collection, fieldName, descriptor, value);
488
+ result[fieldName] = value;
489
+ }
490
+ return result;
491
+ }
492
+ function validateFieldValue(collection, fieldName, descriptor, value) {
493
+ switch (descriptor.kind) {
494
+ case "string": {
495
+ if (typeof value !== "string") {
496
+ throw new SchemaValidationError(
497
+ `Field "${fieldName}" in collection "${collection}" must be a string, got ${typeof value}`,
498
+ { collection, field: fieldName, expectedType: "string", receivedType: typeof value }
499
+ );
500
+ }
501
+ break;
502
+ }
503
+ case "number": {
504
+ if (typeof value !== "number" || Number.isNaN(value)) {
505
+ throw new SchemaValidationError(
506
+ `Field "${fieldName}" in collection "${collection}" must be a number, got ${typeof value}`,
507
+ { collection, field: fieldName, expectedType: "number", receivedType: typeof value }
508
+ );
509
+ }
510
+ break;
511
+ }
512
+ case "boolean": {
513
+ if (typeof value !== "boolean") {
514
+ throw new SchemaValidationError(
515
+ `Field "${fieldName}" in collection "${collection}" must be a boolean, got ${typeof value}`,
516
+ { collection, field: fieldName, expectedType: "boolean", receivedType: typeof value }
517
+ );
518
+ }
519
+ break;
520
+ }
521
+ case "timestamp": {
522
+ if (typeof value !== "number" || !Number.isFinite(value)) {
523
+ throw new SchemaValidationError(
524
+ `Field "${fieldName}" in collection "${collection}" must be a timestamp (number), got ${typeof value}`,
525
+ {
526
+ collection,
527
+ field: fieldName,
528
+ expectedType: "timestamp",
529
+ receivedType: typeof value
530
+ }
531
+ );
532
+ }
533
+ break;
534
+ }
535
+ case "enum": {
536
+ if (typeof value !== "string") {
537
+ throw new SchemaValidationError(
538
+ `Field "${fieldName}" in collection "${collection}" must be a string (enum), got ${typeof value}`,
539
+ { collection, field: fieldName, expectedType: "enum", receivedType: typeof value }
540
+ );
541
+ }
542
+ if (descriptor.enumValues && !descriptor.enumValues.includes(value)) {
543
+ throw new SchemaValidationError(
544
+ `Field "${fieldName}" in collection "${collection}" must be one of: ${descriptor.enumValues.join(", ")}. Got "${value}"`,
545
+ {
546
+ collection,
547
+ field: fieldName,
548
+ allowedValues: [...descriptor.enumValues],
549
+ received: value
550
+ }
551
+ );
552
+ }
553
+ break;
554
+ }
555
+ case "array": {
556
+ if (!Array.isArray(value)) {
557
+ throw new SchemaValidationError(
558
+ `Field "${fieldName}" in collection "${collection}" must be an array, got ${typeof value}`,
559
+ { collection, field: fieldName, expectedType: "array", receivedType: typeof value }
560
+ );
561
+ }
562
+ if (descriptor.itemKind) {
563
+ const expectedType = jsTypeForKind(descriptor.itemKind);
564
+ for (let i = 0; i < value.length; i++) {
565
+ const item = value[i];
566
+ if (!matchesJsType(item, expectedType)) {
567
+ throw new SchemaValidationError(
568
+ `Field "${fieldName}[${i}]" in collection "${collection}" must be a ${descriptor.itemKind}, got ${typeof item}`,
569
+ {
570
+ collection,
571
+ field: `${fieldName}[${i}]`,
572
+ expectedType: descriptor.itemKind,
573
+ receivedType: typeof item
574
+ }
575
+ );
576
+ }
577
+ }
578
+ }
579
+ break;
580
+ }
581
+ case "richtext": {
582
+ if (!(value instanceof Uint8Array) && typeof value !== "string") {
583
+ throw new SchemaValidationError(
584
+ `Field "${fieldName}" in collection "${collection}" must be a Uint8Array or string for richtext, got ${typeof value}`,
585
+ {
586
+ collection,
587
+ field: fieldName,
588
+ expectedType: "richtext",
589
+ receivedType: typeof value
590
+ }
591
+ );
592
+ }
593
+ break;
594
+ }
595
+ }
596
+ }
597
+ function jsTypeForKind(kind) {
598
+ switch (kind) {
599
+ case "string":
600
+ case "enum":
601
+ return "string";
602
+ case "number":
603
+ case "timestamp":
604
+ return "number";
605
+ case "boolean":
606
+ return "boolean";
607
+ default:
608
+ return "object";
609
+ }
610
+ }
611
+ function matchesJsType(value, expected) {
612
+ switch (expected) {
613
+ case "string":
614
+ return typeof value === "string";
615
+ case "number":
616
+ return typeof value === "number";
617
+ case "boolean":
618
+ return typeof value === "boolean";
619
+ case "object":
620
+ return typeof value === "object";
621
+ default:
622
+ return false;
623
+ }
624
+ }
625
+
626
+ // src/version-vector/version-vector.ts
627
+ function createVersionVector() {
628
+ return /* @__PURE__ */ new Map();
629
+ }
630
+ function mergeVectors(a, b) {
631
+ const merged = new Map(a);
632
+ for (const [nodeId, seq] of b) {
633
+ merged.set(nodeId, Math.max(merged.get(nodeId) ?? 0, seq));
634
+ }
635
+ return merged;
636
+ }
637
+ function advanceVector(vector, nodeId, seq) {
638
+ const updated = new Map(vector);
639
+ updated.set(nodeId, Math.max(updated.get(nodeId) ?? 0, seq));
640
+ return updated;
641
+ }
642
+ function dominates(a, b) {
643
+ for (const [nodeId, bSeq] of b) {
644
+ if ((a.get(nodeId) ?? 0) < bSeq) return false;
645
+ }
646
+ return true;
647
+ }
648
+ function vectorsEqual(a, b) {
649
+ if (a.size !== b.size) return false;
650
+ for (const [nodeId, aSeq] of a) {
651
+ if (b.get(nodeId) !== aSeq) return false;
652
+ }
653
+ return true;
654
+ }
655
+ function computeDelta(localVector, remoteVector, operationLog) {
656
+ const missing = [];
657
+ for (const [nodeId, localSeq] of localVector) {
658
+ const remoteSeq = remoteVector.get(nodeId) ?? 0;
659
+ if (localSeq > remoteSeq) {
660
+ missing.push(...operationLog.getRange(nodeId, remoteSeq + 1, localSeq));
661
+ }
662
+ }
663
+ return topologicalSort(missing);
664
+ }
665
+ function serializeVector(vector) {
666
+ const entries = [...vector.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
667
+ return JSON.stringify(entries);
668
+ }
669
+ function deserializeVector(s) {
670
+ const entries = JSON.parse(s);
671
+ return new Map(entries);
672
+ }
673
+ export {
674
+ ArrayFieldBuilder,
675
+ CONNECTION_QUALITIES,
676
+ ClockDriftError,
677
+ EnumFieldBuilder,
678
+ FieldBuilder,
679
+ HybridLogicalClock,
680
+ KoraError,
681
+ MERGE_STRATEGIES,
682
+ MergeConflictError,
683
+ OperationError,
684
+ SchemaValidationError,
685
+ StorageError,
686
+ SyncError,
687
+ advanceVector,
688
+ computeDelta,
689
+ createOperation,
690
+ createVersionVector,
691
+ defineSchema,
692
+ deserializeVector,
693
+ dominates,
694
+ extractTimestamp,
695
+ generateFullDDL,
696
+ generateSQL,
697
+ generateUUIDv7,
698
+ isValidOperation,
699
+ isValidUUIDv7,
700
+ mergeVectors,
701
+ serializeVector,
702
+ t,
703
+ validateRecord,
704
+ vectorsEqual,
705
+ verifyOperationIntegrity
706
+ };
707
+ //# sourceMappingURL=index.js.map