@memberjunction/global 6.2.0-edge.4 → 6.2.0-edge.6

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 (75) hide show
  1. package/dist/ClassFactory.d.ts +16 -3
  2. package/dist/ClassFactory.d.ts.map +1 -1
  3. package/dist/ClassFactory.js +112 -48
  4. package/dist/ClassFactory.js.map +1 -1
  5. package/dist/DeepDiff.d.ts +2 -0
  6. package/dist/DeepDiff.d.ts.map +1 -1
  7. package/dist/DeepDiff.js +40 -38
  8. package/dist/DeepDiff.js.map +1 -1
  9. package/dist/KeyedSerialTaskQueue.d.ts +18 -1
  10. package/dist/KeyedSerialTaskQueue.d.ts.map +1 -1
  11. package/dist/KeyedSerialTaskQueue.js +24 -3
  12. package/dist/KeyedSerialTaskQueue.js.map +1 -1
  13. package/dist/ObjectCache.d.ts +1 -0
  14. package/dist/ObjectCache.d.ts.map +1 -1
  15. package/dist/ObjectCache.js +14 -22
  16. package/dist/ObjectCache.js.map +1 -1
  17. package/dist/SQLExpressionValidator.d.ts +70 -41
  18. package/dist/SQLExpressionValidator.d.ts.map +1 -1
  19. package/dist/SQLExpressionValidator.js +183 -134
  20. package/dist/SQLExpressionValidator.js.map +1 -1
  21. package/dist/SQLScreeningScanner.d.ts +117 -0
  22. package/dist/SQLScreeningScanner.d.ts.map +1 -0
  23. package/dist/SQLScreeningScanner.js +475 -0
  24. package/dist/SQLScreeningScanner.js.map +1 -0
  25. package/dist/__tests__/FieldSecurityEgressCompliance.test.d.ts +2 -0
  26. package/dist/__tests__/FieldSecurityEgressCompliance.test.d.ts.map +1 -0
  27. package/dist/__tests__/FieldSecurityEgressCompliance.test.js +228 -0
  28. package/dist/__tests__/FieldSecurityEgressCompliance.test.js.map +1 -0
  29. package/dist/__tests__/KeyedSerialTaskQueue.test.js +92 -0
  30. package/dist/__tests__/KeyedSerialTaskQueue.test.js.map +1 -1
  31. package/dist/__tests__/ProofOfPossession.test.d.ts +2 -0
  32. package/dist/__tests__/ProofOfPossession.test.d.ts.map +1 -0
  33. package/dist/__tests__/ProofOfPossession.test.js +123 -0
  34. package/dist/__tests__/ProofOfPossession.test.js.map +1 -0
  35. package/dist/__tests__/SQLExpressionValidator.dialect.test.d.ts +2 -0
  36. package/dist/__tests__/SQLExpressionValidator.dialect.test.d.ts.map +1 -0
  37. package/dist/__tests__/SQLExpressionValidator.dialect.test.js +183 -0
  38. package/dist/__tests__/SQLExpressionValidator.dialect.test.js.map +1 -0
  39. package/dist/__tests__/SQLExpressionValidator.security.test.js +176 -2
  40. package/dist/__tests__/SQLExpressionValidator.security.test.js.map +1 -1
  41. package/dist/__tests__/SQLScreeningScanner.test.d.ts +2 -0
  42. package/dist/__tests__/SQLScreeningScanner.test.d.ts.map +1 -0
  43. package/dist/__tests__/SQLScreeningScanner.test.js +172 -0
  44. package/dist/__tests__/SQLScreeningScanner.test.js.map +1 -0
  45. package/dist/__tests__/SQLScreeningScanner.tokens.test.d.ts +2 -0
  46. package/dist/__tests__/SQLScreeningScanner.tokens.test.d.ts.map +1 -0
  47. package/dist/__tests__/SQLScreeningScanner.tokens.test.js +82 -0
  48. package/dist/__tests__/SQLScreeningScanner.tokens.test.js.map +1 -0
  49. package/dist/__tests__/UUIDCompliance.test.js +4 -4
  50. package/dist/__tests__/UUIDCompliance.test.js.map +1 -1
  51. package/dist/__tests__/UserScopedCacheKey.test.d.ts +2 -0
  52. package/dist/__tests__/UserScopedCacheKey.test.d.ts.map +1 -0
  53. package/dist/__tests__/UserScopedCacheKey.test.js +25 -0
  54. package/dist/__tests__/UserScopedCacheKey.test.js.map +1 -0
  55. package/dist/__tests__/util.valuesEqual.test.d.ts +2 -0
  56. package/dist/__tests__/util.valuesEqual.test.d.ts.map +1 -0
  57. package/dist/__tests__/util.valuesEqual.test.js +37 -0
  58. package/dist/__tests__/util.valuesEqual.test.js.map +1 -0
  59. package/dist/index.d.ts +3 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +3 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/util/ProofOfPossession.d.ts +68 -0
  64. package/dist/util/ProofOfPossession.d.ts.map +1 -0
  65. package/dist/util/ProofOfPossession.js +163 -0
  66. package/dist/util/ProofOfPossession.js.map +1 -0
  67. package/dist/util/UserScopedCacheKey.d.ts +42 -0
  68. package/dist/util/UserScopedCacheKey.d.ts.map +1 -0
  69. package/dist/util/UserScopedCacheKey.js +49 -0
  70. package/dist/util/UserScopedCacheKey.js.map +1 -0
  71. package/dist/util.d.ts +25 -0
  72. package/dist/util.d.ts.map +1 -1
  73. package/dist/util.js +38 -0
  74. package/dist/util.js.map +1 -1
  75. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"KeyedSerialTaskQueue.d.ts","sourceRoot":"","sources":["../src/KeyedSerialTaskQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IAClC,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAoB;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,KAAK,CAAwC;IACrD,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAA4B;IAC5C,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAyC;gBAEtD,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE;IAIvE;;;;;;;;;;;;;;OAcG;IACI,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAgCnJ,uCAAuC;IAChC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAInJ;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAQpD,qCAAqC;IACxB,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC;CAGvD"}
