@cldmv/slothlet 3.3.0 → 3.3.2

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 (127) hide show
  1. package/README.md +6 -8
  2. package/REFERENCE.md +23 -0
  3. package/dist/lib/builders/api-assignment.mjs +1 -589
  4. package/dist/lib/builders/api_builder.mjs +1 -1385
  5. package/dist/lib/builders/builder.mjs +1 -78
  6. package/dist/lib/builders/modes-processor.mjs +1 -1800
  7. package/dist/lib/errors.mjs +9 -211
  8. package/dist/lib/factories/component-base.mjs +1 -80
  9. package/dist/lib/factories/context.mjs +1 -22
  10. package/dist/lib/handlers/api-cache-manager.mjs +1 -200
  11. package/dist/lib/handlers/api-manager.mjs +1 -2536
  12. package/dist/lib/handlers/context-async.mjs +1 -172
  13. package/dist/lib/handlers/context-live.mjs +1 -173
  14. package/dist/lib/handlers/hook-manager.mjs +1 -667
  15. package/dist/lib/handlers/lifecycle-token.mjs +1 -28
  16. package/dist/lib/handlers/lifecycle.mjs +1 -115
  17. package/dist/lib/handlers/materialize-manager.mjs +1 -48
  18. package/dist/lib/handlers/metadata.mjs +1 -501
  19. package/dist/lib/handlers/ownership.mjs +1 -322
  20. package/dist/lib/handlers/permission-manager.mjs +1 -392
  21. package/dist/lib/handlers/unified-wrapper.mjs +1 -3110
  22. package/dist/lib/handlers/version-manager.mjs +1 -885
  23. package/dist/lib/helpers/class-instance-wrapper.mjs +1 -109
  24. package/dist/lib/helpers/config.mjs +1 -439
  25. package/dist/lib/helpers/eventemitter-context.mjs +1 -349
  26. package/dist/lib/helpers/hint-detector.mjs +1 -47
  27. package/dist/lib/helpers/modes-utils.mjs +1 -37
  28. package/dist/lib/helpers/pattern-matcher.mjs +1 -125
  29. package/dist/lib/helpers/resolve-from-caller.mjs +1 -169
  30. package/dist/lib/helpers/sanitize.mjs +1 -340
  31. package/dist/lib/helpers/utilities.mjs +1 -70
  32. package/dist/lib/i18n/translations.mjs +1 -126
  33. package/dist/lib/modes/eager.mjs +1 -59
  34. package/dist/lib/modes/lazy.mjs +1 -81
  35. package/dist/lib/processors/flatten.mjs +1 -437
  36. package/dist/lib/processors/loader.mjs +1 -339
  37. package/dist/lib/processors/type-generator.mjs +1 -275
  38. package/dist/lib/processors/typescript.mjs +1 -172
  39. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -113
  40. package/dist/lib/runtime/runtime-livebindings.mjs +1 -78
  41. package/dist/lib/runtime/runtime.mjs +1 -102
  42. package/dist/slothlet.mjs +1 -817
  43. package/package.json +34 -31
  44. package/types/dist/lib/builders/api-assignment.d.mts +3 -92
  45. package/types/dist/lib/builders/api-assignment.d.mts.map +1 -1
  46. package/types/dist/lib/builders/api_builder.d.mts +102 -91
  47. package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
  48. package/types/dist/lib/builders/builder.d.mts +1 -55
  49. package/types/dist/lib/builders/builder.d.mts.map +1 -1
  50. package/types/dist/lib/builders/modes-processor.d.mts +3 -27
  51. package/types/dist/lib/builders/modes-processor.d.mts.map +1 -1
  52. package/types/dist/lib/errors.d.mts +19 -109
  53. package/types/dist/lib/errors.d.mts.map +1 -1
  54. package/types/dist/lib/factories/component-base.d.mts +7 -177
  55. package/types/dist/lib/factories/component-base.d.mts.map +1 -1
  56. package/types/dist/lib/factories/context.d.mts +4 -22
  57. package/types/dist/lib/factories/context.d.mts.map +1 -1
  58. package/types/dist/lib/handlers/api-cache-manager.d.mts +20 -203
  59. package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -1
  60. package/types/dist/lib/handlers/api-manager.d.mts +33 -408
  61. package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
  62. package/types/dist/lib/handlers/context-async.d.mts +23 -61
  63. package/types/dist/lib/handlers/context-async.d.mts.map +1 -1
  64. package/types/dist/lib/handlers/context-live.d.mts +22 -59
  65. package/types/dist/lib/handlers/context-live.d.mts.map +1 -1
  66. package/types/dist/lib/handlers/hook-manager.d.mts +46 -185
  67. package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -1
  68. package/types/dist/lib/handlers/lifecycle-token.d.mts +3 -48
  69. package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -1
  70. package/types/dist/lib/handlers/lifecycle.d.mts +5 -82
  71. package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -1
  72. package/types/dist/lib/handlers/materialize-manager.d.mts +8 -70
  73. package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -1
  74. package/types/dist/lib/handlers/metadata.d.mts +17 -221
  75. package/types/dist/lib/handlers/metadata.d.mts.map +1 -1
  76. package/types/dist/lib/handlers/ownership.d.mts +44 -160
  77. package/types/dist/lib/handlers/ownership.d.mts.map +1 -1
  78. package/types/dist/lib/handlers/permission-manager.d.mts +37 -141
  79. package/types/dist/lib/handlers/permission-manager.d.mts.map +1 -1
  80. package/types/dist/lib/handlers/unified-wrapper.d.mts +26 -239
  81. package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
  82. package/types/dist/lib/handlers/version-manager.d.mts +28 -225
  83. package/types/dist/lib/handlers/version-manager.d.mts.map +1 -1
  84. package/types/dist/lib/helpers/class-instance-wrapper.d.mts +2 -52
  85. package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -1
  86. package/types/dist/lib/helpers/config.d.mts +125 -139
  87. package/types/dist/lib/helpers/config.d.mts.map +1 -1
  88. package/types/dist/lib/helpers/eventemitter-context.d.mts +3 -29
  89. package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -1
  90. package/types/dist/lib/helpers/hint-detector.d.mts +2 -15
  91. package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -1
  92. package/types/dist/lib/helpers/modes-utils.d.mts +3 -30
  93. package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -1
  94. package/types/dist/lib/helpers/pattern-matcher.d.mts +3 -43
  95. package/types/dist/lib/helpers/pattern-matcher.d.mts.map +1 -1
  96. package/types/dist/lib/helpers/resolve-from-caller.d.mts +3 -27
  97. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  98. package/types/dist/lib/helpers/sanitize.d.mts +4 -92
  99. package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
  100. package/types/dist/lib/helpers/utilities.d.mts +4 -52
  101. package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
  102. package/types/dist/lib/i18n/translations.d.mts +4 -37
  103. package/types/dist/lib/i18n/translations.d.mts.map +1 -1
  104. package/types/dist/lib/modes/eager.d.mts +8 -30
  105. package/types/dist/lib/modes/eager.d.mts.map +1 -1
  106. package/types/dist/lib/modes/lazy.d.mts +10 -43
  107. package/types/dist/lib/modes/lazy.d.mts.map +1 -1
  108. package/types/dist/lib/processors/flatten.d.mts +56 -107
  109. package/types/dist/lib/processors/flatten.d.mts.map +1 -1
  110. package/types/dist/lib/processors/loader.d.mts +6 -41
  111. package/types/dist/lib/processors/loader.d.mts.map +1 -1
  112. package/types/dist/lib/processors/type-generator.d.mts +2 -16
  113. package/types/dist/lib/processors/type-generator.d.mts.map +1 -1
  114. package/types/dist/lib/processors/typescript.d.mts +6 -53
  115. package/types/dist/lib/processors/typescript.d.mts.map +1 -1
  116. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +3 -71
  117. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  118. package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -37
  119. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  120. package/types/dist/lib/runtime/runtime.d.mts +3 -39
  121. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  122. package/types/dist/slothlet.d.mts +3 -249
  123. package/types/dist/slothlet.d.mts.map +1 -1
  124. package/types/index.d.mts +36 -16
  125. package/types/index.d.mts.map +1 -0
  126. package/AGENT-USAGE.md +0 -736
  127. package/docs/API-RULES.md +0 -712
