@korajs/core 1.0.0-beta.2 → 1.0.0-beta.5

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
@@ -359,6 +359,20 @@ interface AtomicOpSentinel {
359
359
  * @returns true if the value is an AtomicOpSentinel
360
360
  */
361
361
  declare function isAtomicOp(value: unknown): value is AtomicOpSentinel;
362
+ /**
363
+ * Apply a plain atomic op (`{ type, value }`) to a current value, producing the
364
+ * concrete result. This is the single source of truth for atomic-op semantics:
365
+ * {@link resolveAtomicOp} (sentinel form, used on the write path) delegates here,
366
+ * and the server's materialization replays stored atomic ops through it, so the
367
+ * server's materialized view composes concurrent atomic writes identically to the
368
+ * client. Folding this over a field's writes in HLC order yields, for a same-type
369
+ * atomic chain, exactly the composed intent (sum of increments, max of maxes, ...).
370
+ *
371
+ * @param currentValue - The current value of the field
372
+ * @param atomicOp - The atomic op to apply (type + operand)
373
+ * @returns The resulting concrete value
374
+ */
375
+ declare function applyAtomicOp(currentValue: unknown, atomicOp: AtomicOp): unknown;
362
376
  /**
363
377
  * Resolve an atomic op sentinel against a current value to produce a concrete result.
364
378
  *
@@ -437,6 +451,40 @@ declare const op: {
437
451
  */
438
452
  declare function toAtomicOp(sentinel: AtomicOpSentinel): AtomicOp;
439
453
 
454
+ /** One operation's contribution to a record's materialized state. */
455
+ interface ReplayOperation {
456
+ type: string;
457
+ data: Record<string, unknown> | null;
458
+ /**
459
+ * Atomic-op intents carried by this operation, per field. When present, a field
460
+ * is composed (rather than overwritten) if the previous writer on that field was
461
+ * an atomic op of the same type. Absent means every field resolves by
462
+ * last-write-wins.
463
+ */
464
+ atomicOps?: Record<string, AtomicOp> | null;
465
+ }
466
+ /**
467
+ * Replay a list of operations (MUST be pre-sorted in HLC total order —
468
+ * wallTime, then logical, then nodeId) to produce the current state of a single
469
+ * record, composing atomic-op intents.
470
+ *
471
+ * Per field, an atomic op composes onto the running value only when the field's
472
+ * previous writer was an atomic op of the SAME type (a same-type atomic chain:
473
+ * concurrent increments sum, maxes take the max, appends accumulate); any other
474
+ * write, including the first atomic write after a plain set, resolves by
475
+ * last-write-wins on the resolved value. Because operations are folded in HLC order,
476
+ * the current op always wins that last-write-wins comparison, so the result is
477
+ * deterministic and identical on every replica that folds the same operation set.
478
+ *
479
+ * This is the single definition of atomic materialization shared by the client
480
+ * (`@korajs/store` apply pipeline) and the server (`@korajs/server` materialization),
481
+ * so both converge to the same value for any number of concurrent atomic writers.
482
+ *
483
+ * Returns the record field data (without `id`) or null if the record was deleted
484
+ * or never inserted.
485
+ */
486
+ declare function replayOperationsForRecord(ops: ReplayOperation[]): Record<string, unknown> | null;
487
+
440
488
  /**
441
489
  * Canonical JSON-safe encoding of binary bytes stored inside `op.data` /
442
490
  * `op.previousData` (currently richtext field values). A raw Uint8Array
@@ -1077,4 +1125,4 @@ interface ProtoOutput {
1077
1125
  */
1078
1126
  declare function generateProtoDefinitions(schema: SchemaDefinition): ProtoOutput;
1079
1127
 
1080
- export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, BlobRef, type BlobRefMetadata, 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, MissingSecretKeyError, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, RemoteClockDriftError, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, type SecretKeyProvider, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, base64ToBytes, buildStateMachineConstraints, bytesToBase64, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createBlobRef, createReversibleMigration, createVersionVector, decodeBytesFromOpData, decryptSecret, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, encodeBytesForOpData, encryptSecret, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, hashBlob, hashSecret, isApplyFailure, isAtomicOp, isBlobRef, isCollectionSyncScoped, isKoraBytesValue, isLegacyNumericByteObject, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, quoteIdent, resolveAtomicOp, revealSecret, rollbackStepsToSQL, serializeVector, toAtomicOp, transformSecretFieldsForWrite, validateRecord, validateTransition, vectorsEqual, verifySecret, verifySecretValue };
1128
+ export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, BlobRef, type BlobRefMetadata, 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, MissingSecretKeyError, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, RemoteClockDriftError, type ReplayOperation, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, type SecretKeyProvider, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyAtomicOp, applyOperationTransforms, base64ToBytes, buildStateMachineConstraints, bytesToBase64, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createBlobRef, createReversibleMigration, createVersionVector, decodeBytesFromOpData, decryptSecret, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, encodeBytesForOpData, encryptSecret, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, hashBlob, hashSecret, isApplyFailure, isAtomicOp, isBlobRef, isCollectionSyncScoped, isKoraBytesValue, isLegacyNumericByteObject, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, quoteIdent, replayOperationsForRecord, resolveAtomicOp, revealSecret, rollbackStepsToSQL, serializeVector, toAtomicOp, transformSecretFieldsForWrite, validateRecord, validateTransition, vectorsEqual, verifySecret, verifySecretValue };
package/dist/index.d.ts CHANGED
@@ -359,6 +359,20 @@ interface AtomicOpSentinel {
359
359
  * @returns true if the value is an AtomicOpSentinel
360
360
  */
361
361
  declare function isAtomicOp(value: unknown): value is AtomicOpSentinel;
362
+ /**
363
+ * Apply a plain atomic op (`{ type, value }`) to a current value, producing the
364
+ * concrete result. This is the single source of truth for atomic-op semantics:
365
+ * {@link resolveAtomicOp} (sentinel form, used on the write path) delegates here,
366
+ * and the server's materialization replays stored atomic ops through it, so the
367
+ * server's materialized view composes concurrent atomic writes identically to the
368
+ * client. Folding this over a field's writes in HLC order yields, for a same-type
369
+ * atomic chain, exactly the composed intent (sum of increments, max of maxes, ...).
370
+ *
371
+ * @param currentValue - The current value of the field
372
+ * @param atomicOp - The atomic op to apply (type + operand)
373
+ * @returns The resulting concrete value
374
+ */
375
+ declare function applyAtomicOp(currentValue: unknown, atomicOp: AtomicOp): unknown;
362
376
  /**
363
377
  * Resolve an atomic op sentinel against a current value to produce a concrete result.
364
378
  *
@@ -437,6 +451,40 @@ declare const op: {
437
451
  */
438
452
  declare function toAtomicOp(sentinel: AtomicOpSentinel): AtomicOp;
439
453
 
454
+ /** One operation's contribution to a record's materialized state. */
455
+ interface ReplayOperation {
456
+ type: string;
457
+ data: Record<string, unknown> | null;
458
+ /**
459
+ * Atomic-op intents carried by this operation, per field. When present, a field
460
+ * is composed (rather than overwritten) if the previous writer on that field was
461
+ * an atomic op of the same type. Absent means every field resolves by
462
+ * last-write-wins.
463
+ */
464
+ atomicOps?: Record<string, AtomicOp> | null;
465
+ }
466
+ /**
467
+ * Replay a list of operations (MUST be pre-sorted in HLC total order —
468
+ * wallTime, then logical, then nodeId) to produce the current state of a single
469
+ * record, composing atomic-op intents.
470
+ *
471
+ * Per field, an atomic op composes onto the running value only when the field's
472
+ * previous writer was an atomic op of the SAME type (a same-type atomic chain:
473
+ * concurrent increments sum, maxes take the max, appends accumulate); any other
474
+ * write, including the first atomic write after a plain set, resolves by
475
+ * last-write-wins on the resolved value. Because operations are folded in HLC order,
476
+ * the current op always wins that last-write-wins comparison, so the result is
477
+ * deterministic and identical on every replica that folds the same operation set.
478
+ *
479
+ * This is the single definition of atomic materialization shared by the client
480
+ * (`@korajs/store` apply pipeline) and the server (`@korajs/server` materialization),
481
+ * so both converge to the same value for any number of concurrent atomic writers.
482
+ *
483
+ * Returns the record field data (without `id`) or null if the record was deleted
484
+ * or never inserted.
485
+ */
486
+ declare function replayOperationsForRecord(ops: ReplayOperation[]): Record<string, unknown> | null;
487
+
440
488
  /**
441
489
  * Canonical JSON-safe encoding of binary bytes stored inside `op.data` /
442
490
  * `op.previousData` (currently richtext field values). A raw Uint8Array
@@ -1077,4 +1125,4 @@ interface ProtoOutput {
1077
1125
  */
1078
1126
  declare function generateProtoDefinitions(schema: SchemaDefinition): ProtoOutput;
1079
1127
 
1080
- export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, BlobRef, type BlobRefMetadata, 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, MissingSecretKeyError, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, RemoteClockDriftError, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, type SecretKeyProvider, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, base64ToBytes, buildStateMachineConstraints, bytesToBase64, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createBlobRef, createReversibleMigration, createVersionVector, decodeBytesFromOpData, decryptSecret, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, encodeBytesForOpData, encryptSecret, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, hashBlob, hashSecret, isApplyFailure, isAtomicOp, isBlobRef, isCollectionSyncScoped, isKoraBytesValue, isLegacyNumericByteObject, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, quoteIdent, resolveAtomicOp, revealSecret, rollbackStepsToSQL, serializeVector, toAtomicOp, transformSecretFieldsForWrite, validateRecord, validateTransition, vectorsEqual, verifySecret, verifySecretValue };
1128
+ export { APPLY_FAILURE_CODES, APPLY_RESULTS, AppNotReadyError, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, BlobRef, type BlobRefMetadata, 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, MissingSecretKeyError, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, RemoteClockDriftError, type ReplayOperation, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, type SecretKeyProvider, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyAtomicOp, applyOperationTransforms, base64ToBytes, buildStateMachineConstraints, bytesToBase64, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createBlobRef, createReversibleMigration, createVersionVector, decodeBytesFromOpData, decryptSecret, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, encodeBytesForOpData, encryptSecret, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, hashBlob, hashSecret, isApplyFailure, isAtomicOp, isBlobRef, isCollectionSyncScoped, isKoraBytesValue, isLegacyNumericByteObject, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, quoteIdent, replayOperationsForRecord, resolveAtomicOp, revealSecret, rollbackStepsToSQL, serializeVector, toAtomicOp, transformSecretFieldsForWrite, validateRecord, validateTransition, vectorsEqual, verifySecret, verifySecretValue };
package/dist/index.js CHANGED
@@ -365,32 +365,36 @@ var KORA_ATOMIC_OP = /* @__PURE__ */ Symbol.for("kora.atomic_op");
365
365
  function isAtomicOp(value) {
366
366
  return typeof value === "object" && value !== null && KORA_ATOMIC_OP in value && value[KORA_ATOMIC_OP] === true;
367
367
  }
