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).
21 lines
607 B
TypeScript
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';
|