aws-service-stack 0.18.371 → 0.18.372

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 (64) hide show
  1. package/README.md +0 -149
  2. package/dist/controller/controller-api.d.ts +0 -8
  3. package/dist/controller/controller-api.js +1 -39
  4. package/dist/controller/controller-api.js.map +1 -1
  5. package/dist/controller/index.d.ts +0 -1
  6. package/dist/controller/index.js +0 -1
  7. package/dist/controller/index.js.map +1 -1
  8. package/dist/function/cognito/cognito.function.d.ts +0 -5
  9. package/dist/function/cognito/cognito.function.js +0 -22
  10. package/dist/function/cognito/cognito.function.js.map +1 -1
  11. package/dist/function/cognito/index.d.ts +1 -3
  12. package/dist/function/cognito/index.js +0 -2
  13. package/dist/function/cognito/index.js.map +1 -1
  14. package/dist/function/index.d.ts +0 -2
  15. package/dist/model/base.config.d.ts +1 -12
  16. package/dist/model/base.config.js +0 -20
  17. package/dist/model/base.config.js.map +1 -1
  18. package/dist/model/base.model.d.ts +0 -19
  19. package/dist/model/base.model.js +1 -14
  20. package/dist/model/base.model.js.map +1 -1
  21. package/dist/model/index.d.ts +0 -1
  22. package/dist/model/index.js.map +1 -1
  23. package/dist/model/validation.model.d.ts +1 -1
  24. package/dist/model/validation.model.js.map +1 -1
  25. package/dist/service/index.d.ts +0 -3
  26. package/dist/service/index.js +0 -3
  27. package/dist/service/index.js.map +1 -1
  28. package/dist/utils/data.util.d.ts +20 -0
  29. package/dist/utils/data.util.js +73 -0
  30. package/dist/utils/data.util.js.map +1 -0
  31. package/dist/utils/date.util.d.ts +13 -0
  32. package/dist/utils/date.util.js +35 -0
  33. package/dist/utils/date.util.js.map +1 -1
  34. package/dist/utils/index.d.ts +1 -0
  35. package/dist/utils/index.js +1 -0
  36. package/dist/utils/index.js.map +1 -1
  37. package/package.json +31 -31
  38. package/dist/_examples/controller/property/property-crud.d.ts +0 -4
  39. package/dist/_examples/controller/property/property-crud.js +0 -58
  40. package/dist/_examples/controller/property/property-crud.js.map +0 -1
  41. package/dist/_examples/controller/property/property.config.d.ts +0 -10
  42. package/dist/_examples/controller/property/property.config.js +0 -55
  43. package/dist/_examples/controller/property/property.config.js.map +0 -1
  44. package/dist/_examples/controller/property/property.controller.d.ts +0 -14
  45. package/dist/_examples/controller/property/property.controller.js +0 -72
  46. package/dist/_examples/controller/property/property.controller.js.map +0 -1
  47. package/dist/_examples/controller/property/property.permissions.d.ts +0 -2
  48. package/dist/_examples/controller/property/property.permissions.js +0 -19
  49. package/dist/_examples/controller/property/property.permissions.js.map +0 -1
  50. package/dist/controller/controller-role.d.ts +0 -56
  51. package/dist/controller/controller-role.js +0 -140
  52. package/dist/controller/controller-role.js.map +0 -1
  53. package/dist/model/role.model.d.ts +0 -20
  54. package/dist/model/role.model.js +0 -12
  55. package/dist/model/role.model.js.map +0 -1
  56. package/dist/service/permission.cache.d.ts +0 -24
  57. package/dist/service/permission.cache.js +0 -61
  58. package/dist/service/permission.cache.js.map +0 -1
  59. package/dist/service/permission.repo.d.ts +0 -16
  60. package/dist/service/permission.repo.js +0 -63
  61. package/dist/service/permission.repo.js.map +0 -1
  62. package/dist/service/permission.service.d.ts +0 -39
  63. package/dist/service/permission.service.js +0 -151
  64. package/dist/service/permission.service.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"permission.repo.js","sourceRoot":"","sources":["../../src/service/permission.repo.ts"],"names":[],"mappings":";;;AAAA,+DAA8D;AAC9D,4DAAyD;AAEzD,4DAAuD;AAGvD,SAAS,wBAAwB;IAC/B,MAAM,GAAG,GAAG,IAAI,+BAAc,EAAE,CAAC;IACjC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;IACpH,GAAG,CAAC,SAAS,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAa,cAAc;IACR,EAAE,CAA6B;IAEhD,YAAY,SAAiB;QAC3B,IAAI,CAAC,EAAE,GAAG,IAAI,6BAAc,EAAc,CAAC;QAC3C,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC5B,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,wBAAwB,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,aAAqB;QAC7C,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;YACrB,SAAS,EAAE,iBAAiB;YAC5B,UAAU,EAAE,aAAa;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAA+B;QACxC,IAAI,CAAC,UAAU,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,GAAG,IAAA,6BAAY,GAAE,CAAC;QAEnD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,UAAU,CAAC,SAAS;YAAE,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC;QACtD,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC;QAE3B,2CAA2C;QAC3C,IAAI,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YAC/D,UAAU,CAAC,aAAa,GAAG,GAAG,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;QAC7F,CAAC;QAED,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAA+B;QAC1C,UAAU,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAEhD,8CAA8C;QAC9C,IAAI,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YAC/D,UAAU,CAAC,aAAa,GAAG,GAAG,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;QAC7F,CAAC;QAED,OAAO,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,OAAO,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;CACF;AArDD,wCAqDC","sourcesContent":["import { BaseRepoDBImpl } from \"../repositories/base-db.repo\";\nimport { DynamoIndexMap } from \"../model/dynamodb.model\";\nimport { Permission } from \"../model/role.model\";\nimport { generateUUID } from \"../utils/dynamodb.utils\";\nimport { List } from \"../model/base.model\";\n\nfunction createPermissionIndexMap(): DynamoIndexMap {\n const map = new DynamoIndexMap();\n map.partitionKey = \"id\";\n map.set(\"byPermissionKey\", { field: \"permissionKey\", rFields: [\"role\", \"resource\", \"scope\"], fieldSeparator: \"#\" });\n map.mapFields = [\"method\"];\n return map;\n}\n\n/**\n * Repository layer for Permission table.\n * Pure DB operations — no caching, no business logic.\n */\nexport class PermissionRepo {\n private readonly db: BaseRepoDBImpl<Permission>;\n\n constructor(tableName: string) {\n this.db = new BaseRepoDBImpl<Permission>();\n this.db.setTable(tableName);\n this.db.setIndexMap(createPermissionIndexMap());\n }\n\n async findById(id: string): Promise<Permission | null> {\n return this.db.findById(id);\n }\n\n async findByPermissionKey(permissionKey: string): Promise<Permission | null> {\n return this.db.findOne({\n indexName: \"byPermissionKey\",\n indexValue: permissionKey,\n });\n }\n\n async save(permission: Partial<Permission>): Promise<Permission> {\n if (!permission.id) permission.id = generateUUID();\n\n const now = new Date().toISOString();\n if (!permission.createdAt) permission.createdAt = now;\n permission.updatedAt = now;\n\n // Build composite key: role#resource#scope\n if (permission.role && permission.resource && permission.scope) {\n permission.permissionKey = `${permission.role}#${permission.resource}#${permission.scope}`;\n }\n\n return this.db.save(permission);\n }\n\n async update(permission: Partial<Permission>): Promise<Permission> {\n permission.updatedAt = new Date().toISOString();\n\n // Rebuild composite key if components changed\n if (permission.role && permission.resource && permission.scope) {\n permission.permissionKey = `${permission.role}#${permission.resource}#${permission.scope}`;\n }\n\n return this.db.update(permission);\n }\n\n async delete(id: string): Promise<boolean> {\n return this.db.delete(id);\n }\n\n async scan(): Promise<List<Partial<Permission>>> {\n return this.db.scan({});\n }\n}\n"]}
