@korajs/core 0.6.0 → 1.0.0-beta.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.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { R as RandomSource, A as AtomicOpType, a as AtomicOp, F as FieldBuilder, C as CustomResolver, M as MigrationDefinition, S as SchemaDefinition, b as CollectionDefinition, c as RelationDefinition, O as OperationType, E as EnumFieldBuilder, d as ArrayFieldBuilder, e as FieldKind, f as Operation, V as VersionVector, g as MigrationStep, h as StateMachineConstraint, T as TransitionMap, i as TransitionValidationResult } from './events-D-FWfFg8.cjs';
2
- export { j as CONNECTION_QUALITIES, k as ConnectionQuality, l as Constraint, m as FieldDescriptor, n as FieldMergeStrategy, H as HLCTimestamp, K as KoraEvent, o as KoraEventByType, p as KoraEventEmitter, q as KoraEventListener, r as KoraEventType, s as MERGE_STRATEGIES, t as MergeStrategy, u as MergeTrace, v as MigrationBuilder, w as OnDeleteAction, x as OperationInput, y as RelationType, z as RollbackBuilder, B as SequenceConfig, D as StateMachineDefinition, G as SyncDiagnosticsSnapshot, I as SyncRuleDefinition, J as TimeSource, L as migrate, N as t } from './events-D-FWfFg8.cjs';
3
- export { H as HybridLogicalClock, c as createOperation, i as isValidOperation, v as verifyOperationIntegrity } from './operation-3-ZJJf3w.cjs';
4
- export { S as ScopeMap, b as buildScopeMap } from './build-scope-map-B1CcncZN.cjs';
1
+ import { R as RandomSource, A as AtomicOpType, a as AtomicOp, F as FieldBuilder, C as CustomResolver, M as MigrationDefinition, S as SchemaDefinition, b as CollectionDefinition, c as RelationDefinition, O as OperationType, E as EnumFieldBuilder, d as ArrayFieldBuilder, e as FieldKind, f as Operation, V as VersionVector, g as MigrationStep, h as StateMachineConstraint, T as TransitionMap, i as TransitionValidationResult } from './events-BynBOsO3.cjs';
2
+ export { j as CONNECTION_QUALITIES, k as ConnectionQuality, l as Constraint, m as FieldDescriptor, n as FieldMergeStrategy, H as HLCTimestamp, K as KoraEvent, o as KoraEventByType, p as KoraEventEmitter, q as KoraEventListener, r as KoraEventType, s as MERGE_STRATEGIES, t as MergeStrategy, u as MergeTrace, v as MigrationBuilder, w as OnDeleteAction, x as OperationInput, y as RelationType, z as RollbackBuilder, B as SequenceConfig, D as StateMachineDefinition, G as SyncDiagnosticsSnapshot, I as SyncRuleDefinition, J as TimeSource, L as migrate, N as t } from './events-BynBOsO3.cjs';
3
+ export { H as HybridLogicalClock, M as MAX_LOGICAL, c as createOperation, i as isValidOperation, v as verifyOperationIntegrity } from './operation-BpZlYSpe.cjs';
4
+ export { S as ScopeMap, b as buildScopeMap } from './build-scope-map-BIeawJzC.cjs';
5
5
 
6
6
  /**
7
7
  * Base error class for all Kora errors.
@@ -71,6 +71,23 @@ declare class AppNotReadyError extends KoraError {
71
71
  * Thrown when the HLC detects excessive clock drift.
72
72
  * Drift > 60s: warning. Drift > 5min: this error is thrown, refusing to generate timestamps.
73
73
  */
