@modelprofile.com/flexharness 9.10.0 → 10.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.
Files changed (54) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/classes.flexharness.d.ts +2 -0
  3. package/dist_ts/classes.flexharness.js +101 -7
  4. package/dist_ts/classes.stores.js +11 -119
  5. package/dist_ts/errors.d.ts +7 -2
  6. package/dist_ts/errors.js +1 -1
  7. package/dist_ts/interfaces.d.ts +1 -0
  8. package/dist_ts/utils.harnessoptions.js +2 -1
  9. package/dist_ts/utils.storerules.d.ts +28 -0
  10. package/dist_ts/utils.storerules.js +107 -0
  11. package/dist_ts_chat_harness/classes.transcript.d.ts +88 -0
  12. package/dist_ts_chat_harness/classes.transcript.js +268 -0
  13. package/dist_ts_chat_harness/functions.intents.d.ts +62 -0
  14. package/dist_ts_chat_harness/functions.intents.js +96 -0
  15. package/dist_ts_chat_harness/functions.mapping.d.ts +33 -0
  16. package/dist_ts_chat_harness/functions.mapping.js +220 -0
  17. package/dist_ts_chat_harness/index.d.ts +3 -0
  18. package/dist_ts_chat_harness/index.js +4 -0
  19. package/dist_ts_chat_harness/plugins.d.ts +1 -0
  20. package/dist_ts_chat_harness/plugins.js +2 -0
  21. package/dist_ts_kvm/classes.browserkvm.js +3 -3
  22. package/dist_ts_kvm/plugins.d.ts +1 -1
  23. package/dist_ts_kvm/plugins.js +2 -2
  24. package/dist_ts_stores_nosqldb/classes.nosqldbstores.d.ts +31 -0
  25. package/dist_ts_stores_nosqldb/classes.nosqldbstores.js +312 -0
  26. package/dist_ts_stores_nosqldb/classes.recordstore.d.ts +110 -0
  27. package/dist_ts_stores_nosqldb/classes.recordstore.js +437 -0
  28. package/dist_ts_stores_nosqldb/index.d.ts +2 -0
  29. package/dist_ts_stores_nosqldb/index.js +3 -0
  30. package/dist_ts_stores_nosqldb/plugins.d.ts +7 -0
  31. package/dist_ts_stores_nosqldb/plugins.js +10 -0
  32. package/package.json +27 -7
  33. package/readme.md +14 -6
  34. package/ts/00_commitinfo_data.ts +1 -1
  35. package/ts/classes.flexharness.ts +102 -7
  36. package/ts/classes.stores.ts +28 -172
  37. package/ts/errors.ts +8 -2
  38. package/ts/interfaces.ts +1 -0
  39. package/ts/readme.md +14 -6
  40. package/ts/utils.harnessoptions.ts +1 -0
  41. package/ts/utils.storerules.ts +177 -0
  42. package/ts_chat_harness/classes.transcript.ts +365 -0
  43. package/ts_chat_harness/functions.intents.ts +169 -0
  44. package/ts_chat_harness/functions.mapping.ts +267 -0
  45. package/ts_chat_harness/index.ts +33 -0
  46. package/ts_chat_harness/plugins.ts +18 -0
  47. package/ts_chat_harness/readme.md +114 -0
  48. package/ts_kvm/classes.browserkvm.ts +2 -2
  49. package/ts_kvm/plugins.ts +1 -1
  50. package/ts_stores_nosqldb/classes.nosqldbstores.ts +465 -0
  51. package/ts_stores_nosqldb/classes.recordstore.ts +560 -0
  52. package/ts_stores_nosqldb/index.ts +9 -0
  53. package/ts_stores_nosqldb/plugins.ts +38 -0
  54. package/ts_stores_nosqldb/readme.md +53 -0
