@ztimson/utils 0.27.9 → 0.27.11

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/dist/cache.d.ts CHANGED
@@ -38,6 +38,7 @@ export declare class Cache<K extends string | number | symbol, T> {
38
38
  */
39
39
  constructor(key?: keyof T | undefined, options?: CacheOptions);
40
40
  private getKey;
41
+ /** Save item to storage */
41
42
  private save;
42
43
  private clearTimer;
43
44
  private touchLRU;
@@ -48,72 +49,30 @@ export declare class Cache<K extends string | number | symbol, T> {
48
49
  all(expired?: boolean): CachedValue<T>[];
49
50
  /**
50
51
  * Add a new item to the cache. Like set, but finds key automatically
51
- * @param {T} value Item to add to cache
52
- * @param {number | undefined} ttl Override default expiry
53
- * @return {this}
54
52
  */
55
53
  add(value: T, ttl?: any): this;
56
54
  /**
57
55
  * Add several rows to the cache
58
- * @param {T[]} rows Several items that will be cached using the default key
59
- * @param complete Mark cache as complete & reliable, defaults to true
60
- * @return {this}
61
56
  */
62
57
  addAll(rows: T[], complete?: boolean): this;
63
- /**
64
- * Remove all keys from cache
65
- */
58
+ /** Remove all keys */
66
59
  clear(): this;
67
- /**
68
- * Delete an item from the cache
69
- * @param {K} key Item's primary key
70
- */
60
+ /** Delete a cached item */
71
61
  delete(key: K): this;
72
- /**
73
- * Return cache as an array of key-value pairs
74
- * @return {[K, T][]} Key-value pairs array
75
- */
62
+ /** Return entries as array */
76
63
  entries(expired?: boolean): [K, CachedValue<T>][];
77
- /**
78
- * Manually expire a cached item
79
- * @param {K} key Key to expire
80
- */
64
+ /** Manually expire a cached item */
81
65
  expire(key: K): this;
82
- /**
83
- * Find the first cached item to match a filter
84
- * @param {Partial<T>} filter Partial item to match
85
- * @param {Boolean} expired Include expired items, defaults to false
86
- * @returns {T | undefined} Cached item or undefined if nothing matched
87
- */
66
+ /** Find first matching item */
88
67
  find(filter: Partial<T>, expired?: boolean): T | undefined;
89
- /**
90
- * Get item from the cache
91
- * @param {K} key Key to lookup
92
- * @param expired Include expired items
93
- * @return {T} Cached item
94
- */
68
+ /** Get cached item by key */
95
69
  get(key: K, expired?: boolean): CachedValue<T> | null;
96
- /**
97
- * Get a list of cached keys
98
- * @return {K[]} Array of keys
99
- */
70
+ /** Return list of keys */
100
71
  keys(expired?: boolean): K[];
101
- /**
102
- * Get map of cached items
103
- * @return {Record<K, T>}
104
- */
72
+ /** Return map of key → item */
105
73
  map(expired?: boolean): Record<K, CachedValue<T>>;
106
- /**
107
- * Add an item to the cache manually specifying the key
108
- * @param {K} key Key item will be cached under
109
- * @param {T} value Item to cache
110
- * @param {number | undefined} ttl Override default expiry in seconds
111
- * @return {this}
112
- */
74
+ /** Add item manually specifying the key */
113
75
  set(key: K, value: T, ttl?: number | undefined): this;
114
- /**
115
- * Get all cached items
116
- * @return {T[]} Array of items
117
- */
76
+ /** Get all cached items */
118
77
  values: (expired?: boolean) => CachedValue<T>[];
119
78
  }
package/dist/index.cjs CHANGED
@@ -186,6 +186,9 @@ ${opts.message || this.desc}`;
186
186
  return obj;
187
187
  }
188
188
  function deepCopy(value) {
189
+ if (value == null) return value;
190
+ const t = typeof value;
191
+ if (t === "string" || t === "number" || t === "boolean" || t === "function") return value;
189
192
  try {
190
193
  return structuredClone(value);
191
194
  } catch {
@@ -467,10 +470,7 @@ ${opts.message || this.desc}`;
467
470
  __publicField(this, "complete", false);
468
471
  /** Await initial loading */
469
472
  __publicField(this, "loading", new Promise((r) => this._loading = r));
470
- /**
471
- * Get all cached items
472
- * @return {T[]} Array of items
473
- */
473
+ /** Get all cached items */
474
474
  __publicField(this, "values", this.all);
475
475
  var _a, _b, _c, _d;
476
476
  this.key = key;
@@ -518,6 +518,7 @@ ${opts.message || this.desc}`;
518
518
  if (value[this.key] === void 0) throw new Error(`${this.key.toString()} Doesn't exist on ${JSON.stringify(value, null, 2)}`);
519
519
  return value[this.key];
520
520
  }