74
+ declare class RemoteClockDriftError extends KoraError {
75
+ readonly remoteWallTime: number;
76
+ readonly localReferenceTime: number;
77
+ constructor(remoteWallTime: number, localReferenceTime: number);
78
+ }
79
+ /**
80
+ * Thrown when an HLC timestamp has structurally invalid fields: non-integer or
81
+ * negative wallTime/logical, or a logical counter beyond the serializable cap.
82
+ * Rejected BEFORE any clock state changes, so a malformed remote timestamp can
83
+ * never corrupt a replica's clock or break the lexicographic ordering of the
84
+ * serialized form.
85
+ */
86
+ declare class InvalidTimestampError extends KoraError {
87
+ readonly wallTime: number;
88
+ readonly logical: number;
89
+ constructor(message: string, wallTime: number, logical: number);
90
+ }
74
91
  declare class ClockDriftError extends KoraError {
75
92
  readonly currentHlcTime: number;
76
93
  readonly physicalTime: number;
@@ -289,6 +306,57 @@ declare const op: {
289
306
  */
290
307
  declare function toAtomicOp(sentinel: AtomicOpSentinel): AtomicOp;
291
308
 
309
+ /**
310
+ * Canonical JSON-safe encoding of binary bytes stored inside `op.data` /
311
+ * `op.previousData` (currently richtext field values). A raw Uint8Array
312
+ * JSON-serializes to a numeric-key object and an ArrayBuffer to `{}` (silent
313
+ * data loss), which breaks content-hash stability, persistence round-trips,
314
+ * remote application, and merge. Tagging the bytes as base64 at
315
+ * operation-creation time makes the hashed value, the persisted JSON, the wire
316
+ * payload, and the value the merge engine sees the identical canonical value.
317
+ *
318
+ * This convention lives in `@korajs/core` because `op.data` is a core concept:
319
+ * both `@korajs/store` (persistence, creation) and `@korajs/merge` (CRDT merge)
320
+ * must agree on it, and neither may depend on the other.
321
+ */
322
+ interface KoraBytesValue {
323
+ $koraBytes: string;
324
+ }
325
+ /**
326
+ * Type guard for the tagged binary form. Requires exactly the single
327
+ * `$koraBytes` key so arbitrary user objects that happen to contain the key
328
+ * are never silently reinterpreted as bytes.
329
+ */
330
+ declare function isKoraBytesValue(value: unknown): value is KoraBytesValue;
331
+ /**
332
+ * Detects the numeric-key object shape a raw Uint8Array produced when
333
+ * JSON-serialized before the tagged encoding existed ({"0":1,"1":2,...}).
334
+ */
335
+ declare function isLegacyNumericByteObject(value: unknown): value is Record<string, number>;
336
+ /**
337
+ * Dependency-free base64 encoder. btoa is unavailable for bytes in Node and
338
+ * Buffer is unavailable in browsers, so a manual implementation is the only
339
+ * deterministic option that works in every runtime Kora targets.
340
+ */
341
+ declare function bytesToBase64(bytes: Uint8Array): string;
342
+ /**
343
+ * Inverse of {@link bytesToBase64}.
344
+ */
345
+ declare function base64ToBytes(base64: string): Uint8Array;
346
+ /**
347
+ * Normalize a validated binary-or-string value into the canonical form stored
348
+ * in `op.data`: strings pass through unchanged (backward compatible with every
349
+ * existing operation), binary values become the tagged base64 form.
350
+ */
351
+ declare function encodeBytesForOpData(value: string | Uint8Array | ArrayBuffer): string | KoraBytesValue;
352
+ /**
353
+ * Reverse of {@link encodeBytesForOpData}, tolerant of every shape a binary
354
+ * op-data value has ever taken: canonical strings and tagged bytes, in-memory
355
+ * Uint8Array/ArrayBuffer (ops that never round-tripped through JSON), and
356
+ * pre-fix numeric-key objects.
357
+ */
358
+ declare function decodeBytesFromOpData(value: unknown): string | Uint8Array;
359
+
292
360
  /**
293
361
  * Input shape for defineSchema() — what the developer writes.
294
362
  */
@@ -842,4 +910,4 @@ interface ProtoOutput {
842
910
  */
843
911
  declare function generateProtoDefinitions(schema: SchemaDefinition): ProtoOutput;
844
912
 
845
- export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, CausalTracker, ClockDriftError, CollectionDefinition, type CollectionInput, type ConstraintInput, CustomResolver, EnumFieldBuilder, FieldBuilder, FieldKind, type FieldKindToType, type InferFieldType, type InferInsertInput, type InferRecord, type InferUpdateInput, KORA_ERROR_FIX_SUGGESTIONS, KoraError, MergeConflictError, MigrationDefinition, MigrationRollbackError, MigrationStep, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, buildStateMachineConstraints, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createReversibleMigration, createVersionVector, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, isApplyFailure, isAtomicOp, isCollectionSyncScoped, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, resolveAtomicOp, rollbackStepsToSQL, serializeVector, toAtomicOp, validateRecord, validateTransition, vectorsEqual };
913
+ export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, CausalTracker, ClockDriftError, CollectionDefinition, type CollectionInput, type ConstraintInput, CustomResolver, EnumFieldBuilder, FieldBuilder, FieldKind, type FieldKindToType, type InferFieldType, type InferInsertInput, type InferRecord, type InferUpdateInput, InvalidTimestampError, KORA_ERROR_FIX_SUGGESTIONS, type KoraBytesValue, KoraError, MergeConflictError, MigrationDefinition, MigrationRollbackError, MigrationStep, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, RemoteClockDriftError, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, base64ToBytes, buildStateMachineConstraints, bytesToBase64, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createReversibleMigration, createVersionVector, decodeBytesFromOpData, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, encodeBytesForOpData, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, isApplyFailure, isAtomicOp, isCollectionSyncScoped, isKoraBytesValue, isLegacyNumericByteObject, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, resolveAtomicOp, rollbackStepsToSQL, serializeVector, toAtomicOp, validateRecord, validateTransition, vectorsEqual };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { R as RandomSource, A as AtomicOpType, a as AtomicOp, F as FieldBuilder, C as CustomResolver, M as MigrationDefinition, S as SchemaDefinition, b as CollectionDefinition, c as RelationDefinition, O as OperationType, E as EnumFieldBuilder, d as ArrayFieldBuilder, e as FieldKind, f as Operation, V as VersionVector, g as MigrationStep, h as StateMachineConstraint, T as TransitionMap, i as TransitionValidationResult } from './events-D-FWfFg8.js';
2
- export { j as CONNECTION_QUALITIES, k as ConnectionQuality, l as Constraint, m as FieldDescriptor, n as FieldMergeStrategy, H as HLCTimestamp, K as KoraEvent, o as KoraEventByType, p as KoraEventEmitter, q as KoraEventListener, r as KoraEventType, s as MERGE_STRATEGIES, t as MergeStrategy, u as MergeTrace, v as MigrationBuilder, w as OnDeleteAction, x as OperationInput, y as RelationType, z as RollbackBuilder, B as SequenceConfig, D as StateMachineDefinition, G as SyncDiagnosticsSnapshot, I as SyncRuleDefinition, J as TimeSource, L as migrate, N as t } from './events-D-FWfFg8.js';
3
- export { H as HybridLogicalClock, c as createOperation, i as isValidOperation, v as verifyOperationIntegrity } from './operation-CygOwIjZ.js';
4
- export { S as ScopeMap, b as buildScopeMap } from './build-scope-map-BmgAT82h.js';
1
+ import { R as RandomSource, A as AtomicOpType, a as AtomicOp, F as FieldBuilder, C as CustomResolver, M as MigrationDefinition, S as SchemaDefinition, b as CollectionDefinition, c as RelationDefinition, O as OperationType, E as EnumFieldBuilder, d as ArrayFieldBuilder, e as FieldKind, f as Operation, V as VersionVector, g as MigrationStep, h as StateMachineConstraint, T as TransitionMap, i as TransitionValidationResult } from './events-BynBOsO3.js';
2
+ export { j as CONNECTION_QUALITIES, k as ConnectionQuality, l as Constraint, m as FieldDescriptor, n as FieldMergeStrategy, H as HLCTimestamp, K as KoraEvent, o as KoraEventByType, p as KoraEventEmitter, q as KoraEventListener, r as KoraEventType, s as MERGE_STRATEGIES, t as MergeStrategy, u as MergeTrace, v as MigrationBuilder, w as OnDeleteAction, x as OperationInput, y as RelationType, z as RollbackBuilder, B as SequenceConfig, D as StateMachineDefinition, G as SyncDiagnosticsSnapshot, I as SyncRuleDefinition, J as TimeSource, L as migrate, N as t } from './events-BynBOsO3.js';
3
+ export { H as HybridLogicalClock, M as MAX_LOGICAL, c as createOperation, i as isValidOperation, v as verifyOperationIntegrity } from './operation-D5WOZYvy.js';
4
+ export { S as ScopeMap, b as buildScopeMap } from './build-scope-map-DOf4JLTh.js';
5
5
 
6
6
  /**
7
7
  * Base error class for all Kora errors.
@@ -71,6 +71,23 @@ declare class AppNotReadyError extends KoraError {
71
71
  * Thrown when the HLC detects excessive clock drift.
72
72
  * Drift > 60s: warning. Drift > 5min: this error is thrown, refusing to generate timestamps.
73
73
  */
74
+ declare class RemoteClockDriftError extends KoraError {
75
+ readonly remoteWallTime: number;
76
+ readonly localReferenceTime: number;
77
+ constructor(remoteWallTime: number, localReferenceTime: number);
78
+ }
79
+ /**
80
+ * Thrown when an HLC timestamp has structurally invalid fields: non-integer or
81
+ * negative wallTime/logical, or a logical counter beyond the serializable cap.
82
+ * Rejected BEFORE any clock state changes, so a malformed remote timestamp can
83
+ * never corrupt a replica's clock or break the lexicographic ordering of the
84
+ * serialized form.
85
+ */
86
+ declare class InvalidTimestampError extends KoraError {
87
+ readonly wallTime: number;
88
+ readonly logical: number;
89
+ constructor(message: string, wallTime: number, logical: number);
90
+ }
74
91
  declare class ClockDriftError extends KoraError {
75
92
  readonly currentHlcTime: number;
76
93
  readonly physicalTime: number;
@@ -289,6 +306,57 @@ declare const op: {
289
306
  */
290
307
  declare function toAtomicOp(sentinel: AtomicOpSentinel): AtomicOp;
291
308
 
309
+ /**
310
+ * Canonical JSON-safe encoding of binary bytes stored inside `op.data` /
311
+ * `op.previousData` (currently richtext field values). A raw Uint8Array
312
+ * JSON-serializes to a numeric-key object and an ArrayBuffer to `{}` (silent
313
+ * data loss), which breaks content-hash stability, persistence round-trips,
314
+ * remote application, and merge. Tagging the bytes as base64 at
315
+ * operation-creation time makes the hashed value, the persisted JSON, the wire
316
+ * payload, and the value the merge engine sees the identical canonical value.
317
+ *
318
+ * This convention lives in `@korajs/core` because `op.data` is a core concept:
319
+ * both `@korajs/store` (persistence, creation) and `@korajs/merge` (CRDT merge)
320
+ * must agree on it, and neither may depend on the other.
321
+ */
322
+ interface KoraBytesValue {
323
+ $koraBytes: string;
324
+ }
325
+ /**
326
+ * Type guard for the tagged binary form. Requires exactly the single
327
+ * `$koraBytes` key so arbitrary user objects that happen to contain the key
328
+ * are never silently reinterpreted as bytes.
329
+ */
330
+ declare function isKoraBytesValue(value: unknown): value is KoraBytesValue;
331
+ /**
332
+ * Detects the numeric-key object shape a raw Uint8Array produced when
333
+ * JSON-serialized before the tagged encoding existed ({"0":1,"1":2,...}).
334
+ */
335
+ declare function isLegacyNumericByteObject(value: unknown): value is Record<string, number>;
336
+ /**
337
+ * Dependency-free base64 encoder. btoa is unavailable for bytes in Node and
338
+ * Buffer is unavailable in browsers, so a manual implementation is the only
339
+ * deterministic option that works in every runtime Kora targets.
340
+ */
341
+ declare function bytesToBase64(bytes: Uint8Array): string;
342
+ /**
343
+ * Inverse of {@link bytesToBase64}.
344
+ */
345
+ declare function base64ToBytes(base64: string): Uint8Array;
346
+ /**
347
+ * Normalize a validated binary-or-string value into the canonical form stored
348
+ * in `op.data`: strings pass through unchanged (backward compatible with every
349
+ * existing operation), binary values become the tagged base64 form.
350
+ */
351
+ declare function encodeBytesForOpData(value: string | Uint8Array | ArrayBuffer): string | KoraBytesValue;
352
+ /**
353
+ * Reverse of {@link encodeBytesForOpData}, tolerant of every shape a binary
354
+ * op-data value has ever taken: canonical strings and tagged bytes, in-memory
355
+ * Uint8Array/ArrayBuffer (ops that never round-tripped through JSON), and
356
+ * pre-fix numeric-key objects.
357
+ */
358
+ declare function decodeBytesFromOpData(value: unknown): string | Uint8Array;
359
+
292
360
  /**
293
361
  * Input shape for defineSchema() — what the developer writes.
294
362
  */
@@ -842,4 +910,4 @@ interface ProtoOutput {
842
910
  */
843
911
  declare function generateProtoDefinitions(schema: SchemaDefinition): ProtoOutput;
844
912
 
845
- export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, CausalTracker, ClockDriftError, CollectionDefinition, type CollectionInput, type ConstraintInput, CustomResolver, EnumFieldBuilder, FieldBuilder, FieldKind, type FieldKindToType, type InferFieldType, type InferInsertInput, type InferRecord, type InferUpdateInput, KORA_ERROR_FIX_SUGGESTIONS, KoraError, MergeConflictError, MigrationDefinition, MigrationRollbackError, MigrationStep, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, buildStateMachineConstraints, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createReversibleMigration, createVersionVector, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, isApplyFailure, isAtomicOp, isCollectionSyncScoped, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, resolveAtomicOp, rollbackStepsToSQL, serializeVector, toAtomicOp, validateRecord, validateTransition, vectorsEqual };
913
+ export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, CausalTracker, ClockDriftError, CollectionDefinition, type CollectionInput, type ConstraintInput, CustomResolver, EnumFieldBuilder, FieldBuilder, FieldKind, type FieldKindToType, type InferFieldType, type InferInsertInput, type InferRecord, type InferUpdateInput, InvalidTimestampError, KORA_ERROR_FIX_SUGGESTIONS, type KoraBytesValue, KoraError, MergeConflictError, MigrationDefinition, MigrationRollbackError, MigrationStep, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, RemoteClockDriftError, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, base64ToBytes, buildStateMachineConstraints, bytesToBase64, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createReversibleMigration, createVersionVector, decodeBytesFromOpData, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, encodeBytesForOpData, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, isApplyFailure, isAtomicOp, isCollectionSyncScoped, isKoraBytesValue, isLegacyNumericByteObject, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, resolveAtomicOp, rollbackStepsToSQL, serializeVector, toAtomicOp, validateRecord, validateTransition, vectorsEqual };
package/dist/index.js CHANGED
@@ -2,10 +2,13 @@ import {
2
2
  AppNotReadyError,
3
3
  ClockDriftError,
4
4
  HybridLogicalClock,
5
+ InvalidTimestampError,
5
6
  KORA_ERROR_FIX_SUGGESTIONS,
6
7
  KoraError,
8
+ MAX_LOGICAL,
7
9
  MergeConflictError,
8
10
  OperationError,
11
+ RemoteClockDriftError,
9
12
  SchemaValidationError,
10
13
  StorageError,
11
14
  SyncError,
@@ -14,7 +17,7 @@ import {
14
17
  isValidOperation,
15
18
  topologicalSort,
16
19
  verifyOperationIntegrity
17
- } from "./chunk-3VIOZT7D.js";
20
+ } from "./chunk-5IICSH6H.js";
18
21
 
19
22
  // src/types.ts
20
23
  var MERGE_STRATEGIES = [
@@ -235,6 +238,106 @@ function toAtomicOp(sentinel) {
235
238
  return { type: sentinel.type, value: sentinel.value };
236
239
  }
237
240
 
241
+ // src/operations/op-data-binary.ts
242
+ function isKoraBytesValue(value) {
243
+ if (typeof value !== "object" || value === null) {
244
+ return false;
245
+ }
246
+ const record = value;
247
+ return Object.keys(record).length === 1 && typeof record.$koraBytes === "string";
248
+ }
249
+ function isLegacyNumericByteObject(value) {
250
+ if (typeof value !== "object" || value === null || ArrayBuffer.isView(value)) {
251
+ return false;
252
+ }
253
+ const record = value;
254
+ const keys = Object.keys(record);
255
+ if (keys.length === 0) {
256
+ return false;
257
+ }
258
+ for (let i = 0; i < keys.length; i++) {
259
+ const byte = record[String(i)];
260
+ if (typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255) {
261
+ return false;
262
+ }
263
+ }
264
+ return true;
265
+ }
266
+ var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
267
+ var BASE64_REVERSE = new Map(
268
+ Array.from(BASE64_ALPHABET, (char, index) => [char, index])
269
+ );
270
+ function bytesToBase64(bytes) {
271
+ let out = "";
272
+ for (let i = 0; i < bytes.length; i += 3) {
273
+ const b0 = bytes[i] ?? 0;
274
+ const b1 = bytes[i + 1] ?? 0;
275
+ const b2 = bytes[i + 2] ?? 0;
276
+ const triple = b0 << 16 | b1 << 8 | b2;
277
+ out += BASE64_ALPHABET[triple >> 18 & 63] ?? "";
278
+ out += BASE64_ALPHABET[triple >> 12 & 63] ?? "";
279
+ out += i + 1 < bytes.length ? BASE64_ALPHABET[triple >> 6 & 63] ?? "" : "=";
280
+ out += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] ?? "" : "=";
281
+ }
282
+ return out;
283
+ }
284
+ function base64ToBytes(base64) {
285
+ const cleaned = base64.replace(/=+$/, "");
286
+ const out = new Uint8Array(Math.floor(cleaned.length * 6 / 8));
287
+ let buffer = 0;
288
+ let bits = 0;
289
+ let index = 0;
290
+ for (const char of cleaned) {
291
+ const value = BASE64_REVERSE.get(char);
292
+ if (value === void 0) {
293
+ throw new OperationError(`Invalid base64 character "${char}" in tagged binary value.`, {
294
+ char
295
+ });
296
+ }
297
+ buffer = buffer << 6 | value;
298
+ bits += 6;
299
+ if (bits >= 8) {
300
+ bits -= 8;
301
+ out[index] = buffer >> bits & 255;
302
+ index += 1;
303
+ }
304
+ }
305
+ return out;
306
+ }
307
+ function encodeBytesForOpData(value) {
308
+ if (typeof value === "string") {
309
+ return value;
310
+ }
311
+ const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
312
+ return { $koraBytes: bytesToBase64(bytes) };
313
+ }
314
+ function decodeBytesFromOpData(value) {
315
+ if (typeof value === "string") {
316
+ return value;
317
+ }
318
+ if (value instanceof Uint8Array) {
319
+ return value;
320
+ }
321
+ if (value instanceof ArrayBuffer) {
322
+ return new Uint8Array(value);
323
+ }
324
+ if (isKoraBytesValue(value)) {
325
+ return base64ToBytes(value.$koraBytes);
326
+ }
327
+ if (isLegacyNumericByteObject(value)) {
328
+ const keys = Object.keys(value);
329
+ const bytes = new Uint8Array(keys.length);
330
+ for (let i = 0; i < keys.length; i++) {
331
+ bytes[i] = value[String(i)] ?? 0;
332
+ }
333
+ return bytes;
334
+ }
335
+ throw new OperationError(
336
+ "Binary op-data value must be a string, Uint8Array, ArrayBuffer, tagged { $koraBytes } object, or legacy numeric-key byte object.",
337
+ { receivedType: typeof value }
338
+ );
339
+ }
340
+
238
341
  // src/scopes/sync-scope-bindings.ts
239
342
  function hasSchemaSyncRules(schema) {
240
343
  return schema.sync !== void 0 && Object.keys(schema.sync).length > 0;
@@ -664,6 +767,7 @@ function generateSQL(collectionName, collection, relations) {
664
767
  columns.push("_created_at INTEGER NOT NULL");
665
768
  columns.push("_updated_at INTEGER NOT NULL");
666
769
  columns.push("_version TEXT NOT NULL DEFAULT ''");
770
+ columns.push("_field_versions TEXT NOT NULL DEFAULT '{}'");
667
771
  columns.push("_deleted INTEGER NOT NULL DEFAULT 0");
668
772
  statements.push(`CREATE TABLE IF NOT EXISTS ${collectionName} (
669
773
  ${columns.join(",\n ")}
@@ -676,6 +780,10 @@ ALTER TABLE ${collectionName} ADD COLUMN ${colDef}`);
676
780
  statements.push(
677
781
  `--kora:safe-alter
678
782
  ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
783
+ );
784
+ statements.push(
785
+ `--kora:safe-alter
786
+ ALTER TABLE ${collectionName} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '{}'`
679
787
  );
680
788
  for (const indexField of collection.indexes) {
681
789
  statements.push(
@@ -703,6 +811,9 @@ ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
703
811
  schema_version INTEGER NOT NULL
704
812
  )`
705
813
  );
814
+ statements.push(
815
+ `CREATE INDEX IF NOT EXISTS idx_kora_ops_${collectionName}_record_id ON _kora_ops_${collectionName} (record_id)`
816
+ );
706
817
  return statements;
707
818
  }
708
819
  function generateFullDDL(schema) {
@@ -1168,9 +1279,9 @@ function validateFieldValue(collection, fieldName, descriptor, value) {
1168
1279
  break;
1169
1280
  }
1170
1281
  case "richtext": {
1171
- if (!(value instanceof Uint8Array) && typeof value !== "string") {
1282
+ if (!(value instanceof Uint8Array) && !(value instanceof ArrayBuffer) && typeof value !== "string") {
1172
1283
  throw new SchemaValidationError(
1173
- `Field "${fieldName}" in collection "${collection}" must be a Uint8Array or string for richtext, got ${typeof value}`,
1284
+ `Field "${fieldName}" in collection "${collection}" must be a Uint8Array, ArrayBuffer, or string for richtext, got ${typeof value}`,
1174
1285
  {
1175
1286
  collection,
1176
1287
  field: fieldName,
@@ -1915,21 +2026,26 @@ export {
1915
2026
  EnumFieldBuilder,
1916
2027
  FieldBuilder,
1917
2028
  HybridLogicalClock,
2029
+ InvalidTimestampError,
1918
2030
  KORA_ERROR_FIX_SUGGESTIONS,
1919
2031
  KoraError,
2032
+ MAX_LOGICAL,
1920
2033
  MERGE_STRATEGIES,
1921
2034
  MergeConflictError,
1922
2035
  MigrationBuilder,
1923
2036
  MigrationRollbackError,
1924
2037
  OperationError,
2038
+ RemoteClockDriftError,
1925
2039
  RollbackBuilder,
1926
2040
  SchemaValidationError,
1927
2041
  StorageError,
1928
2042
  SyncError,
1929
2043
  advanceVector,
1930
2044
  applyOperationTransforms,
2045
+ base64ToBytes,
1931
2046
  buildScopeMap,
1932
2047
  buildStateMachineConstraints,
2048
+ bytesToBase64,
1933
2049
  canAutoRollback,
1934
2050
  collectSchemaScopeFields,
1935
2051
  collectSchemaScopeValueKeys,
@@ -1937,11 +2053,13 @@ export {
1937
2053
  createOperation,
1938
2054
  createReversibleMigration,
1939
2055
  createVersionVector,
2056
+ decodeBytesFromOpData,
1940
2057
  defaultApplyFailureReason,
1941
2058
  defaultSequenceFormat,
1942
2059
  defineSchema,
1943
2060
  deserializeVector,
1944
2061
  dominates,
2062
+ encodeBytesForOpData,
1945
2063
  extractScopeValuesFromClaims,
1946
2064
  extractTimestamp,
1947
2065
  formatSequenceValue,
@@ -1957,6 +2075,8 @@ export {
1957
2075
  isApplyFailure,
1958
2076
  isAtomicOp,
1959
2077
  isCollectionSyncScoped,
2078
+ isKoraBytesValue,
2079
+ isLegacyNumericByteObject,
1960
2080
  isValidOperation,
1961
2081
  isValidUUIDv7,
1962
2082
  mergeVectors,