@@ -0,0 +1,312 @@
1
+ import * as plugins from './plugins.js';
2
+ import { FlexHarnessStoreConflictError, FlexHarnessValidationError } from '../dist_ts/errors.js';
3
+ import { assertFlexPermissionSnapshot, assertFlexProjectionSnapshot, assertFlexScopeSnapshot, } from '../dist_ts/utils.json.js';
4
+ import { assertFlexProjectManagementRecord, assertFlexProjectManagementSnapshot, assertFlexProjectManagementTombstone, } from '../dist_ts/utils.projectmanagement.js';
5
+ import { createAgentEventSnapshot, createToolJobSnapshot, normalizeJsonObjectUndefined, validateAgentEventArchive, validateAgentEventSnapshot, validateToolJobSnapshot, } from '../dist_ts/utils.storecodecs.js';
6
+ import { assertProjectionSession, assertProjectManagementSaveAdmitted, assertSameArchiveEvents, isProjectManagementTombstoneAdmitted, providerKey, validateExpectedRevision, validateIdentifier, validateProjectManagementWriteContext, validateSnapshotSave, } from '../dist_ts/utils.storerules.js';
7
+ import { FlexRecordStore } from './classes.recordstore.js';
8
+ function revisionWrite(current, snapshot, expectedRevision, conflict) {
9
+ const actualRevision = current?.revision ?? 0;
10
+ if (actualRevision !== expectedRevision)
11
+ throw conflict(actualRevision);
12
+ return { revision: snapshot.revision, value: snapshot };
13
+ }
14
+ function validateScope(value) {
15
+ assertFlexScopeSnapshot(value);
16
+ return value;
17
+ }
18
+ function validatePermission(value) {
19
+ assertFlexPermissionSnapshot(value);
20
+ return value;
21
+ }
22
+ function validateProjectManagement(value) {
23
+ assertFlexProjectManagementRecord(value);
24
+ return value;
25
+ }
26
+ class NoSqlScopeStore {
27
+ records;
28
+ constructor(records) {
29
+ this.records = records;
30
+ }
31
+ load(storageKey) {
32
+ validateIdentifier(storageKey, 'storageKey');
33
+ return this.records.read({ domain: 'scope', storageKey }, validateScope);
34
+ }
35
+ async save(storageKey, snapshot, expectedRevision) {
36
+ validateIdentifier(storageKey, 'storageKey');
37
+ const validated = validateSnapshotSave(snapshot, expectedRevision, assertFlexScopeSnapshot);
38
+ const conflict = (actualRevision) => new FlexHarnessStoreConflictError(storageKey, expectedRevision, actualRevision);
39
+ await this.records.mutate({ domain: 'scope', storageKey }, {
40
+ validate: validateScope,
41
+ decide: (current) => revisionWrite(current, validated, expectedRevision, conflict),
42
+ contention: conflict,
43
+ });
44
+ }
45
+ }
46
+ class NoSqlProjectionStore {
47
+ records;
48
+ constructor(records) {
49
+ this.records = records;
50
+ }
51
+ validator(sessionId) {
52
+ return (value) => {
53
+ assertFlexProjectionSnapshot(value);
54
+ assertProjectionSession(value, sessionId);
55
+ return value;
56
+ };
57
+ }
58
+ load(storageKey, sessionId) {
59
+ providerKey(storageKey, sessionId);
60
+ return this.records.read({ domain: 'projection', storageKey, sessionId }, this.validator(sessionId));
61
+ }
62
+ async save(storageKey, sessionId, snapshot, expectedRevision) {
63
+ const key = providerKey(storageKey, sessionId);
64
+ const validated = validateSnapshotSave(snapshot, expectedRevision, assertFlexProjectionSnapshot);
65
+ assertProjectionSession(validated, sessionId);
66
+ const conflict = (actualRevision) => new FlexHarnessStoreConflictError(key, expectedRevision, actualRevision);
67
+ await this.records.mutate({ domain: 'projection', storageKey, sessionId }, {
68
+ validate: this.validator(sessionId),
69
+ decide: (current) => revisionWrite(current, validated, expectedRevision, conflict),
70
+ contention: conflict,
71
+ });
72
+ }
73
+ deleteSession(storageKey, sessionId) {
74
+ providerKey(storageKey, sessionId);
75
+ return this.records.deleteOwner(storageKey, sessionId, ['projection']);
76
+ }
77
+ }
78
+ class NoSqlPermissionStore {
79
+ records;
80
+ constructor(records) {
81
+ this.records = records;
82
+ }
83
+ load(storageKey, sessionId) {
84
+ providerKey(storageKey, sessionId);
85
+ return this.records.read({ domain: 'permission', storageKey, sessionId }, validatePermission);
86
+ }
87
+ async save(storageKey, sessionId, snapshot, expectedRevision) {
88
+ const key = providerKey(storageKey, sessionId);
89
+ const validated = validateSnapshotSave(snapshot, expectedRevision, assertFlexPermissionSnapshot);
90
+ const conflict = (actualRevision) => new FlexHarnessStoreConflictError(key, expectedRevision, actualRevision);
91
+ await this.records.mutate({ domain: 'permission', storageKey, sessionId }, {
92
+ validate: validatePermission,
93
+ decide: (current) => revisionWrite(current, validated, expectedRevision, conflict),
94
+ contention: conflict,
95
+ });
96
+ }
97
+ deleteSession(storageKey, sessionId) {
98
+ providerKey(storageKey, sessionId);
99
+ return this.records.deleteOwner(storageKey, sessionId, ['permission']);
100
+ }
101
+ }
102
+ class NoSqlProjectManagementStore {
103
+ records;
104
+ constructor(records) {
105
+ this.records = records;
106
+ }
107
+ load(storageKey, sessionId) {
108
+ providerKey(storageKey, sessionId);
109
+ return this.records.read({ domain: 'projectManagement', storageKey, sessionId }, validateProjectManagement);
110
+ }
111
+ async save(storageKey, sessionId, snapshot, expectedRevision, writeContext) {
112
+ const key = providerKey(storageKey, sessionId);
113
+ validateProjectManagementWriteContext(writeContext);
114
+ const validated = validateSnapshotSave(snapshot, expectedRevision, assertFlexProjectManagementSnapshot);
115
+ await this.records.mutate({ domain: 'projectManagement', storageKey, sessionId }, {
116
+ validate: validateProjectManagement,
117
+ decide: (current) => {
118
+ assertProjectManagementSaveAdmitted(key, current, validated, expectedRevision);
119
+ return { revision: validated.revision, value: validated };
120
+ },
121
+ contention: (actualRevision) => new FlexHarnessStoreConflictError(key, expectedRevision, actualRevision),
122
+ });
123
+ }
124
+ async tombstoneSession(storageKey, sessionId, tombstone, expectedRevision) {
125
+ const key = providerKey(storageKey, sessionId);
126
+ const validated = validateSnapshotSave(tombstone, expectedRevision, assertFlexProjectManagementTombstone);
127
+ await this.records.mutate({ domain: 'projectManagement', storageKey, sessionId }, {
128
+ validate: validateProjectManagement,
129
+ decide: (current) => (isProjectManagementTombstoneAdmitted(key, current, validated, expectedRevision)
130
+ ? { revision: validated.revision, value: validated }
131
+ : undefined),
132
+ contention: (actualRevision) => new FlexHarnessStoreConflictError(key, expectedRevision, actualRevision),
133
+ });
134
+ }
135
+ purgeNamespace(storageKey) {
136
+ validateIdentifier(storageKey, 'storageKey');
137
+ return this.records.purgeNamespace(storageKey, 'projectManagement');
138
+ }
139
+ }
140
+ class NoSqlAgentEventStore {
141
+ records;
142
+ storageKey;
143
+ sessionId;
144
+ eventSchemaVersion = 2;
145
+ constructor(records, storageKey, sessionId) {
146
+ this.records = records;
147
+ this.storageKey = storageKey;
148
+ this.sessionId = sessionId;
149
+ }
150
+ assertSession(sessionId) {
151
+ if (sessionId !== this.sessionId) {
152
+ throw new FlexHarnessValidationError(`Agent event store is bound to session "${this.sessionId}", not "${sessionId}".`);
153
+ }
154
+ }
155
+ validateSnapshot = (value) => validateAgentEventSnapshot(value, this.sessionId);
156
+ load(sessionId) {
157
+ this.assertSession(sessionId);
158
+ return this.records.read({ domain: 'agentEvents', storageKey: this.storageKey, sessionId }, this.validateSnapshot);
159
+ }
160
+ async save(sessionId, events, expectedRevision) {
161
+ this.assertSession(sessionId);
162
+ validateExpectedRevision(expectedRevision);
163
+ const snapshot = createAgentEventSnapshot(sessionId, events, expectedRevision + 1);
164
+ validateAgentEventSnapshot(snapshot, sessionId);
165
+ const conflict = (actualRevision) => new plugins.AgentEventStoreConflictError(sessionId, expectedRevision, actualRevision);
166
+ await this.records.mutate({ domain: 'agentEvents', storageKey: this.storageKey, sessionId }, {
167
+ validate: this.validateSnapshot,
168
+ decide: (current) => revisionWrite(current, snapshot, expectedRevision, conflict),
169
+ contention: conflict,
170
+ });
171
+ return snapshot.revision;
172
+ }
173
+ async archive(archive) {
174
+ this.assertSession(archive.sessionId);
175
+ const normalized = normalizeJsonObjectUndefined(archive, '$archive');
176
+ const validated = validateAgentEventArchive(normalized, this.sessionId, archive.archiveId);
177
+ const address = {
178
+ domain: 'agentEventArchive',
179
+ storageKey: this.storageKey,
180
+ sessionId: this.sessionId,
181
+ archiveId: archive.archiveId,
182
+ };
183
+ await this.records.mutate(address, {
184
+ validate: (value) => validateAgentEventArchive(value, this.sessionId, archive.archiveId),
185
+ decide: (current) => {
186
+ if (!current)
187
+ return { revision: 0, value: validated };
188
+ assertSameArchiveEvents(current, validated);
189
+ return undefined;
190
+ },
191
+ contention: () => new Error(`Archive "${archive.archiveId}" is contended by concurrent writers.`),
192
+ });
193
+ }
194
+ loadArchive(sessionId, archiveId) {
195
+ this.assertSession(sessionId);
196
+ validateIdentifier(archiveId, 'archiveId');
197
+ return this.records.read({ domain: 'agentEventArchive', storageKey: this.storageKey, sessionId, archiveId }, (value) => validateAgentEventArchive(value, this.sessionId, archiveId));
198
+ }
199
+ }
200
+ class NoSqlAgentEventStoreProvider {
201
+ records;
202
+ stores = new Map();
203
+ constructor(records) {
204
+ this.records = records;
205
+ }
206
+ getStore(storageKey, sessionId) {
207
+ const key = providerKey(storageKey, sessionId);
208
+ let store = this.stores.get(key);
209
+ if (!store) {
210
+ store = new NoSqlAgentEventStore(this.records, storageKey, sessionId);
211
+ this.stores.set(key, store);
212
+ }
213
+ return store;
214
+ }
215
+ releaseSession(storageKey, sessionId) {
216
+ this.stores.delete(providerKey(storageKey, sessionId));
217
+ }
218
+ async deleteSession(storageKey, sessionId) {
219
+ const key = providerKey(storageKey, sessionId);
220
+ await this.records.deleteOwner(storageKey, sessionId, ['agentEvents', 'agentEventArchive']);
221
+ this.stores.delete(key);
222
+ }
223
+ }
224
+ class NoSqlToolJobStore {
225
+ records;
226
+ storageKey;
227
+ sessionId;
228
+ constructor(records, storageKey, sessionId) {
229
+ this.records = records;
230
+ this.storageKey = storageKey;
231
+ this.sessionId = sessionId;
232
+ }
233
+ async load(abortSignal) {
234
+ abortSignal?.throwIfAborted();
235
+ const snapshot = await this.records.read({ domain: 'jobs', storageKey: this.storageKey, sessionId: this.sessionId }, validateToolJobSnapshot);
236
+ abortSignal?.throwIfAborted();
237
+ return snapshot;
238
+ }
239
+ async save(jobs, expectedRevision, abortSignal) {
240
+ abortSignal?.throwIfAborted();
241
+ validateExpectedRevision(expectedRevision);
242
+ const snapshot = createToolJobSnapshot(jobs, expectedRevision + 1);
243
+ const conflict = (actualRevision) => new plugins.ToolJobStoreConflictError(expectedRevision, actualRevision);
244
+ await this.records.mutate({ domain: 'jobs', storageKey: this.storageKey, sessionId: this.sessionId }, {
245
+ validate: validateToolJobSnapshot,
246
+ decide: (current) => {
247
+ abortSignal?.throwIfAborted();
248
+ return revisionWrite(current, snapshot, expectedRevision, conflict);
249
+ },
250
+ contention: conflict,
251
+ });
252
+ return snapshot.revision;
253
+ }
254
+ }
255
+ class NoSqlToolJobStoreProvider {
256
+ records;
257
+ stores = new Map();
258
+ constructor(records) {
259
+ this.records = records;
260
+ }
261
+ getStore(storageKey, sessionId) {
262
+ const key = providerKey(storageKey, sessionId);
263
+ let store = this.stores.get(key);
264
+ if (!store) {
265
+ store = new NoSqlToolJobStore(this.records, storageKey, sessionId);
266
+ this.stores.set(key, store);
267
+ }
268
+ return store;
269
+ }
270
+ releaseSession(storageKey, sessionId) {
271
+ this.stores.delete(providerKey(storageKey, sessionId));
272
+ }
273
+ async deleteSession(storageKey, sessionId) {
274
+ const key = providerKey(storageKey, sessionId);
275
+ await this.records.deleteOwner(storageKey, sessionId, ['jobs']);
276
+ this.stores.delete(key);
277
+ }
278
+ }
279
+ /**
280
+ * Every FlexHarness persistence domain on `@lossless.org/client/nosqldb`, with compare-and-swap
281
+ * that holds across processes. Records live in `flexharness_records`; payloads longer than one
282
+ * chunk live in `flexharness_chunks`.
283
+ */
284
+ export class NoSqlFlexHarnessStores {
285
+ scopes;
286
+ projections;
287
+ permissions;
288
+ projectManagement;
289
+ agentEvents;
290
+ jobs;
291
+ records;
292
+ constructor(options) {
293
+ if (!(options?.db instanceof plugins.SmartdataDb)) {
294
+ throw new FlexHarnessValidationError('NoSqlFlexHarnessStores requires a SmartdataDb.');
295
+ }
296
+ this.records = new FlexRecordStore(options);
297
+ this.scopes = new NoSqlScopeStore(this.records);
298
+ this.projections = new NoSqlProjectionStore(this.records);
299
+ this.permissions = new NoSqlPermissionStore(this.records);
300
+ this.projectManagement = new NoSqlProjectManagementStore(this.records);
301
+ this.agentEvents = new NoSqlAgentEventStoreProvider(this.records);
302
+ this.jobs = new NoSqlToolJobStoreProvider(this.records);
303
+ }
304
+ /**
305
+ * Creates or verifies the two collections and their indexes. Every store operation awaits the
306
+ * same preparation, so calling it at startup only moves that work, and its failure, forward.
307
+ */
308
+ prepare() {
309
+ return this.records.prepare();
310
+ }
311
+ }
312
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ub3NxbGRic3RvcmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfc3RvcmVzX25vc3FsZGIvY2xhc3Nlcy5ub3NxbGRic3RvcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSwwQkFBMEIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBa0I1RixPQUFPLEVBQ0wsNEJBQTRCLEVBQzVCLDRCQUE0QixFQUM1Qix1QkFBdUIsR0FDeEIsTUFBTSxxQkFBcUIsQ0FBQztBQUM3QixPQUFPLEVBQ0wsaUNBQWlDLEVBQ2pDLG1DQUFtQyxFQUNuQyxvQ0FBb0MsR0FDckMsTUFBTSxrQ0FBa0MsQ0FBQztBQUMxQyxPQUFPLEVBQ0wsd0JBQXdCLEVBQ3hCLHFCQUFxQixFQUNyQiw0QkFBNEIsRUFDNUIseUJBQXlCLEVBQ3pCLDBCQUEwQixFQUMxQix1QkFBdUIsR0FDeEIsTUFBTSw0QkFBNEIsQ0FBQztBQUNwQyxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLG1DQUFtQyxFQUNuQyx1QkFBdUIsRUFDdkIsb0NBQW9DLEVBQ3BDLFdBQVcsRUFDWCx3QkFBd0IsRUFDeEIsa0JBQWtCLEVBQ2xCLHFDQUFxQyxFQUNyQyxvQkFBb0IsR0FDckIsTUFBTSwyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLEVBQUUsZUFBZSxFQUF5QixNQUFNLDBCQUEwQixDQUFDO0FBRWxGLFNBQVMsYUFBYSxDQUNwQixPQUF5QyxFQUN6QyxRQUFtQixFQUNuQixnQkFBd0IsRUFDeEIsUUFBMkM7SUFFM0MsTUFBTSxjQUFjLEdBQUcsT0FBTyxFQUFFLFFBQVEsSUFBSSxDQUFDLENBQUM7SUFDOUMsSUFBSSxjQUFjLEtBQUssZ0JBQWdCO1FBQUUsTUFBTSxRQUFRLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDeEUsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLENB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
@@ -0,0 +1,110 @@
1
+ import * as plugins from './plugins.js';
2
+ export type TFlexRecordDomain = 'scope' | 'projection' | 'permission' | 'projectManagement' | 'agentEvents' | 'agentEventArchive' | 'jobs';
3
+ /** Names one record: a domain snapshot of a storage key, a session or an archive. */
4
+ export interface IFlexRecordAddress {
5
+ domain: TFlexRecordDomain;
6
+ storageKey: string;
7
+ sessionId?: string;
8
+ archiveId?: string;
9
+ }
10
+ /** The next stored value a mutation decided on. */
11
+ export interface IFlexRecordWrite {
12
+ revision: number;
13
+ value: unknown;
14
+ }
15
+ export interface IFlexRecordMutation<TValue> {
16
+ validate: (value: unknown) => TValue;
17
+ /** Decides on the stored value: the next value, nothing to write, or a thrown rejection. */
18
+ decide: (current: TValue | undefined) => IFlexRecordWrite | undefined;
19
+ /** The conflict to report when concurrent writers keep winning the compare-and-swap. */
20
+ contention: (actualRevision: number) => Error;
21
+ }
22
+ export declare const FLEX_RECORD_COLLECTION = "flexharness_records";
23
+ export declare const FLEX_CHUNK_COLLECTION = "flexharness_chunks";
24
+ /**
25
+ * UTF-16 code units a record holds inline, and the size of each chunk of a longer payload. A
26
+ * chunk stays below 3 MiB of UTF-8, far under the 16 MiB document limit NoSQLDB and MongoDB
27
+ * enforce, so a record and each of its chunks always fit one document.
28
+ */
29
+ export declare const FLEX_RECORD_CHUNK_CODE_UNITS: number;
30
+ /**
31
+ * The head of one record. A payload up to one chunk is stored inline; a longer one lives in
32
+ * immutable chunks named by `blobId`. `writeToken` changes on every committed write and is the
33
+ * compare-and-swap fence. A head without payload or blob is a placeholder that reserves the
34
+ * record for a chunked first write; it reads as absent.
35
+ */
36
+ export declare class FlexRecordDocument extends plugins.SmartDataDbDoc<FlexRecordDocument, FlexRecordDocument> {
37
+ recordId: string;
38
+ domain: TFlexRecordDomain;
39
+ ownerKey: string;
40
+ namespaceKey: string;
41
+ revision: number;
42
+ writeToken: string;
43
+ payload: string | null;
44
+ blobId: string | null;
45
+ chunkCount: number;
46
+ payloadSha256: string | null;
47
+ /** Set before the first chunk of this record is written; the record's writes then sweep. */
48
+ chunked: boolean;
49
+ sweepCount: number;
50
+ }
51
+ /** One immutable piece of a chunked payload, written against the head token it will replace. */
52
+ export declare class FlexChunkDocument extends plugins.SmartDataDbDoc<FlexChunkDocument, FlexChunkDocument> {
53
+ chunkId: string;
54
+ recordId: string;
55
+ domain: TFlexRecordDomain;
56
+ ownerKey: string;
57
+ namespaceKey: string;
58
+ blobId: string;
59
+ baseToken: string;
60
+ index: number;
61
+ data: string;
62
+ }
63
+ export interface IFlexRecordStoreOptions {
64
+ db: plugins.SmartdataDb;
65
+ operationTimeoutMs?: number;
66
+ }
67
+ /**
68
+ * Compare-and-swap records on `@lossless.org/client/nosqldb`. A write reads the head, decides,
69
+ * writes any chunks, and swaps the head only while its `writeToken` is unchanged. Chunks of a
70
+ * replaced or abandoned payload are swept in a transaction that also touches the head, so a
71
+ * concurrent deletion or writer can never lose chunks its head references.
72
+ */
73
+ export declare class FlexRecordStore {
74
+ private readonly db;
75
+ private readonly timeoutMs;
76
+ private readonly records;
77
+ private readonly chunks;
78
+ private preparation?;
79
+ constructor(options: IFlexRecordStoreOptions);
80
+ /** Creates or verifies both collections and their indexes once; a failed attempt is retried. */
81
+ prepare(): Promise<void>;
82
+ private prepareCollections;
83
+ private resolve;
84
+ read<TValue>(address: IFlexRecordAddress, validate: (value: unknown) => TValue): Promise<TValue | undefined>;
85
+ mutate<TValue>(address: IFlexRecordAddress, mutation: IFlexRecordMutation<TValue>): Promise<void>;
86
+ /** Deletes every record and chunk one session owns in the given domains. */
87
+ deleteOwner(storageKey: string, sessionId: string, domains: readonly TFlexRecordDomain[]): Promise<void>;
88
+ /** Deletes every record and chunk of one domain in a storage namespace. */
89
+ purgeNamespace(storageKey: string, domain: TFlexRecordDomain): Promise<void>;
90
+ private deleteInTransaction;
91
+ private parse;
92
+ private findHead;
93
+ private readHead;
94
+ private readBlob;
95
+ /** Returns false when another writer swapped the head first; nothing of this write remains. */
96
+ private commit;
97
+ private newHead;
98
+ private insertHead;
99
+ /** Reserves the record for a chunked first write, so its chunks have a head token to replace. */
100
+ private insertPlaceholder;
101
+ private markChunked;
102
+ private writeBlob;
103
+ /**
104
+ * Deletes the record's chunks that no head can reference any more: every blob but the current
105
+ * one that was written against another head token. A writer that read the current token keeps
106
+ * its chunks. Touching the head in the same transaction makes a concurrent deletion conflict,
107
+ * so chunks written after a deletion are never swept by a transaction that predates it.
108
+ */
109
+ private sweep;
110
+ }