521
+ /** Save item to storage */
521
522
  save(key) {
522
523
  var _a, _b;
523
524
  const persists = this.options.persistentStorage;
@@ -571,9 +572,6 @@ ${opts.message || this.desc}`;
571
572
  }
572
573
  /**
573
574
  * Add a new item to the cache. Like set, but finds key automatically
574
- * @param {T} value Item to add to cache
575
- * @param {number | undefined} ttl Override default expiry
576
- * @return {this}
577
575
  */
578
576
  add(value, ttl = this.ttl) {
579
577
  const key = this.getKey(value);
@@ -582,9 +580,6 @@ ${opts.message || this.desc}`;
582
580
  }
583
581
  /**
584
582
  * Add several rows to the cache
585
- * @param {T[]} rows Several items that will be cached using the default key
586
- * @param complete Mark cache as complete & reliable, defaults to true
587
- * @return {this}
588
583
  */
589
584
  addAll(rows, complete = true) {
590
585
  this.clear();
@@ -592,9 +587,7 @@ ${opts.message || this.desc}`;
592
587
  this.complete = complete;
593
588
  return this;
594
589
  }
595
- /**
596
- * Remove all keys from cache
597
- */
590
+ /** Remove all keys */
598
591
  clear() {
599
592
  this.complete = false;
600
593
  for (const [k, t] of this.timers) clearTimeout(t);
@@ -604,10 +597,7 @@ ${opts.message || this.desc}`;
604
597
  this.save();
605
598
  return this;
606
599
  }
607
- /**
608
- * Delete an item from the cache
609
- * @param {K} key Item's primary key
610
- */
600
+ /** Delete a cached item */
611
601
  delete(key) {
612
602
  this.clearTimer(key);
613
603
  const idx = this.lruOrder.indexOf(key);
@@ -616,10 +606,7 @@ ${opts.message || this.desc}`;
616
606
  this.save(key);
617
607
  return this;
618
608
  }
619
- /**
620
- * Return cache as an array of key-value pairs
621
- * @return {[K, T][]} Key-value pairs array
622
- */
609
+ /** Return entries as array */
623
610
  entries(expired) {
624
611
  const out = [];
625
612
  for (const [k, v] of this.store.entries()) {
@@ -628,10 +615,7 @@ ${opts.message || this.desc}`;
628
615
  }
629
616
  return out;
630
617
  }
631
- /**
632
- * Manually expire a cached item
633
- * @param {K} key Key to expire
634
- */
618
+ /** Manually expire a cached item */
635
619
  expire(key) {
636
620
  this.complete = false;
637
621
  if (this.options.expiryPolicy == "keep") {
@@ -644,12 +628,7 @@ ${opts.message || this.desc}`;
644
628
  } else this.delete(key);
645
629
  return this;
646
630
  }
647
- /**
648
- * Find the first cached item to match a filter
649
- * @param {Partial<T>} filter Partial item to match
650
- * @param {Boolean} expired Include expired items, defaults to false
651
- * @returns {T | undefined} Cached item or undefined if nothing matched
652
- */
631
+ /** Find first matching item */
653
632
  find(filter, expired) {
654
633
  for (const v of this.store.values()) {
655
634
  const row = v;
@@ -657,24 +636,16 @@ ${opts.message || this.desc}`;
657
636
  }
658
637
  return void 0;
659
638
  }
660
- /**
661
- * Get item from the cache
662
- * @param {K} key Key to lookup
663
- * @param expired Include expired items
664
- * @return {T} Cached item
665
- */
639
+ /** Get cached item by key */
666
640
  get(key, expired) {
667
641
  const raw = this.store.get(key);
668
642
  if (raw == null) return null;
669
- const cached = deepCopy(raw);
670
643
  this.touchLRU(key);
671
- if (expired || !(cached == null ? void 0 : cached._expired)) return cached;
644
+ const isExpired = raw == null ? void 0 : raw._expired;
645
+ if (expired || !isExpired) return deepCopy(raw);
672
646
  return null;
673
647
  }