1
+ {"version":3,"file":"KeyedSerialTaskQueue.d.ts","sourceRoot":"","sources":["../src/KeyedSerialTaskQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IAClC,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAoB;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,KAAK,CAAwC;IACrD,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAA4B;IAC5C,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAyC;gBAEtD,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE;IAIvE;;;;;;;;;;;;;;;;;;;;OAoBG;IACI,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAqC1K;;;;;;;OAOG;IACI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C,uCAAuC;IAChC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAI1K;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAQpD,qCAAqC;IACxB,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC;CAGvD"}
@@ -41,13 +41,23 @@ export class KeyedSerialTaskQueue {
41
41
  * @param opts.after Optional dependency key — the task waits for this key's latest task to settle
42
42
  * before running, in addition to waiting for its own key's prior tasks. Use this to honour
43
43
  * cross-instance ordering constraints like self-referencing foreign keys (e.g. a child step's
44
- * INSERT must land after its parent step's INSERT).
44
+ * INSERT must land after its parent step's INSERT). This is the in-queue form of the wait; when the
45
+ * dependent write lives in a *different* queue, use {@link Settled} instead.
46
+ * @param opts.ready Optional promise the task also waits for. Use it when the task depends on work
47
+ * outside this queue — typically another queue's {@link Settled}, so a child row waits for a parent
48
+ * row that a different queue is writing. The task is queued (and counted by {@link Flush}) at once;
49
+ * only its start is held back, so the caller never waits. A rejected `ready` still runs the task,
50
+ * the same as a failed prior task: the task's own outcome is what gets tallied.
45
51
  */
46
52
  Enqueue(key, task, opts) {
47
53
  const prior = this.tails.get(key) ?? Promise.resolve();
48
54
  const dependency = opts?.after ? (this.tails.get(opts.after) ?? Promise.resolve()) : Promise.resolve();
49
- // Run the task exactly once, after `prior` AND `dependency` settle, collapsing to a never-rejecting outcome.
50
- const gate = Promise.all([prior, dependency]);
55
+ // Settle `ready` to a never-rejecting promise first. `prior` and `dependency` are tails, which never
56
+ // reject; a rejecting `ready` would make Promise.all settle early and start the task before its
57
+ // key's prior task finished, breaking the per-key order this class exists to guarantee.
58
+ const ready = opts?.ready ? opts.ready.then(() => undefined, () => undefined) : Promise.resolve();
59
+ // Run the task exactly once, after `prior`, `dependency` AND `ready` settle, collapsing to a never-rejecting outcome.
60
+ const gate = Promise.all([prior, dependency, ready]);
51
61
  const settled = gate.then(() => task(), () => task()).then((value) => ({ value: value, ok: opts?.isOk ? !!opts.isOk(value) : true, rejected: false, err: undefined }), (err) => ({ value: undefined, ok: false, rejected: true, err }));
52
62
  // Never-rejecting tail: tally failures + route to onError. Serves as the per-key chain link.
53
63
  const tail = settled.then((o) => {
@@ -67,6 +77,17 @@ export class KeyedSerialTaskQueue {
67
77
  this.inFlight.add(tracked);
68
78
  return settled.then((o) => (o.ok ? o.value : undefined));
69
79
  }
80
+ /**
81
+ * Resolves once every task enqueued so far for `key` has settled — at once when the key has no
82
+ * pending task. The cross-queue form of `Enqueue`'s `after` option: `after` can only see keys in this
83
+ * instance, so a caller that must order a write in another queue behind this key awaits this instead.
84
+ *
85
+ * A pure read: it never rejects (a failed task is already tallied and routed to `onError`), and it
86
+ * neither enqueues anything nor delays later tasks. Tasks enqueued after the call are not awaited.
87
+ */
88
+ Settled(key) {
89
+ return this.tails.get(key) ?? Promise.resolve();
90
+ }
70
91
  /** @deprecated Use {@link Enqueue}. */
71
92
  enqueue(key, task, opts) {
72
93
  return this.Enqueue(key, task, opts);
@@ -1 +1 @@
1
- {"version":3,"file":"KeyedSerialTaskQueue.js","sourceRoot":"","sources":["../src/KeyedSerialTaskQueue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"KeyedSerialTaskQueue.js","sourceRoot":"","sources":["../src/KeyedSerialTaskQueue.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@ export declare class ObjectCacheEntry<T = any> {
12
12
  */
13
13
  export declare class ObjectCache {
14
14
  private _entries;
15
+ private normalizeKey;
15
16
  /**
16
17
  * Remove all entries from the object cache
17
18
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectCache.d.ts","sourceRoot":"","sources":["../src/ObjectCache.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAgB,CAAC,CAAC,GAAG,GAAG;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,CAAC,CAAA;gBAEI,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;CAItC;AAED;;;GAGG;AACH,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAA0B;IAE1C;;OAEG;IACI,KAAK;IAIZ;;;;OAIG;IACI,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAYpC;;;;OAIG;IACI,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAKxC;;;OAGG;IACI,MAAM,CAAC,GAAG,EAAE,MAAM;IASzB;;;;OAIG;IACI,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;CAQxC"}
1
+ {"version":3,"file":"ObjectCache.d.ts","sourceRoot":"","sources":["../src/ObjectCache.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAgB,CAAC,CAAC,GAAG,GAAG;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,CAAC,CAAA;gBAEI,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;CAItC;AAED;;;GAGG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAuC;IAEvD,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACI,KAAK;IAIZ;;;;OAIG;IACI,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAQpC;;;;OAIG;IACI,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAKxC;;;OAGG;IACI,MAAM,CAAC,GAAG,EAAE,MAAM;IAIzB;;;;OAIG;IACI,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;CAOxC"}
@@ -13,14 +13,16 @@ export class ObjectCacheEntry {
13
13
  */
14
14
  export class ObjectCache {
15
15
  constructor() {
16
- // our internal array is an array of ObjectCacheEntry objects
17
- this._entries = []; //starts off empty
16
+ this._entries = new Map();
17
+ }
18
+ normalizeKey(key) {
19
+ return key.trim().toLowerCase();
18
20
  }
19
21
  /**
20
22
  * Remove all entries from the object cache
21
23
  */
22
24
  Clear() {
23
- this._entries = []; // set to a blank
25
+ this._entries.clear();
24
26
  }
25
27
  /**
26
28
  * Add a new object to the cache. If the key already exists, an exception is thrown
@@ -28,15 +30,11 @@ export class ObjectCache {
28
30
  * @param object
29
31
  */
30
32
  Add(key, object) {
31
- // make sure that if we have the key already we throw an exception
32
- const idx = this._entries.findIndex((i) => i.key.trim().toLowerCase() === key.trim().toLowerCase());
33
- if (idx >= 0) {
33
+ const normalizedKey = this.normalizeKey(key);
34
+ if (this._entries.has(normalizedKey)) {
34
35
  throw new Error(`An object with the key ${key} already exists in the cache. Remove it first before adding a new one.`);
35
36
  }
36
- else {
37
- const entry = new ObjectCacheEntry(key, object);
38
- this._entries.push(entry);
39
- }
37
+ this._entries.set(normalizedKey, new ObjectCacheEntry(key, object));
40
38
  }
41
39
  /**
42
40
  * Replace an existing object in the cache with a new one. If the key does not exist, the new object is added
@@ -44,20 +42,15 @@ export class ObjectCache {
44
42
  * @param object
45
43
  */
46
44
  Replace(key, object) {
47
- this.Remove(key); // remove the existing one if it exists
48
- this.Add(key, object); // add the new one
45
+ const normalizedKey = this.normalizeKey(key);
46
+ this._entries.set(normalizedKey, new ObjectCacheEntry(key, object));
49
47
  }
50
48
  /**
51
49
  * Remove an object from the cache based on the key
52
50
  * @param key
53
51
  */
54
52
  Remove(key) {
55
- // first, see if we get a match for the index
56
- const idx = this._entries.findIndex((i) => i.key.trim().toLowerCase() === key.trim().toLowerCase());
57
- if (idx >= 0) {
58
- // now remove it
59
- this._entries.splice(idx, 1);
60
- }
53
+ this._entries.delete(this.normalizeKey(key));
61
54
  }
62
55
  /**
63
56
  * Returns an object from the cache based on the key and only if it matches the type provided in the generic
@@ -65,10 +58,9 @@ export class ObjectCache {
65
58
  * @returns
66
59
  */
67
60
  Find(key) {
68
- // first, see if we get a match for the index
69
- const idx = this._entries.findIndex((i) => i.key.trim().toLowerCase() === key.trim().toLowerCase());
70
- if (idx >= 0) {
71
- return this._entries[idx].object;
61
+ const entry = this._entries.get(this.normalizeKey(key));
62
+ if (entry !== undefined) {
63
+ return entry.object;
72
64
  }
73
65
  return null;
74
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectCache.js","sourceRoot":"","sources":["../src/ObjectCache.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAIzB,YAAa,GAAW,EAAE,MAAS;QAC/B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACxB,CAAC;CACJ;AAED;;;GAGG;AACH,MAAM,OAAO,WAAW;IAAxB;QACI,6DAA6D;QACrD,aAAQ,GAAuB,EAAE,CAAC,CAAC,kBAAkB;IA8DjE,CAAC;IA5DG;;OAEG;IACI,KAAK;QACR,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,iBAAiB;IACzC,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAI,GAAW,EAAE,MAAS;QAChC,kEAAkE;QAClE,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;QACrH,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,wEAAwE,CAAC,CAAC;QAC3H,CAAC;aACI,CAAC;YACF,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAI,GAAG,EAAE,MAAM,CAAC,CAAC;YACnD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,OAAO,CAAI,GAAW,EAAE,MAAS;QACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,uCAAuC;QACzD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,kBAAkB;IAC7C,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,GAAW;QACrB,6CAA6C;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;QACrH,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACX,gBAAgB;YAChB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACjC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,IAAI,CAAI,GAAW;QACtB,6CAA6C;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;QACrH,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACX,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAW,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
1
+ {"version":3,"file":"ObjectCache.js","sourceRoot":"","sources":["../src/ObjectCache.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAIzB,YAAa,GAAW,EAAE,MAAS;QAC/B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACxB,CAAC;CACJ;AAED;;;GAGG;AACH,MAAM,OAAO,WAAW;IAAxB;QACY,aAAQ,GAAG,IAAI,GAAG,EAA4B,CAAC;IAwD3D,CAAC;IAtDW,YAAY,CAAC,GAAW;QAC5B,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;IAED;;OAEG;IACI,KAAK;QACR,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAI,GAAW,EAAE,MAAS;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,wEAAwE,CAAC,CAAC;QAC3H,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,gBAAgB,CAAI,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED;;;;OAIG;IACI,OAAO,CAAI,GAAW,EAAE,MAAS;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,gBAAgB,CAAI,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,GAAW;QACrB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,CAAC;IAED;;;;OAIG;IACI,IAAI,CAAI,GAAW;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC,MAAW,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
@@ -10,6 +10,7 @@
10
10
  * @module @memberjunction/global/SQLExpressionValidator
11
11
  */
12
12
  import { BaseSingleton } from './BaseSingleton.js';
13
+ import { SQLScreeningDialect } from './SQLScreeningScanner.js';
13
14
  /**
14
15
  * Dangerous SQL keywords that are never allowed in user-provided expressions
15
16
  */
@@ -26,47 +27,68 @@ export declare const FULL_QUERY_ALLOWED_KEYWORDS: readonly ["EXISTS", "ANY", "AL
26
27
  * a schema-enumeration and credential-exfiltration primitive
27
28
  * (e.g. `SELECT name, password_hash FROM sys.sql_logins`,
28
29
  * `SELECT * FROM INFORMATION_SCHEMA.COLUMNS`, `SELECT * FROM pg_catalog.pg_authid`).
29
- * String literals are stripped before this check runs, so a literal value like `'sys.x'` is safe.
30
+ * The check reads {@link SQLScreeningScan.CodeWithIdentifiers}: string literals are removed, so a
31
+ * literal value like `'sys.x'` is safe, and quoted names are unwrapped, so `[sys].[x]` is caught.
30
32
  */
31
33
  export declare const BLOCKED_SYSTEM_OBJECT_PATTERNS: RegExp[];
32
34
  /**
33
- * Removes SQL string literals from a clause or expression so that a keyword denylist can be
34
- * applied to the *code* portion without tripping over keywords that appear inside quoted data
35
- * (e.g. `Comments LIKE '%--%'`).
35
+ * Replaces each SQL string literal with one space so that a keyword denylist can be applied to the
36
+ * rest without tripping over keywords inside quoted data (e.g. `Comments LIKE '%--%'`). Quoted
37
+ * identifiers and comments are left as written.
36
38
  *
37
- * 🚨 SECURITY — this is the single implementation of literal-stripping for MJ's SQL screens, and
38
- * it MUST stay byte-for-byte consistent with how the database parses literals. If the stripper
39
- * removes a span the database does NOT treat as a literal, everything hidden inside that span
40
- * bypasses the denylist while the database still executes it.
41
- *
42
- * An earlier version of this logic (duplicated in two places, which is how it survived) honored
43
- * **backslash escaping** — `/(['"])(?:(?=(\\?))\2[\s\S])*?\1/g`. SQL Server and PostgreSQL do not
44
- * treat `\` as an escape character, so `x = 'a\') ; DROP TABLE Users; --'` was swallowed whole as
45
- * one "literal" and stripped to `x = `, which passed every denylist — while the database closed
46
- * the literal at the real quote and executed the stacked statement.
47
- *
48
- * **Do NOT reintroduce backslash-escape handling here, and do NOT inline a second copy of this
49
- * regex anywhere else — call this function.**
39
+ * 🚨 SECURITY — literals are found by {@link ScanSQLForScreening}, which reads the text the way the
40
+ * database does: a quote inside a T-SQL `[…]` identifier, a comment or a PostgreSQL `$$…$$` string
41
+ * opens no literal, and a backslash escapes a quote only inside a PostgreSQL `E'…'` string. A
42
+ * stripper that removes a span the database does NOT treat as a literal hides everything inside it
43
+ * from the denylist while the database still runs it. Screens should call
44
+ * {@link ScanSQLForScreening} directly, which also reports comments and text it cannot classify.
50
45
  *
51
46
  * @param sql The clause, expression, or query to strip literals from
52
- * @returns The input with every complete string literal removed. An UNTERMINATED literal is left
53
- * in place on purpose: the stray quote and everything after it stay visible to the
54
- * denylist rather than being silently swallowed.
47
+ * @param dialect The dialect that will run the SQL. Without one, the text must read the same way
48
+ * under every dialect the scanner knows.
49
+ * @returns The input with each string literal replaced by a space. The input is returned unchanged
50
+ * when it cannot be classified (for example an unterminated literal) or when the dialects
51
+ * disagree on where its literals are, so a denylist still sees all of it.
55
52
  */
56
- export declare function StripSQLStringLiterals(sql: string): string;
53
+ export declare function StripSQLStringLiterals(sql: string, dialect?: SQLScreeningDialect): string;
57
54
  /**
58
- * Safe SQL functions allowed in expressions, organized by category
55
+ * Safe SQL functions allowed in expressions, organized by category.
56
+ *
57
+ * WHY THIS IS A UNION RATHER THAN A PER-DIALECT TABLE
58
+ * ---------------------------------------------------
59
+ * This is a deny-by-default SAFETY list, not a dialect grammar. Its job is to
60
+ * stop a user-supplied expression from smuggling in something that is not a
61
+ * read-only scalar/aggregate call — it is not, and must not become, a
62
+ * per-platform parser. So it carries the union of every supported dialect's
63
+ * read-only vocabulary, and `checkFunctionNames` consults the flattened union.
64
+ *
65
+ * Keying the buckets off `ResolvePlatformKey` is the alternative, and it costs
66
+ * more than it buys: a PostgreSQL name that reaches a SQL Server tenant is
67
+ * rejected by the SQL Server parser on its own — `DATE_TRUNC(...)` simply never
68
+ * parses there. The union therefore cannot widen the blast radius past what the
69
+ * target server will already accept, whereas a dialect switch here would add a
70
+ * second independent place for the dialect to be resolved, and so a second place
71
+ * for it to be wrong.
72
+ *
73
+ * Before this list held the PostgreSQL names, a perfectly ordinary PG filter
74
+ * such as `DATE_TRUNC('month', CreatedAt)` was rejected outright with
75
+ * "Function 'DATE_TRUNC' is not allowed" on every PostgreSQL tenant.
76
+ *
77
+ * Every name here is read-only by construction. Anything that writes, escalates,
78
+ * or reaches outside the statement stays on {@link DANGEROUS_SQL_KEYWORDS}, which
79
+ * is checked FIRST (step 2 of `validate`) — a name added here can never unblock
80
+ * one that is denied there.
59
81
  */
60
82
  export declare const ALLOWED_SQL_FUNCTIONS: {
61
- readonly aggregates: readonly ["COUNT", "COUNT_BIG", "SUM", "AVG", "MIN", "MAX", "STDEV", "STDEVP", "VAR", "VARP", "STRING_AGG", "CHECKSUM_AGG"];
62
- readonly math: readonly ["ABS", "CEILING", "FLOOR", "ROUND", "POWER", "SQRT", "LOG", "LOG10", "EXP", "SIGN", "RAND"];
63
- readonly string: readonly ["LEN", "LENGTH", "UPPER", "LOWER", "LTRIM", "RTRIM", "TRIM", "LEFT", "RIGHT", "SUBSTRING", "CHARINDEX", "REPLACE", "CONCAT", "STUFF"];
64
- readonly date: readonly ["DATEPART", "DATEDIFF", "DATEADD", "YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND", "GETDATE", "GETUTCDATE", "SYSDATETIME", "EOMONTH"];
65
- readonly conversion: readonly ["CAST", "CONVERT", "TRY_CAST", "TRY_CONVERT", "FORMAT"];
66
- readonly nullHandling: readonly ["ISNULL", "COALESCE", "NULLIF", "IIF"];
83
+ readonly aggregates: readonly ["COUNT", "COUNT_BIG", "SUM", "AVG", "MIN", "MAX", "STDEV", "STDEVP", "VAR", "VARP", "STRING_AGG", "CHECKSUM_AGG", "ARRAY_AGG", "JSONB_AGG", "JSON_AGG", "BOOL_AND", "BOOL_OR", "EVERY", "PERCENTILE_CONT", "PERCENTILE_DISC", "MODE"];
84
+ readonly math: readonly ["ABS", "CEILING", "FLOOR", "ROUND", "POWER", "SQRT", "LOG", "LOG10", "EXP", "SIGN", "RAND", "CEIL", "RANDOM", "TRUNC", "MOD", "DIV", "GREATEST", "LEAST", "WIDTH_BUCKET"];
85
+ readonly string: readonly ["LEN", "LENGTH", "UPPER", "LOWER", "LTRIM", "RTRIM", "TRIM", "LEFT", "RIGHT", "SUBSTRING", "CHARINDEX", "REPLACE", "CONCAT", "STUFF", "POSITION", "STRPOS", "SPLIT_PART", "INITCAP", "LPAD", "RPAD", "REPEAT", "REVERSE", "TRANSLATE", "REGEXP_REPLACE", "REGEXP_MATCHES", "REGEXP_SUBSTR", "TO_CHAR", "MD5", "ASCII", "CHR"];
86
+ readonly date: readonly ["DATEPART", "DATEDIFF", "DATEADD", "YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND", "GETDATE", "GETUTCDATE", "SYSDATETIME", "EOMONTH", "NOW", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "LOCALTIMESTAMP", "DATE_TRUNC", "DATE_PART", "EXTRACT", "AGE", "TO_DATE", "TO_TIMESTAMP", "MAKE_DATE", "MAKE_TIMESTAMP", "JUSTIFY_DAYS"];
87
+ readonly conversion: readonly ["CAST", "CONVERT", "TRY_CAST", "TRY_CONVERT", "FORMAT", "TO_CHAR", "TO_DATE", "TO_NUMBER", "TO_TIMESTAMP"];
88
+ readonly nullHandling: readonly ["ISNULL", "COALESCE", "NULLIF", "IIF", "GREATEST", "LEAST"];
67
89
  readonly conditional: readonly ["CASE", "WHEN", "THEN", "ELSE", "END"];
68
- readonly logical: readonly ["AND", "OR", "NOT", "IS", "NULL", "LIKE", "BETWEEN", "IN"];
69
- readonly ordering: readonly ["ASC", "ASCENDING", "DESC", "DESCENDING", "OVER", "PARTITION", "BY", "ORDER", "ROWS", "RANGE", "UNBOUNDED", "PRECEDING", "FOLLOWING", "CURRENT", "ROW"];
90
+ readonly logical: readonly ["AND", "OR", "NOT", "IS", "NULL", "LIKE", "BETWEEN", "IN", "ILIKE", "SIMILAR", "ANY", "ALL", "SOME", "EXISTS"];
91
+ readonly ordering: readonly ["ASC", "ASCENDING", "DESC", "DESCENDING", "OVER", "PARTITION", "BY", "ORDER", "ROWS", "RANGE", "UNBOUNDED", "PRECEDING", "FOLLOWING", "CURRENT", "ROW", "NULLS", "FIRST", "LAST", "FILTER", "WITHIN", "GROUPING"];
70
92
  };
71
93
  /**
72
94
  * Validation context - affects what's allowed
@@ -101,6 +123,11 @@ export interface SQLValidationOptions {
101
123
  additionalAllowed?: string[];
102
124
  /** Custom blocked keywords to add */
103
125
  additionalBlocked?: string[];
126
+ /**
127
+ * The dialect that will run the SQL, which decides how quotes, identifiers and comments are read.
128
+ * Without one, the SQL must pass under every dialect {@link ScanSQLForScreening} knows.
129
+ */
130
+ Dialect?: SQLScreeningDialect;
104
131
  }
105
132
  /**
106
133
  * Central SQL expression validator for preventing SQL injection.
@@ -141,16 +168,20 @@ export declare class SQLExpressionValidator extends BaseSingleton<SQLExpressionV
141
168
  */
142
169
  validate(expression: string, options: SQLValidationOptions): SQLValidationResult;
143
170
  /**
144
- * Remove string literals to avoid false positives in keyword detection.
171
+ * Runs every check against one dialect's reading of the expression.
145
172
  *
146
- * 🚨 SECURITY: delegates to {@link StripSQLStringLiterals} — read the warning there before
147
- * changing anything about how literals are matched. This must never grow its own regex again.
173
+ * 🚨 SECURITY: the checks read the scanner's projections, never the raw text. Do not add a regex
174
+ * that finds literals, identifiers or comments here; see {@link ScanSQLForScreening}.
148
175
  */
149
- private removeStringLiterals;
176
+ private validateReading;
150
177
  /**
151
178
  * Check for dangerous SQL patterns that indicate injection attempts
152
179
  */
153
180
  private checkDangerousPatterns;
181
+ /**
182
+ * The keywords {@link checkDangerousPatterns} rejects in this context.
183
+ */
184
+ private blockedKeywords;
154
185
  /**
155
186
  * Check that function names are in the allowlist
156
187
  */
@@ -163,12 +194,6 @@ export declare class SQLExpressionValidator extends BaseSingleton<SQLExpressionV
163
194
  * Validate field references exist in entity (lenient mode - just for logging)
164
195
  */
165
196
  private checkFieldReferences;
166
- /**
167
- * Strip SQL comments (single-line -- and multi-line block comments) from a query.
168
- * Used by full_query context to allow agent-generated header comments
169
- * without triggering the comment injection check.
170
- */
171
- private stripSQLComments;
172
197
  /**
173
198
  * Escape special regex characters in a string
174
199
  */
@@ -186,8 +211,12 @@ export declare class SQLExpressionValidator extends BaseSingleton<SQLExpressionV
186
211
  * Validate a full SQL query (SELECT or WITH/CTE statement).
187
212
  * Blocks mutations, dangerous operations, and multi-statement injection.
188
213
  * Allows SELECT, subqueries, set operations, and SQL comments.
214
+ *
215
+ * @param sql The query to validate
216
+ * @param dialect The dialect that will run the query. Without one, the query must pass under
217
+ * every dialect {@link ScanSQLForScreening} knows.
189
218
  */
190
- ValidateFullQuery(sql: string): SQLValidationResult;
219
+ ValidateFullQuery(sql: string, dialect?: SQLScreeningDialect): SQLValidationResult;
191
220
  /** @deprecated Use {@link ValidateFullQuery}. */
192
221
  validateFullQuery(sql: string): SQLValidationResult;
193
222
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SQLExpressionValidator.d.ts","sourceRoot":"","sources":["../src/SQLExpressionValidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,sBAAsB,2gBA0CzB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B,wFAe9B,CAAC;AAEX;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,EAAE,MAAM,EAMlD,CAAC;AAcF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;CA2BxB,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,YAAY,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,OAAO,EAAE,oBAAoB,CAAC;IAE9B,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,wHAAwH;IACxH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,iFAAiF;IACjF,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,qCAAqC;IACrC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,sBAAuB,SAAQ,aAAa,CAAC,sBAAsB,CAAC;IAC/E;;OAEG;;IAKH;;OAEG;IACH,WAAkB,QAAQ,IAAI,sBAAsB,CAEnD;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB;IAoCvF;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkF9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiCzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA4B5B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IAQ9B;;;;OAIG;IACI,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB;IAQ1D,iDAAiD;IAC1C,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB;CAG3D"}
1
+ {"version":3,"file":"SQLExpressionValidator.d.ts","sourceRoot":"","sources":["../src/SQLExpressionValidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAGL,mBAAmB,EAGpB,MAAM,uBAAuB,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,sBAAsB,2gBA0CzB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B,wFAe9B,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,EAAE,MAAM,EAMlD,CAAC;AAQF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAKzF;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;CA2CxB,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,YAAY,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,OAAO,EAAE,oBAAoB,CAAC;IAE9B,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,wHAAwH;IACxH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,iFAAiF;IACjF,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,qCAAqC;IACrC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;;OAGG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,sBAAuB,SAAQ,aAAa,CAAC,sBAAsB,CAAC;IAC/E;;OAEG;;IAKH;;OAEG;IACH,WAAkB,QAAQ,IAAI,sBAAsB,CAEnD;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB;IAmBvF;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IA8BvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA0D9B;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+BzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA4B5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IAQ9B;;;;;;;;OAQG;IACI,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB;IAQzF,iDAAiD;IAC1C,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB;CAG3D"}