@@ -1,39 +0,0 @@
1
- import { Permission } from "../model/role.model";
2
- /**
3
- * PermissionService — cache + orchestration layer for Permission entities.
4
- *
5
- * Design decisions:
6
- * - Read-through cache via PermissionCache (in-flight dedup, TTL).
7
- * - Repository handles only DB I/O.
8
- * - Cache invalidation on every mutation.
9
- * - ~100 roles total, read-heavy → caching makes sense.
10
- * - Cache keyed by `perm:{permissionKey}` for deterministic, namespaced lookup.
11
- */
12
- export declare class PermissionService {
13
- private readonly repo;
14
- private readonly cache;
15
- constructor(tableName: string, cacheTtlMs?: number);
16
- /** Fetch Permission by composite key (role#resource#scope). Read-through cached. */
17
- getPermissionByKey(permissionKey: string): Promise<Permission | null>;
18
- /** Fetch Permission by id. Read-through cached. */
19
- getPermissionById(id: string): Promise<Permission | null>;
20
- /**
21
- * Check if the given role+resource+scope has a specific HTTP method allowed.
22
- *
23
- * Builds the permissionKey `role#resource#scope`, fetches the Permission,
24
- * and checks the method map.
25
- *
26
- * @returns `true` if allowed, `false` otherwise.
27
- */
28
- hasPermission(role: string, resource: string, scope: string, method: string): Promise<boolean>;
29
- createPermission(input: {
30
- role: string;
31
- resource: string;
32
- scope: string;
33
- method: Permission["method"];
34
- }): Promise<Permission>;
35
- updatePermission(id: string, updates: Partial<Pick<Permission, "role" | "resource" | "scope" | "method">>): Promise<Permission>;
36
- deletePermission(id: string): Promise<boolean>;
37
- listPermissions(): Promise<Permission[]>;
38
- clearCache(): void;
39
- }
@@ -1,151 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PermissionService = void 0;
4
- const exception_1 = require("../exception");
5
- const permission_cache_1 = require("./permission.cache");
6
- const permission_repo_1 = require("./permission.repo");
7
- /**
8
- * PermissionService — cache + orchestration layer for Permission entities.
9
- *
10
- * Design decisions:
11
- * - Read-through cache via PermissionCache (in-flight dedup, TTL).
12
- * - Repository handles only DB I/O.
13
- * - Cache invalidation on every mutation.
14
- * - ~100 roles total, read-heavy → caching makes sense.
15
- * - Cache keyed by `perm:{permissionKey}` for deterministic, namespaced lookup.
16
- */
17
- class PermissionService {
18
- repo;
19
- cache;
20
- constructor(tableName, cacheTtlMs) {
21
- this.repo = new permission_repo_1.PermissionRepo(tableName);
22
- this.cache = new permission_cache_1.PermissionCache(cacheTtlMs);
23
- }
24
- // ---------------------------------------------------------------------------
25
- // Core lookup
26
- // ---------------------------------------------------------------------------
27
- /** Fetch Permission by composite key (role#resource#scope). Read-through cached. */
28
- async getPermissionByKey(permissionKey) {
29
- if (!permissionKey)
30
- return null;
31
- return this.cache.getOrFetch(`perm:${permissionKey}`, () => this.repo.findByPermissionKey(permissionKey));
32
- }
33
- /** Fetch Permission by id. Read-through cached. */
34
- async getPermissionById(id) {
35
- if (!id)
36
- return null;
37
- return this.cache.getOrFetch(`id:${id}`, () => this.repo.findById(id));
38
- }
39
- // ---------------------------------------------------------------------------
40
- // Permission check
41
- // ---------------------------------------------------------------------------
42
- /**
43
- * Check if the given role+resource+scope has a specific HTTP method allowed.
44
- *
45
- * Builds the permissionKey `role#resource#scope`, fetches the Permission,
46
- * and checks the method map.
47
- *
48
- * @returns `true` if allowed, `false` otherwise.
49
- */
50
- async hasPermission(role, resource, scope, method) {
51
- if (!role || !resource || !scope || !method)
52
- return false;
53
- const permissionKey = `${role}#${resource}#${scope}`;
54
- const permission = await this.getPermissionByKey(permissionKey);
55
- if (!permission?.method)
56
- return false;
57
- const methodLower = method.toUpperCase();
58
- return permission.method[methodLower] === true;
59
- }
60
- // ---------------------------------------------------------------------------
61
- // CRUD — create
62
- // ---------------------------------------------------------------------------
63
- async createPermission(input) {
64
- if (!input.role?.trim()) {
65
- throw new exception_1.ErrorHttp({ code: 400, error: "BadRequest" }, "Role is required");
66
- }
67
- if (!input.resource?.trim()) {
68
- throw new exception_1.ErrorHttp({ code: 400, error: "BadRequest" }, "Resource is required");
69
- }
70
- if (!input.scope?.trim()) {
71
- throw new exception_1.ErrorHttp({ code: 400, error: "BadRequest" }, "Scope is required");
72
- }
73
- // Duplicate check
74
- const permissionKey = `${input.role}#${input.resource}#${input.scope}`;
75
- const existing = await this.repo.findByPermissionKey(permissionKey);
76
- if (existing) {
77
- throw new exception_1.ErrorHttp({ code: 409, error: "Conflict" }, `Permission "${permissionKey}" already exists`);
78
- }
79
- const saved = await this.repo.save({
80
- role: input.role.trim(),
81
- resource: input.resource.trim(),
82
- scope: input.scope.trim(),
83
- method: input.method ?? {},
84
- permissionKey,
85
- });
86
- this.cache.clear();
87
- return saved;
88
- }
89
- // ---------------------------------------------------------------------------
90
- // CRUD — update
91
- // ---------------------------------------------------------------------------
92
- async updatePermission(id, updates) {
93
- if (!id)
94
- throw new exception_1.ErrorHttp({ code: 400, error: "BadRequest" }, "Permission id is required");
95
- const existing = await this.repo.findById(id);
96
- if (!existing)
97
- throw new exception_1.ErrorHttp({ code: 404, error: "NotFound" }, `Permission "${id}" not found`);
98
- const data = { id };
99
- if (updates.role !== undefined)
100
- data.role = updates.role.trim();
101
- if (updates.resource !== undefined)
102
- data.resource = updates.resource.trim();
103
- if (updates.scope !== undefined)
104
- data.scope = updates.scope.trim();
105
- if (updates.method !== undefined)
106
- data.method = updates.method;
107
- // Rebuild key if any key component changed
108
- const role = data.role ?? existing.role;
109
- const resource = data.resource ?? existing.resource;
110
- const scope = data.scope ?? existing.scope;
111
- data.permissionKey = `${role}#${resource}#${scope}`;
112
- // Check for duplicate key if components changed
113
- if (data.permissionKey !== existing.permissionKey) {
114
- const conflict = await this.repo.findByPermissionKey(data.permissionKey);
115
- if (conflict) {
116
- throw new exception_1.ErrorHttp({ code: 409, error: "Conflict" }, `Permission "${data.permissionKey}" already exists`);
117
- }
118
- }
119
- const updated = await this.repo.update(data);
120
- this.cache.clear();
121
- return updated;
122
- }
123
- // ---------------------------------------------------------------------------
124
- // CRUD — delete
125
- // ---------------------------------------------------------------------------
126
- async deletePermission(id) {
127
- if (!id)
128
- throw new exception_1.ErrorHttp({ code: 400, error: "BadRequest" }, "Permission id is required");
129
- const existing = await this.repo.findById(id);
130
- if (!existing)
131
- throw new exception_1.ErrorHttp({ code: 404, error: "NotFound" }, `Permission "${id}" not found`);
132
- const result = await this.repo.delete(id);
133
- this.cache.clear();
134
- return result;
135
- }
136
- // ---------------------------------------------------------------------------
137
- // CRUD — list
138
- // ---------------------------------------------------------------------------
139
- async listPermissions() {
140
- const result = await this.repo.scan();
141
- return (result.items ?? []);
142
- }
143
- // ---------------------------------------------------------------------------
144
- // Cache control (for testing)
145
- // ---------------------------------------------------------------------------
146
- clearCache() {
147
- this.cache.clear();
148
- }
149
- }
150
- exports.PermissionService = PermissionService;
151
- //# sourceMappingURL=permission.service.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"permission.service.js","sourceRoot":"","sources":["../../src/service/permission.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Permission } from \"../model/role.model\";\nimport { ErrorHttp } from \"../exception\";\nimport { PermissionCache } from \"./permission.cache\";\nimport { PermissionRepo } from \"./permission.repo\";\n\n/**\n * PermissionService — cache + orchestration layer for Permission entities.\n *\n * Design decisions:\n * - Read-through cache via PermissionCache (in-flight dedup, TTL).\n * - Repository handles only DB I/O.\n * - Cache invalidation on every mutation.\n * - ~100 roles total, read-heavy → caching makes sense.\n * - Cache keyed by `perm:{permissionKey}` for deterministic, namespaced lookup.\n */\nexport class PermissionService {\n private readonly repo: PermissionRepo;\n private readonly cache: PermissionCache;\n\n constructor(tableName: string, cacheTtlMs?: number) {\n this.repo = new PermissionRepo(tableName);\n this.cache = new PermissionCache(cacheTtlMs);\n }\n\n // ---------------------------------------------------------------------------\n // Core lookup\n // ---------------------------------------------------------------------------\n\n /** Fetch Permission by composite key (role#resource#scope). Read-through cached. */\n async getPermissionByKey(permissionKey: string): Promise<Permission | null> {\n if (!permissionKey) return null;\n\n return this.cache.getOrFetch(`perm:${permissionKey}`, () => this.repo.findByPermissionKey(permissionKey));\n }\n\n /** Fetch Permission by id. Read-through cached. */\n async getPermissionById(id: string): Promise<Permission | null> {\n if (!id) return null;\n\n return this.cache.getOrFetch(`id:${id}`, () => this.repo.findById(id));\n }\n\n // ---------------------------------------------------------------------------\n // Permission check\n // ---------------------------------------------------------------------------\n\n /**\n * Check if the given role+resource+scope has a specific HTTP method allowed.\n *\n * Builds the permissionKey `role#resource#scope`, fetches the Permission,\n * and checks the method map.\n *\n * @returns `true` if allowed, `false` otherwise.\n */\n async hasPermission(role: string, resource: string, scope: string, method: string): Promise<boolean> {\n if (!role || !resource || !scope || !method) return false;\n\n const permissionKey = `${role}#${resource}#${scope}`;\n const permission = await this.getPermissionByKey(permissionKey);\n if (!permission?.method) return false;\n\n const methodLower = method.toUpperCase() as keyof Permission[\"method\"];\n\n return permission.method[methodLower] === true;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — create\n // ---------------------------------------------------------------------------\n\n async createPermission(input: {\n role: string;\n resource: string;\n scope: string;\n method: Permission[\"method\"];\n }): Promise<Permission> {\n if (!input.role?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Role is required\");\n }\n if (!input.resource?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Resource is required\");\n }\n if (!input.scope?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Scope is required\");\n }\n\n // Duplicate check\n const permissionKey = `${input.role}#${input.resource}#${input.scope}`;\n const existing = await this.repo.findByPermissionKey(permissionKey);\n if (existing) {\n throw new ErrorHttp({ code: 409, error: \"Conflict\" }, `Permission \"${permissionKey}\" already exists`);\n }\n\n const saved = await this.repo.save({\n role: input.role.trim(),\n resource: input.resource.trim(),\n scope: input.scope.trim(),\n method: input.method ?? {},\n permissionKey,\n });\n\n this.cache.clear();\n return saved;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — update\n // ---------------------------------------------------------------------------\n\n async updatePermission(\n id: string,\n updates: Partial<Pick<Permission, \"role\" | \"resource\" | \"scope\" | \"method\">>,\n ): Promise<Permission> {\n if (!id) throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Permission id is required\");\n\n const existing = await this.repo.findById(id);\n if (!existing) throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Permission \"${id}\" not found`);\n\n const data: Partial<Permission> = { id };\n if (updates.role !== undefined) data.role = updates.role.trim();\n if (updates.resource !== undefined) data.resource = updates.resource.trim();\n if (updates.scope !== undefined) data.scope = updates.scope.trim();\n if (updates.method !== undefined) data.method = updates.method;\n\n // Rebuild key if any key component changed\n const role = data.role ?? existing.role;\n const resource = data.resource ?? existing.resource;\n const scope = data.scope ?? existing.scope;\n data.permissionKey = `${role}#${resource}#${scope}`;\n\n // Check for duplicate key if components changed\n if (data.permissionKey !== existing.permissionKey) {\n const conflict = await this.repo.findByPermissionKey(data.permissionKey);\n if (conflict) {\n throw new ErrorHttp({ code: 409, error: \"Conflict\" }, `Permission \"${data.permissionKey}\" already exists`);\n }\n }\n\n const updated = await this.repo.update(data);\n\n this.cache.clear();\n return updated;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — delete\n // ---------------------------------------------------------------------------\n\n async deletePermission(id: string): Promise<boolean> {\n if (!id) throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Permission id is required\");\n\n const existing = await this.repo.findById(id);\n if (!existing) throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Permission \"${id}\" not found`);\n\n const result = await this.repo.delete(id);\n\n this.cache.clear();\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — list\n // ---------------------------------------------------------------------------\n\n async listPermissions(): Promise<Permission[]> {\n const result = await this.repo.scan();\n return (result.items ?? []) as Permission[];\n }\n\n // ---------------------------------------------------------------------------\n // Cache control (for testing)\n // ---------------------------------------------------------------------------\n\n clearCache(): void {\n this.cache.clear();\n }\n}\n"]}