@lossless.org/client 1.1.0 → 1.3.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/.smartconfig.json +1 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/nosqldb/classes.atomicdelete.js +3 -3
- package/dist_ts/nosqldb/classes.atomicfindoneandupdate.d.ts +20 -0
- package/dist_ts/nosqldb/classes.atomicfindoneandupdate.js +45 -2
- package/dist_ts/nosqldb/classes.atomicupdate.js +4 -4
- package/dist_ts/nosqldb/classes.collection.d.ts +37 -1
- package/dist_ts/nosqldb/classes.collection.js +235 -30
- package/dist_ts/nosqldb/classes.collectiontopology.d.ts +19 -0
- package/dist_ts/nosqldb/classes.collectiontopology.js +29 -3
- package/dist_ts/nosqldb/classes.cursor.d.ts +12 -1
- package/dist_ts/nosqldb/classes.cursor.js +20 -3
- package/dist_ts/nosqldb/classes.doc.d.ts +138 -11
- package/dist_ts/nosqldb/classes.doc.js +490 -91
- package/dist_ts/nosqldb/classes.exactpersistence.js +10 -2
- package/dist_ts/nosqldb/classes.namespaceinspection.js +2 -2
- package/dist_ts/nosqldb/classes.persistence.d.ts +6 -0
- package/dist_ts/nosqldb/classes.persistence.js +13 -1
- package/dist_ts/nosqldb/classes.session.d.ts +15 -1
- package/dist_ts/nosqldb/classes.session.js +32 -6
- package/package.json +4 -1
- package/readme.md +38 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/nosqldb/classes.atomicdelete.ts +2 -4
- package/ts/nosqldb/classes.atomicfindoneandupdate.ts +69 -2
- package/ts/nosqldb/classes.atomicupdate.ts +3 -6
- package/ts/nosqldb/classes.collection.ts +372 -35
- package/ts/nosqldb/classes.collectiontopology.ts +56 -1
- package/ts/nosqldb/classes.cursor.ts +22 -2
- package/ts/nosqldb/classes.doc.ts +819 -132
- package/ts/nosqldb/classes.exactpersistence.ts +9 -1
- package/ts/nosqldb/classes.namespaceinspection.ts +1 -1
- package/ts/nosqldb/classes.persistence.ts +17 -0
- package/ts/nosqldb/classes.session.ts +52 -13
|
@@ -38,6 +38,8 @@ export interface ISmartdataCollectionTopologyIndex {
|
|
|
38
38
|
export interface ISmartdataExpectedCollectionTopology {
|
|
39
39
|
readonly collectionName: string;
|
|
40
40
|
readonly indexes: readonly ISmartdataCollectionTopologyIndex[];
|
|
41
|
+
/** Undeclared installed indexes the model accepts by name, never verifies. */
|
|
42
|
+
readonly toleratedIndexNames: readonly string[];
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export interface ISmartdataCollectionTopologyInspection {
|
|
@@ -45,7 +47,13 @@ export interface ISmartdataCollectionTopologyInspection {
|
|
|
45
47
|
readonly status: TSmartdataCollectionTopologyStatus;
|
|
46
48
|
readonly reasonCodes: readonly TSmartdataCollectionTopologyReasonCode[];
|
|
47
49
|
readonly expectedIndexes: readonly ISmartdataCollectionTopologyIndex[];
|
|
50
|
+
/** Installed indexes SmartData declares and verifies. */
|
|
48
51
|
readonly actualIndexes: readonly ISmartdataCollectionTopologyIndex[];
|
|
52
|
+
/**
|
|
53
|
+
* Installed indexes the model tolerates by name. They are observed, never
|
|
54
|
+
* verified, created or dropped, and they never make the topology divergent.
|
|
55
|
+
*/
|
|
56
|
+
readonly toleratedIndexes: readonly string[];
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
type TCollectionModelSchemaResolver = () => INormalizedCollectionModelSchema;
|
|
@@ -295,9 +303,37 @@ export const getExpectedCollectionTopologyForSchema = (
|
|
|
295
303
|
return Object.freeze({
|
|
296
304
|
collectionName: schema.collectionName,
|
|
297
305
|
indexes,
|
|
306
|
+
toleratedIndexNames: Object.freeze(
|
|
307
|
+
[...schema.toleratedIndexNames].sort(compareSmartdataTopologyStrings),
|
|
308
|
+
),
|
|
298
309
|
});
|
|
299
310
|
};
|
|
300
311
|
|
|
312
|
+
/**
|
|
313
|
+
* @internal A single-field ascending unique index is what makes a declared
|
|
314
|
+
* identity globally constraining, whatever the index is called.
|
|
315
|
+
*/
|
|
316
|
+
export const isIdentityIndexFor = (
|
|
317
|
+
indexArg: INormalizedCollectionModelSchema['indexes'][number],
|
|
318
|
+
fieldArg: string,
|
|
319
|
+
): boolean =>
|
|
320
|
+
indexArg.options.unique === true &&
|
|
321
|
+
indexArg.key.length === 1 &&
|
|
322
|
+
indexArg.key[0][0] === fieldArg &&
|
|
323
|
+
indexArg.key[0][1] === 1;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* @internal Name of the unique index that backs an identity field in a bound
|
|
327
|
+
* schema. Index-creation fallbacks resolve it here so that a model naming its
|
|
328
|
+
* own backing index never gets a second unique index over the same key.
|
|
329
|
+
*/
|
|
330
|
+
export const getIdentityIndexName = (
|
|
331
|
+
schemaArg: INormalizedCollectionModelSchema | undefined,
|
|
332
|
+
fieldArg: string,
|
|
333
|
+
): string | undefined =>
|
|
334
|
+
schemaArg?.indexes.find((indexArg) => isIdentityIndexFor(indexArg, fieldArg))
|
|
335
|
+
?.name;
|
|
336
|
+
|
|
301
337
|
const normalizeActualIndex = (
|
|
302
338
|
rawIndexArg: unknown,
|
|
303
339
|
): IActualIndexNormalization => {
|
|
@@ -487,6 +523,7 @@ const createInspection = (
|
|
|
487
523
|
statusArg: TSmartdataCollectionTopologyStatus,
|
|
488
524
|
reasonsArg: ReadonlySet<TSmartdataCollectionTopologyReasonCode>,
|
|
489
525
|
actualIndexesArg: readonly ISmartdataCollectionTopologyIndex[],
|
|
526
|
+
toleratedIndexesArg: readonly string[] = [],
|
|
490
527
|
): ISmartdataCollectionTopologyInspection =>
|
|
491
528
|
Object.freeze({
|
|
492
529
|
collectionName: expectedArg.collectionName,
|
|
@@ -494,6 +531,9 @@ const createInspection = (
|
|
|
494
531
|
reasonCodes: buildReasonCodes(reasonsArg),
|
|
495
532
|
expectedIndexes: expectedArg.indexes,
|
|
496
533
|
actualIndexes: sortIndexes(actualIndexesArg),
|
|
534
|
+
toleratedIndexes: Object.freeze(
|
|
535
|
+
[...toleratedIndexesArg].sort(compareSmartdataTopologyStrings),
|
|
536
|
+
),
|
|
497
537
|
});
|
|
498
538
|
|
|
499
539
|
export const inspectCollectionTopologyForModel = async (
|
|
@@ -574,8 +614,17 @@ export const inspectCollectionTopologySnapshot = (
|
|
|
574
614
|
}
|
|
575
615
|
const normalizedIndexes: ISmartdataCollectionTopologyIndex[] = [];
|
|
576
616
|
const unsupportedNames = new Set<string>();
|
|
617
|
+
const toleratedNames = new Set(expected.toleratedIndexNames);
|
|
618
|
+
const toleratedIndexes: string[] = [];
|
|
577
619
|
for (const rawIndex of rawIndexes) {
|
|
578
620
|
const normalized = normalizeActualIndex(rawIndex);
|
|
621
|
+
if (normalized.name && toleratedNames.has(normalized.name)) {
|
|
622
|
+
// A tolerated index belongs to whoever created it — a migration, an
|
|
623
|
+
// operator — so it is observed by name and nothing about its keys or
|
|
624
|
+
// options is asserted. SmartData neither creates nor drops it.
|
|
625
|
+
toleratedIndexes.push(normalized.name);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
579
628
|
if (!normalized.supported) {
|
|
580
629
|
reasons.add('unsupported_actual_index');
|
|
581
630
|
if (normalized.name) {
|
|
@@ -635,7 +684,13 @@ export const inspectCollectionTopologySnapshot = (
|
|
|
635
684
|
)
|
|
636
685
|
? 'compatible'
|
|
637
686
|
: 'divergent';
|
|
638
|
-
return createInspection(
|
|
687
|
+
return createInspection(
|
|
688
|
+
expected,
|
|
689
|
+
status,
|
|
690
|
+
reasons,
|
|
691
|
+
normalizedIndexes,
|
|
692
|
+
toleratedIndexes,
|
|
693
|
+
);
|
|
639
694
|
} catch {
|
|
640
695
|
throw new SmartdataPersistenceError(
|
|
641
696
|
'unsupported_operation',
|
|
@@ -10,9 +10,20 @@ export class SmartdataDbCursor<T = any> {
|
|
|
10
10
|
// INSTANCE
|
|
11
11
|
public mongodbCursor: plugins.mongodb.FindCursor<T>;
|
|
12
12
|
private smartdataDbDoc: typeof SmartDataDbDoc;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Releases an owned SmartData session lease the cursor holds for its whole
|
|
15
|
+
* lifetime. Supplied by the operation that created the cursor; inert for a
|
|
16
|
+
* raw driver session or no session at all.
|
|
17
|
+
*/
|
|
18
|
+
private releaseSession: () => void;
|
|
19
|
+
constructor(
|
|
20
|
+
cursorArg: plugins.mongodb.FindCursor<T>,
|
|
21
|
+
dbDocArg: typeof SmartDataDbDoc,
|
|
22
|
+
releaseSessionArg: () => void = () => {},
|
|
23
|
+
) {
|
|
14
24
|
this.mongodbCursor = cursorArg;
|
|
15
25
|
this.smartdataDbDoc = dbDocArg;
|
|
26
|
+
this.releaseSession = releaseSessionArg;
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
public async next(closeAtEnd = true): Promise<T | null> {
|
|
@@ -73,8 +84,17 @@ export class SmartdataDbCursor<T = any> {
|
|
|
73
84
|
return convertedResult;
|
|
74
85
|
}
|
|
75
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Closes the driver cursor and releases an owned session lease even when the
|
|
89
|
+
* driver's own cleanup rejects, so a failed close can never strand the
|
|
90
|
+
* session and block every later operation on it.
|
|
91
|
+
*/
|
|
76
92
|
public async close() {
|
|
77
|
-
|
|
93
|
+
try {
|
|
94
|
+
await this.mongodbCursor.close();
|
|
95
|
+
} finally {
|
|
96
|
+
this.releaseSession();
|
|
97
|
+
}
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
private async closeAfterError(errorArg: unknown): Promise<never> {
|