674
- /**
675
- * Get a list of cached keys
676
- * @return {K[]} Array of keys
677
- */
648
+ /** Return list of keys */
678
649
  keys(expired) {
679
650
  const out = [];
680
651
  for (const [k, v] of this.store.entries()) {
@@ -683,10 +654,7 @@ ${opts.message || this.desc}`;
683
654
  }
684
655
  return out;
685
656
  }
686
- /**
687
- * Get map of cached items
688
- * @return {Record<K, T>}
689
- */
657
+ /** Return map of key → item */
690
658
  map(expired) {
691
659
  const copy = {};
692
660
  for (const [k, v] of this.store.entries()) {
@@ -695,13 +663,7 @@ ${opts.message || this.desc}`;
695
663
  }
696
664
  return copy;
697
665
  }
698
- /**
699
- * Add an item to the cache manually specifying the key
700
- * @param {K} key Key item will be cached under
701
- * @param {T} value Item to cache
702
- * @param {number | undefined} ttl Override default expiry in seconds
703
- * @return {this}
704
- */
666
+ /** Add item manually specifying the key */
705
667
  set(key, value, ttl = this.options.ttl) {
706
668
  if (this.options.expiryPolicy == "keep") delete value._expired;
707
669
  this.clearTimer(key);
@@ -712,7 +674,7 @@ ${opts.message || this.desc}`;
712
674
  const t = setTimeout(() => {
713
675
  this.expire(key);
714
676
  this.save(key);
715
- }, (ttl || 0) * 1e3);
677
+ }, ttl * 1e3);
716
678
  this.timers.set(key, t);
717
679
  }
718
680
  return this;
@@ -1957,10 +1919,12 @@ ${opts.message || this.desc}`;
1957
1919
  __publicField(this, "fullPath");
1958
1920
  /** Path including the name, excluding the module */
1959
1921
  __publicField(this, "path");
1960
- /** Last sagment of path */
1922
+ /** Last segment of path */
1961
1923
  __publicField(this, "name");
1962
1924
  /** List of methods */
1963
1925
  __publicField(this, "methods");
1926
+ /** Whether this path contains glob patterns */
1927
+ __publicField(this, "hasGlob");
1964
1928
  if (typeof e == "object") {
1965
1929
  Object.assign(this, e);
1966
1930
  return;
@@ -1969,17 +1933,34 @@ ${opts.message || this.desc}`;
1969
1933
  Object.assign(this, _PathEvent.pathEventCache.get(e));
1970
1934
  return;
1971
1935
  }
1972
- let [p, scope, method] = e.replaceAll(/\/{2,}/g, "/").split(":");
1973
- if (!method) method = scope || "*";
1974
- if (p == "*" || !p && method == "*") {
1975
- p = "";
1976
- method = "*";
1936
+ let [p, method] = e.replaceAll(/\/{2,}/g, "/").split(":");
1937
+ if (!method) method = "*";
1938
+ if (p === "" || p === void 0) {
1939
+ this.module = "";
1940
+ this.path = "";
1941
+ this.fullPath = "";
1942
+ this.name = "";
1943
+ this.methods = new ASet(["n"]);
1944
+ this.hasGlob = false;
1945
+ _PathEvent.pathEventCache.set(e, this);
1946
+ return;
1947
+ }
1948
+ if (p === "*") {
1949
+ this.module = "";
1950
+ this.path = "";
1951
+ this.fullPath = "**";
1952
+ this.name = "";
1953
+ this.methods = new ASet(["*"]);
1954
+ this.hasGlob = true;
1955
+ _PathEvent.pathEventCache.set(e, this);
1956
+ return;
1977
1957
  }
1978
1958
  let temp = p.split("/").filter((p2) => !!p2);
1979
1959
  this.module = temp.splice(0, 1)[0] || "";
1980
1960
  this.path = temp.join("/");
1981
1961
  this.fullPath = `${this.module}${this.module && this.path ? "/" : ""}${this.path}`;
1982
1962
  this.name = temp.pop() || "";
1963
+ this.hasGlob = this.fullPath.includes("*");
1983
1964
  this.methods = new ASet(method.split(""));
1984
1965
  _PathEvent.pathEventCache.set(e, this);
1985
1966
  }
@@ -2004,6 +1985,13 @@ ${opts.message || this.desc}`;
2004
1985
  set create(v) {
2005
1986
  v ? this.methods.delete("n").delete("*").add("c") : this.methods.delete("c");
2006
1987
  }
1988
+ /** Execute method specified */
1989
+ get execute() {
1990
+ return !this.methods.has("n") && (this.methods.has("*") || this.methods.has("x"));
1991
+ }
1992
+ set execute(v) {
1993
+ v ? this.methods.delete("n").delete("*").add("x") : this.methods.delete("x");
1994
+ }
2007
1995
  /** Read method specified */
2008
1996
  get read() {
2009
1997
  return !this.methods.has("n") && (this.methods.has("*") || this.methods.has("r"));
@@ -2029,6 +2017,64 @@ ${opts.message || this.desc}`;
2029
2017
  static clearCache() {
2030
2018
  _PathEvent.pathEventCache.clear();
2031
2019
  }
2020
+ /** Clear the permission cache */
2021
+ static clearPermissionCache() {
2022
+ _PathEvent.permissionCache.clear();
2023
+ }
2024
+ /**
2025
+ * Score a path for specificity ranking (lower = more specific = higher priority)
2026
+ * @private
2027
+ */
2028
+ static scoreSpecificity(path) {
2029
+ if (path === "**" || path === "") return Number.MAX_SAFE_INTEGER;
2030
+ const segments = path.split("/").filter((p) => !!p);
2031
+ let score = -segments.length;
2032
+ segments.forEach((seg) => {
2033
+ if (seg === "**") score += 0.5;
2034
+ else if (seg === "*") score += 0.25;
2035
+ });
2036
+ return score;
2037
+ }
2038
+ /**
2039
+ * Check if a path matches a glob pattern
2040
+ * @private
2041
+ */
2042
+ static pathMatchesGlob(path, pattern) {
2043
+ if (pattern === path) return true;
2044
+ const pathParts = path.split("/").filter((p) => !!p);
2045
+ const patternParts = pattern.split("/").filter((p) => !!p);
2046
+ let pathIdx = 0;
2047
+ let patternIdx = 0;
2048
+ while (patternIdx < patternParts.length && pathIdx < pathParts.length) {
2049
+ const patternPart = patternParts[patternIdx];
2050
+ if (patternPart === "**") {
2051
+ if (patternIdx === patternParts.length - 1) {
2052
+ return true;
2053
+ }
2054
+ patternParts[patternIdx + 1];
2055
+ while (pathIdx < pathParts.length) {
2056
+ if (_PathEvent.pathMatchesGlob(pathParts.slice(pathIdx).join("/"), patternParts.slice(patternIdx + 1).join("/"))) {
2057
+ return true;
2058
+ }
2059
+ pathIdx++;
2060
+ }
2061
+ return false;
2062
+ } else if (patternPart === "*") {
2063
+ pathIdx++;
2064
+ patternIdx++;
2065
+ } else {
2066
+ if (patternPart !== pathParts[pathIdx]) {
2067
+ return false;
2068
+ }
2069
+ pathIdx++;
2070
+ patternIdx++;
2071
+ }
2072
+ }
2073
+ if (patternIdx < patternParts.length) {
2074
+ return patternParts.slice(patternIdx).every((p) => p === "**");
2075
+ }
2076
+ return pathIdx === pathParts.length;
2077
+ }
2032
2078
  /**
2033
2079
  * Combine multiple events into one parsed object. Longest path takes precedent, but all subsequent methods are
2034
2080
  * combined until a "none" is reached
@@ -2037,38 +2083,58 @@ ${opts.message || this.desc}`;
2037
2083
  * @return {PathEvent} Final combined permission
2038
2084
  */
2039
2085
  static combine(...paths) {
2040
- let hitNone = false;
2041
- const combined = paths.map((p) => p instanceof _PathEvent ? p : new _PathEvent(p)).toSorted((p1, p2) => {
2042
- const l1 = p1.fullPath.length, l2 = p2.fullPath.length;
2043
- return l1 < l2 ? 1 : l1 > l2 ? -1 : 0;
2044
- }).reduce((acc, p) => {
2045
- if (acc && !acc.fullPath.startsWith(p.fullPath)) return acc;
2046
- if (p.none) hitNone = true;
2047
- if (!acc) return p;
2048
- if (hitNone) return acc;
2049
- acc.methods = new ASet([...acc.methods, ...p.methods]);
2050
- return acc;
2051
- }, null);
2052
- return combined;
2086
+ const parsed = paths.map((p) => p instanceof _PathEvent ? p : new _PathEvent(p));
2087
+ const sorted = parsed.toSorted((p1, p2) => {
2088
+ const score1 = _PathEvent.scoreSpecificity(p1.fullPath);
2089
+ const score2 = _PathEvent.scoreSpecificity(p2.fullPath);
2090
+ return score1 - score2;
2091
+ });
2092
+ let result = null;
2093
+ for (const p of sorted) {
2094
+ if (!result) {
2095
+ result = p;
2096
+ } else {
2097
+ if (result.fullPath.startsWith(p.fullPath)) {
2098
+ if (p.none) {
2099
+ break;
2100
+ }
2101
+ result.methods = new ASet([...result.methods, ...p.methods]);
2102
+ }
2103
+ }
2104
+ }
2105
+ return result || new _PathEvent("");
2053
2106
  }
2054
2107
  /**
2055
2108
  * Filter a set of paths based on the target
2056
2109
  *
2057
2110
  * @param {string | PathEvent | (string | PathEvent)[]} target Array of events that will filtered
2058
- * @param filter {...PathEvent} Must container one of
2059
- * @return {boolean} Whether there is any overlap
2111
+ * @param filter {...PathEvent} Must contain one of
2112
+ * @return {PathEvent[]} Filtered results
2060
2113
  */
2061
2114
  static filter(target, ...filter) {
2062
2115
  const parsedTarget = makeArray(target).map((pe) => pe instanceof _PathEvent ? pe : new _PathEvent(pe));
2063
2116
  const parsedFilter = makeArray(filter).map((pe) => pe instanceof _PathEvent ? pe : new _PathEvent(pe));
2064
- return parsedTarget.filter((t) => !!parsedFilter.find((r) => {
2065
- const wildcard = r.fullPath == "*" || t.fullPath == "*";
2066
- const p1 = r.fullPath.includes("*") ? r.fullPath.slice(0, r.fullPath.indexOf("*")) : r.fullPath;
2067
- const p2 = t.fullPath.includes("*") ? t.fullPath.slice(0, t.fullPath.indexOf("*")) : t.fullPath;
2068
- const scope = p1.startsWith(p2) || p2.startsWith(p1);
2069
- const methods = r.all || t.all || r.methods.intersection(t.methods).length;
2070
- return (wildcard || scope) && methods;
2071
- }));
2117
+ return parsedTarget.filter((t) => {
2118
+ const combined = _PathEvent.combine(t);
2119
+ return !!parsedFilter.find((r) => _PathEvent.matches(r, combined));
2120
+ });
2121
+ }
2122
+ /**
2123
+ * Check if a filter pattern matches a target path
2124
+ * @private
2125
+ */
2126
+ static matches(pattern, target) {
2127
+ if (pattern.fullPath === "" || target.fullPath === "") return false;
2128
+ if (pattern.fullPath === "*" || target.fullPath === "*") return pattern.methods.has("*") || target.methods.has("*") || pattern.methods.intersection(target.methods).length > 0;
2129
+ const methodsMatch = pattern.all || target.all || pattern.methods.intersection(target.methods).length > 0;
2130
+ if (!methodsMatch) return false;
2131
+ if (!pattern.hasGlob && !target.hasGlob) {
2132
+ return pattern.fullPath === target.fullPath;
2133
+ }
2134
+ if (pattern.hasGlob) {
2135
+ return this.pathMatchesGlob(target.fullPath, pattern.fullPath);
2136
+ }
2137
+ return this.pathMatchesGlob(pattern.fullPath, target.fullPath);
2072
2138
  }
2073
2139
  /**
2074
2140
  * Squash 2 sets of paths & return true if any overlap is found
@@ -2080,21 +2146,15 @@ ${opts.message || this.desc}`;
2080
2146
  static has(target, ...has) {
2081
2147
  const parsedTarget = makeArray(target).map((pe) => pe instanceof _PathEvent ? pe : new _PathEvent(pe));
2082
2148
  const parsedRequired = makeArray(has).map((pe) => pe instanceof _PathEvent ? pe : new _PathEvent(pe));
2083
- return !!parsedRequired.find((r) => !!parsedTarget.find((t) => {
2084
- const wildcard = r.fullPath == "*" || t.fullPath == "*";
2085
- const p1 = r.fullPath.includes("*") ? r.fullPath.slice(0, r.fullPath.indexOf("*")) : r.fullPath;
2086
- const p2 = t.fullPath.includes("*") ? t.fullPath.slice(0, t.fullPath.indexOf("*")) : t.fullPath;
2087
- const scope = p1.startsWith(p2);
2088
- const methods = r.all || t.all || r.methods.intersection(t.methods).length;
2089
- return (wildcard || scope) && methods;
2090
- }));
2149
+ const effectiveTarget = parsedTarget.length === 1 ? parsedTarget[0] : _PathEvent.combine(...parsedTarget);
2150
+ return !!parsedRequired.find((r) => _PathEvent.matches(r, effectiveTarget));
2091
2151
  }
2092
2152
  /**
2093
2153
  * Squash 2 sets of paths & return true if the target has all paths
2094
2154
  *
2095
2155
  * @param {string | PathEvent | (string | PathEvent)[]} target Array of Events as strings or pre-parsed
2096
2156
  * @param has Target must have all these paths
2097
- * @return {boolean} Whether there is any overlap
2157
+ * @return {boolean} Whether all are present
2098
2158
  */
2099
2159
  static hasAll(target, ...has) {
2100
2160
  return has.filter((h) => _PathEvent.has(target, h)).length == has.length;
@@ -2102,7 +2162,7 @@ ${opts.message || this.desc}`;
2102
2162
  /**
2103
2163
  * Same as `has` but raises an error if there is no overlap
2104
2164
  *
2105
- * @param {string | string[]} target Array of Events as strings or pre-parsed
2165
+ * @param {string | PathEvent | (string | PathEvent)[]} target Array of Events as strings or pre-parsed
2106
2166
  * @param has Target must have at least one of these path
2107
2167
  */
2108
2168
  static hasFatal(target, ...has) {
@@ -2111,7 +2171,7 @@ ${opts.message || this.desc}`;
2111
2171
  /**
2112
2172
  * Same as `hasAll` but raises an error if the target is missing any paths
2113
2173
  *
2114
- * @param {string | string[]} target Array of Events as strings or pre-parsed
2174
+ * @param {string | PathEvent | (string | PathEvent)[]} target Array of Events as strings or pre-parsed
2115
2175
  * @param has Target must have all these paths
2116
2176
  */
2117
2177
  static hasAllFatal(target, ...has) {
@@ -2143,7 +2203,7 @@ ${opts.message || this.desc}`;
2143
2203
  * Squash 2 sets of paths & return true if the target has all paths
2144
2204
  *
2145
2205
  * @param has Target must have all these paths
2146
- * @return {boolean} Whether there is any overlap
2206
+ * @return {boolean} Whether all are present
2147
2207
  */
2148
2208
  hasAll(...has) {
2149
2209
  return _PathEvent.hasAll(this, ...has);
@@ -2168,7 +2228,7 @@ ${opts.message || this.desc}`;
2168
2228
  * Filter a set of paths based on this event
2169
2229
  *
2170
2230
  * @param {string | PathEvent | (string | PathEvent)[]} target Array of events that will filtered
2171
- * @return {boolean} Whether there is any overlap
2231
+ * @return {PathEvent[]} Filtered results
2172
2232
  */
2173
2233
  filter(target) {
2174
2234
  return _PathEvent.filter(target, this);
@@ -2184,6 +2244,10 @@ ${opts.message || this.desc}`;
2184
2244
  };
2185
2245
  /** Internal cache for PathEvent instances to avoid redundant parsing */
2186
2246
  __publicField(_PathEvent, "pathEventCache", /* @__PURE__ */ new Map());
2247
+ /** Cache for compiled permissions (path + required permissions → result) */
2248
+ __publicField(_PathEvent, "permissionCache", /* @__PURE__ */ new Map());
2249
+ /** Max size for permission cache before LRU eviction */
2250
+ __publicField(_PathEvent, "MAX_PERMISSION_CACHE_SIZE", 1e3);
2187
2251
  let PathEvent = _PathEvent;
2188
2252
  class PathEventEmitter {
2189
2253
  constructor(prefix = "") {
@@ -2192,16 +2256,27 @@ ${opts.message || this.desc}`;
2192
2256
  }
2193
2257
  emit(event, ...args) {
2194
2258
  const parsed = event instanceof PathEvent ? event : new PathEvent(`${this.prefix}/${event}`);
2195
- this.listeners.filter((l) => PathEvent.has(l[0], parsed)).forEach(async (l) => l[1](parsed, ...args));
2259
+ this.listeners.filter((l) => PathEvent.has(l[0], parsed)).forEach((l) => l[1](parsed, ...args));
2196
2260
  }
2197
2261
  off(listener) {
2198
2262
  this.listeners = this.listeners.filter((l) => l[1] != listener);
2199
2263
  }
2200
2264
  on(event, listener) {
2201
2265
  makeArray(event).forEach((e) => {
2202
- if (typeof e == "string" && e[0] == "*" && this.prefix) e = e.slice(1);
2266
+ let fullEvent;
2267
+ if (typeof e === "string") {
2268
+ if (e[0] === ":" && this.prefix) {
2269
+ fullEvent = `${this.prefix}${e}`;
2270
+ } else if (this.prefix) {
2271
+ fullEvent = `${this.prefix}/${e}`;
2272
+ } else {
2273
+ fullEvent = e;
2274
+ }
2275
+ } else {
2276
+ fullEvent = e instanceof PathEvent ? PathEvent.toString(e.fullPath, e.methods) : e;
2277
+ }
2203
2278
  this.listeners.push([
2204
- e instanceof PathEvent ? e : new PathEvent(`${this.prefix}/${e}`),
2279
+ new PathEvent(fullEvent),
2205
2280
  listener
2206
2281
  ]);
2207
2282
  });
@@ -2217,7 +2292,7 @@ ${opts.message || this.desc}`;
2217
2292
  });
2218
2293
  }
2219
2294
  relayEvents(emitter) {
2220
- emitter.on("*", (event, ...args) => this.emit(event, ...args));
2295
+ emitter.on("**", (event, ...args) => this.emit(event, ...args));
2221
2296
  }
2222
2297
  }
2223
2298
  function search(rows, search2, regex, transform = (r) => r) {
@@ -2426,7 +2501,7 @@ ${opts.message || this.desc}`;
2426
2501
  }
2427
2502
  }
2428
2503
  memoryStorage.MemoryStorage = MemoryStorage;
2429
- (function(exports3) {
2504
+ (function(exports$1) {
2430
2505
  var __createBinding = commonjsGlobal && commonjsGlobal.__createBinding || (Object.create ? function(o, m, k, k2) {
2431
2506
  if (k2 === void 0) k2 = k;
2432
2507
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -2440,12 +2515,12 @@ ${opts.message || this.desc}`;
2440
2515
  if (k2 === void 0) k2 = k;
2441
2516
  o[k2] = m[k];
2442
2517
  });
2443
- var __exportStar = commonjsGlobal && commonjsGlobal.__exportStar || function(m, exports4) {
2444
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports4, p)) __createBinding(exports4, m, p);
2518
+ var __exportStar = commonjsGlobal && commonjsGlobal.__exportStar || function(m, exports$12) {
2519
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$12, p)) __createBinding(exports$12, m, p);
2445
2520
  };
2446
- Object.defineProperty(exports3, "__esModule", { value: true });
2447
- __exportStar(persist$1, exports3);
2448
- __exportStar(memoryStorage, exports3);
2521
+ Object.defineProperty(exports$1, "__esModule", { value: true });
2522
+ __exportStar(persist$1, exports$1);
2523
+ __exportStar(memoryStorage, exports$1);
2449
2524
  })(dist);
2450
2525
  exports2.ASet = ASet;
2451
2526
  exports2.ArgParser = ArgParser;