@uniforge/testing 0.1.0-alpha.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.
- package/dist/auth/index.d.cts +177 -0
- package/dist/auth/index.d.ts +177 -0
- package/dist/auth/index.js +459 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +418 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/billing/index.d.cts +27 -0
- package/dist/billing/index.d.ts +27 -0
- package/dist/billing/index.js +208 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/index.mjs +178 -0
- package/dist/billing/index.mjs.map +1 -0
- package/dist/database/index.d.cts +399 -0
- package/dist/database/index.d.ts +399 -0
- package/dist/database/index.js +19054 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/index.mjs +19046 -0
- package/dist/database/index.mjs.map +1 -0
- package/dist/graphql/index.d.cts +23 -0
- package/dist/graphql/index.d.ts +23 -0
- package/dist/graphql/index.js +18511 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/index.mjs +18505 -0
- package/dist/graphql/index.mjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -0
- package/dist/multi-store/index.d.cts +66 -0
- package/dist/multi-store/index.d.ts +66 -0
- package/dist/multi-store/index.js +319 -0
- package/dist/multi-store/index.js.map +1 -0
- package/dist/multi-store/index.mjs +287 -0
- package/dist/multi-store/index.mjs.map +1 -0
- package/dist/multi-tenant/index.d.cts +15 -0
- package/dist/multi-tenant/index.d.ts +15 -0
- package/dist/multi-tenant/index.js +87 -0
- package/dist/multi-tenant/index.js.map +1 -0
- package/dist/multi-tenant/index.mjs +57 -0
- package/dist/multi-tenant/index.mjs.map +1 -0
- package/dist/performance/index.d.cts +60 -0
- package/dist/performance/index.d.ts +60 -0
- package/dist/performance/index.js +280 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/index.mjs +246 -0
- package/dist/performance/index.mjs.map +1 -0
- package/dist/platform/index.d.cts +71 -0
- package/dist/platform/index.d.ts +71 -0
- package/dist/platform/index.js +435 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +396 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/rbac/index.d.cts +21 -0
- package/dist/rbac/index.d.ts +21 -0
- package/dist/rbac/index.js +178 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/index.mjs +150 -0
- package/dist/rbac/index.mjs.map +1 -0
- package/dist/security/index.d.cts +73 -0
- package/dist/security/index.d.ts +73 -0
- package/dist/security/index.js +246 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/index.mjs +211 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/shopify-api/index.d.cts +139 -0
- package/dist/shopify-api/index.d.ts +139 -0
- package/dist/shopify-api/index.js +469 -0
- package/dist/shopify-api/index.js.map +1 -0
- package/dist/shopify-api/index.mjs +439 -0
- package/dist/shopify-api/index.mjs.map +1 -0
- package/dist/shopify-compliance/index.d.cts +85 -0
- package/dist/shopify-compliance/index.d.ts +85 -0
- package/dist/shopify-compliance/index.js +287 -0
- package/dist/shopify-compliance/index.js.map +1 -0
- package/dist/shopify-compliance/index.mjs +259 -0
- package/dist/shopify-compliance/index.mjs.map +1 -0
- package/dist/webhooks/index.d.cts +127 -0
- package/dist/webhooks/index.d.ts +127 -0
- package/dist/webhooks/index.js +18934 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/index.mjs +18916 -0
- package/dist/webhooks/index.mjs.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// src/rbac/mock-rbac-service.ts
|
|
2
|
+
import { ROLE_HIERARCHY } from "@uniforge/platform-core/rbac";
|
|
3
|
+
var MOCK_ROLE_PERMISSIONS = {
|
|
4
|
+
owner: [],
|
|
5
|
+
admin: [
|
|
6
|
+
"products:read",
|
|
7
|
+
"products:write",
|
|
8
|
+
"orders:read",
|
|
9
|
+
"orders:write",
|
|
10
|
+
"customers:read",
|
|
11
|
+
"customers:write",
|
|
12
|
+
"analytics:read",
|
|
13
|
+
"settings:read",
|
|
14
|
+
"settings:write",
|
|
15
|
+
"settings:manage",
|
|
16
|
+
"staff:read",
|
|
17
|
+
"staff:write"
|
|
18
|
+
],
|
|
19
|
+
staff: [
|
|
20
|
+
"products:read",
|
|
21
|
+
"products:write",
|
|
22
|
+
"orders:read",
|
|
23
|
+
"orders:write",
|
|
24
|
+
"customers:read",
|
|
25
|
+
"analytics:read",
|
|
26
|
+
"settings:read"
|
|
27
|
+
],
|
|
28
|
+
collaborator: [
|
|
29
|
+
"products:read",
|
|
30
|
+
"orders:read",
|
|
31
|
+
"analytics:read"
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
function makeKey(shopDomain, userId) {
|
|
35
|
+
return `${shopDomain}:${userId}`;
|
|
36
|
+
}
|
|
37
|
+
function createMockRBACService() {
|
|
38
|
+
const _members = /* @__PURE__ */ new Map();
|
|
39
|
+
function _reset() {
|
|
40
|
+
_members.clear();
|
|
41
|
+
}
|
|
42
|
+
async function upsertMember(input) {
|
|
43
|
+
const key = makeKey(input.shopDomain, input.userId);
|
|
44
|
+
const existing = _members.get(key);
|
|
45
|
+
const now = /* @__PURE__ */ new Date();
|
|
46
|
+
const member = {
|
|
47
|
+
id: existing?.id ?? `mock-member-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
48
|
+
shopDomain: input.shopDomain,
|
|
49
|
+
userId: input.userId,
|
|
50
|
+
email: input.email,
|
|
51
|
+
role: input.role,
|
|
52
|
+
customPermissions: input.customPermissions ?? null,
|
|
53
|
+
createdAt: existing?.createdAt ?? now,
|
|
54
|
+
updatedAt: now
|
|
55
|
+
};
|
|
56
|
+
_members.set(key, member);
|
|
57
|
+
return member;
|
|
58
|
+
}
|
|
59
|
+
async function getMember(shopDomain, userId) {
|
|
60
|
+
return _members.get(makeKey(shopDomain, userId)) ?? null;
|
|
61
|
+
}
|
|
62
|
+
async function listMembers(shopDomain) {
|
|
63
|
+
const result = [];
|
|
64
|
+
for (const member of _members.values()) {
|
|
65
|
+
if (member.shopDomain === shopDomain) {
|
|
66
|
+
result.push(member);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
async function updateMember(shopDomain, userId, input) {
|
|
72
|
+
const key = makeKey(shopDomain, userId);
|
|
73
|
+
const existing = _members.get(key);
|
|
74
|
+
if (!existing) {
|
|
75
|
+
throw new Error(`Member not found: ${shopDomain}:${userId}`);
|
|
76
|
+
}
|
|
77
|
+
if (input.role !== void 0) {
|
|
78
|
+
existing.role = input.role;
|
|
79
|
+
}
|
|
80
|
+
if (input.email !== void 0) {
|
|
81
|
+
existing.email = input.email;
|
|
82
|
+
}
|
|
83
|
+
if (input.customPermissions !== void 0) {
|
|
84
|
+
existing.customPermissions = input.customPermissions;
|
|
85
|
+
}
|
|
86
|
+
existing.updatedAt = /* @__PURE__ */ new Date();
|
|
87
|
+
return existing;
|
|
88
|
+
}
|
|
89
|
+
async function removeMember(shopDomain, userId) {
|
|
90
|
+
_members.delete(makeKey(shopDomain, userId));
|
|
91
|
+
}
|
|
92
|
+
async function hasPermission(shopDomain, userId, permission) {
|
|
93
|
+
const member = _members.get(makeKey(shopDomain, userId));
|
|
94
|
+
if (!member) return false;
|
|
95
|
+
if (member.role === "owner") return true;
|
|
96
|
+
const perms = resolvePerms(member.role, member.customPermissions);
|
|
97
|
+
return perms.includes(permission);
|
|
98
|
+
}
|
|
99
|
+
async function getEffectivePermissions(shopDomain, userId) {
|
|
100
|
+
const member = _members.get(makeKey(shopDomain, userId));
|
|
101
|
+
if (!member) return [];
|
|
102
|
+
if (member.role === "owner") return ["*"];
|
|
103
|
+
return resolvePerms(member.role, member.customPermissions);
|
|
104
|
+
}
|
|
105
|
+
function isRoleAtLeast(role, minimumRole) {
|
|
106
|
+
const roleIndex = ROLE_HIERARCHY.indexOf(role);
|
|
107
|
+
const minIndex = ROLE_HIERARCHY.indexOf(minimumRole);
|
|
108
|
+
if (roleIndex === -1 || minIndex === -1) return false;
|
|
109
|
+
return roleIndex <= minIndex;
|
|
110
|
+
}
|
|
111
|
+
function resolvePerms(role, custom) {
|
|
112
|
+
const rolePerms = MOCK_ROLE_PERMISSIONS[role] ?? [];
|
|
113
|
+
if (!custom || custom.length === 0) return [...rolePerms];
|
|
114
|
+
const combined = /* @__PURE__ */ new Set([...rolePerms, ...custom]);
|
|
115
|
+
return [...combined];
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
_members,
|
|
119
|
+
_reset,
|
|
120
|
+
upsertMember,
|
|
121
|
+
getMember,
|
|
122
|
+
listMembers,
|
|
123
|
+
updateMember,
|
|
124
|
+
removeMember,
|
|
125
|
+
hasPermission,
|
|
126
|
+
getEffectivePermissions,
|
|
127
|
+
isRoleAtLeast
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/rbac/factories.ts
|
|
132
|
+
function createTestShopMember(overrides) {
|
|
133
|
+
const now = /* @__PURE__ */ new Date();
|
|
134
|
+
const defaults = {
|
|
135
|
+
id: `member-${Date.now()}`,
|
|
136
|
+
shopDomain: "test-shop.myshopify.com",
|
|
137
|
+
userId: 12345,
|
|
138
|
+
email: "staff@test-shop.com",
|
|
139
|
+
role: "staff",
|
|
140
|
+
customPermissions: null,
|
|
141
|
+
createdAt: now,
|
|
142
|
+
updatedAt: now
|
|
143
|
+
};
|
|
144
|
+
return { ...defaults, ...overrides };
|
|
145
|
+
}
|
|
146
|
+
export {
|
|
147
|
+
createMockRBACService,
|
|
148
|
+
createTestShopMember
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/rbac/mock-rbac-service.ts","../../src/rbac/factories.ts"],"sourcesContent":["/**\n * Mock RBACService for unit testing.\n *\n * Provides an in-memory implementation with internal Maps.\n */\n\nimport type {\n RBACService,\n Role,\n ShopMember,\n UpsertShopMemberInput,\n UpdateShopMemberInput,\n} from '@uniforge/platform-core/rbac';\nimport { ROLE_HIERARCHY } from '@uniforge/platform-core/rbac';\n\nexport type MockRBACService = RBACService & {\n _members: Map<string, ShopMember>;\n _reset(): void;\n};\n\n// Default permissions for each role (mirrors core implementation)\nconst MOCK_ROLE_PERMISSIONS: Record<Role, string[]> = {\n owner: [],\n admin: [\n 'products:read', 'products:write',\n 'orders:read', 'orders:write',\n 'customers:read', 'customers:write',\n 'analytics:read',\n 'settings:read', 'settings:write', 'settings:manage',\n 'staff:read', 'staff:write',\n ],\n staff: [\n 'products:read', 'products:write',\n 'orders:read', 'orders:write',\n 'customers:read',\n 'analytics:read',\n 'settings:read',\n ],\n collaborator: [\n 'products:read',\n 'orders:read',\n 'analytics:read',\n ],\n};\n\nfunction makeKey(shopDomain: string, userId: number): string {\n return `${shopDomain}:${userId}`;\n}\n\nexport function createMockRBACService(): MockRBACService {\n const _members = new Map<string, ShopMember>();\n\n function _reset(): void {\n _members.clear();\n }\n\n async function upsertMember(input: UpsertShopMemberInput): Promise<ShopMember> {\n const key = makeKey(input.shopDomain, input.userId);\n const existing = _members.get(key);\n const now = new Date();\n const member: ShopMember = {\n id: existing?.id ?? `mock-member-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n shopDomain: input.shopDomain,\n userId: input.userId,\n email: input.email,\n role: input.role,\n customPermissions: input.customPermissions ?? null,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n };\n _members.set(key, member);\n return member;\n }\n\n async function getMember(shopDomain: string, userId: number): Promise<ShopMember | null> {\n return _members.get(makeKey(shopDomain, userId)) ?? null;\n }\n\n async function listMembers(shopDomain: string): Promise<ShopMember[]> {\n const result: ShopMember[] = [];\n for (const member of _members.values()) {\n if (member.shopDomain === shopDomain) {\n result.push(member);\n }\n }\n return result;\n }\n\n async function updateMember(\n shopDomain: string,\n userId: number,\n input: UpdateShopMemberInput,\n ): Promise<ShopMember> {\n const key = makeKey(shopDomain, userId);\n const existing = _members.get(key);\n if (!existing) {\n throw new Error(`Member not found: ${shopDomain}:${userId}`);\n }\n if (input.role !== undefined) {\n existing.role = input.role;\n }\n if (input.email !== undefined) {\n existing.email = input.email;\n }\n if (input.customPermissions !== undefined) {\n existing.customPermissions = input.customPermissions;\n }\n existing.updatedAt = new Date();\n return existing;\n }\n\n async function removeMember(shopDomain: string, userId: number): Promise<void> {\n _members.delete(makeKey(shopDomain, userId));\n }\n\n async function hasPermission(\n shopDomain: string,\n userId: number,\n permission: string,\n ): Promise<boolean> {\n const member = _members.get(makeKey(shopDomain, userId));\n if (!member) return false;\n if (member.role === 'owner') return true;\n const perms = resolvePerms(member.role, member.customPermissions);\n return perms.includes(permission);\n }\n\n async function getEffectivePermissions(\n shopDomain: string,\n userId: number,\n ): Promise<string[]> {\n const member = _members.get(makeKey(shopDomain, userId));\n if (!member) return [];\n if (member.role === 'owner') return ['*'];\n return resolvePerms(member.role, member.customPermissions);\n }\n\n function isRoleAtLeast(role: Role, minimumRole: Role): boolean {\n const roleIndex = ROLE_HIERARCHY.indexOf(role);\n const minIndex = ROLE_HIERARCHY.indexOf(minimumRole);\n if (roleIndex === -1 || minIndex === -1) return false;\n return roleIndex <= minIndex;\n }\n\n function resolvePerms(role: Role, custom: string[] | null): string[] {\n const rolePerms = MOCK_ROLE_PERMISSIONS[role] ?? [];\n if (!custom || custom.length === 0) return [...rolePerms];\n const combined = new Set([...rolePerms, ...custom]);\n return [...combined];\n }\n\n return {\n _members,\n _reset,\n upsertMember,\n getMember,\n listMembers,\n updateMember,\n removeMember,\n hasPermission,\n getEffectivePermissions,\n isRoleAtLeast,\n };\n}\n","/**\n * Test data factories for RBAC types.\n */\n\nimport type { ShopMember } from '@uniforge/platform-core/rbac';\n\nexport function createTestShopMember(overrides?: Partial<ShopMember>): ShopMember {\n const now = new Date();\n const defaults: ShopMember = {\n id: `member-${Date.now()}`,\n shopDomain: 'test-shop.myshopify.com',\n userId: 12345,\n email: 'staff@test-shop.com',\n role: 'staff',\n customPermissions: null,\n createdAt: now,\n updatedAt: now,\n };\n return { ...defaults, ...overrides };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SecurityHeadersConfig, CSPDirectives, RateLimitConfig, SecurityAuditFinding, SecurityAuditResult, InputValidationRule, SanitizeOptions, InputValidator, InputValidationResult, RequestRateLimiter, RateLimitResult } from '@uniforge/platform-core/security';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test data factories for security types.
|
|
5
|
+
*
|
|
6
|
+
* Each factory provides sensible defaults that can be overridden.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare function createTestSecurityHeadersConfig(overrides?: Partial<SecurityHeadersConfig>): SecurityHeadersConfig;
|
|
10
|
+
declare function createTestCSPDirectives(overrides?: Partial<CSPDirectives>): CSPDirectives;
|
|
11
|
+
declare function createTestRateLimitConfig(overrides?: Partial<RateLimitConfig>): RateLimitConfig;
|
|
12
|
+
declare function createTestAuditFinding(overrides?: Partial<SecurityAuditFinding>): SecurityAuditFinding;
|
|
13
|
+
declare function createTestAuditResult(overrides?: Partial<SecurityAuditResult>): SecurityAuditResult;
|
|
14
|
+
declare function createTestValidationRule(overrides?: Partial<InputValidationRule>): InputValidationRule;
|
|
15
|
+
declare function createTestSanitizeOptions(overrides?: Partial<SanitizeOptions>): SanitizeOptions;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Mock InputValidator for unit testing.
|
|
19
|
+
*
|
|
20
|
+
* Provides a configurable in-memory implementation of InputValidator
|
|
21
|
+
* that tracks all calls for test assertions.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
interface ValidateCallRecord {
|
|
25
|
+
input: Record<string, unknown>;
|
|
26
|
+
rules: InputValidationRule[];
|
|
27
|
+
}
|
|
28
|
+
interface SanitizeCallRecord {
|
|
29
|
+
value: string;
|
|
30
|
+
options?: SanitizeOptions;
|
|
31
|
+
}
|
|
32
|
+
declare class MockInputValidator implements InputValidator {
|
|
33
|
+
/** Recorded validate() calls for assertions. */
|
|
34
|
+
validateCalls: ValidateCallRecord[];
|
|
35
|
+
/** Recorded sanitize() calls for assertions. */
|
|
36
|
+
sanitizeCalls: SanitizeCallRecord[];
|
|
37
|
+
/** Configurable return value for validate(). */
|
|
38
|
+
validateResult: InputValidationResult;
|
|
39
|
+
/** Configurable return value for sanitize(). When set, overrides the default passthrough. */
|
|
40
|
+
sanitizeReturnValue: string | undefined;
|
|
41
|
+
validate(input: Record<string, unknown>, rules: InputValidationRule[]): InputValidationResult;
|
|
42
|
+
sanitize(value: string, options?: SanitizeOptions): string;
|
|
43
|
+
isValidShopDomain(domain: string): boolean;
|
|
44
|
+
isValidEmail(email: string): boolean;
|
|
45
|
+
isValidUrl(url: string): boolean;
|
|
46
|
+
isValidApiKey(key: string): boolean;
|
|
47
|
+
/** Reset all recorded calls and configurable values. */
|
|
48
|
+
_reset(): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Mock RequestRateLimiter for unit testing.
|
|
53
|
+
*
|
|
54
|
+
* Provides an in-memory rate limiter that can be configured to
|
|
55
|
+
* allow or deny all requests, with call tracking for assertions.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
declare class MockRateLimiter implements RequestRateLimiter {
|
|
59
|
+
readonly config: RateLimitConfig;
|
|
60
|
+
/** Recorded keys passed to check(). */
|
|
61
|
+
checkCalls: string[];
|
|
62
|
+
/** Recorded keys passed to reset(). */
|
|
63
|
+
resetCalls: string[];
|
|
64
|
+
/** When true, all requests are allowed. When false, all are denied. */
|
|
65
|
+
allowAll: boolean;
|
|
66
|
+
constructor(config?: Partial<RateLimitConfig>);
|
|
67
|
+
check(key: string): Promise<RateLimitResult>;
|
|
68
|
+
reset(key: string): Promise<void>;
|
|
69
|
+
/** Reset all recorded calls and restore default behavior. */
|
|
70
|
+
_reset(): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { MockInputValidator, MockRateLimiter, type SanitizeCallRecord, type ValidateCallRecord, createTestAuditFinding, createTestAuditResult, createTestCSPDirectives, createTestRateLimitConfig, createTestSanitizeOptions, createTestSecurityHeadersConfig, createTestValidationRule };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SecurityHeadersConfig, CSPDirectives, RateLimitConfig, SecurityAuditFinding, SecurityAuditResult, InputValidationRule, SanitizeOptions, InputValidator, InputValidationResult, RequestRateLimiter, RateLimitResult } from '@uniforge/platform-core/security';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test data factories for security types.
|
|
5
|
+
*
|
|
6
|
+
* Each factory provides sensible defaults that can be overridden.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare function createTestSecurityHeadersConfig(overrides?: Partial<SecurityHeadersConfig>): SecurityHeadersConfig;
|
|
10
|
+
declare function createTestCSPDirectives(overrides?: Partial<CSPDirectives>): CSPDirectives;
|
|
11
|
+
declare function createTestRateLimitConfig(overrides?: Partial<RateLimitConfig>): RateLimitConfig;
|
|
12
|
+
declare function createTestAuditFinding(overrides?: Partial<SecurityAuditFinding>): SecurityAuditFinding;
|
|
13
|
+
declare function createTestAuditResult(overrides?: Partial<SecurityAuditResult>): SecurityAuditResult;
|
|
14
|
+
declare function createTestValidationRule(overrides?: Partial<InputValidationRule>): InputValidationRule;
|
|
15
|
+
declare function createTestSanitizeOptions(overrides?: Partial<SanitizeOptions>): SanitizeOptions;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Mock InputValidator for unit testing.
|
|
19
|
+
*
|
|
20
|
+
* Provides a configurable in-memory implementation of InputValidator
|
|
21
|
+
* that tracks all calls for test assertions.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
interface ValidateCallRecord {
|
|
25
|
+
input: Record<string, unknown>;
|
|
26
|
+
rules: InputValidationRule[];
|
|
27
|
+
}
|
|
28
|
+
interface SanitizeCallRecord {
|
|
29
|
+
value: string;
|
|
30
|
+
options?: SanitizeOptions;
|
|
31
|
+
}
|
|
32
|
+
declare class MockInputValidator implements InputValidator {
|
|
33
|
+
/** Recorded validate() calls for assertions. */
|
|
34
|
+
validateCalls: ValidateCallRecord[];
|
|
35
|
+
/** Recorded sanitize() calls for assertions. */
|
|
36
|
+
sanitizeCalls: SanitizeCallRecord[];
|
|
37
|
+
/** Configurable return value for validate(). */
|
|
38
|
+
validateResult: InputValidationResult;
|
|
39
|
+
/** Configurable return value for sanitize(). When set, overrides the default passthrough. */
|
|
40
|
+
sanitizeReturnValue: string | undefined;
|
|
41
|
+
validate(input: Record<string, unknown>, rules: InputValidationRule[]): InputValidationResult;
|
|
42
|
+
sanitize(value: string, options?: SanitizeOptions): string;
|
|
43
|
+
isValidShopDomain(domain: string): boolean;
|
|
44
|
+
isValidEmail(email: string): boolean;
|
|
45
|
+
isValidUrl(url: string): boolean;
|
|
46
|
+
isValidApiKey(key: string): boolean;
|
|
47
|
+
/** Reset all recorded calls and configurable values. */
|
|
48
|
+
_reset(): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Mock RequestRateLimiter for unit testing.
|
|
53
|
+
*
|
|
54
|
+
* Provides an in-memory rate limiter that can be configured to
|
|
55
|
+
* allow or deny all requests, with call tracking for assertions.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
declare class MockRateLimiter implements RequestRateLimiter {
|
|
59
|
+
readonly config: RateLimitConfig;
|
|
60
|
+
/** Recorded keys passed to check(). */
|
|
61
|
+
checkCalls: string[];
|
|
62
|
+
/** Recorded keys passed to reset(). */
|
|
63
|
+
resetCalls: string[];
|
|
64
|
+
/** When true, all requests are allowed. When false, all are denied. */
|
|
65
|
+
allowAll: boolean;
|
|
66
|
+
constructor(config?: Partial<RateLimitConfig>);
|
|
67
|
+
check(key: string): Promise<RateLimitResult>;
|
|
68
|
+
reset(key: string): Promise<void>;
|
|
69
|
+
/** Reset all recorded calls and restore default behavior. */
|
|
70
|
+
_reset(): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { MockInputValidator, MockRateLimiter, type SanitizeCallRecord, type ValidateCallRecord, createTestAuditFinding, createTestAuditResult, createTestCSPDirectives, createTestRateLimitConfig, createTestSanitizeOptions, createTestSecurityHeadersConfig, createTestValidationRule };
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/security/index.ts
|
|
21
|
+
var security_exports = {};
|
|
22
|
+
__export(security_exports, {
|
|
23
|
+
MockInputValidator: () => MockInputValidator,
|
|
24
|
+
MockRateLimiter: () => MockRateLimiter,
|
|
25
|
+
createTestAuditFinding: () => createTestAuditFinding,
|
|
26
|
+
createTestAuditResult: () => createTestAuditResult,
|
|
27
|
+
createTestCSPDirectives: () => createTestCSPDirectives,
|
|
28
|
+
createTestRateLimitConfig: () => createTestRateLimitConfig,
|
|
29
|
+
createTestSanitizeOptions: () => createTestSanitizeOptions,
|
|
30
|
+
createTestSecurityHeadersConfig: () => createTestSecurityHeadersConfig,
|
|
31
|
+
createTestValidationRule: () => createTestValidationRule
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(security_exports);
|
|
34
|
+
|
|
35
|
+
// src/security/factories.ts
|
|
36
|
+
function createTestSecurityHeadersConfig(overrides) {
|
|
37
|
+
const defaults = {
|
|
38
|
+
hsts: true,
|
|
39
|
+
noSniff: true,
|
|
40
|
+
frameOptions: "DENY",
|
|
41
|
+
xssProtection: true,
|
|
42
|
+
referrerPolicy: "strict-origin-when-cross-origin"
|
|
43
|
+
};
|
|
44
|
+
const result = { ...defaults, ...overrides };
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function createTestCSPDirectives(overrides) {
|
|
48
|
+
const defaults = {
|
|
49
|
+
defaultSrc: ["'self'"],
|
|
50
|
+
scriptSrc: ["'self'"],
|
|
51
|
+
styleSrc: ["'self'"],
|
|
52
|
+
imgSrc: ["'self'"],
|
|
53
|
+
connectSrc: ["'self'"],
|
|
54
|
+
fontSrc: ["'self'"],
|
|
55
|
+
frameSrc: ["'self'"],
|
|
56
|
+
frameAncestors: ["'self'"]
|
|
57
|
+
};
|
|
58
|
+
return { ...defaults, ...overrides };
|
|
59
|
+
}
|
|
60
|
+
function createTestRateLimitConfig(overrides) {
|
|
61
|
+
const defaults = {
|
|
62
|
+
windowMs: 6e4,
|
|
63
|
+
maxRequests: 100
|
|
64
|
+
};
|
|
65
|
+
const result = { ...defaults };
|
|
66
|
+
if (overrides?.windowMs !== void 0) {
|
|
67
|
+
result.windowMs = overrides.windowMs;
|
|
68
|
+
}
|
|
69
|
+
if (overrides?.maxRequests !== void 0) {
|
|
70
|
+
result.maxRequests = overrides.maxRequests;
|
|
71
|
+
}
|
|
72
|
+
if (overrides?.keyPrefix !== void 0) {
|
|
73
|
+
result.keyPrefix = overrides.keyPrefix;
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
function createTestAuditFinding(overrides) {
|
|
78
|
+
const defaults = {
|
|
79
|
+
id: "TEST-001",
|
|
80
|
+
severity: "medium",
|
|
81
|
+
category: "test",
|
|
82
|
+
title: "Test finding",
|
|
83
|
+
description: "A test security finding for unit tests.",
|
|
84
|
+
recommendation: "Address this finding in a test environment."
|
|
85
|
+
};
|
|
86
|
+
return { ...defaults, ...overrides };
|
|
87
|
+
}
|
|
88
|
+
function createTestAuditResult(overrides) {
|
|
89
|
+
const defaults = {
|
|
90
|
+
passed: true,
|
|
91
|
+
score: 100,
|
|
92
|
+
findings: [],
|
|
93
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
94
|
+
};
|
|
95
|
+
return { ...defaults, ...overrides };
|
|
96
|
+
}
|
|
97
|
+
function createTestValidationRule(overrides) {
|
|
98
|
+
const defaults = {
|
|
99
|
+
field: "testField",
|
|
100
|
+
type: "string"
|
|
101
|
+
};
|
|
102
|
+
const result = { ...defaults };
|
|
103
|
+
if (overrides?.field !== void 0) {
|
|
104
|
+
result.field = overrides.field;
|
|
105
|
+
}
|
|
106
|
+
if (overrides?.type !== void 0) {
|
|
107
|
+
result.type = overrides.type;
|
|
108
|
+
}
|
|
109
|
+
if (overrides?.required !== void 0) {
|
|
110
|
+
result.required = overrides.required;
|
|
111
|
+
}
|
|
112
|
+
if (overrides?.maxLength !== void 0) {
|
|
113
|
+
result.maxLength = overrides.maxLength;
|
|
114
|
+
}
|
|
115
|
+
if (overrides?.pattern !== void 0) {
|
|
116
|
+
result.pattern = overrides.pattern;
|
|
117
|
+
}
|
|
118
|
+
if (overrides?.message !== void 0) {
|
|
119
|
+
result.message = overrides.message;
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
function createTestSanitizeOptions(overrides) {
|
|
124
|
+
const defaults = {
|
|
125
|
+
stripHtml: true
|
|
126
|
+
};
|
|
127
|
+
const result = { ...defaults };
|
|
128
|
+
if (overrides?.stripHtml !== void 0) {
|
|
129
|
+
result.stripHtml = overrides.stripHtml;
|
|
130
|
+
}
|
|
131
|
+
if (overrides?.maxLength !== void 0) {
|
|
132
|
+
result.maxLength = overrides.maxLength;
|
|
133
|
+
}
|
|
134
|
+
if (overrides?.allowedPattern !== void 0) {
|
|
135
|
+
result.allowedPattern = overrides.allowedPattern;
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/security/mock-input-validator.ts
|
|
141
|
+
var MockInputValidator = class {
|
|
142
|
+
/** Recorded validate() calls for assertions. */
|
|
143
|
+
validateCalls = [];
|
|
144
|
+
/** Recorded sanitize() calls for assertions. */
|
|
145
|
+
sanitizeCalls = [];
|
|
146
|
+
/** Configurable return value for validate(). */
|
|
147
|
+
validateResult = {
|
|
148
|
+
valid: true,
|
|
149
|
+
errors: [],
|
|
150
|
+
sanitized: {}
|
|
151
|
+
};
|
|
152
|
+
/** Configurable return value for sanitize(). When set, overrides the default passthrough. */
|
|
153
|
+
sanitizeReturnValue;
|
|
154
|
+
validate(input, rules) {
|
|
155
|
+
this.validateCalls.push({ input, rules });
|
|
156
|
+
return this.validateResult;
|
|
157
|
+
}
|
|
158
|
+
sanitize(value, options) {
|
|
159
|
+
const callRecord = { value };
|
|
160
|
+
if (options) {
|
|
161
|
+
callRecord.options = options;
|
|
162
|
+
}
|
|
163
|
+
this.sanitizeCalls.push(callRecord);
|
|
164
|
+
if (this.sanitizeReturnValue !== void 0) {
|
|
165
|
+
return this.sanitizeReturnValue;
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
isValidShopDomain(domain) {
|
|
170
|
+
return domain.endsWith(".myshopify.com");
|
|
171
|
+
}
|
|
172
|
+
isValidEmail(email) {
|
|
173
|
+
return email.includes("@");
|
|
174
|
+
}
|
|
175
|
+
isValidUrl(url) {
|
|
176
|
+
return url.startsWith("http");
|
|
177
|
+
}
|
|
178
|
+
isValidApiKey(key) {
|
|
179
|
+
return key.length >= 32;
|
|
180
|
+
}
|
|
181
|
+
/** Reset all recorded calls and configurable values. */
|
|
182
|
+
_reset() {
|
|
183
|
+
this.validateCalls = [];
|
|
184
|
+
this.sanitizeCalls = [];
|
|
185
|
+
this.validateResult = { valid: true, errors: [], sanitized: {} };
|
|
186
|
+
this.sanitizeReturnValue = void 0;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/security/mock-rate-limiter.ts
|
|
191
|
+
var MockRateLimiter = class {
|
|
192
|
+
config;
|
|
193
|
+
/** Recorded keys passed to check(). */
|
|
194
|
+
checkCalls = [];
|
|
195
|
+
/** Recorded keys passed to reset(). */
|
|
196
|
+
resetCalls = [];
|
|
197
|
+
/** When true, all requests are allowed. When false, all are denied. */
|
|
198
|
+
allowAll = true;
|
|
199
|
+
constructor(config) {
|
|
200
|
+
this.config = {
|
|
201
|
+
windowMs: config?.windowMs ?? 6e4,
|
|
202
|
+
maxRequests: config?.maxRequests ?? 100
|
|
203
|
+
};
|
|
204
|
+
if (config?.keyPrefix !== void 0) {
|
|
205
|
+
this.config.keyPrefix = config.keyPrefix;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async check(key) {
|
|
209
|
+
this.checkCalls.push(key);
|
|
210
|
+
if (this.allowAll) {
|
|
211
|
+
return {
|
|
212
|
+
allowed: true,
|
|
213
|
+
remaining: 10,
|
|
214
|
+
resetAt: Date.now() + 6e4
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
allowed: false,
|
|
219
|
+
remaining: 0,
|
|
220
|
+
resetAt: Date.now() + 6e4,
|
|
221
|
+
retryAfter: 3e4
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async reset(key) {
|
|
225
|
+
this.resetCalls.push(key);
|
|
226
|
+
}
|
|
227
|
+
/** Reset all recorded calls and restore default behavior. */
|
|
228
|
+
_reset() {
|
|
229
|
+
this.checkCalls = [];
|
|
230
|
+
this.resetCalls = [];
|
|
231
|
+
this.allowAll = true;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
235
|
+
0 && (module.exports = {
|
|
236
|
+
MockInputValidator,
|
|
237
|
+
MockRateLimiter,
|
|
238
|
+
createTestAuditFinding,
|
|
239
|
+
createTestAuditResult,
|
|
240
|
+
createTestCSPDirectives,
|
|
241
|
+
createTestRateLimitConfig,
|
|
242
|
+
createTestSanitizeOptions,
|
|
243
|
+
createTestSecurityHeadersConfig,
|
|
244
|
+
createTestValidationRule
|
|
245
|
+
});
|
|
246
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/security/index.ts","../../src/security/factories.ts","../../src/security/mock-input-validator.ts","../../src/security/mock-rate-limiter.ts"],"sourcesContent":["/**\n * @uniforge/testing - Security\n *\n * Mock security utilities and test data factories for security testing.\n */\n\nexport {\n createTestSecurityHeadersConfig,\n createTestCSPDirectives,\n createTestRateLimitConfig,\n createTestAuditFinding,\n createTestAuditResult,\n createTestValidationRule,\n createTestSanitizeOptions,\n} from './factories';\n\nexport { MockInputValidator } from './mock-input-validator';\nexport type { ValidateCallRecord, SanitizeCallRecord } from './mock-input-validator';\n\nexport { MockRateLimiter } from './mock-rate-limiter';\n","/**\n * Test data factories for security types.\n *\n * Each factory provides sensible defaults that can be overridden.\n */\n\nimport type {\n SecurityHeadersConfig,\n CSPDirectives,\n RateLimitConfig,\n SecurityAuditFinding,\n SecurityAuditResult,\n InputValidationRule,\n SanitizeOptions,\n} from '@uniforge/platform-core/security';\n\nexport function createTestSecurityHeadersConfig(\n overrides?: Partial<SecurityHeadersConfig>,\n): SecurityHeadersConfig {\n const defaults: SecurityHeadersConfig = {\n hsts: true,\n noSniff: true,\n frameOptions: 'DENY',\n xssProtection: true,\n referrerPolicy: 'strict-origin-when-cross-origin',\n };\n\n const result: SecurityHeadersConfig = { ...defaults, ...overrides };\n return result;\n}\n\nexport function createTestCSPDirectives(\n overrides?: Partial<CSPDirectives>,\n): CSPDirectives {\n const defaults: CSPDirectives = {\n defaultSrc: [\"'self'\"],\n scriptSrc: [\"'self'\"],\n styleSrc: [\"'self'\"],\n imgSrc: [\"'self'\"],\n connectSrc: [\"'self'\"],\n fontSrc: [\"'self'\"],\n frameSrc: [\"'self'\"],\n frameAncestors: [\"'self'\"],\n };\n\n return { ...defaults, ...overrides };\n}\n\nexport function createTestRateLimitConfig(\n overrides?: Partial<RateLimitConfig>,\n): RateLimitConfig {\n const defaults: RateLimitConfig = {\n windowMs: 60000,\n maxRequests: 100,\n };\n\n const result: RateLimitConfig = { ...defaults };\n\n if (overrides?.windowMs !== undefined) {\n result.windowMs = overrides.windowMs;\n }\n if (overrides?.maxRequests !== undefined) {\n result.maxRequests = overrides.maxRequests;\n }\n if (overrides?.keyPrefix !== undefined) {\n result.keyPrefix = overrides.keyPrefix;\n }\n\n return result;\n}\n\nexport function createTestAuditFinding(\n overrides?: Partial<SecurityAuditFinding>,\n): SecurityAuditFinding {\n const defaults: SecurityAuditFinding = {\n id: 'TEST-001',\n severity: 'medium',\n category: 'test',\n title: 'Test finding',\n description: 'A test security finding for unit tests.',\n recommendation: 'Address this finding in a test environment.',\n };\n\n return { ...defaults, ...overrides };\n}\n\nexport function createTestAuditResult(\n overrides?: Partial<SecurityAuditResult>,\n): SecurityAuditResult {\n const defaults: SecurityAuditResult = {\n passed: true,\n score: 100,\n findings: [],\n timestamp: new Date().toISOString(),\n };\n\n return { ...defaults, ...overrides };\n}\n\nexport function createTestValidationRule(\n overrides?: Partial<InputValidationRule>,\n): InputValidationRule {\n const defaults: InputValidationRule = {\n field: 'testField',\n type: 'string',\n };\n\n const result: InputValidationRule = { ...defaults };\n\n if (overrides?.field !== undefined) {\n result.field = overrides.field;\n }\n if (overrides?.type !== undefined) {\n result.type = overrides.type;\n }\n if (overrides?.required !== undefined) {\n result.required = overrides.required;\n }\n if (overrides?.maxLength !== undefined) {\n result.maxLength = overrides.maxLength;\n }\n if (overrides?.pattern !== undefined) {\n result.pattern = overrides.pattern;\n }\n if (overrides?.message !== undefined) {\n result.message = overrides.message;\n }\n\n return result;\n}\n\nexport function createTestSanitizeOptions(\n overrides?: Partial<SanitizeOptions>,\n): SanitizeOptions {\n const defaults: SanitizeOptions = {\n stripHtml: true,\n };\n\n const result: SanitizeOptions = { ...defaults };\n\n if (overrides?.stripHtml !== undefined) {\n result.stripHtml = overrides.stripHtml;\n }\n if (overrides?.maxLength !== undefined) {\n result.maxLength = overrides.maxLength;\n }\n if (overrides?.allowedPattern !== undefined) {\n result.allowedPattern = overrides.allowedPattern;\n }\n\n return result;\n}\n","/**\n * Mock InputValidator for unit testing.\n *\n * Provides a configurable in-memory implementation of InputValidator\n * that tracks all calls for test assertions.\n */\n\nimport type {\n InputValidator,\n InputValidationRule,\n InputValidationResult,\n SanitizeOptions,\n} from '@uniforge/platform-core/security';\n\nexport interface ValidateCallRecord {\n input: Record<string, unknown>;\n rules: InputValidationRule[];\n}\n\nexport interface SanitizeCallRecord {\n value: string;\n options?: SanitizeOptions;\n}\n\nexport class MockInputValidator implements InputValidator {\n /** Recorded validate() calls for assertions. */\n validateCalls: ValidateCallRecord[] = [];\n\n /** Recorded sanitize() calls for assertions. */\n sanitizeCalls: SanitizeCallRecord[] = [];\n\n /** Configurable return value for validate(). */\n validateResult: InputValidationResult = {\n valid: true,\n errors: [],\n sanitized: {},\n };\n\n /** Configurable return value for sanitize(). When set, overrides the default passthrough. */\n sanitizeReturnValue: string | undefined;\n\n validate(\n input: Record<string, unknown>,\n rules: InputValidationRule[],\n ): InputValidationResult {\n this.validateCalls.push({ input, rules });\n return this.validateResult;\n }\n\n sanitize(value: string, options?: SanitizeOptions): string {\n const callRecord: SanitizeCallRecord = { value };\n if (options) {\n callRecord.options = options;\n }\n this.sanitizeCalls.push(callRecord);\n if (this.sanitizeReturnValue !== undefined) {\n return this.sanitizeReturnValue;\n }\n return value;\n }\n\n isValidShopDomain(domain: string): boolean {\n return domain.endsWith('.myshopify.com');\n }\n\n isValidEmail(email: string): boolean {\n return email.includes('@');\n }\n\n isValidUrl(url: string): boolean {\n return url.startsWith('http');\n }\n\n isValidApiKey(key: string): boolean {\n return key.length >= 32;\n }\n\n /** Reset all recorded calls and configurable values. */\n _reset(): void {\n this.validateCalls = [];\n this.sanitizeCalls = [];\n this.validateResult = { valid: true, errors: [], sanitized: {} };\n this.sanitizeReturnValue = undefined;\n }\n}\n","/**\n * Mock RequestRateLimiter for unit testing.\n *\n * Provides an in-memory rate limiter that can be configured to\n * allow or deny all requests, with call tracking for assertions.\n */\n\nimport type {\n RequestRateLimiter,\n RateLimitConfig,\n RateLimitResult,\n} from '@uniforge/platform-core/security';\n\nexport class MockRateLimiter implements RequestRateLimiter {\n readonly config: RateLimitConfig;\n\n /** Recorded keys passed to check(). */\n checkCalls: string[] = [];\n\n /** Recorded keys passed to reset(). */\n resetCalls: string[] = [];\n\n /** When true, all requests are allowed. When false, all are denied. */\n allowAll = true;\n\n constructor(config?: Partial<RateLimitConfig>) {\n this.config = {\n windowMs: config?.windowMs ?? 60000,\n maxRequests: config?.maxRequests ?? 100,\n };\n if (config?.keyPrefix !== undefined) {\n (this.config as { keyPrefix: string }).keyPrefix = config.keyPrefix;\n }\n }\n\n async check(key: string): Promise<RateLimitResult> {\n this.checkCalls.push(key);\n\n if (this.allowAll) {\n return {\n allowed: true,\n remaining: 10,\n resetAt: Date.now() + 60000,\n };\n }\n\n return {\n allowed: false,\n remaining: 0,\n resetAt: Date.now() + 60000,\n retryAfter: 30000,\n };\n }\n\n async reset(key: string): Promise<void> {\n this.resetCalls.push(key);\n }\n\n /** Reset all recorded calls and restore default behavior. */\n _reset(): void {\n this.checkCalls = [];\n this.resetCalls = [];\n this.allowAll = true;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgBO,SAAS,gCACd,WACuB;AACvB,QAAM,WAAkC;AAAA,IACtC,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd,eAAe;AAAA,IACf,gBAAgB;AAAA,EAClB;AAEA,QAAM,SAAgC,EAAE,GAAG,UAAU,GAAG,UAAU;AAClE,SAAO;AACT;AAEO,SAAS,wBACd,WACe;AACf,QAAM,WAA0B;AAAA,IAC9B,YAAY,CAAC,QAAQ;AAAA,IACrB,WAAW,CAAC,QAAQ;AAAA,IACpB,UAAU,CAAC,QAAQ;AAAA,IACnB,QAAQ,CAAC,QAAQ;AAAA,IACjB,YAAY,CAAC,QAAQ;AAAA,IACrB,SAAS,CAAC,QAAQ;AAAA,IAClB,UAAU,CAAC,QAAQ;AAAA,IACnB,gBAAgB,CAAC,QAAQ;AAAA,EAC3B;AAEA,SAAO,EAAE,GAAG,UAAU,GAAG,UAAU;AACrC;AAEO,SAAS,0BACd,WACiB;AACjB,QAAM,WAA4B;AAAA,IAChC,UAAU;AAAA,IACV,aAAa;AAAA,EACf;AAEA,QAAM,SAA0B,EAAE,GAAG,SAAS;AAE9C,MAAI,WAAW,aAAa,QAAW;AACrC,WAAO,WAAW,UAAU;AAAA,EAC9B;AACA,MAAI,WAAW,gBAAgB,QAAW;AACxC,WAAO,cAAc,UAAU;AAAA,EACjC;AACA,MAAI,WAAW,cAAc,QAAW;AACtC,WAAO,YAAY,UAAU;AAAA,EAC/B;AAEA,SAAO;AACT;AAEO,SAAS,uBACd,WACsB;AACtB,QAAM,WAAiC;AAAA,IACrC,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,EAClB;AAEA,SAAO,EAAE,GAAG,UAAU,GAAG,UAAU;AACrC;AAEO,SAAS,sBACd,WACqB;AACrB,QAAM,WAAgC;AAAA,IACpC,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,UAAU,CAAC;AAAA,IACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AAEA,SAAO,EAAE,GAAG,UAAU,GAAG,UAAU;AACrC;AAEO,SAAS,yBACd,WACqB;AACrB,QAAM,WAAgC;AAAA,IACpC,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAEA,QAAM,SAA8B,EAAE,GAAG,SAAS;AAElD,MAAI,WAAW,UAAU,QAAW;AAClC,WAAO,QAAQ,UAAU;AAAA,EAC3B;AACA,MAAI,WAAW,SAAS,QAAW;AACjC,WAAO,OAAO,UAAU;AAAA,EAC1B;AACA,MAAI,WAAW,aAAa,QAAW;AACrC,WAAO,WAAW,UAAU;AAAA,EAC9B;AACA,MAAI,WAAW,cAAc,QAAW;AACtC,WAAO,YAAY,UAAU;AAAA,EAC/B;AACA,MAAI,WAAW,YAAY,QAAW;AACpC,WAAO,UAAU,UAAU;AAAA,EAC7B;AACA,MAAI,WAAW,YAAY,QAAW;AACpC,WAAO,UAAU,UAAU;AAAA,EAC7B;AAEA,SAAO;AACT;AAEO,SAAS,0BACd,WACiB;AACjB,QAAM,WAA4B;AAAA,IAChC,WAAW;AAAA,EACb;AAEA,QAAM,SAA0B,EAAE,GAAG,SAAS;AAE9C,MAAI,WAAW,cAAc,QAAW;AACtC,WAAO,YAAY,UAAU;AAAA,EAC/B;AACA,MAAI,WAAW,cAAc,QAAW;AACtC,WAAO,YAAY,UAAU;AAAA,EAC/B;AACA,MAAI,WAAW,mBAAmB,QAAW;AAC3C,WAAO,iBAAiB,UAAU;AAAA,EACpC;AAEA,SAAO;AACT;;;AC/HO,IAAM,qBAAN,MAAmD;AAAA;AAAA,EAExD,gBAAsC,CAAC;AAAA;AAAA,EAGvC,gBAAsC,CAAC;AAAA;AAAA,EAGvC,iBAAwC;AAAA,IACtC,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,WAAW,CAAC;AAAA,EACd;AAAA;AAAA,EAGA;AAAA,EAEA,SACE,OACA,OACuB;AACvB,SAAK,cAAc,KAAK,EAAE,OAAO,MAAM,CAAC;AACxC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAS,OAAe,SAAmC;AACzD,UAAM,aAAiC,EAAE,MAAM;AAC/C,QAAI,SAAS;AACX,iBAAW,UAAU;AAAA,IACvB;AACA,SAAK,cAAc,KAAK,UAAU;AAClC,QAAI,KAAK,wBAAwB,QAAW;AAC1C,aAAO,KAAK;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,kBAAkB,QAAyB;AACzC,WAAO,OAAO,SAAS,gBAAgB;AAAA,EACzC;AAAA,EAEA,aAAa,OAAwB;AACnC,WAAO,MAAM,SAAS,GAAG;AAAA,EAC3B;AAAA,EAEA,WAAW,KAAsB;AAC/B,WAAO,IAAI,WAAW,MAAM;AAAA,EAC9B;AAAA,EAEA,cAAc,KAAsB;AAClC,WAAO,IAAI,UAAU;AAAA,EACvB;AAAA;AAAA,EAGA,SAAe;AACb,SAAK,gBAAgB,CAAC;AACtB,SAAK,gBAAgB,CAAC;AACtB,SAAK,iBAAiB,EAAE,OAAO,MAAM,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAC/D,SAAK,sBAAsB;AAAA,EAC7B;AACF;;;ACvEO,IAAM,kBAAN,MAAoD;AAAA,EAChD;AAAA;AAAA,EAGT,aAAuB,CAAC;AAAA;AAAA,EAGxB,aAAuB,CAAC;AAAA;AAAA,EAGxB,WAAW;AAAA,EAEX,YAAY,QAAmC;AAC7C,SAAK,SAAS;AAAA,MACZ,UAAU,QAAQ,YAAY;AAAA,MAC9B,aAAa,QAAQ,eAAe;AAAA,IACtC;AACA,QAAI,QAAQ,cAAc,QAAW;AACnC,MAAC,KAAK,OAAiC,YAAY,OAAO;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAuC;AACjD,SAAK,WAAW,KAAK,GAAG;AAExB,QAAI,KAAK,UAAU;AACjB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW;AAAA,QACX,SAAS,KAAK,IAAI,IAAI;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,WAAW;AAAA,MACX,SAAS,KAAK,IAAI,IAAI;AAAA,MACtB,YAAY;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAA4B;AACtC,SAAK,WAAW,KAAK,GAAG;AAAA,EAC1B;AAAA;AAAA,EAGA,SAAe;AACb,SAAK,aAAa,CAAC;AACnB,SAAK,aAAa,CAAC;AACnB,SAAK,WAAW;AAAA,EAClB;AACF;","names":[]}
|