@diister/quick-permission 0.9.0-beta.5

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 (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +747 -0
  3. package/aggregation.ts +114 -0
  4. package/core/filtering.ts +70 -0
  5. package/core/matching.ts +82 -0
  6. package/core/merging.ts +143 -0
  7. package/dist/aggregation.d.ts +62 -0
  8. package/dist/aggregation.d.ts.map +1 -0
  9. package/dist/aggregation.js +97 -0
  10. package/dist/aggregation.js.map +1 -0
  11. package/dist/core/filtering.d.ts +35 -0
  12. package/dist/core/filtering.d.ts.map +1 -0
  13. package/dist/core/filtering.js +62 -0
  14. package/dist/core/filtering.js.map +1 -0
  15. package/dist/core/matching.d.ts +31 -0
  16. package/dist/core/matching.d.ts.map +1 -0
  17. package/dist/core/matching.js +75 -0
  18. package/dist/core/matching.js.map +1 -0
  19. package/dist/core/merging.d.ts +29 -0
  20. package/dist/core/merging.d.ts.map +1 -0
  21. package/dist/core/merging.js +124 -0
  22. package/dist/core/merging.js.map +1 -0
  23. package/dist/indirect-aggregation.d.ts +41 -0
  24. package/dist/indirect-aggregation.d.ts.map +1 -0
  25. package/dist/indirect-aggregation.js +185 -0
  26. package/dist/indirect-aggregation.js.map +1 -0
  27. package/dist/indirect-resource.d.ts +126 -0
  28. package/dist/indirect-resource.d.ts.map +1 -0
  29. package/dist/indirect-resource.js +109 -0
  30. package/dist/indirect-resource.js.map +1 -0
  31. package/dist/mod.d.ts +25 -0
  32. package/dist/mod.d.ts.map +1 -0
  33. package/dist/mod.js +25 -0
  34. package/dist/mod.js.map +1 -0
  35. package/dist/mongo-query.d.ts +38 -0
  36. package/dist/mongo-query.d.ts.map +1 -0
  37. package/dist/mongo-query.js +88 -0
  38. package/dist/mongo-query.js.map +1 -0
  39. package/dist/permission.d.ts +57 -0
  40. package/dist/permission.d.ts.map +1 -0
  41. package/dist/permission.js +60 -0
  42. package/dist/permission.js.map +1 -0
  43. package/dist/resource.d.ts +48 -0
  44. package/dist/resource.d.ts.map +1 -0
  45. package/dist/resource.js +298 -0
  46. package/dist/resource.js.map +1 -0
  47. package/dist/rules.d.ts +106 -0
  48. package/dist/rules.d.ts.map +1 -0
  49. package/dist/rules.js +183 -0
  50. package/dist/rules.js.map +1 -0
  51. package/dist/sift/core.d.ts +104 -0
  52. package/dist/sift/core.d.ts.map +1 -0
  53. package/dist/sift/core.js +248 -0
  54. package/dist/sift/core.js.map +1 -0
  55. package/dist/sift/index.d.ts +10 -0
  56. package/dist/sift/index.d.ts.map +1 -0
  57. package/dist/sift/index.js +18 -0
  58. package/dist/sift/index.js.map +1 -0
  59. package/dist/sift/operations.d.ts +87 -0
  60. package/dist/sift/operations.d.ts.map +1 -0
  61. package/dist/sift/operations.js +257 -0
  62. package/dist/sift/operations.js.map +1 -0
  63. package/dist/sift/utils.d.ts +12 -0
  64. package/dist/sift/utils.d.ts.map +1 -0
  65. package/dist/sift/utils.js +80 -0
  66. package/dist/sift/utils.js.map +1 -0
  67. package/dist/system.d.ts +113 -0
  68. package/dist/system.d.ts.map +1 -0
  69. package/dist/system.js +712 -0
  70. package/dist/system.js.map +1 -0
  71. package/dist/target.d.ts +18 -0
  72. package/dist/target.d.ts.map +1 -0
  73. package/dist/target.js +41 -0
  74. package/dist/target.js.map +1 -0
  75. package/dist/types.d.ts +345 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +10 -0
  78. package/dist/types.js.map +1 -0
  79. package/indirect-aggregation.ts +216 -0
  80. package/indirect-resource.ts +205 -0
  81. package/mod.ts +81 -0
  82. package/mongo-query.ts +94 -0
  83. package/package.json +58 -0
  84. package/permission.ts +88 -0
  85. package/resource.ts +352 -0
  86. package/rules.ts +241 -0
  87. package/sift/MIT-LICENSE.txt +20 -0
  88. package/sift/core.ts +551 -0
  89. package/sift/index.ts +62 -0
  90. package/sift/operations.ts +449 -0
  91. package/sift/utils.ts +96 -0
  92. package/system.ts +974 -0
  93. package/target.ts +84 -0
  94. package/types.ts +408 -0
@@ -0,0 +1,449 @@
1
+ // Vendored from sift.js (https://github.com/crcn/sift.js) — MIT licensed.
2
+ // `$where` operator removed (security: arbitrary code execution).
3
+
4
+ import {
5
+ BaseOperation,
6
+ containsOperation,
7
+ createQueryOperation,
8
+ createTester,
9
+ EqualsOperation,
10
+ NamedGroupOperation,
11
+ numericalOperation,
12
+ type Operation,
13
+ type Options,
14
+ type Query,
15
+ type QueryOperation,
16
+ type Tester,
17
+ } from "./core.ts";
18
+ import { comparable, isArray, type Key } from "./utils.ts";
19
+
20
+ class $Ne extends BaseOperation<unknown> {
21
+ readonly propop = true;
22
+ // `declare` so no runtime initializer clobbers the value set by init().
23
+ declare private _test: Tester;
24
+ override init(): void {
25
+ this._test = createTester(this.params, this.options.compare);
26
+ }
27
+ override reset(): void {
28
+ super.reset();
29
+ this.keep = true;
30
+ }
31
+ next(item: unknown): void {
32
+ if (this._test(item)) {
33
+ this.done = true;
34
+ this.keep = false;
35
+ }
36
+ }
37
+ }
38
+
39
+ class $ElemMatch extends BaseOperation<Query<unknown>> {
40
+ readonly propop = true;
41
+ declare private _queryOperation: QueryOperation<unknown>;
42
+ override init(): void {
43
+ if (!this.params || typeof this.params !== "object") {
44
+ throw new Error(`Malformed query. $elemMatch must by an object.`);
45
+ }
46
+ this._queryOperation = createQueryOperation(
47
+ this.params,
48
+ this.owneryQuery,
49
+ this.options,
50
+ );
51
+ }
52
+ override reset(): void {
53
+ super.reset();
54
+ this._queryOperation.reset();
55
+ }
56
+ next(item: unknown): void {
57
+ if (isArray(item)) {
58
+ for (let i = 0, { length } = item; i < length; i++) {
59
+ // Reset between elements: a passing element must satisfy ALL child ops on its own.
60
+ this._queryOperation.reset();
61
+
62
+ const child = item[i];
63
+ this._queryOperation.next(child, i, item, false);
64
+ this.keep = this.keep || this._queryOperation.keep;
65
+ }
66
+ this.done = true;
67
+ } else {
68
+ this.done = false;
69
+ this.keep = false;
70
+ }
71
+ }
72
+ }
73
+
74
+ class $Not extends BaseOperation<Query<unknown>> {
75
+ readonly propop = true;
76
+ declare private _queryOperation: QueryOperation<unknown>;
77
+ override init(): void {
78
+ this._queryOperation = createQueryOperation(
79
+ this.params,
80
+ this.owneryQuery,
81
+ this.options,
82
+ );
83
+ }
84
+ override reset(): void {
85
+ super.reset();
86
+ this._queryOperation.reset();
87
+ }
88
+ next(item: unknown, key?: Key, owner?: unknown, root?: boolean): void {
89
+ this._queryOperation.next(item, key, owner, root);
90
+ this.done = this._queryOperation.done;
91
+ this.keep = !this._queryOperation.keep;
92
+ }
93
+ }
94
+
95
+ export class $Size extends BaseOperation<number> {
96
+ readonly propop = true;
97
+ next(item: unknown): void {
98
+ if (isArray(item) && item.length === this.params) {
99
+ this.done = true;
100
+ this.keep = true;
101
+ }
102
+ }
103
+ }
104
+
105
+ const assertGroupNotEmpty = (values: unknown[]): void => {
106
+ if (values.length === 0) {
107
+ throw new Error(`$and/$or/$nor must be a nonempty array`);
108
+ }
109
+ };
110
+
111
+ class $Or extends BaseOperation<Query<unknown>[]> {
112
+ readonly propop = false;
113
+ declare private _ops: Operation<unknown>[];
114
+ override init(): void {
115
+ assertGroupNotEmpty(this.params);
116
+ this._ops = this.params.map((op) =>
117
+ createQueryOperation(op, null, this.options),
118
+ );
119
+ }
120
+ override reset(): void {
121
+ this.done = false;
122
+ this.keep = false;
123
+ for (let i = 0, { length } = this._ops; i < length; i++) {
124
+ this._ops[i].reset();
125
+ }
126
+ }
127
+ next(item: unknown, key?: Key, owner?: unknown): void {
128
+ let done = false;
129
+ let success = false;
130
+ for (let i = 0, { length } = this._ops; i < length; i++) {
131
+ const op = this._ops[i];
132
+ op.next(item, key, owner);
133
+ if (op.keep) {
134
+ done = true;
135
+ success = op.keep;
136
+ break;
137
+ }
138
+ }
139
+
140
+ this.keep = success;
141
+ this.done = done;
142
+ }
143
+ }
144
+
145
+ class $Nor extends $Or {
146
+ override readonly propop = false;
147
+ override next(item: unknown, key?: Key, owner?: unknown): void {
148
+ super.next(item, key, owner);
149
+ this.keep = !this.keep;
150
+ }
151
+ }
152
+
153
+ class $In extends BaseOperation<unknown> {
154
+ readonly propop = true;
155
+ declare private _testers: Tester[];
156
+ override init(): void {
157
+ const params = Array.isArray(this.params) ? this.params : [this.params];
158
+ this._testers = params.map((value: unknown) => {
159
+ if (containsOperation(value, this.options)) {
160
+ throw new Error(
161
+ `cannot nest $ under ${(this.name ?? "").toLowerCase()}`,
162
+ );
163
+ }
164
+ return createTester(value, this.options.compare);
165
+ });
166
+ }
167
+ next(item: unknown, _key?: Key, _owner?: unknown): void {
168
+ let done = false;
169
+ let success = false;
170
+ for (let i = 0, { length } = this._testers; i < length; i++) {
171
+ const test = this._testers[i];
172
+ if (test(item)) {
173
+ done = true;
174
+ success = true;
175
+ break;
176
+ }
177
+ }
178
+
179
+ this.keep = success;
180
+ this.done = done;
181
+ }
182
+ }
183
+
184
+ class $Nin extends BaseOperation<unknown> {
185
+ readonly propop = true;
186
+ private _in: $In;
187
+ constructor(
188
+ params: unknown,
189
+ ownerQuery: unknown,
190
+ options: Options,
191
+ name: string,
192
+ ) {
193
+ super(params, ownerQuery, options, name);
194
+ this._in = new $In(params, ownerQuery, options, name);
195
+ }
196
+ next(item: unknown, key?: Key, owner?: unknown, root?: boolean): void {
197
+ this._in.next(item, key, owner);
198
+
199
+ if (isArray(owner) && !root) {
200
+ if (this._in.keep) {
201
+ this.keep = false;
202
+ this.done = true;
203
+ } else if (key == owner.length - 1) {
204
+ this.keep = true;
205
+ this.done = true;
206
+ }
207
+ } else {
208
+ this.keep = !this._in.keep;
209
+ this.done = true;
210
+ }
211
+ }
212
+ override reset(): void {
213
+ super.reset();
214
+ this._in.reset();
215
+ }
216
+ }
217
+
218
+ class $Exists extends BaseOperation<boolean> {
219
+ readonly propop = true;
220
+ next(
221
+ _item: unknown,
222
+ key?: Key,
223
+ owner?: unknown,
224
+ _root?: boolean,
225
+ leaf?: boolean,
226
+ ): void {
227
+ if (!leaf) {
228
+ this.done = true;
229
+ this.keep = !this.params;
230
+ } else if (
231
+ owner != null &&
232
+ Object.prototype.hasOwnProperty.call(owner, key as PropertyKey) ===
233
+ this.params
234
+ ) {
235
+ this.done = true;
236
+ this.keep = true;
237
+ }
238
+ }
239
+ }
240
+
241
+ class $And extends NamedGroupOperation {
242
+ readonly propop = false;
243
+ constructor(
244
+ params: Query<unknown>[],
245
+ owneryQuery: Query<unknown>,
246
+ options: Options,
247
+ name: string,
248
+ ) {
249
+ super(
250
+ params,
251
+ owneryQuery,
252
+ options,
253
+ params.map((query) => createQueryOperation(query, owneryQuery, options)),
254
+ name,
255
+ );
256
+
257
+ assertGroupNotEmpty(params);
258
+ }
259
+ next(item: unknown, key?: Key, owner?: unknown, root?: boolean): void {
260
+ this.childrenNext(item, key, owner, root ?? false);
261
+ }
262
+ }
263
+
264
+ class $All extends NamedGroupOperation {
265
+ readonly propop = true;
266
+ constructor(
267
+ params: Query<unknown>[],
268
+ owneryQuery: Query<unknown>,
269
+ options: Options,
270
+ name: string,
271
+ ) {
272
+ super(
273
+ params,
274
+ owneryQuery,
275
+ options,
276
+ params.map((query) => createQueryOperation(query, owneryQuery, options)),
277
+ name,
278
+ );
279
+ }
280
+ next(item: unknown, key?: Key, owner?: unknown, root?: boolean): void {
281
+ this.childrenNext(item, key, owner, root ?? false);
282
+ }
283
+ }
284
+
285
+ export const $eq = (
286
+ params: unknown,
287
+ owneryQuery: Query<unknown>,
288
+ options: Options,
289
+ ): EqualsOperation<unknown> =>
290
+ new EqualsOperation(params, owneryQuery, options);
291
+
292
+ export const $ne = (
293
+ params: unknown,
294
+ owneryQuery: Query<unknown>,
295
+ options: Options,
296
+ name: string,
297
+ ): $Ne => new $Ne(params, owneryQuery, options, name);
298
+
299
+ export const $or = (
300
+ params: Query<unknown>[],
301
+ owneryQuery: Query<unknown>,
302
+ options: Options,
303
+ name: string,
304
+ ): $Or => new $Or(params, owneryQuery, options, name);
305
+
306
+ export const $nor = (
307
+ params: Query<unknown>[],
308
+ owneryQuery: Query<unknown>,
309
+ options: Options,
310
+ name: string,
311
+ ): $Nor => new $Nor(params, owneryQuery, options, name);
312
+
313
+ export const $elemMatch = (
314
+ params: unknown,
315
+ owneryQuery: Query<unknown>,
316
+ options: Options,
317
+ name: string,
318
+ ): $ElemMatch =>
319
+ new $ElemMatch(params as Query<unknown>, owneryQuery, options, name);
320
+
321
+ export const $nin = (
322
+ params: unknown,
323
+ owneryQuery: Query<unknown>,
324
+ options: Options,
325
+ name: string,
326
+ ): $Nin => new $Nin(params, owneryQuery, options, name);
327
+
328
+ export const $in = (
329
+ params: unknown,
330
+ owneryQuery: Query<unknown>,
331
+ options: Options,
332
+ name: string,
333
+ ): $In => new $In(params, owneryQuery, options, name);
334
+
335
+ export const $lt = numericalOperation(
336
+ (params) => (b) => b != null && (b as number) < (params as number),
337
+ );
338
+ export const $lte = numericalOperation(
339
+ (params) => (b) =>
340
+ b === params || (b != null && (b as number) <= (params as number)),
341
+ );
342
+ export const $gt = numericalOperation(
343
+ (params) => (b) => b != null && (b as number) > (params as number),
344
+ );
345
+ export const $gte = numericalOperation(
346
+ (params) => (b) =>
347
+ b === params || (b != null && (b as number) >= (params as number)),
348
+ );
349
+
350
+ export const $mod = (
351
+ modParams: number[],
352
+ owneryQuery: Query<unknown>,
353
+ options: Options,
354
+ ): EqualsOperation<unknown> => {
355
+ const [mod, equalsValue] = modParams;
356
+ return new EqualsOperation(
357
+ // EqualsOperation accepts a Tester; cast retains that contract.
358
+ ((b: unknown) =>
359
+ (comparable(b) as number) % mod === equalsValue) as unknown,
360
+ owneryQuery,
361
+ options,
362
+ );
363
+ };
364
+
365
+ export const $exists = (
366
+ params: boolean,
367
+ owneryQuery: Query<unknown>,
368
+ options: Options,
369
+ name: string,
370
+ ): $Exists => new $Exists(params, owneryQuery, options, name);
371
+
372
+ export const $regex = (
373
+ pattern: string,
374
+ owneryQuery: Query<unknown>,
375
+ options: Options,
376
+ ): EqualsOperation<RegExp> =>
377
+ new EqualsOperation(
378
+ new RegExp(pattern, (owneryQuery as { $options?: string }).$options),
379
+ owneryQuery,
380
+ options,
381
+ );
382
+
383
+ export const $not = (
384
+ params: unknown,
385
+ owneryQuery: Query<unknown>,
386
+ options: Options,
387
+ name: string,
388
+ ): $Not => new $Not(params as Query<unknown>, owneryQuery, options, name);
389
+
390
+ const typeAliases: Record<string, (v: unknown) => boolean> = {
391
+ number: (v) => typeof v === "number",
392
+ string: (v) => typeof v === "string",
393
+ bool: (v) => typeof v === "boolean",
394
+ array: (v) => Array.isArray(v),
395
+ null: (v) => v === null,
396
+ timestamp: (v) => v instanceof Date,
397
+ };
398
+
399
+ export const $type = (
400
+ // deno-lint-ignore ban-types
401
+ clazz: Function | string,
402
+ owneryQuery: Query<unknown>,
403
+ options: Options,
404
+ ): EqualsOperation<unknown> =>
405
+ new EqualsOperation(
406
+ // EqualsOperation accepts a Tester; cast retains that contract.
407
+ ((b: unknown) => {
408
+ if (typeof clazz === "string") {
409
+ const alias = typeAliases[clazz];
410
+ if (!alias) {
411
+ throw new Error(`Type alias does not exist`);
412
+ }
413
+ return alias(b);
414
+ }
415
+ return b != null
416
+ ? b instanceof (clazz as new (...args: unknown[]) => unknown) ||
417
+ (b as { constructor?: unknown }).constructor === clazz
418
+ : false;
419
+ }) as unknown,
420
+ owneryQuery,
421
+ options,
422
+ );
423
+
424
+ export const $and = (
425
+ params: Query<unknown>[],
426
+ ownerQuery: Query<unknown>,
427
+ options: Options,
428
+ name: string,
429
+ ): $And => new $And(params, ownerQuery, options, name);
430
+
431
+ export const $all = (
432
+ params: Query<unknown>[],
433
+ ownerQuery: Query<unknown>,
434
+ options: Options,
435
+ name: string,
436
+ ): $All => new $All(params, ownerQuery, options, name);
437
+
438
+ export const $size = (
439
+ params: number,
440
+ ownerQuery: Query<unknown>,
441
+ options: Options,
442
+ ): $Size => new $Size(params, ownerQuery, options, "$size");
443
+
444
+ export const $options = (): null => null;
445
+
446
+ // $where intentionally removed from this fork.
447
+ // Upstream evaluated arbitrary JS via `new Function()`. Forbidden in a permission
448
+ // system whose grants come from the DB — code-execution risk.
449
+ // `validateSpec()` in mongo-query.ts is the second barrier.
package/sift/utils.ts ADDED
@@ -0,0 +1,96 @@
1
+ // Vendored from sift.js (https://github.com/crcn/sift.js) — MIT licensed.
2
+ // `$where` operator removed (security: arbitrary code execution).
3
+
4
+ export type Key = string | number;
5
+ export type Comparator = (a: unknown, b: unknown) => boolean;
6
+
7
+ export const typeChecker = <TType>(type: string) => {
8
+ const typeString = "[object " + type + "]";
9
+ return function (value: unknown): value is TType {
10
+ return getClassName(value) === typeString;
11
+ };
12
+ };
13
+
14
+ const getClassName = (value: unknown): string =>
15
+ Object.prototype.toString.call(value);
16
+
17
+ export const comparable = (value: unknown): unknown => {
18
+ if (value instanceof Date) {
19
+ return value.getTime();
20
+ } else if (isArray(value)) {
21
+ return value.map(comparable);
22
+ } else if (
23
+ value != null &&
24
+ typeof (value as { toJSON?: unknown }).toJSON === "function"
25
+ ) {
26
+ return (value as { toJSON: () => unknown }).toJSON();
27
+ }
28
+
29
+ return value;
30
+ };
31
+
32
+ export const coercePotentiallyNull = (value: unknown): unknown =>
33
+ value == null ? null : value;
34
+
35
+ export const isArray = typeChecker<unknown[]>("Array");
36
+ // deno-lint-ignore ban-types
37
+ export const isObject = typeChecker<Object>("Object");
38
+ // deno-lint-ignore ban-types
39
+ export const isFunction = typeChecker<Function>("Function");
40
+
41
+ export const isProperty = (item: object, key: PropertyKey): boolean => {
42
+ return (
43
+ Object.prototype.hasOwnProperty.call(item, key) &&
44
+ !isFunction((item as Record<PropertyKey, unknown>)[key as string])
45
+ );
46
+ };
47
+
48
+ export const isVanillaObject = (value: unknown): boolean => {
49
+ if (value == null || typeof value !== "object") return false;
50
+ const ctor = (value as { constructor?: unknown }).constructor as
51
+ | { toString(): string }
52
+ | undefined;
53
+ if (!ctor) return false;
54
+ return (
55
+ (ctor === Object ||
56
+ ctor === Array ||
57
+ ctor.toString() === "function Object() { [native code] }" ||
58
+ ctor.toString() === "function Array() { [native code] }") &&
59
+ !(value as { toJSON?: unknown }).toJSON
60
+ );
61
+ };
62
+
63
+ export const equals = (a: unknown, b: unknown): boolean => {
64
+ if (a == null && a == b) {
65
+ return true;
66
+ }
67
+ if (a === b) {
68
+ return true;
69
+ }
70
+
71
+ if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) {
72
+ return false;
73
+ }
74
+
75
+ if (isArray(a)) {
76
+ const arrB = b as unknown[];
77
+ if (a.length !== arrB.length) {
78
+ return false;
79
+ }
80
+ for (let i = 0, { length } = a; i < length; i++) {
81
+ if (!equals(a[i], arrB[i])) return false;
82
+ }
83
+ return true;
84
+ } else if (isObject(a)) {
85
+ const objA = a as Record<string, unknown>;
86
+ const objB = b as Record<string, unknown>;
87
+ if (Object.keys(objA).length !== Object.keys(objB).length) {
88
+ return false;
89
+ }
90
+ for (const key in objA) {
91
+ if (!equals(objA[key], objB[key])) return false;
92
+ }
93
+ return true;
94
+ }
95
+ return false;
96
+ };