@@ -14,395 +14,4 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
-
18
-
19
-
20
-
21
- import { ComponentBase } from "@cldmv/slothlet/factories/component-base";
22
- import { compilePattern } from "@cldmv/slothlet/helpers/pattern-matcher";
23
- import { translate } from "@cldmv/slothlet/i18n";
24
-
25
-
26
- let ruleIdCounter = 0;
27
-
28
-
29
- export class PermissionManager extends ComponentBase {
30
-
31
- static slothletProperty = "permissionManager";
32
-
33
-
34
- #rules = new Map();
35
-
36
-
37
- #defaultPolicy = "allow";
38
-
39
-
40
- #enabled = false;
41
-
42
-
43
- #audit = "default";
44
-
45
-
46
- #resolvedCache = new Map();
47
-
48
-
49
- #compiledCache = new Map();
50
-
51
-
52
- constructor(slothlet) {
53
- super(slothlet);
54
-
55
- const permConfig = slothlet.config?.permissions;
56
- if (permConfig) {
57
-
58
-
59
- this.#defaultPolicy = permConfig.defaultPolicy || "allow";
60
- this.#enabled = permConfig.enabled !== false;
61
- this.#audit = permConfig.audit || "default";
62
-
63
-
64
- if (Array.isArray(permConfig.rules)) {
65
- for (const rule of permConfig.rules) {
66
- this.addRule(rule, null);
67
- }
68
- }
69
- }
70
-
71
-
72
- this.addRule({ caller: "**", target: "slothlet.permissions.control.**", effect: "deny" }, "__builtin__");
73
- }
74
-
75
-
76
- addRule(rule, ownerModuleID = null, ruleId = null) {
77
- this.#validateRule(rule);
78
-
79
- const id = ruleId || `perm-${++ruleIdCounter}`;
80
- const entry = {
81
- id,
82
- caller: rule.caller,
83
- target: rule.target,
84
- effect: rule.effect,
85
- ownerModuleID: ownerModuleID,
86
- registeredAt: Date.now()
87
- };
88
-
89
- this.#rules.set(id, entry);
90
- this.#clearCache();
91
-
92
- this.debug("permissions", {
93
- key: "DEBUG_PERMISSION_RULE_ADDED",
94
- ruleId: id,
95
- caller: rule.caller,
96
- target: rule.target,
97
- effect: rule.effect,
98
- ownerModuleID
99
- });
100
-
101
- return id;
102
- }
103
-
104
-
105
- removeRule(ruleId, callerModuleID = null) {
106
- const entry = this.#rules.get(ruleId);
107
- if (!entry) return false;
108
-
109
-
110
-
111
-
112
-
113
-
114
- if (callerModuleID && entry.ownerModuleID && callerModuleID === entry.ownerModuleID) {
115
- throw new this.SlothletError("PERMISSION_SELF_MODIFY", {
116
- ruleId,
117
- moduleID: callerModuleID
118
- });
119
- }
120
-
121
- this.#rules.delete(ruleId);
122
- this.#clearCache();
123
-
124
- this.debug("permissions", {
125
- key: "DEBUG_PERMISSION_RULE_REMOVED",
126
- ruleId,
127
- caller: entry.caller,
128
- target: entry.target,
129
- effect: entry.effect
130
- });
131
-
132
- return true;
133
- }
134
-
135
-
136
- checkAccess(callerPath, targetPath, callerFilePath = null, targetFilePath = null) {
137
-
138
-
139
-
140
-
141
-
142
-
143
- const isControlTarget = targetPath?.startsWith("slothlet.permissions.control.");
144
- if (!this.#enabled && !isControlTarget) return true;
145
-
146
-
147
- if (callerFilePath && targetFilePath && callerFilePath === targetFilePath) {
148
- this.#emitAuditEvent("permission:self-bypass", {
149
- caller: callerPath,
150
- target: targetPath,
151
- filePath: callerFilePath
152
- });
153
- return true;
154
- }
155
-
156
-
157
- const cacheKey = `${callerPath}::${targetPath}`;
158
- if (this.#resolvedCache.has(cacheKey)) {
159
- const cached = this.#resolvedCache.get(cacheKey);
160
- this.#emitAuditEvent(cached.event, cached.payload);
161
- return cached.allowed;
162
- }
163
-
164
-
165
- const entry = this.#evaluate(callerPath, targetPath);
166
- this.#resolvedCache.set(cacheKey, entry);
167
- this.#emitAuditEvent(entry.event, entry.payload);
168
- return entry.allowed;
169
- }
170
-
171
-
172
- getRulesForPath(targetPath) {
173
- const matching = [];
174
- for (const entry of this.#rules.values()) {
175
- const targetMatcher = this.#getCompiledPattern(entry.target);
176
- if (targetMatcher(targetPath)) {
177
- matching.push(this.#serializeRule(entry));
178
- }
179
- }
180
- return matching;
181
- }
182
-
183
-
184
- getRulesByModule(moduleID) {
185
- const matching = [];
186
- for (const entry of this.#rules.values()) {
187
- if (entry.ownerModuleID === moduleID) {
188
- matching.push(this.#serializeRule(entry));
189
- }
190
- }
191
- return matching;
192
- }
193
-
194
-
195
- getRulesForCaller(callerPath) {
196
- const matching = [];
197
- for (const entry of this.#rules.values()) {
198
- const callerMatcher = this.#getCompiledPattern(entry.caller);
199
- if (callerMatcher(callerPath)) {
200
- matching.push(this.#serializeRule(entry));
201
- }
202
- }
203
- return matching;
204
- }
205
-
206
-
207
- enable() {
208
- this.#enabled = true;
209
- this.#clearCache();
210
- }
211
-
212
-
213
- disable() {
214
- this.#enabled = false;
215
- this.#clearCache();
216
- }
217
-
218
-
219
- isEnabled() {
220
- return this.#enabled;
221
- }
222
-
223
-
224
-
225
-
226
-
227
- exportRules() {
228
- const rules = [];
229
- for (const entry of this.#rules.values()) {
230
- rules.push({
231
- rule: { caller: entry.caller, target: entry.target, effect: entry.effect },
232
- ownerModuleID: entry.ownerModuleID
233
- });
234
- }
235
- return rules;
236
- }
237
-
238
-
239
- importRules(registrations) {
240
- if (!Array.isArray(registrations)) return;
241
- for (const reg of registrations) {
242
- this.addRule(reg.rule, reg.ownerModuleID);
243
- }
244
- }
245
-
246
-
247
-
248
- async shutdown() {
249
- this.#rules.clear();
250
- this.#resolvedCache.clear();
251
- this.#compiledCache.clear();
252
- this.#enabled = false;
253
- this.#defaultPolicy = "allow";
254
- this.#audit = "default";
255
- }
256
-
257
-
258
-
259
-
260
- #validateRule(rule) {
261
- if (!rule || typeof rule !== "object") {
262
- throw new this.SlothletError("INVALID_PERMISSION_RULE", {
263
- reason: translate("PERM_RULE_NOT_OBJECT"),
264
- received: typeof rule
265
- });
266
- }
267
- if (typeof rule.caller !== "string" || !rule.caller) {
268
- throw new this.SlothletError("INVALID_PERMISSION_RULE", {
269
- reason: translate("PERM_RULE_CALLER_REQUIRED"),
270
- received: typeof rule.caller
271
- });
272
- }
273
- if (typeof rule.target !== "string" || !rule.target) {
274
- throw new this.SlothletError("INVALID_PERMISSION_RULE", {
275
- reason: translate("PERM_RULE_TARGET_REQUIRED"),
276
- received: typeof rule.target
277
- });
278
- }
279
- if (rule.effect !== "allow" && rule.effect !== "deny") {
280
- throw new this.SlothletError("INVALID_PERMISSION_RULE", {
281
- reason: translate("PERM_RULE_EFFECT_INVALID"),
282
- received: rule.effect
283
- });
284
- }
285
- }
286
-
287
-
288
- #evaluate(callerPath, targetPath) {
289
-
290
- const matches = [];
291
-
292
- for (const entry of this.#rules.values()) {
293
- const callerMatcher = this.#getCompiledPattern(entry.caller);
294
- const targetMatcher = this.#getCompiledPattern(entry.target);
295
-
296
- if (callerMatcher(callerPath) && targetMatcher(targetPath)) {
297
- matches.push(entry);
298
- }
299
- }
300
-
301
-
302
- if (matches.length === 0) {
303
- const allowed = this.#defaultPolicy === "allow";
304
- return {
305
- allowed,
306
- event: "permission:default",
307
- payload: { caller: callerPath, target: targetPath, policy: this.#defaultPolicy }
308
- };
309
- }
310
-
311
-
312
- matches.sort((a, b) => {
313
- const specA = this.#computeSpecificity(a, callerPath, targetPath);
314
- const specB = this.#computeSpecificity(b, callerPath, targetPath);
315
- if (specA !== specB) return specB - specA;
316
- return a.registeredAt - b.registeredAt;
317
- });
318
-
319
-
320
- const highestSpec = this.#computeSpecificity(matches[0], callerPath, targetPath);
321
- const topTier = matches.filter((m) => this.#computeSpecificity(m, callerPath, targetPath) === highestSpec);
322
-
323
- const winner = topTier[topTier.length - 1];
324
- const allowed = winner.effect === "allow";
325
-
326
- return {
327
- allowed,
328
- event: allowed ? "permission:allowed" : "permission:denied",
329
- payload: { caller: callerPath, target: targetPath, rule: this.#serializeRule(winner) }
330
- };
331
- }
332
-
333
-
334
- #computeSpecificity(entry, callerPath, targetPath) {
335
- return this.#patternSpecificity(entry.caller, callerPath) + this.#patternSpecificity(entry.target, targetPath);
336
- }
337
-
338
-
339
- #patternSpecificity(pattern, _path) {
340
-
341
- if (!pattern.includes("*") && !pattern.includes("?") && !pattern.includes("{")) {
342
- return 3;
343
- }
344
-
345
- if (pattern.includes("**")) {
346
- return 1;
347
- }
348
-
349
- return 2;
350
- }
351
-
352
-
353
- #getCompiledPattern(pattern) {
354
- let matcher = this.#compiledCache.get(pattern);
355
- if (!matcher) {
356
- matcher = compilePattern(pattern);
357
- this.#compiledCache.set(pattern, matcher);
358
- }
359
- return matcher;
360
- }
361
-
362
-
363
- #clearCache() {
364
- this.#resolvedCache.clear();
365
- }
366
-
367
-
368
- #emitAuditEvent(event, payload) {
369
-
370
- this.debug("permissions", {
371
- key:
372
- event === "permission:denied"
373
- ? "DEBUG_PERMISSION_DENIED"
374
- : event === "permission:allowed"
375
- ? "DEBUG_PERMISSION_ALLOWED"
376
- : event === "permission:self-bypass"
377
- ? "DEBUG_PERMISSION_SELF_BYPASS"
378
- : "DEBUG_PERMISSION_DEFAULT",
379
- ...payload
380
- });
381
-
382
-
383
- const alwaysEmit = event === "permission:denied" || event === "permission:self-bypass";
384
- if (!alwaysEmit && this.#audit !== "verbose") return;
385
-
386
- const lifecycle = this.slothlet.handlers?.lifecycle;
387
- if (lifecycle) {
388
- lifecycle.emit(event, { ...payload, timestamp: Date.now() });
389
- }
390
- }
391
-
392
-
393
- #serializeRule(entry) {
394
- return {
395
- id: entry.id,
396
- caller: entry.caller,
397
- target: entry.target,
398
- effect: entry.effect,
399
- ownerModuleID: entry.ownerModuleID,
400
- registeredAt: entry.registeredAt
401
- };
402
- }
403
-
404
-
405
- debug(category, data) {
406
- this.slothlet.debug(category, data);
407
- }
408
- }
17
+ import{ComponentBase}from"@cldmv/slothlet/factories/component-base";import{compilePattern}from"@cldmv/slothlet/helpers/pattern-matcher";import{translate}from"@cldmv/slothlet/i18n";let ruleIdCounter=0;class PermissionManager extends ComponentBase{static slothletProperty="permissionManager";#rules=new Map;#defaultPolicy="allow";#enabled=false;#audit="default";#resolvedCache=new Map;#compiledCache=new Map;constructor(slothlet){super(slothlet);const permConfig=slothlet.config?.permissions;if(permConfig){this.#defaultPolicy=permConfig.defaultPolicy||"allow";this.#enabled=permConfig.enabled!==false;this.#audit=permConfig.audit||"default";if(Array.isArray(permConfig.rules)){for(const rule of permConfig.rules){this.addRule(rule,null)}}}this.addRule({caller:"**",target:"slothlet.permissions.control.**",effect:"deny"},"__builtin__")}addRule(rule,ownerModuleID=null,ruleId=null){this.#validateRule(rule);const id=ruleId||`perm-${++ruleIdCounter}`;const entry={id,caller:rule.caller,target:rule.target,effect:rule.effect,ownerModuleID,registeredAt:Date.now()};this.#rules.set(id,entry);this.#clearCache();this.debug("permissions",{key:"DEBUG_PERMISSION_RULE_ADDED",ruleId:id,caller:rule.caller,target:rule.target,effect:rule.effect,ownerModuleID});return id}removeRule(ruleId,callerModuleID=null){const entry=this.#rules.get(ruleId);if(!entry)return false;if(callerModuleID&&entry.ownerModuleID&&callerModuleID===entry.ownerModuleID){throw new this.SlothletError("PERMISSION_SELF_MODIFY",{ruleId,moduleID:callerModuleID})}this.#rules.delete(ruleId);this.#clearCache();this.debug("permissions",{key:"DEBUG_PERMISSION_RULE_REMOVED",ruleId,caller:entry.caller,target:entry.target,effect:entry.effect});return true}checkAccess(callerPath,targetPath,callerFilePath=null,targetFilePath=null){const isControlTarget=targetPath?.startsWith("slothlet.permissions.control.");if(!this.#enabled&&!isControlTarget)return true;if(callerFilePath&&targetFilePath&&callerFilePath===targetFilePath){this.#emitAuditEvent("permission:self-bypass",{caller:callerPath,target:targetPath,filePath:callerFilePath});return true}const cacheKey=`${callerPath}::${targetPath}`;if(this.#resolvedCache.has(cacheKey)){const cached=this.#resolvedCache.get(cacheKey);this.#emitAuditEvent(cached.event,cached.payload);return cached.allowed}const entry=this.#evaluate(callerPath,targetPath);this.#resolvedCache.set(cacheKey,entry);this.#emitAuditEvent(entry.event,entry.payload);return entry.allowed}getRulesForPath(targetPath){const matching=[];for(const entry of this.#rules.values()){const targetMatcher=this.#getCompiledPattern(entry.target);if(targetMatcher(targetPath)){matching.push(this.#serializeRule(entry))}}return matching}getRulesByModule(moduleID){const matching=[];for(const entry of this.#rules.values()){if(entry.ownerModuleID===moduleID){matching.push(this.#serializeRule(entry))}}return matching}getRulesForCaller(callerPath){const matching=[];for(const entry of this.#rules.values()){const callerMatcher=this.#getCompiledPattern(entry.caller);if(callerMatcher(callerPath)){matching.push(this.#serializeRule(entry))}}return matching}enable(){this.#enabled=true;this.#clearCache()}disable(){this.#enabled=false;this.#clearCache()}isEnabled(){return this.#enabled}exportRules(){const rules=[];for(const entry of this.#rules.values()){rules.push({rule:{caller:entry.caller,target:entry.target,effect:entry.effect},ownerModuleID:entry.ownerModuleID})}return rules}importRules(registrations){if(!Array.isArray(registrations))return;for(const reg of registrations){this.addRule(reg.rule,reg.ownerModuleID)}}async shutdown(){this.#rules.clear();this.#resolvedCache.clear();this.#compiledCache.clear();this.#enabled=false;this.#defaultPolicy="allow";this.#audit="default"}#validateRule(rule){if(!rule||typeof rule!=="object"){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_NOT_OBJECT"),received:typeof rule})}if(typeof rule.caller!=="string"||!rule.caller){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_CALLER_REQUIRED"),received:typeof rule.caller})}if(typeof rule.target!=="string"||!rule.target){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_TARGET_REQUIRED"),received:typeof rule.target})}if(rule.effect!=="allow"&&rule.effect!=="deny"){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_EFFECT_INVALID"),received:rule.effect})}}#evaluate(callerPath,targetPath){const matches=[];for(const entry of this.#rules.values()){const callerMatcher=this.#getCompiledPattern(entry.caller);const targetMatcher=this.#getCompiledPattern(entry.target);if(callerMatcher(callerPath)&&targetMatcher(targetPath)){matches.push(entry)}}if(matches.length===0){const allowed2=this.#defaultPolicy==="allow";return{allowed:allowed2,event:"permission:default",payload:{caller:callerPath,target:targetPath,policy:this.#defaultPolicy}}}matches.sort((a,b)=>{const specA=this.#computeSpecificity(a,callerPath,targetPath);const specB=this.#computeSpecificity(b,callerPath,targetPath);if(specA!==specB)return specB-specA;return a.registeredAt-b.registeredAt});const highestSpec=this.#computeSpecificity(matches[0],callerPath,targetPath);const topTier=matches.filter(m=>this.#computeSpecificity(m,callerPath,targetPath)===highestSpec);const winner=topTier[topTier.length-1];const allowed=winner.effect==="allow";return{allowed,event:allowed?"permission:allowed":"permission:denied",payload:{caller:callerPath,target:targetPath,rule:this.#serializeRule(winner)}}}#computeSpecificity(entry,callerPath,targetPath){return this.#patternSpecificity(entry.caller,callerPath)+this.#patternSpecificity(entry.target,targetPath)}#patternSpecificity(pattern,_path){if(!pattern.includes("*")&&!pattern.includes("?")&&!pattern.includes("{")){return 3}if(pattern.includes("**")){return 1}return 2}#getCompiledPattern(pattern){let matcher=this.#compiledCache.get(pattern);if(!matcher){matcher=compilePattern(pattern);this.#compiledCache.set(pattern,matcher)}return matcher}#clearCache(){this.#resolvedCache.clear()}#emitAuditEvent(event,payload){this.debug("permissions",{key:event==="permission:denied"?"DEBUG_PERMISSION_DENIED":event==="permission:allowed"?"DEBUG_PERMISSION_ALLOWED":event==="permission:self-bypass"?"DEBUG_PERMISSION_SELF_BYPASS":"DEBUG_PERMISSION_DEFAULT",...payload});const alwaysEmit=event==="permission:denied"||event==="permission:self-bypass";if(!alwaysEmit&&this.#audit!=="verbose")return;const lifecycle=this.slothlet.handlers?.lifecycle;if(lifecycle){lifecycle.emit(event,{...payload,timestamp:Date.now()})}}#serializeRule(entry){return{id:entry.id,caller:entry.caller,target:entry.target,effect:entry.effect,ownerModuleID:entry.ownerModuleID,registeredAt:entry.registeredAt}}debug(category,data){this.slothlet.debug(category,data)}}export{PermissionManager};