learning_ai_common_plat/packages/datastore/src/index.ts
saravanakumardb1 40fd0e05ad feat(datastore): add updateIfMatch optimistic-concurrency write (If-Match / atomic CAS)
Adds an additive, backward-compatible conditional write to the datastore abstraction
so consumers can do true single-winner compare-and-swap:

  updateIfMatch(id, partitionKey, expected: { etag?, rev? }, patch)
    -> { ok: true, doc } | { ok: false, reason: 'conflict' | 'not_found' }

- types: ConcurrencyToken + UpdateIfMatchResult; optional _etag on BaseDocument
  (provider-managed, surfaced on reads); new method on DocumentCollection; exported.
- memory provider: get -> compare -> set in ONE synchronous block (no await/yield),
  so two concurrent callers cannot interleave under the single-threaded event loop —
  the first wins and bumps rev + _etag, the rest get conflict. True in-process atomicity.
- cosmos provider: conditional replace with accessCondition { type: 'IfMatch',
  condition: _etag }; translates Cosmos 412 -> conflict, 404 -> not_found; also
  compares/bumps rev for parity.

Existing method signatures are unchanged (additive only). Tests: memory match/stale/
missing + an N-concurrent Promise.all atomicity proof; cosmos If-Match mapping via a
fake @azure/cosmos (match writes, stale etag -> 412 conflict, missing -> not_found).
2026-05-29 20:58:55 -07:00

21 lines
607 B
TypeScript

export type {
BaseDocument,
FilterMap,
FilterValue,
FilterOperator,
FilterCondition,
CollectionQuery,
AggregateQuery,
AggregationField,
DocumentCollection,
DatastoreProvider,
DatastoreProviderType,
ConcurrencyToken,
UpdateIfMatchResult,
} from './types.js';
export { getDatastore, createDatastoreProvider, setDatastore, _resetDatastore } from './factory.js';
export { CosmosDatastoreProvider, type CosmosProviderConfig } from './providers/cosmos.js';
export { MemoryDatastoreProvider } from './providers/memory.js';
export { matchesFilter, filterToCosmosSQL } from './filter.js';