@peac/policy-kit 0.9.18 → 0.9.31
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/LICENSE +190 -0
- package/dist/enforce.d.ts +256 -0
- package/dist/enforce.d.ts.map +1 -0
- package/dist/enforce.js +309 -0
- package/dist/enforce.js.map +1 -0
- package/dist/enforcement-profiles.d.ts +164 -0
- package/dist/enforcement-profiles.d.ts.map +1 -0
- package/dist/enforcement-profiles.js +293 -0
- package/dist/enforcement-profiles.js.map +1 -0
- package/dist/generated/profiles.d.ts +17 -0
- package/dist/generated/profiles.d.ts.map +1 -0
- package/dist/generated/profiles.js +212 -0
- package/dist/generated/profiles.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -1
- package/dist/index.js.map +1 -1
- package/dist/profiles.d.ts +210 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +368 -0
- package/dist/profiles.js.map +1 -0
- package/dist/types.d.ts +620 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +261 -1
- package/dist/types.js.map +1 -1
- package/package.json +14 -10
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Loader API
|
|
3
|
+
*
|
|
4
|
+
* Convenience functions for working with pre-built policy profiles.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { listProfiles, loadProfile, customizeProfile } from '@peac/policy-kit';
|
|
9
|
+
*
|
|
10
|
+
* // List available profiles
|
|
11
|
+
* const ids = listProfiles(); // ['api-provider', 'news-media', ...]
|
|
12
|
+
*
|
|
13
|
+
* // Load a profile
|
|
14
|
+
* const profile = loadProfile('news-media');
|
|
15
|
+
*
|
|
16
|
+
* // Customize with parameters
|
|
17
|
+
* const policy = customizeProfile('news-media', {
|
|
18
|
+
* contact: 'licensing@example.com',
|
|
19
|
+
* rate_limit: '100/hour',
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
import { type ProfileId } from './generated/profiles';
|
|
26
|
+
import type { ProfileDefinition, PolicyDocument, RateLimitConfig } from './types';
|
|
27
|
+
/**
|
|
28
|
+
* Error thrown when profile operations fail
|
|
29
|
+
*/
|
|
30
|
+
export declare class ProfileError extends Error {
|
|
31
|
+
readonly code: 'PROFILE_NOT_FOUND' | 'INVALID_PARAMETER' | 'MISSING_REQUIRED_PARAMETER' | 'VALIDATION_FAILED';
|
|
32
|
+
constructor(message: string, code: 'PROFILE_NOT_FOUND' | 'INVALID_PARAMETER' | 'MISSING_REQUIRED_PARAMETER' | 'VALIDATION_FAILED');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Result of parameter validation
|
|
36
|
+
*/
|
|
37
|
+
export interface ValidationResult {
|
|
38
|
+
valid: boolean;
|
|
39
|
+
errors: ValidationError[];
|
|
40
|
+
warnings: ValidationWarning[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validation error details
|
|
44
|
+
*/
|
|
45
|
+
export interface ValidationError {
|
|
46
|
+
parameter: string;
|
|
47
|
+
message: string;
|
|
48
|
+
code: 'MISSING_REQUIRED' | 'INVALID_FORMAT' | 'UNKNOWN_PARAMETER';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validation warning details
|
|
52
|
+
*/
|
|
53
|
+
export interface ValidationWarning {
|
|
54
|
+
parameter: string;
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Customization result with policy and applied defaults
|
|
59
|
+
*/
|
|
60
|
+
export interface CustomizeResult {
|
|
61
|
+
policy: PolicyDocument;
|
|
62
|
+
appliedDefaults: {
|
|
63
|
+
requirements?: {
|
|
64
|
+
receipt?: boolean;
|
|
65
|
+
};
|
|
66
|
+
rate_limit?: RateLimitConfig;
|
|
67
|
+
};
|
|
68
|
+
parameters: Record<string, string | number | boolean>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* List all available profile IDs
|
|
72
|
+
*
|
|
73
|
+
* @returns Array of profile IDs
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const ids = listProfiles();
|
|
78
|
+
* // ['api-provider', 'news-media', 'open-source', 'saas-docs']
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function listProfiles(): ProfileId[];
|
|
82
|
+
/**
|
|
83
|
+
* Check if a profile ID exists
|
|
84
|
+
*
|
|
85
|
+
* @param id - Profile ID to check
|
|
86
|
+
* @returns true if profile exists
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* if (hasProfile('news-media')) {
|
|
91
|
+
* const profile = loadProfile('news-media');
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function hasProfile(id: string): id is ProfileId;
|
|
96
|
+
/**
|
|
97
|
+
* Load a profile by ID
|
|
98
|
+
*
|
|
99
|
+
* @param id - Profile ID
|
|
100
|
+
* @returns Profile definition
|
|
101
|
+
* @throws ProfileError if profile not found
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const profile = loadProfile('news-media');
|
|
106
|
+
* console.log(profile.name); // 'News Media Publisher'
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export declare function loadProfile(id: ProfileId): ProfileDefinition;
|
|
110
|
+
/**
|
|
111
|
+
* Get a profile by ID, returning undefined if not found
|
|
112
|
+
*
|
|
113
|
+
* @param id - Profile ID
|
|
114
|
+
* @returns Profile definition or undefined
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const profile = getProfile('news-media');
|
|
119
|
+
* if (profile) {
|
|
120
|
+
* console.log(profile.name);
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function getProfile(id: string): ProfileDefinition | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Validate parameters against a profile's requirements
|
|
127
|
+
*
|
|
128
|
+
* @param profile - Profile definition or ID
|
|
129
|
+
* @param params - Parameters to validate
|
|
130
|
+
* @returns Validation result with errors and warnings
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const result = validateProfileParams('news-media', {
|
|
135
|
+
* contact: 'invalid-email',
|
|
136
|
+
* });
|
|
137
|
+
*
|
|
138
|
+
* if (!result.valid) {
|
|
139
|
+
* console.error(result.errors);
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function validateProfileParams(profile: ProfileId | ProfileDefinition, params: Record<string, unknown>): ValidationResult;
|
|
144
|
+
/**
|
|
145
|
+
* Customize a profile with parameters to produce a PolicyDocument
|
|
146
|
+
*
|
|
147
|
+
* This applies parameter values and profile defaults to create a ready-to-use policy.
|
|
148
|
+
*
|
|
149
|
+
* @param profile - Profile definition or ID
|
|
150
|
+
* @param params - Parameters to apply
|
|
151
|
+
* @returns Customization result with policy and applied defaults
|
|
152
|
+
* @throws ProfileError if validation fails
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* const result = customizeProfile('news-media', {
|
|
157
|
+
* contact: 'licensing@example.com',
|
|
158
|
+
* rate_limit: '100/hour',
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* // result.policy is a PolicyDocument ready for use
|
|
162
|
+
* const decision = evaluate(result.policy, context);
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export declare function customizeProfile(profile: ProfileId | ProfileDefinition, params?: Record<string, unknown>): CustomizeResult;
|
|
166
|
+
/**
|
|
167
|
+
* Get all profiles as an array
|
|
168
|
+
*
|
|
169
|
+
* @returns Array of all profile definitions
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const profiles = getAllProfiles();
|
|
174
|
+
* for (const profile of profiles) {
|
|
175
|
+
* console.log(`${profile.id}: ${profile.name}`);
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export declare function getAllProfiles(): ProfileDefinition[];
|
|
180
|
+
/**
|
|
181
|
+
* Get profile summary for display purposes
|
|
182
|
+
*
|
|
183
|
+
* @param profile - Profile definition or ID
|
|
184
|
+
* @returns Summary object with key information
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const summary = getProfileSummary('news-media');
|
|
189
|
+
* console.log(summary);
|
|
190
|
+
* // {
|
|
191
|
+
* // id: 'news-media',
|
|
192
|
+
* // name: 'News Media Publisher',
|
|
193
|
+
* // defaultDecision: 'deny',
|
|
194
|
+
* // ruleCount: 3,
|
|
195
|
+
* // requiresReceipt: true,
|
|
196
|
+
* // requiredParams: ['contact']
|
|
197
|
+
* // }
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export declare function getProfileSummary(profile: ProfileId | ProfileDefinition): {
|
|
201
|
+
id: string;
|
|
202
|
+
name: string;
|
|
203
|
+
description: string;
|
|
204
|
+
defaultDecision: 'allow' | 'deny' | 'review';
|
|
205
|
+
ruleCount: number;
|
|
206
|
+
requiresReceipt: boolean;
|
|
207
|
+
requiredParams: string[];
|
|
208
|
+
optionalParams: string[];
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=profiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAoB,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGpG;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAChB,mBAAmB,GACnB,mBAAmB,GACnB,4BAA4B,GAC5B,mBAAmB;gBALvB,OAAO,EAAE,MAAM,EACC,IAAI,EAChB,mBAAmB,GACnB,mBAAmB,GACnB,4BAA4B,GAC5B,mBAAmB;CAK1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;CACnE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE;QACf,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC,UAAU,CAAC,EAAE,eAAe,CAAC;KAC9B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,SAAS,EAAE,CAE1C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,SAAS,CAEtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,SAAS,GAAG,iBAAiB,CAM5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAKpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,GAAG,iBAAiB,EACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,gBAAgB,CAyDlB;AA8DD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,GAAG,iBAAiB,EACtC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,eAAe,CAmDjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,EAAE,CAEpD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,GAAG,iBAAiB,GAAG;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAwBA"}
|
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Profile Loader API
|
|
4
|
+
*
|
|
5
|
+
* Convenience functions for working with pre-built policy profiles.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { listProfiles, loadProfile, customizeProfile } from '@peac/policy-kit';
|
|
10
|
+
*
|
|
11
|
+
* // List available profiles
|
|
12
|
+
* const ids = listProfiles(); // ['api-provider', 'news-media', ...]
|
|
13
|
+
*
|
|
14
|
+
* // Load a profile
|
|
15
|
+
* const profile = loadProfile('news-media');
|
|
16
|
+
*
|
|
17
|
+
* // Customize with parameters
|
|
18
|
+
* const policy = customizeProfile('news-media', {
|
|
19
|
+
* contact: 'licensing@example.com',
|
|
20
|
+
* rate_limit: '100/hour',
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @packageDocumentation
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.ProfileError = void 0;
|
|
28
|
+
exports.listProfiles = listProfiles;
|
|
29
|
+
exports.hasProfile = hasProfile;
|
|
30
|
+
exports.loadProfile = loadProfile;
|
|
31
|
+
exports.getProfile = getProfile;
|
|
32
|
+
exports.validateProfileParams = validateProfileParams;
|
|
33
|
+
exports.customizeProfile = customizeProfile;
|
|
34
|
+
exports.getAllProfiles = getAllProfiles;
|
|
35
|
+
exports.getProfileSummary = getProfileSummary;
|
|
36
|
+
const profiles_1 = require("./generated/profiles");
|
|
37
|
+
const types_1 = require("./types");
|
|
38
|
+
/**
|
|
39
|
+
* Error thrown when profile operations fail
|
|
40
|
+
*/
|
|
41
|
+
class ProfileError extends Error {
|
|
42
|
+
code;
|
|
43
|
+
constructor(message, code) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.code = code;
|
|
46
|
+
this.name = 'ProfileError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.ProfileError = ProfileError;
|
|
50
|
+
/**
|
|
51
|
+
* List all available profile IDs
|
|
52
|
+
*
|
|
53
|
+
* @returns Array of profile IDs
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const ids = listProfiles();
|
|
58
|
+
* // ['api-provider', 'news-media', 'open-source', 'saas-docs']
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
function listProfiles() {
|
|
62
|
+
return [...profiles_1.PROFILE_IDS];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if a profile ID exists
|
|
66
|
+
*
|
|
67
|
+
* @param id - Profile ID to check
|
|
68
|
+
* @returns true if profile exists
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* if (hasProfile('news-media')) {
|
|
73
|
+
* const profile = loadProfile('news-media');
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
function hasProfile(id) {
|
|
78
|
+
return profiles_1.PROFILE_IDS.includes(id);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load a profile by ID
|
|
82
|
+
*
|
|
83
|
+
* @param id - Profile ID
|
|
84
|
+
* @returns Profile definition
|
|
85
|
+
* @throws ProfileError if profile not found
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const profile = loadProfile('news-media');
|
|
90
|
+
* console.log(profile.name); // 'News Media Publisher'
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
function loadProfile(id) {
|
|
94
|
+
const profile = profiles_1.PROFILES[id];
|
|
95
|
+
if (!profile) {
|
|
96
|
+
throw new ProfileError(`Profile not found: ${id}`, 'PROFILE_NOT_FOUND');
|
|
97
|
+
}
|
|
98
|
+
return profile;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get a profile by ID, returning undefined if not found
|
|
102
|
+
*
|
|
103
|
+
* @param id - Profile ID
|
|
104
|
+
* @returns Profile definition or undefined
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const profile = getProfile('news-media');
|
|
109
|
+
* if (profile) {
|
|
110
|
+
* console.log(profile.name);
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
function getProfile(id) {
|
|
115
|
+
if (!hasProfile(id)) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
return profiles_1.PROFILES[id];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Validate parameters against a profile's requirements
|
|
122
|
+
*
|
|
123
|
+
* @param profile - Profile definition or ID
|
|
124
|
+
* @param params - Parameters to validate
|
|
125
|
+
* @returns Validation result with errors and warnings
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* const result = validateProfileParams('news-media', {
|
|
130
|
+
* contact: 'invalid-email',
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* if (!result.valid) {
|
|
134
|
+
* console.error(result.errors);
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
function validateProfileParams(profile, params) {
|
|
139
|
+
const def = typeof profile === 'string' ? loadProfile(profile) : profile;
|
|
140
|
+
const errors = [];
|
|
141
|
+
const warnings = [];
|
|
142
|
+
const paramDefs = def.parameters || {};
|
|
143
|
+
const providedKeys = new Set(Object.keys(params));
|
|
144
|
+
// Check for required parameters
|
|
145
|
+
for (const [key, paramDef] of Object.entries(paramDefs)) {
|
|
146
|
+
if (paramDef.required && !providedKeys.has(key)) {
|
|
147
|
+
errors.push({
|
|
148
|
+
parameter: key,
|
|
149
|
+
message: `Required parameter missing: ${key}`,
|
|
150
|
+
code: 'MISSING_REQUIRED',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Validate provided parameters
|
|
155
|
+
for (const [key, value] of Object.entries(params)) {
|
|
156
|
+
const paramDef = paramDefs[key];
|
|
157
|
+
if (!paramDef) {
|
|
158
|
+
errors.push({
|
|
159
|
+
parameter: key,
|
|
160
|
+
message: `Unknown parameter: ${key}`,
|
|
161
|
+
code: 'UNKNOWN_PARAMETER',
|
|
162
|
+
});
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
// Skip validation if value is undefined/null
|
|
166
|
+
if (value === undefined || value === null) {
|
|
167
|
+
if (paramDef.required) {
|
|
168
|
+
errors.push({
|
|
169
|
+
parameter: key,
|
|
170
|
+
message: `Required parameter is null/undefined: ${key}`,
|
|
171
|
+
code: 'MISSING_REQUIRED',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// Type-specific validation
|
|
177
|
+
const strValue = String(value);
|
|
178
|
+
const validationError = validateParameterValue(key, strValue, paramDef);
|
|
179
|
+
if (validationError) {
|
|
180
|
+
errors.push(validationError);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
valid: errors.length === 0,
|
|
185
|
+
errors,
|
|
186
|
+
warnings,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate a single parameter value
|
|
191
|
+
*/
|
|
192
|
+
function validateParameterValue(key, value, paramDef) {
|
|
193
|
+
if (!paramDef.validate) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
switch (paramDef.validate) {
|
|
197
|
+
case 'email': {
|
|
198
|
+
// Basic email validation
|
|
199
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
200
|
+
if (!emailRegex.test(value)) {
|
|
201
|
+
return {
|
|
202
|
+
parameter: key,
|
|
203
|
+
message: `Invalid email format: ${value}`,
|
|
204
|
+
code: 'INVALID_FORMAT',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case 'url': {
|
|
210
|
+
try {
|
|
211
|
+
new URL(value);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return {
|
|
215
|
+
parameter: key,
|
|
216
|
+
message: `Invalid URL format: ${value}`,
|
|
217
|
+
code: 'INVALID_FORMAT',
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
case 'rate_limit': {
|
|
223
|
+
try {
|
|
224
|
+
(0, types_1.parseRateLimit)(value);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// Also try parsing as RateLimitConfig object
|
|
228
|
+
const parsed = types_1.RateLimitConfigSchema.safeParse(value);
|
|
229
|
+
if (!parsed.success) {
|
|
230
|
+
return {
|
|
231
|
+
parameter: key,
|
|
232
|
+
message: `Invalid rate limit format: ${value}. Expected format: "100/hour" or RateLimitConfig object`,
|
|
233
|
+
code: 'INVALID_FORMAT',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Customize a profile with parameters to produce a PolicyDocument
|
|
244
|
+
*
|
|
245
|
+
* This applies parameter values and profile defaults to create a ready-to-use policy.
|
|
246
|
+
*
|
|
247
|
+
* @param profile - Profile definition or ID
|
|
248
|
+
* @param params - Parameters to apply
|
|
249
|
+
* @returns Customization result with policy and applied defaults
|
|
250
|
+
* @throws ProfileError if validation fails
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* const result = customizeProfile('news-media', {
|
|
255
|
+
* contact: 'licensing@example.com',
|
|
256
|
+
* rate_limit: '100/hour',
|
|
257
|
+
* });
|
|
258
|
+
*
|
|
259
|
+
* // result.policy is a PolicyDocument ready for use
|
|
260
|
+
* const decision = evaluate(result.policy, context);
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
function customizeProfile(profile, params = {}) {
|
|
264
|
+
const def = typeof profile === 'string' ? loadProfile(profile) : profile;
|
|
265
|
+
// Validate parameters
|
|
266
|
+
const validation = validateProfileParams(def, params);
|
|
267
|
+
if (!validation.valid) {
|
|
268
|
+
const errorMessages = validation.errors.map((e) => `${e.parameter}: ${e.message}`).join(', ');
|
|
269
|
+
throw new ProfileError(`Parameter validation failed: ${errorMessages}`, 'VALIDATION_FAILED');
|
|
270
|
+
}
|
|
271
|
+
// Apply defaults for missing optional parameters
|
|
272
|
+
const appliedParams = {};
|
|
273
|
+
for (const [key, paramDef] of Object.entries(def.parameters || {})) {
|
|
274
|
+
if (key in params && params[key] !== undefined && params[key] !== null) {
|
|
275
|
+
appliedParams[key] = params[key];
|
|
276
|
+
}
|
|
277
|
+
else if (paramDef.default !== undefined) {
|
|
278
|
+
appliedParams[key] = paramDef.default;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Deep clone the policy to avoid mutations
|
|
282
|
+
const policy = JSON.parse(JSON.stringify(def.policy));
|
|
283
|
+
// Apply profile defaults
|
|
284
|
+
const appliedDefaults = {};
|
|
285
|
+
if (def.defaults?.requirements) {
|
|
286
|
+
appliedDefaults.requirements = { ...def.defaults.requirements };
|
|
287
|
+
}
|
|
288
|
+
if (def.defaults?.rate_limit) {
|
|
289
|
+
appliedDefaults.rate_limit = { ...def.defaults.rate_limit };
|
|
290
|
+
}
|
|
291
|
+
// If rate_limit parameter was provided, parse and apply it
|
|
292
|
+
if (appliedParams.rate_limit) {
|
|
293
|
+
const rateLimitValue = appliedParams.rate_limit;
|
|
294
|
+
if (typeof rateLimitValue === 'string') {
|
|
295
|
+
try {
|
|
296
|
+
appliedDefaults.rate_limit = (0, types_1.parseRateLimit)(rateLimitValue);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Already validated, should not happen
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
policy,
|
|
305
|
+
appliedDefaults,
|
|
306
|
+
parameters: appliedParams,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get all profiles as an array
|
|
311
|
+
*
|
|
312
|
+
* @returns Array of all profile definitions
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* const profiles = getAllProfiles();
|
|
317
|
+
* for (const profile of profiles) {
|
|
318
|
+
* console.log(`${profile.id}: ${profile.name}`);
|
|
319
|
+
* }
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
function getAllProfiles() {
|
|
323
|
+
return profiles_1.PROFILE_IDS.map((id) => profiles_1.PROFILES[id]);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Get profile summary for display purposes
|
|
327
|
+
*
|
|
328
|
+
* @param profile - Profile definition or ID
|
|
329
|
+
* @returns Summary object with key information
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* const summary = getProfileSummary('news-media');
|
|
334
|
+
* console.log(summary);
|
|
335
|
+
* // {
|
|
336
|
+
* // id: 'news-media',
|
|
337
|
+
* // name: 'News Media Publisher',
|
|
338
|
+
* // defaultDecision: 'deny',
|
|
339
|
+
* // ruleCount: 3,
|
|
340
|
+
* // requiresReceipt: true,
|
|
341
|
+
* // requiredParams: ['contact']
|
|
342
|
+
* // }
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
function getProfileSummary(profile) {
|
|
346
|
+
const def = typeof profile === 'string' ? loadProfile(profile) : profile;
|
|
347
|
+
const requiredParams = [];
|
|
348
|
+
const optionalParams = [];
|
|
349
|
+
for (const [key, paramDef] of Object.entries(def.parameters || {})) {
|
|
350
|
+
if (paramDef.required) {
|
|
351
|
+
requiredParams.push(key);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
optionalParams.push(key);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
id: def.id,
|
|
359
|
+
name: def.name,
|
|
360
|
+
description: def.description,
|
|
361
|
+
defaultDecision: def.policy.defaults.decision,
|
|
362
|
+
ruleCount: def.policy.rules?.length || 0,
|
|
363
|
+
requiresReceipt: def.defaults?.requirements?.receipt ?? false,
|
|
364
|
+
requiredParams: requiredParams.sort(),
|
|
365
|
+
optionalParams: optionalParams.sort(),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;AAwEH,oCAEC;AAeD,gCAEC;AAeD,kCAMC;AAgBD,gCAKC;AAoBD,sDA4DC;AAmFD,4CAsDC;AAeD,wCAEC;AAsBD,8CAiCC;AApaD,mDAA6E;AAE7E,mCAAgE;AAEhE;;GAEG;AACH,MAAa,YAAa,SAAQ,KAAK;IAGnB;IAFlB,YACE,OAAe,EACC,IAIO;QAEvB,KAAK,CAAC,OAAO,CAAC,CAAC;QANC,SAAI,GAAJ,IAAI,CAIG;QAGvB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAZD,oCAYC;AAwCD;;;;;;;;;;GAUG;AACH,SAAgB,YAAY;IAC1B,OAAO,CAAC,GAAG,sBAAW,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,UAAU,CAAC,EAAU;IACnC,OAAO,sBAAW,CAAC,QAAQ,CAAC,EAAe,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,WAAW,CAAC,EAAa;IACvC,MAAM,OAAO,GAAG,mBAAQ,CAAC,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,sBAAsB,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,UAAU,CAAC,EAAU;IACnC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;QACpB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,mBAAQ,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,qBAAqB,CACnC,OAAsC,EACtC,MAA+B;IAE/B,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACzE,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IAEzC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;IACvC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAElD,gCAAgC;IAChC,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACxD,IAAI,QAAQ,CAAC,QAAQ,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC;gBACV,SAAS,EAAE,GAAG;gBACd,OAAO,EAAE,+BAA+B,GAAG,EAAE;gBAC7C,IAAI,EAAE,kBAAkB;aACzB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAiC,CAAC;QAEhE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC;gBACV,SAAS,EAAE,GAAG;gBACd,OAAO,EAAE,sBAAsB,GAAG,EAAE;gBACpC,IAAI,EAAE,mBAAmB;aAC1B,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,6CAA6C;QAC7C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC1C,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC;oBACV,SAAS,EAAE,GAAG;oBACd,OAAO,EAAE,yCAAyC,GAAG,EAAE;oBACvD,IAAI,EAAE,kBAAkB;iBACzB,CAAC,CAAC;YACL,CAAC;YACD,SAAS;QACX,CAAC;QAED,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,eAAe,GAAG,sBAAsB,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACxE,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAC7B,GAAW,EACX,KAAa,EACb,QAA0B;IAE1B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,yBAAyB;YACzB,MAAM,UAAU,GAAG,4BAA4B,CAAC;YAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,OAAO;oBACL,SAAS,EAAE,GAAG;oBACd,OAAO,EAAE,yBAAyB,KAAK,EAAE;oBACzC,IAAI,EAAE,gBAAgB;iBACvB,CAAC;YACJ,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,IAAI,CAAC;gBACH,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;oBACL,SAAS,EAAE,GAAG;oBACd,OAAO,EAAE,uBAAuB,KAAK,EAAE;oBACvC,IAAI,EAAE,gBAAgB;iBACvB,CAAC;YACJ,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,IAAI,CAAC;gBACH,IAAA,sBAAc,EAAC,KAAK,CAAC,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,6CAA6C;gBAC7C,MAAM,MAAM,GAAG,6BAAqB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpB,OAAO;wBACL,SAAS,EAAE,GAAG;wBACd,OAAO,EAAE,8BAA8B,KAAK,yDAAyD;wBACrG,IAAI,EAAE,gBAAgB;qBACvB,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,gBAAgB,CAC9B,OAAsC,EACtC,SAAkC,EAAE;IAEpC,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAEzE,sBAAsB;IACtB,MAAM,UAAU,GAAG,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,MAAM,IAAI,YAAY,CAAC,gCAAgC,aAAa,EAAE,EAAE,mBAAmB,CAAC,CAAC;IAC/F,CAAC;IAED,iDAAiD;IACjD,MAAM,aAAa,GAA8C,EAAE,CAAC;IACpE,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;QACnE,IAAI,GAAG,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;YACvE,aAAa,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAA8B,CAAC;QAChE,CAAC;aAAM,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1C,aAAa,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC;QACxC,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,MAAM,MAAM,GAAmB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtE,yBAAyB;IACzB,MAAM,eAAe,GAAuC,EAAE,CAAC;IAE/D,IAAI,GAAG,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;QAC/B,eAAe,CAAC,YAAY,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;IAClE,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC;QAC7B,eAAe,CAAC,UAAU,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IAC9D,CAAC;IAED,2DAA2D;IAC3D,IAAI,aAAa,CAAC,UAAU,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAG,aAAa,CAAC,UAAU,CAAC;QAChD,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC;gBACH,eAAe,CAAC,UAAU,GAAG,IAAA,sBAAc,EAAC,cAAc,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,uCAAuC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM;QACN,eAAe;QACf,UAAU,EAAE,aAAa;KAC1B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,cAAc;IAC5B,OAAO,sBAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,mBAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,iBAAiB,CAAC,OAAsC;IAUtE,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAEzE,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;QACnE,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACtB,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ;QAC7C,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;QACxC,eAAe,EAAE,GAAG,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,IAAI,KAAK;QAC7D,cAAc,EAAE,cAAc,CAAC,IAAI,EAAE;QACrC,cAAc,EAAE,cAAc,CAAC,IAAI,EAAE;KACtC,CAAC;AACJ,CAAC"}
|