@frogfish/k2db 3.0.1 → 3.0.3
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/README.md +351 -13
- package/data.d.ts +28 -5
- package/data.js +45 -15
- package/db.d.ts +133 -22
- package/db.js +652 -59
- package/package.json +1 -1
package/db.d.ts
CHANGED
|
@@ -23,6 +23,11 @@ export interface DatabaseConfig {
|
|
|
23
23
|
beforeQuery?: (op: string, details: any) => void;
|
|
24
24
|
afterQuery?: (op: string, details: any, durationMs: number) => void;
|
|
25
25
|
};
|
|
26
|
+
ownershipMode?: "lax" | "strict";
|
|
27
|
+
aggregationMode?: "loose" | "guarded" | "strict";
|
|
28
|
+
secureFieldPrefixes?: string[];
|
|
29
|
+
secureFieldEncryptionKey?: string;
|
|
30
|
+
secureFieldEncryptionKeyId?: string;
|
|
26
31
|
}
|
|
27
32
|
export interface BaseDocument {
|
|
28
33
|
_id?: ObjectId;
|
|
@@ -67,6 +72,7 @@ export interface VersionInfo {
|
|
|
67
72
|
_v: number;
|
|
68
73
|
_at: number;
|
|
69
74
|
}
|
|
75
|
+
export type Scope = string | "*";
|
|
70
76
|
export declare class K2DB {
|
|
71
77
|
private conf;
|
|
72
78
|
private db;
|
|
@@ -75,7 +81,20 @@ export declare class K2DB {
|
|
|
75
81
|
private initialized;
|
|
76
82
|
private initPromise?;
|
|
77
83
|
private schemas;
|
|
84
|
+
private readonly ownershipMode;
|
|
85
|
+
private readonly aggregationMode;
|
|
86
|
+
private readonly secureFieldPrefixes;
|
|
87
|
+
private readonly secureFieldEncryptionKey?;
|
|
88
|
+
private readonly secureFieldEncryptionKeyId?;
|
|
78
89
|
constructor(conf: DatabaseConfig);
|
|
90
|
+
/**
|
|
91
|
+
* Normalize a scope value for ownership enforcement.
|
|
92
|
+
*/
|
|
93
|
+
private normalizeScope;
|
|
94
|
+
/**
|
|
95
|
+
* Apply a scope constraint to criteria for ownership enforcement.
|
|
96
|
+
*/
|
|
97
|
+
private applyScopeToCriteria;
|
|
79
98
|
/**
|
|
80
99
|
* Initializes the MongoDB connection.
|
|
81
100
|
*/
|
|
@@ -91,15 +110,21 @@ export declare class K2DB {
|
|
|
91
110
|
* @param collectionName - Name of the collection.
|
|
92
111
|
*/
|
|
93
112
|
private getCollection;
|
|
94
|
-
get(collectionName: string, uuid: string): Promise<BaseDocument>;
|
|
95
113
|
/**
|
|
96
114
|
* Retrieves a single document by UUID.
|
|
97
115
|
* @param collectionName - Name of the collection.
|
|
98
116
|
* @param uuid - UUID of the document.
|
|
99
|
-
* @param
|
|
117
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
118
|
+
*/
|
|
119
|
+
get(collectionName: string, uuid: string, scope?: Scope | string): Promise<BaseDocument>;
|
|
120
|
+
/**
|
|
121
|
+
* Retrieves a single document by criteria.
|
|
122
|
+
* @param collectionName - Name of the collection.
|
|
123
|
+
* @param criteria - Criteria to find the document.
|
|
100
124
|
* @param fields - Optional array of fields to include.
|
|
125
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
101
126
|
*/
|
|
102
|
-
findOne(collectionName: string, criteria: any, fields?: Array<string
|
|
127
|
+
findOne(collectionName: string, criteria: any, fields?: Array<string>, scope?: Scope | string): Promise<BaseDocument | null>;
|
|
103
128
|
/**
|
|
104
129
|
* Finds documents based on parameters with pagination support.
|
|
105
130
|
* @param collectionName - Name of the collection.
|
|
@@ -107,16 +132,72 @@ export declare class K2DB {
|
|
|
107
132
|
* @param params - Optional search parameters (for sorting, including/excluding fields).
|
|
108
133
|
* @param skip - Number of documents to skip (for pagination).
|
|
109
134
|
* @param limit - Maximum number of documents to return.
|
|
135
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
110
136
|
*/
|
|
111
|
-
find(collectionName: string, filter: any, params?: any, skip?: number, limit?: number): Promise<BaseDocument[]>;
|
|
137
|
+
find(collectionName: string, filter: any, params?: any, skip?: number, limit?: number, scope?: Scope | string): Promise<BaseDocument[]>;
|
|
112
138
|
/**
|
|
113
|
-
* Aggregates documents based on criteria with pagination support.
|
|
139
|
+
* Aggregates documents based on criteria with pagination support (may validate/limit stages in guarded/strict aggregationMode). Secure-prefixed fields may be stripped from results when configured.
|
|
114
140
|
* @param collectionName - Name of the collection.
|
|
115
141
|
* @param criteria - Aggregation pipeline criteria.
|
|
116
142
|
* @param skip - Number of documents to skip (for pagination).
|
|
117
143
|
* @param limit - Maximum number of documents to return.
|
|
144
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
145
|
+
*/
|
|
146
|
+
aggregate(collectionName: string, criteria: any[], skip?: number, limit?: number, scope?: Scope | string): Promise<BaseDocument[]>;
|
|
147
|
+
/**
|
|
148
|
+
* Validate an aggregation pipeline for safety based on aggregationMode.
|
|
149
|
+
* - loose: no validation
|
|
150
|
+
* - guarded: deny obvious footguns (writes, server-side code) and enforce basic caps
|
|
151
|
+
* - strict: allow only a small safe subset of stages and enforce basic caps
|
|
152
|
+
*/
|
|
153
|
+
private validateAggregationPipeline;
|
|
154
|
+
/** Collect top-level stage operators for a pipeline (e.g. "$match", "$lookup"). */
|
|
155
|
+
private collectStageOps;
|
|
156
|
+
/** True if a field key is considered secure and must not be returned. */
|
|
157
|
+
private isSecureFieldKey;
|
|
158
|
+
/**
|
|
159
|
+
* Recursively strips secure-prefixed fields from objects/arrays (e.g. "#passport_number").
|
|
160
|
+
* This is applied on read results (e.g. aggregate) so pipelines cannot exfiltrate secure fields.
|
|
161
|
+
*/
|
|
162
|
+
private stripSecureFieldsDeep;
|
|
163
|
+
/** True if secure-field encryption is enabled (requires both key and keyId). */
|
|
164
|
+
private hasSecureEncryption;
|
|
165
|
+
/** Encrypt a JS value using AES-256-GCM and return "<kid>:<ivB64>.<tagB64>.<ctB64>". */
|
|
166
|
+
private encryptSecureValueToString;
|
|
167
|
+
/** Decrypt a "<kid>:<ivB64>.<tagB64>.<ctB64>" string back into a JS value. */
|
|
168
|
+
private decryptSecureStringToValue;
|
|
169
|
+
/**
|
|
170
|
+
* Encrypt secure-prefixed fields in an object/array tree.
|
|
171
|
+
* Only keys with secure prefixes are encrypted; other keys are recursed to allow nested secure keys.
|
|
172
|
+
*/
|
|
173
|
+
private encryptSecureFieldsDeep;
|
|
174
|
+
/**
|
|
175
|
+
* Decrypt secure-prefixed fields in an object/array tree.
|
|
176
|
+
* If a secure field value is not an encrypted string, it is returned as-is.
|
|
177
|
+
*/
|
|
178
|
+
private decryptSecureFieldsDeep;
|
|
179
|
+
/**
|
|
180
|
+
* Throws if an aggregation pipeline references any secure-prefixed field paths.
|
|
181
|
+
* This prevents deriving output from secure fields (e.g. {$group: {x: {$sum: "$#passport_number"}}}).
|
|
118
182
|
*/
|
|
119
|
-
|
|
183
|
+
private assertNoSecureFieldRefsInPipeline;
|
|
184
|
+
/** Recursively detects secure field references in an aggregation pipeline AST. */
|
|
185
|
+
private containsSecureFieldRefDeep;
|
|
186
|
+
/**
|
|
187
|
+
* Returns true if a string appears to reference a secure field path.
|
|
188
|
+
* We consider:
|
|
189
|
+
* - "$path.to.field"
|
|
190
|
+
* - "$$var.path.to.field"
|
|
191
|
+
* - plain "path.to.field" (e.g. $getField: { field: "#passport_number" })
|
|
192
|
+
*/
|
|
193
|
+
private stringHasSecureFieldPath;
|
|
194
|
+
/** True if any segment in a dotted path starts with a secure prefix. */
|
|
195
|
+
private pathHasSecureSegment;
|
|
196
|
+
/**
|
|
197
|
+
* Ensures an aggregation pipeline respects ownership scope for the root
|
|
198
|
+
* collection and any joined collections ($lookup, $unionWith, $graphLookup, $facet).
|
|
199
|
+
*/
|
|
200
|
+
private enforceScopeInPipeline;
|
|
120
201
|
/**
|
|
121
202
|
* Ensures an aggregation pipeline excludes soft-deleted documents for the root
|
|
122
203
|
* collection and any joined collections ($lookup, $unionWith, $graphLookup, $facet).
|
|
@@ -135,8 +216,9 @@ export declare class K2DB {
|
|
|
135
216
|
* @param collectionName - Name of the collection.
|
|
136
217
|
* @param criteria - Update criteria.
|
|
137
218
|
* @param values - Values to update or replace with.
|
|
219
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
138
220
|
*/
|
|
139
|
-
updateAll(collectionName: string, criteria: any, values: Partial<BaseDocument
|
|
221
|
+
updateAll(collectionName: string, criteria: any, values: Partial<BaseDocument>, scope?: Scope | string): Promise<UpdateResult>;
|
|
140
222
|
/**
|
|
141
223
|
* Updates a single document by UUID.
|
|
142
224
|
* Can either replace the document or patch it.
|
|
@@ -144,60 +226,70 @@ export declare class K2DB {
|
|
|
144
226
|
* @param id - UUID string to identify the document.
|
|
145
227
|
* @param data - Data to update or replace with.
|
|
146
228
|
* @param replace - If true, replaces the entire document (PUT), otherwise patches (PATCH).
|
|
229
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
147
230
|
*/
|
|
148
|
-
update(collectionName: string, id: string, data: Partial<BaseDocument>, replace?: boolean): Promise<UpdateResult>;
|
|
231
|
+
update(collectionName: string, id: string, data: Partial<BaseDocument>, replace?: boolean, scope?: Scope | string): Promise<UpdateResult>;
|
|
149
232
|
/**
|
|
150
233
|
* Removes (soft deletes) multiple documents based on criteria.
|
|
151
234
|
* @param collectionName - Name of the collection.
|
|
152
235
|
* @param criteria - Removal criteria.
|
|
236
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
153
237
|
*/
|
|
154
|
-
deleteAll(collectionName: string, criteria: any): Promise<DeleteResult>;
|
|
238
|
+
deleteAll(collectionName: string, criteria: any, scope?: Scope | string): Promise<DeleteResult>;
|
|
155
239
|
/**
|
|
156
240
|
* Removes (soft deletes) a single document by UUID.
|
|
157
241
|
* @param collectionName - Name of the collection.
|
|
158
242
|
* @param id - UUID of the document.
|
|
243
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
159
244
|
*/
|
|
160
|
-
delete(collectionName: string, id: string): Promise<DeleteResult>;
|
|
245
|
+
delete(collectionName: string, id: string, scope?: Scope | string): Promise<DeleteResult>;
|
|
161
246
|
/**
|
|
162
247
|
* Permanently deletes a document that has been soft-deleted.
|
|
163
248
|
* @param collectionName - Name of the collection.
|
|
164
249
|
* @param id - UUID of the document.
|
|
250
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
165
251
|
*/
|
|
166
|
-
purge(collectionName: string, id: string): Promise<PurgeResult>;
|
|
252
|
+
purge(collectionName: string, id: string, scope?: Scope | string): Promise<PurgeResult>;
|
|
167
253
|
/**
|
|
168
254
|
* Permanently deletes all documents that are soft-deleted and whose _updated
|
|
169
255
|
* timestamp is older than the provided threshold (in milliseconds ago).
|
|
170
256
|
* @param collectionName - Name of the collection.
|
|
171
257
|
* @param olderThanMs - Age threshold in milliseconds; documents with
|
|
172
258
|
* `_updated <= (Date.now() - olderThanMs)` will be purged.
|
|
259
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
173
260
|
*/
|
|
174
|
-
purgeDeletedOlderThan(collectionName: string, olderThanMs: number): Promise<PurgeManyResult>;
|
|
261
|
+
purgeDeletedOlderThan(collectionName: string, olderThanMs: number, scope?: Scope | string): Promise<PurgeManyResult>;
|
|
175
262
|
/**
|
|
176
263
|
* Restores a soft-deleted document.
|
|
177
264
|
* @param collectionName - Name of the collection.
|
|
178
265
|
* @param criteria - Criteria to identify the document.
|
|
266
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
179
267
|
*/
|
|
180
|
-
restore(collectionName: string, criteria: any): Promise<RestoreResult>;
|
|
268
|
+
restore(collectionName: string, criteria: any, scope?: Scope | string): Promise<RestoreResult>;
|
|
181
269
|
/**
|
|
182
270
|
* Counts documents based on criteria.
|
|
183
271
|
* @param collectionName - Name of the collection.
|
|
184
272
|
* @param criteria - Counting criteria.
|
|
273
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
185
274
|
*/
|
|
186
|
-
count(collectionName: string, criteria: any): Promise<CountResult>;
|
|
275
|
+
count(collectionName: string, criteria: any, scope?: Scope | string): Promise<CountResult>;
|
|
187
276
|
/**
|
|
188
|
-
* Drops an entire collection.
|
|
277
|
+
* Drops an entire collection (global destructive operation).
|
|
189
278
|
* @param collectionName - Name of the collection.
|
|
279
|
+
* @param scope - (optional) Must be "*" in strict ownership mode.
|
|
190
280
|
*/
|
|
191
|
-
drop(collectionName: string): Promise<DropResult>;
|
|
281
|
+
drop(collectionName: string, scope?: Scope | string): Promise<DropResult>;
|
|
192
282
|
/**
|
|
193
283
|
* Sanitizes aggregation criteria.
|
|
194
284
|
* @param criteria - Aggregation stage criteria.
|
|
195
285
|
*/
|
|
196
286
|
private static sanitiseCriteria;
|
|
197
|
-
/** Recursively
|
|
287
|
+
/** Recursively normalizes query fields: `_uuid` uppercased, `_owner` lowercased. */
|
|
198
288
|
private static normalizeCriteriaIds;
|
|
199
289
|
/** Uppercase helper for `_uuid` field supporting operators like $in/$nin/$eq/$ne and arrays. */
|
|
200
290
|
private static normalizeUuidField;
|
|
291
|
+
/** Lowercase helper for `_owner` field supporting operators like $in/$nin/$eq/$ne and arrays. */
|
|
292
|
+
private static normalizeOwnerField;
|
|
201
293
|
/** Strip any user-provided fields that start with '_' (reserved). */
|
|
202
294
|
private static stripReservedFields;
|
|
203
295
|
/** True if string matches K2 ID format (Crockford Base32, 8-4-4-4-6, uppercase). */
|
|
@@ -274,12 +366,31 @@ export declare class K2DB {
|
|
|
274
366
|
/**
|
|
275
367
|
* Update a document and keep the previous version in a history collection.
|
|
276
368
|
* If maxVersions is provided, prunes oldest snapshots beyond that number.
|
|
369
|
+
* @param collectionName - Name of the collection.
|
|
370
|
+
* @param id - UUID string to identify the document.
|
|
371
|
+
* @param data - Data to update or replace with.
|
|
372
|
+
* @param replace - If true, replaces the entire document (PUT), otherwise patches (PATCH).
|
|
373
|
+
* @param maxVersions - Maximum number of versions to keep (optional).
|
|
374
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
375
|
+
*/
|
|
376
|
+
updateVersioned(collectionName: string, id: string, data: Partial<BaseDocument>, replace?: boolean, maxVersions?: number, scope?: Scope | string): Promise<VersionedUpdateResult[]>;
|
|
377
|
+
/**
|
|
378
|
+
* List versions (latest first).
|
|
379
|
+
* @param collectionName - Name of the collection.
|
|
380
|
+
* @param id - UUID string to identify the document.
|
|
381
|
+
* @param skip - Number of versions to skip (for pagination).
|
|
382
|
+
* @param limit - Maximum number of versions to return.
|
|
383
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
384
|
+
*/
|
|
385
|
+
listVersions(collectionName: string, id: string, skip?: number, limit?: number, scope?: Scope | string): Promise<VersionInfo[]>;
|
|
386
|
+
/**
|
|
387
|
+
* Revert the current document to a specific historical version (preserves metadata).
|
|
388
|
+
* @param collectionName - Name of the collection.
|
|
389
|
+
* @param id - UUID string to identify the document.
|
|
390
|
+
* @param version - Version number to revert to.
|
|
391
|
+
* @param scope - (optional) Owner selector; "*" means all owners.
|
|
277
392
|
*/
|
|
278
|
-
|
|
279
|
-
/** List versions (latest first). */
|
|
280
|
-
listVersions(collectionName: string, id: string, skip?: number, limit?: number): Promise<VersionInfo[]>;
|
|
281
|
-
/** Revert the current document to a specific historical version (preserves metadata). */
|
|
282
|
-
revertToVersion(collectionName: string, id: string, version: number): Promise<{
|
|
393
|
+
revertToVersion(collectionName: string, id: string, version: number, scope?: Scope | string): Promise<{
|
|
283
394
|
updated: number;
|
|
284
395
|
}>;
|
|
285
396
|
}
|