@procedures/collections 0.3.0 → 0.5.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/CHANGELOG.md +27 -0
- package/README.md +53 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/storage/interface.d.ts +25 -4
- package/dist/storage/interface.d.ts.map +1 -1
- package/dist/storage/interface.js.map +1 -1
- package/dist/storage/memory.d.ts +39 -7
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +104 -53
- package/dist/storage/memory.js.map +1 -1
- package/dist/testing/check.d.ts +25 -0
- package/dist/testing/check.d.ts.map +1 -0
- package/dist/testing/check.js +85 -0
- package/dist/testing/check.js.map +1 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +10 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/storage-contract.d.ts +70 -0
- package/dist/testing/storage-contract.d.ts.map +1 -0
- package/dist/testing/storage-contract.js +527 -0
- package/dist/testing/storage-contract.js.map +1 -0
- package/package.json +5 -1
- package/src/storage/interface.ts +26 -4
- package/src/storage/memory.ts +120 -54
- package/src/testing/check.ts +81 -0
- package/src/testing/index.ts +19 -0
- package/src/testing/storage-contract.ts +623 -0
package/dist/storage/memory.d.ts
CHANGED
|
@@ -13,6 +13,16 @@ export interface InMemoryStorageOptions {
|
|
|
13
13
|
* @default 10000
|
|
14
14
|
*/
|
|
15
15
|
maxTombstones?: number;
|
|
16
|
+
/**
|
|
17
|
+
* True: the storage keeps a copy of each value that a write takes, and each read gives a new
|
|
18
|
+
* copy (`structuredClone`). Then a change of a written or a read value does not change the
|
|
19
|
+
* stored item, as in each other storage.
|
|
20
|
+
*
|
|
21
|
+
* False: the storage keeps the values themselves, and the reads give them. This is faster.
|
|
22
|
+
* Give it only when no caller changes a value after a write or a read.
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
copies?: boolean;
|
|
16
26
|
}
|
|
17
27
|
/**
|
|
18
28
|
* In-memory storage backed by JavaScript Map.
|
|
@@ -22,7 +32,12 @@ export interface InMemoryStorageOptions {
|
|
|
22
32
|
* - No persistence (data lost on restart)
|
|
23
33
|
* - Memory-bound (limited by available RAM)
|
|
24
34
|
* - Returns Promises for API consistency with remote storage
|
|
35
|
+
* - Each error is a rejected promise: an error of a `find` predicate does not throw at once
|
|
25
36
|
* - A change log: `getChangesSince(version)` gives the changes after a version (roadmap R28)
|
|
37
|
+
* - Copies: a change of a written or a read value does not change the stored item (the option
|
|
38
|
+
* `copies: false` turns this off)
|
|
39
|
+
* - `getIds()` gives the ids without a copy of the values
|
|
40
|
+
* - After `close()`, each operation rejects, and a second `close()` does nothing
|
|
26
41
|
*
|
|
27
42
|
* @example
|
|
28
43
|
* ```typescript
|
|
@@ -33,33 +48,44 @@ export interface InMemoryStorageOptions {
|
|
|
33
48
|
*/
|
|
34
49
|
export declare class InMemoryStorage<T> implements CollectionStorage<T> {
|
|
35
50
|
private data;
|
|
36
|
-
private epoch;
|
|
51
|
+
private readonly epoch;
|
|
37
52
|
private seq;
|
|
38
|
-
/** The number of the last write of each id that has an item */
|
|
53
|
+
/** The number of the last write of each id that has an item, the oldest first */
|
|
39
54
|
private readonly changedAt;
|
|
40
55
|
/** The number of the delete of each id that has no item, the oldest first (the tombstones) */
|
|
41
56
|
private readonly deletedAt;
|
|
42
57
|
/** The log does not know the deletes at or below this number: they were dropped */
|
|
43
58
|
private floor;
|
|
44
59
|
private readonly maxTombstones;
|
|
60
|
+
private readonly copy;
|
|
61
|
+
private closed;
|
|
45
62
|
constructor(options?: InMemoryStorageOptions);
|
|
46
63
|
get(id: string): Promise<T | undefined>;
|
|
47
64
|
getAll(): Promise<T[]>;
|
|
48
65
|
getEntries(): Promise<Array<[string, T]>>;
|
|
66
|
+
/** The ids of the items, in the order of the entries. This method copies no value. */
|
|
67
|
+
getIds(): Promise<string[]>;
|
|
49
68
|
/**
|
|
50
|
-
* The changes after a version (roadmap R28). A version of another storage,
|
|
51
|
-
*
|
|
69
|
+
* The changes after a version (roadmap R28). A version of another storage, or older than the
|
|
70
|
+
* dropped deletes, gives all items (`full: true`) in the order of the entries. A delta gives
|
|
71
|
+
* the changed items and the deleted ids in the order of the last change of each id.
|
|
52
72
|
*/
|
|
53
73
|
getChangesSince(since?: string): Promise<StorageChanges<T>>;
|
|
74
|
+
/** The items for which the predicate gives true. The predicate gets a copy of each item. */
|
|
54
75
|
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
55
76
|
has(id: string): Promise<boolean>;
|
|
56
77
|
size(): Promise<number>;
|
|
57
78
|
set(id: string, value: T): Promise<void>;
|
|
58
79
|
delete(id: string): Promise<boolean>;
|
|
59
80
|
clear(): Promise<void>;
|
|
81
|
+
/** This method sets several items. When one value has no copy, nothing changes. */
|
|
60
82
|
setBatch(items: Array<[string, T]>): Promise<void>;
|
|
61
83
|
deleteBatch(ids: string[]): Promise<number>;
|
|
62
84
|
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
85
|
+
/**
|
|
86
|
+
* This method closes the storage and drops its items. After it, each operation rejects (and
|
|
87
|
+
* each method of the `Map` interface throws). A second `close()` does nothing.
|
|
88
|
+
*/
|
|
63
89
|
close(): Promise<void>;
|
|
64
90
|
getMetadata(): Promise<StorageMetadata>;
|
|
65
91
|
/**
|
|
@@ -69,19 +95,20 @@ export declare class InMemoryStorage<T> implements CollectionStorage<T> {
|
|
|
69
95
|
*/
|
|
70
96
|
keys(): IterableIterator<string>;
|
|
71
97
|
/**
|
|
72
|
-
*
|
|
98
|
+
* The values in storage: copies, unless the option `copies` is false.
|
|
73
99
|
*
|
|
74
100
|
* @returns Iterator of values
|
|
75
101
|
*/
|
|
76
102
|
values(): IterableIterator<T>;
|
|
77
103
|
/**
|
|
78
|
-
*
|
|
104
|
+
* The entries in storage: copies of the values, unless the option `copies` is false.
|
|
79
105
|
*
|
|
80
106
|
* @returns Iterator of [id, value] tuples
|
|
81
107
|
*/
|
|
82
108
|
entries(): IterableIterator<[string, T]>;
|
|
83
109
|
/**
|
|
84
|
-
* Iterate over storage entries.
|
|
110
|
+
* Iterate over storage entries. The callback gets a copy of each value, and a `Map` of the
|
|
111
|
+
* copies, unless the option `copies` is false.
|
|
85
112
|
*
|
|
86
113
|
* @param callback - Function to call for each entry
|
|
87
114
|
*/
|
|
@@ -90,6 +117,11 @@ export declare class InMemoryStorage<T> implements CollectionStorage<T> {
|
|
|
90
117
|
* Make storage iterable.
|
|
91
118
|
*/
|
|
92
119
|
[Symbol.iterator](): IterableIterator<[string, T]>;
|
|
120
|
+
/** This method throws after `close()`. In an async method, the throw rejects the promise. */
|
|
121
|
+
private checkOpen;
|
|
122
|
+
/** The entries with copies of the values. */
|
|
123
|
+
private copiedEntries;
|
|
124
|
+
/** A set moves the id to the end of the order of the log: the order of the last change. */
|
|
93
125
|
private noteSet;
|
|
94
126
|
private noteDelete;
|
|
95
127
|
/** The number of a version of this storage, or undefined for another or an unknown version. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEzF,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/memory.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Fast, volatile storage using JavaScript Map.
|
|
5
5
|
* All operations return Promises for interface consistency.
|
|
6
6
|
*/
|
|
7
|
-
/** A new epoch: a version of another storage
|
|
7
|
+
/** A new epoch: a version of another storage does not match it. */
|
|
8
8
|
function newEpoch() {
|
|
9
9
|
return Math.random().toString(36).slice(2, 10);
|
|
10
10
|
}
|
|
11
|
+
const same = (value) => value;
|
|
12
|
+
const clone = (value) => structuredClone(value);
|
|
11
13
|
/**
|
|
12
14
|
* In-memory storage backed by JavaScript Map.
|
|
13
15
|
*
|
|
@@ -16,7 +18,12 @@ function newEpoch() {
|
|
|
16
18
|
* - No persistence (data lost on restart)
|
|
17
19
|
* - Memory-bound (limited by available RAM)
|
|
18
20
|
* - Returns Promises for API consistency with remote storage
|
|
21
|
+
* - Each error is a rejected promise: an error of a `find` predicate does not throw at once
|
|
19
22
|
* - A change log: `getChangesSince(version)` gives the changes after a version (roadmap R28)
|
|
23
|
+
* - Copies: a change of a written or a read value does not change the stored item (the option
|
|
24
|
+
* `copies: false` turns this off)
|
|
25
|
+
* - `getIds()` gives the ids without a copy of the values
|
|
26
|
+
* - After `close()`, each operation rejects, and a second `close()` does nothing
|
|
20
27
|
*
|
|
21
28
|
* @example
|
|
22
29
|
* ```typescript
|
|
@@ -31,96 +38,118 @@ export class InMemoryStorage {
|
|
|
31
38
|
// `<epoch>:<seq>`.
|
|
32
39
|
epoch = newEpoch();
|
|
33
40
|
seq = 0;
|
|
34
|
-
/** The number of the last write of each id that has an item */
|
|
41
|
+
/** The number of the last write of each id that has an item, the oldest first */
|
|
35
42
|
changedAt = new Map();
|
|
36
43
|
/** The number of the delete of each id that has no item, the oldest first (the tombstones) */
|
|
37
44
|
deletedAt = new Map();
|
|
38
45
|
/** The log does not know the deletes at or below this number: they were dropped */
|
|
39
46
|
floor = 0;
|
|
40
47
|
maxTombstones;
|
|
48
|
+
copy;
|
|
49
|
+
closed = false;
|
|
41
50
|
constructor(options = {}) {
|
|
42
51
|
this.maxTombstones = Math.max(0, options.maxTombstones ?? 10_000);
|
|
52
|
+
this.copy = options.copies === false ? same : clone;
|
|
43
53
|
}
|
|
44
54
|
//
|
|
45
55
|
// ═══ Read Operations ═══
|
|
46
56
|
//
|
|
47
|
-
get(id) {
|
|
48
|
-
|
|
57
|
+
async get(id) {
|
|
58
|
+
this.checkOpen();
|
|
59
|
+
return this.copy(this.data.get(id));
|
|
49
60
|
}
|
|
50
|
-
getAll() {
|
|
51
|
-
|
|
61
|
+
async getAll() {
|
|
62
|
+
this.checkOpen();
|
|
63
|
+
return Array.from(this.data.values(), this.copy);
|
|
52
64
|
}
|
|
53
|
-
getEntries() {
|
|
54
|
-
|
|
65
|
+
async getEntries() {
|
|
66
|
+
this.checkOpen();
|
|
67
|
+
return this.copiedEntries();
|
|
68
|
+
}
|
|
69
|
+
/** The ids of the items, in the order of the entries. This method copies no value. */
|
|
70
|
+
async getIds() {
|
|
71
|
+
this.checkOpen();
|
|
72
|
+
return Array.from(this.data.keys());
|
|
55
73
|
}
|
|
56
74
|
/**
|
|
57
|
-
* The changes after a version (roadmap R28). A version of another storage,
|
|
58
|
-
*
|
|
75
|
+
* The changes after a version (roadmap R28). A version of another storage, or older than the
|
|
76
|
+
* dropped deletes, gives all items (`full: true`) in the order of the entries. A delta gives
|
|
77
|
+
* the changed items and the deleted ids in the order of the last change of each id.
|
|
59
78
|
*/
|
|
60
|
-
getChangesSince(since) {
|
|
79
|
+
async getChangesSince(since) {
|
|
80
|
+
this.checkOpen();
|
|
61
81
|
const version = `${this.epoch}:${this.seq}`;
|
|
62
82
|
const from = this.parseVersion(since);
|
|
63
83
|
if (from === undefined || from < this.floor) {
|
|
64
|
-
return
|
|
84
|
+
return { version, full: true, changed: this.copiedEntries(), deleted: [] };
|
|
65
85
|
}
|
|
66
86
|
const changed = [];
|
|
67
87
|
for (const [id, at] of this.changedAt) {
|
|
68
88
|
if (at > from)
|
|
69
|
-
changed.push([id, this.data.get(id)]);
|
|
89
|
+
changed.push([id, this.copy(this.data.get(id))]);
|
|
70
90
|
}
|
|
71
91
|
const deleted = [];
|
|
72
92
|
for (const [id, at] of this.deletedAt) {
|
|
73
93
|
if (at > from)
|
|
74
94
|
deleted.push(id);
|
|
75
95
|
}
|
|
76
|
-
return
|
|
96
|
+
return { version, full: false, changed, deleted };
|
|
77
97
|
}
|
|
78
|
-
|
|
98
|
+
/** The items for which the predicate gives true. The predicate gets a copy of each item. */
|
|
99
|
+
async find(predicate) {
|
|
100
|
+
this.checkOpen();
|
|
79
101
|
const results = [];
|
|
80
|
-
for (const
|
|
102
|
+
for (const stored of this.data.values()) {
|
|
103
|
+
const item = this.copy(stored);
|
|
81
104
|
if (predicate(item)) {
|
|
82
105
|
results.push(item);
|
|
83
106
|
}
|
|
84
107
|
}
|
|
85
|
-
return
|
|
108
|
+
return results;
|
|
86
109
|
}
|
|
87
|
-
has(id) {
|
|
88
|
-
|
|
110
|
+
async has(id) {
|
|
111
|
+
this.checkOpen();
|
|
112
|
+
return this.data.has(id);
|
|
89
113
|
}
|
|
90
|
-
size() {
|
|
91
|
-
|
|
114
|
+
async size() {
|
|
115
|
+
this.checkOpen();
|
|
116
|
+
return this.data.size;
|
|
92
117
|
}
|
|
93
118
|
//
|
|
94
119
|
// ═══ Write Operations ═══
|
|
95
120
|
//
|
|
96
|
-
set(id, value) {
|
|
97
|
-
this.
|
|
121
|
+
async set(id, value) {
|
|
122
|
+
this.checkOpen();
|
|
123
|
+
this.data.set(id, this.copy(value));
|
|
98
124
|
this.noteSet(id);
|
|
99
|
-
return Promise.resolve();
|
|
100
125
|
}
|
|
101
|
-
delete(id) {
|
|
126
|
+
async delete(id) {
|
|
127
|
+
this.checkOpen();
|
|
102
128
|
const existed = this.data.delete(id);
|
|
103
129
|
if (existed)
|
|
104
130
|
this.noteDelete(id);
|
|
105
|
-
return
|
|
131
|
+
return existed;
|
|
106
132
|
}
|
|
107
|
-
clear() {
|
|
133
|
+
async clear() {
|
|
134
|
+
this.checkOpen();
|
|
108
135
|
for (const id of this.data.keys())
|
|
109
136
|
this.noteDelete(id);
|
|
110
137
|
this.data.clear();
|
|
111
|
-
return Promise.resolve();
|
|
112
138
|
}
|
|
113
139
|
//
|
|
114
140
|
// ═══ Bulk Operations ═══
|
|
115
141
|
//
|
|
116
|
-
|
|
117
|
-
|
|
142
|
+
/** This method sets several items. When one value has no copy, nothing changes. */
|
|
143
|
+
async setBatch(items) {
|
|
144
|
+
this.checkOpen();
|
|
145
|
+
const copies = items.map(([id, value]) => [id, this.copy(value)]);
|
|
146
|
+
for (const [id, value] of copies) {
|
|
118
147
|
this.data.set(id, value);
|
|
119
148
|
this.noteSet(id);
|
|
120
149
|
}
|
|
121
|
-
return Promise.resolve();
|
|
122
150
|
}
|
|
123
|
-
deleteBatch(ids) {
|
|
151
|
+
async deleteBatch(ids) {
|
|
152
|
+
this.checkOpen();
|
|
124
153
|
let deleted = 0;
|
|
125
154
|
for (const id of ids) {
|
|
126
155
|
if (this.data.delete(id)) {
|
|
@@ -128,44 +157,47 @@ export class InMemoryStorage {
|
|
|
128
157
|
deleted++;
|
|
129
158
|
}
|
|
130
159
|
}
|
|
131
|
-
return
|
|
160
|
+
return deleted;
|
|
132
161
|
}
|
|
133
|
-
getBatch(ids) {
|
|
162
|
+
async getBatch(ids) {
|
|
163
|
+
this.checkOpen();
|
|
134
164
|
const result = new Map();
|
|
135
165
|
for (const id of ids) {
|
|
136
166
|
const value = this.data.get(id);
|
|
137
167
|
if (value !== undefined) {
|
|
138
|
-
result.set(id, value);
|
|
168
|
+
result.set(id, this.copy(value));
|
|
139
169
|
}
|
|
140
170
|
}
|
|
141
|
-
return
|
|
171
|
+
return result;
|
|
142
172
|
}
|
|
143
173
|
//
|
|
144
174
|
// ═══ Lifecycle & Metadata ═══
|
|
145
175
|
//
|
|
146
|
-
|
|
147
|
-
|
|
176
|
+
/**
|
|
177
|
+
* This method closes the storage and drops its items. After it, each operation rejects (and
|
|
178
|
+
* each method of the `Map` interface throws). A second `close()` does nothing.
|
|
179
|
+
*/
|
|
180
|
+
async close() {
|
|
181
|
+
if (this.closed)
|
|
182
|
+
return;
|
|
183
|
+
this.closed = true;
|
|
148
184
|
this.data.clear();
|
|
149
|
-
// A new epoch: a version of the closed storage gives all items
|
|
150
185
|
this.changedAt.clear();
|
|
151
186
|
this.deletedAt.clear();
|
|
152
|
-
this.epoch = newEpoch();
|
|
153
|
-
this.seq = 0;
|
|
154
|
-
this.floor = 0;
|
|
155
|
-
return Promise.resolve();
|
|
156
187
|
}
|
|
157
|
-
getMetadata() {
|
|
188
|
+
async getMetadata() {
|
|
189
|
+
this.checkOpen();
|
|
158
190
|
// Rough memory estimation:
|
|
159
191
|
// Each entry has ~overhead of 100 bytes (key string + Map entry overhead)
|
|
160
192
|
const estimatedEntrySize = 100;
|
|
161
193
|
const memoryUsage = this.data.size * estimatedEntrySize;
|
|
162
|
-
return
|
|
194
|
+
return {
|
|
163
195
|
type: "memory",
|
|
164
196
|
size: this.data.size,
|
|
165
197
|
stats: {
|
|
166
198
|
memoryUsage,
|
|
167
199
|
},
|
|
168
|
-
}
|
|
200
|
+
};
|
|
169
201
|
}
|
|
170
202
|
//
|
|
171
203
|
// ═══ Additional Methods (Map compatibility) ═══
|
|
@@ -176,42 +208,61 @@ export class InMemoryStorage {
|
|
|
176
208
|
* @returns Iterator of keys
|
|
177
209
|
*/
|
|
178
210
|
keys() {
|
|
211
|
+
this.checkOpen();
|
|
179
212
|
return this.data.keys();
|
|
180
213
|
}
|
|
181
214
|
/**
|
|
182
|
-
*
|
|
215
|
+
* The values in storage: copies, unless the option `copies` is false.
|
|
183
216
|
*
|
|
184
217
|
* @returns Iterator of values
|
|
185
218
|
*/
|
|
186
219
|
values() {
|
|
187
|
-
|
|
220
|
+
this.checkOpen();
|
|
221
|
+
return this.copiedEntries().map(([, value]) => value)[Symbol.iterator]();
|
|
188
222
|
}
|
|
189
223
|
/**
|
|
190
|
-
*
|
|
224
|
+
* The entries in storage: copies of the values, unless the option `copies` is false.
|
|
191
225
|
*
|
|
192
226
|
* @returns Iterator of [id, value] tuples
|
|
193
227
|
*/
|
|
194
228
|
entries() {
|
|
195
|
-
|
|
229
|
+
this.checkOpen();
|
|
230
|
+
return this.copiedEntries()[Symbol.iterator]();
|
|
196
231
|
}
|
|
197
232
|
/**
|
|
198
|
-
* Iterate over storage entries.
|
|
233
|
+
* Iterate over storage entries. The callback gets a copy of each value, and a `Map` of the
|
|
234
|
+
* copies, unless the option `copies` is false.
|
|
199
235
|
*
|
|
200
236
|
* @param callback - Function to call for each entry
|
|
201
237
|
*/
|
|
202
238
|
forEach(callback) {
|
|
203
|
-
this.
|
|
239
|
+
this.checkOpen();
|
|
240
|
+
new Map(this.copiedEntries()).forEach(callback);
|
|
204
241
|
}
|
|
205
242
|
/**
|
|
206
243
|
* Make storage iterable.
|
|
207
244
|
*/
|
|
208
245
|
[Symbol.iterator]() {
|
|
209
|
-
return this.
|
|
246
|
+
return this.entries();
|
|
247
|
+
}
|
|
248
|
+
//
|
|
249
|
+
// ═══ Internal Helpers ═══
|
|
250
|
+
//
|
|
251
|
+
/** This method throws after `close()`. In an async method, the throw rejects the promise. */
|
|
252
|
+
checkOpen() {
|
|
253
|
+
if (this.closed)
|
|
254
|
+
throw new Error("The InMemoryStorage is closed");
|
|
255
|
+
}
|
|
256
|
+
/** The entries with copies of the values. */
|
|
257
|
+
copiedEntries() {
|
|
258
|
+
return Array.from(this.data, ([id, value]) => [id, this.copy(value)]);
|
|
210
259
|
}
|
|
211
260
|
//
|
|
212
261
|
// ═══ The change log ═══
|
|
213
262
|
//
|
|
263
|
+
/** A set moves the id to the end of the order of the log: the order of the last change. */
|
|
214
264
|
noteSet(id) {
|
|
265
|
+
this.changedAt.delete(id);
|
|
215
266
|
this.changedAt.set(id, ++this.seq);
|
|
216
267
|
this.deletedAt.delete(id);
|
|
217
268
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The checks of the contract suite. The suite does not import a test framework, so a runtime
|
|
3
|
+
* entry of this package never loads one. A failed check throws `ContractViolation`.
|
|
4
|
+
*/
|
|
5
|
+
/** The error of a failed check of the contract suite. */
|
|
6
|
+
export declare class ContractViolation extends Error {
|
|
7
|
+
readonly name = "ContractViolation";
|
|
8
|
+
}
|
|
9
|
+
/** This function gives the text of a value for a message: JSON, with a `Map` as its entries. */
|
|
10
|
+
export declare function show(value: unknown): string;
|
|
11
|
+
/**
|
|
12
|
+
* This function compares two values deeply: JSON values, arrays, plain objects and maps. Two maps
|
|
13
|
+
* are equal when they have the same keys with equal values, in any order.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deepEqual(actual: unknown, expected: unknown): boolean;
|
|
16
|
+
/** This function throws `ContractViolation` when the two values are not deeply equal. */
|
|
17
|
+
export declare function same(actual: unknown, expected: unknown, what: string): void;
|
|
18
|
+
/** This function throws `ContractViolation` when the condition is false. */
|
|
19
|
+
export declare function holds(condition: boolean, what: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* This function starts an operation that must fail with a rejected promise, and gives the error.
|
|
22
|
+
* A synchronous throw breaks the contract too: each method of a storage gives a promise.
|
|
23
|
+
*/
|
|
24
|
+
export declare function rejects(start: () => Promise<unknown>, what: string): Promise<unknown>;
|
|
25
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/testing/check.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,yDAAyD;AACzD,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,SAAkB,IAAI,uBAAuB;CAC9C;AAED,gGAAgG;AAChG,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAO3C;AAQD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAkBrE;AAED,yFAAyF;AACzF,wBAAgB,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI3E;AAED,4EAA4E;AAC5E,wBAAgB,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAc3F"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The checks of the contract suite. The suite does not import a test framework, so a runtime
|
|
3
|
+
* entry of this package never loads one. A failed check throws `ContractViolation`.
|
|
4
|
+
*/
|
|
5
|
+
/** The error of a failed check of the contract suite. */
|
|
6
|
+
export class ContractViolation extends Error {
|
|
7
|
+
name = "ContractViolation";
|
|
8
|
+
}
|
|
9
|
+
/** This function gives the text of a value for a message: JSON, with a `Map` as its entries. */
|
|
10
|
+
export function show(value) {
|
|
11
|
+
if (value === undefined)
|
|
12
|
+
return "undefined";
|
|
13
|
+
try {
|
|
14
|
+
return JSON.stringify(value, (_key, item) => (item instanceof Map ? { Map: [...item.entries()] } : item === undefined ? "<undefined>" : item));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return String(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function isPlainObject(value) {
|
|
21
|
+
if (typeof value !== "object" || value === null)
|
|
22
|
+
return false;
|
|
23
|
+
const prototype = Object.getPrototypeOf(value);
|
|
24
|
+
return prototype === Object.prototype || prototype === null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* This function compares two values deeply: JSON values, arrays, plain objects and maps. Two maps
|
|
28
|
+
* are equal when they have the same keys with equal values, in any order.
|
|
29
|
+
*/
|
|
30
|
+
export function deepEqual(actual, expected) {
|
|
31
|
+
if (Object.is(actual, expected))
|
|
32
|
+
return true;
|
|
33
|
+
if (actual instanceof Map && expected instanceof Map) {
|
|
34
|
+
if (actual.size !== expected.size)
|
|
35
|
+
return false;
|
|
36
|
+
for (const [key, value] of actual) {
|
|
37
|
+
if (!expected.has(key) || !deepEqual(value, expected.get(key)))
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(actual) && Array.isArray(expected)) {
|
|
43
|
+
return actual.length === expected.length && actual.every((item, index) => deepEqual(item, expected[index]));
|
|
44
|
+
}
|
|
45
|
+
if (isPlainObject(actual) && isPlainObject(expected)) {
|
|
46
|
+
const keys = Object.keys(actual);
|
|
47
|
+
if (keys.length !== Object.keys(expected).length)
|
|
48
|
+
return false;
|
|
49
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(expected, key) && deepEqual(actual[key], expected[key]));
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/** This function throws `ContractViolation` when the two values are not deeply equal. */
|
|
54
|
+
export function same(actual, expected, what) {
|
|
55
|
+
if (!deepEqual(actual, expected)) {
|
|
56
|
+
throw new ContractViolation(`${what}: expected ${show(expected)}, got ${show(actual)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** This function throws `ContractViolation` when the condition is false. */
|
|
60
|
+
export function holds(condition, what) {
|
|
61
|
+
if (!condition)
|
|
62
|
+
throw new ContractViolation(what);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* This function starts an operation that must fail with a rejected promise, and gives the error.
|
|
66
|
+
* A synchronous throw breaks the contract too: each method of a storage gives a promise.
|
|
67
|
+
*/
|
|
68
|
+
export async function rejects(start, what) {
|
|
69
|
+
let promise;
|
|
70
|
+
try {
|
|
71
|
+
promise = start();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new ContractViolation(`${what}: the method threw at once (${show(error instanceof Error ? error.message : error)}), and did not give a rejected promise`);
|
|
75
|
+
}
|
|
76
|
+
holds(typeof promise?.then === "function", `${what}: the method did not give a promise`);
|
|
77
|
+
try {
|
|
78
|
+
await promise;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return error;
|
|
82
|
+
}
|
|
83
|
+
throw new ContractViolation(`${what}: the promise did not reject`);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/testing/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@procedures/collections/testing`: the test-only entry of this package.
|
|
3
|
+
*
|
|
4
|
+
* This entry holds the contract suite of `CollectionStorage`. A storage package runs it in its
|
|
5
|
+
* tests with a factory of its storage. The entry imports no test framework: the caller gives
|
|
6
|
+
* `describe` and `it`. The runtime entries of the package do not import this entry.
|
|
7
|
+
*/
|
|
8
|
+
export { collectionStorageContract, describeCollectionStorageContract, type CollectionStorageContractOptions, type ContractCase, type ContractItem, type ContractStorage, type ContractTestApi, type ContractTestContext, } from "./storage-contract.js";
|
|
9
|
+
export { ContractViolation, deepEqual } from "./check.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,yBAAyB,EACzB,iCAAiC,EACjC,KAAK,gCAAgC,EACrC,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,mBAAmB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@procedures/collections/testing`: the test-only entry of this package.
|
|
3
|
+
*
|
|
4
|
+
* This entry holds the contract suite of `CollectionStorage`. A storage package runs it in its
|
|
5
|
+
* tests with a factory of its storage. The entry imports no test framework: the caller gives
|
|
6
|
+
* `describe` and `it`. The runtime entries of the package do not import this entry.
|
|
7
|
+
*/
|
|
8
|
+
export { collectionStorageContract, describeCollectionStorageContract, } from "./storage-contract.js";
|
|
9
|
+
export { ContractViolation, deepEqual } from "./check.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,yBAAyB,EACzB,iCAAiC,GAOlC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
|