368
- function resolveAtomicOp(currentValue, sentinel) {
369
- switch (sentinel.type) {
368
+ function applyAtomicOp(currentValue, atomicOp) {
369
+ switch (atomicOp.type) {
370
370
  case "increment": {
371
371
  const current = typeof currentValue === "number" ? currentValue : 0;
372
- const operand = sentinel.value;
372
+ const operand = atomicOp.value;
373
373
  return current + operand;
374
374
  }
375
375
  case "max": {
376
376
  const current = typeof currentValue === "number" ? currentValue : Number.NEGATIVE_INFINITY;
377
- return Math.max(current, sentinel.value);
377
+ return Math.max(current, atomicOp.value);
378
378
  }
379
379
  case "min": {
380
380
  const current = typeof currentValue === "number" ? currentValue : Number.POSITIVE_INFINITY;
381
- return Math.min(current, sentinel.value);
381
+ return Math.min(current, atomicOp.value);
382
382
  }
383
383
  case "append": {
384
384
  const current = Array.isArray(currentValue) ? [...currentValue] : [];
385
- current.push(sentinel.value);
385
+ current.push(atomicOp.value);
386
386
  return current;
387
387
  }
388
388
  case "remove": {
389
389
  if (!Array.isArray(currentValue)) return [];
390
- return currentValue.filter((item) => item !== sentinel.value);
390
+ const target = JSON.stringify(atomicOp.value);
391
+ return currentValue.filter((item) => JSON.stringify(item) !== target);
391
392
  }
392
393
  }
393
394
  }
395
+ function resolveAtomicOp(currentValue, sentinel) {
396
+ return applyAtomicOp(currentValue, { type: sentinel.type, value: sentinel.value });
397
+ }
394
398
  function createSentinel(type, value) {
395
399
  return Object.freeze({
396
400
  [KORA_ATOMIC_OP]: true,
@@ -458,6 +462,46 @@ function toAtomicOp(sentinel) {
458
462
  return { type: sentinel.type, value: sentinel.value };
459
463
  }
460
464
 
465
+ // src/operations/replay-record.ts
466
+ function replayOperationsForRecord(ops) {
467
+ let record = null;
468
+ let deleted = false;
469
+ const lastWriterKind = {};
470
+ for (const op2 of ops) {
471
+ switch (op2.type) {
472
+ case "insert":
473
+ if (op2.data) {
474
+ record = { ...op2.data };
475
+ deleted = false;
476
+ for (const field of Object.keys(op2.data)) {
477
+ lastWriterKind[field] = "lww";
478
+ }
479
+ }
480
+ break;
481
+ case "update":
482
+ if (op2.data) {
483
+ const next = { ...record ?? {} };
484
+ for (const [field, value] of Object.entries(op2.data)) {
485
+ const atomicOp = op2.atomicOps?.[field];
486
+ if (atomicOp && lastWriterKind[field] === `atomic:${atomicOp.type}`) {
487
+ next[field] = applyAtomicOp(next[field], atomicOp);
488
+ } else {
489
+ next[field] = value;
490
+ }
491
+ lastWriterKind[field] = atomicOp ? `atomic:${atomicOp.type}` : "lww";
492
+ }
493
+ record = next;
494
+ deleted = false;
495
+ }
496
+ break;
497
+ case "delete":
498
+ deleted = true;
499
+ break;
500
+ }
501
+ }
502
+ return deleted ? null : record;
503
+ }
504
+
461
505
  // src/operations/op-data-binary.ts
462
506
  function isKoraBytesValue(value) {
463
507
  if (typeof value !== "object" || value === null) {
@@ -2561,6 +2605,7 @@ export {
2561
2605
  StorageError,
2562
2606
  SyncError,
2563
2607
  advanceVector,
2608
+ applyAtomicOp,
2564
2609
  applyOperationTransforms,
2565
2610
  base64ToBytes,
2566
2611
  buildScopeMap,
@@ -2610,6 +2655,7 @@ export {
2610
2655
  migrationStepsToSQL,
2611
2656
  op,
2612
2657
  quoteIdent,
2658
+ replayOperationsForRecord,
2613
2659
  resolveAtomicOp,
2614
2660
  revealSecret,
2615
2661
  rollbackStepsToSQL,