@veloxts/cli 0.6.26 → 0.6.29
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/CHANGELOG.md +759 -0
- package/GUIDE.md +239 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/tenant.d.ts +19 -0
- package/dist/commands/tenant.d.ts.map +1 -0
- package/dist/commands/tenant.js +635 -0
- package/dist/commands/tenant.js.map +1 -0
- package/dist/generators/templates/model.js +2 -2
- package/dist/generators/templates/model.js.map +1 -1
- package/dist/generators/templates/procedure.js +4 -4
- package/dist/generators/templates/procedure.js.map +1 -1
- package/dist/generators/templates/resource.js +4 -4
- package/dist/generators/templates/resource.js.map +1 -1
- package/package.json +12 -7
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant command - Multi-tenancy management commands
|
|
3
|
+
*
|
|
4
|
+
* Provides subcommands for managing tenant schemas:
|
|
5
|
+
* - tenant:create - Create a new tenant with PostgreSQL schema
|
|
6
|
+
* - tenant:list - List all tenants
|
|
7
|
+
* - tenant:migrate - Run migrations on tenant schemas
|
|
8
|
+
* - tenant:status - Show tenant status
|
|
9
|
+
* - tenant:suspend - Suspend a tenant
|
|
10
|
+
* - tenant:activate - Activate a suspended tenant
|
|
11
|
+
*
|
|
12
|
+
* SECURITY: All SQL queries use parameterized queries to prevent SQL injection
|
|
13
|
+
*/
|
|
14
|
+
import * as p from '@clack/prompts';
|
|
15
|
+
import { createTenantSchemaManager, InvalidSlugError, slugToSchemaName, TenantError, } from '@veloxts/orm/tenant';
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
import pg from 'pg';
|
|
18
|
+
import pc from 'picocolors';
|
|
19
|
+
import { error, info, step, success, warning } from '../utils/output.js';
|
|
20
|
+
const { Client } = pg;
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Security: Input Validation
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Valid tenant statuses
|
|
26
|
+
*/
|
|
27
|
+
const VALID_STATUSES = new Set(['active', 'suspended', 'pending', 'migrating']);
|
|
28
|
+
/**
|
|
29
|
+
* Validate tenant name to prevent SQL injection
|
|
30
|
+
* Names must be alphanumeric with spaces, hyphens, and underscores
|
|
31
|
+
*/
|
|
32
|
+
function validateName(name) {
|
|
33
|
+
if (!name || name.trim().length === 0) {
|
|
34
|
+
throw new Error('Name cannot be empty');
|
|
35
|
+
}
|
|
36
|
+
if (name.length > 100) {
|
|
37
|
+
throw new Error('Name cannot exceed 100 characters');
|
|
38
|
+
}
|
|
39
|
+
// Strict whitelist: only allow safe characters
|
|
40
|
+
const SAFE_NAME_REGEX = /^[a-zA-Z0-9\s\-_.']+$/;
|
|
41
|
+
if (!SAFE_NAME_REGEX.test(name)) {
|
|
42
|
+
throw new Error('Name contains invalid characters. Only letters, numbers, spaces, hyphens, underscores, periods, and apostrophes are allowed.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validate status filter option
|
|
47
|
+
*/
|
|
48
|
+
function validateStatus(status) {
|
|
49
|
+
if (!VALID_STATUSES.has(status)) {
|
|
50
|
+
throw new Error(`Invalid status: ${status}. Must be one of: ${[...VALID_STATUSES].join(', ')}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate slug format (additional security check)
|
|
55
|
+
*/
|
|
56
|
+
function validateSlugInput(slug) {
|
|
57
|
+
if (!slug || slug.trim().length === 0) {
|
|
58
|
+
throw new Error('Slug cannot be empty');
|
|
59
|
+
}
|
|
60
|
+
if (slug.length > 50) {
|
|
61
|
+
throw new Error('Slug cannot exceed 50 characters');
|
|
62
|
+
}
|
|
63
|
+
// Strict whitelist: only lowercase letters, numbers, and hyphens
|
|
64
|
+
const SAFE_SLUG_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
65
|
+
if (!SAFE_SLUG_REGEX.test(slug)) {
|
|
66
|
+
throw new Error('Slug must contain only lowercase letters, numbers, and hyphens, and must start and end with alphanumeric characters.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Security: Database URL Validation
|
|
71
|
+
// ============================================================================
|
|
72
|
+
/**
|
|
73
|
+
* Validate and sanitize DATABASE_URL
|
|
74
|
+
*/
|
|
75
|
+
function validateDatabaseUrl(url) {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = new URL(url);
|
|
78
|
+
// Only allow postgresql:// protocol
|
|
79
|
+
if (parsed.protocol !== 'postgresql:' && parsed.protocol !== 'postgres:') {
|
|
80
|
+
throw new Error('Invalid database protocol. Only postgresql:// is allowed.');
|
|
81
|
+
}
|
|
82
|
+
// Check for shell metacharacters that could indicate injection
|
|
83
|
+
const DANGEROUS_CHARS = /[;|&$`<>(){}[\]!]/;
|
|
84
|
+
if (DANGEROUS_CHARS.test(url)) {
|
|
85
|
+
throw new Error('Database URL contains potentially dangerous characters.');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (err instanceof Error && err.message.includes('Invalid database')) {
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
throw new Error('Invalid DATABASE_URL format');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get database URL from environment with validation
|
|
97
|
+
*/
|
|
98
|
+
function getDatabaseUrl() {
|
|
99
|
+
const url = process.env.DATABASE_URL;
|
|
100
|
+
if (!url) {
|
|
101
|
+
throw new Error('DATABASE_URL environment variable is not set.\n' +
|
|
102
|
+
'Please set it in your .env file or export it in your shell.');
|
|
103
|
+
}
|
|
104
|
+
validateDatabaseUrl(url);
|
|
105
|
+
return url;
|
|
106
|
+
}
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Secure Database Operations
|
|
109
|
+
// ============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Create a PostgreSQL client connection
|
|
112
|
+
* Ensures proper cleanup even if connect() fails
|
|
113
|
+
*/
|
|
114
|
+
async function createDbClient(databaseUrl) {
|
|
115
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
116
|
+
try {
|
|
117
|
+
await client.connect();
|
|
118
|
+
return client;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
// Ensure client is cleaned up even if connect fails
|
|
122
|
+
try {
|
|
123
|
+
await client.end();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Ignore cleanup errors
|
|
127
|
+
}
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Execute a parameterized SQL query safely
|
|
133
|
+
* Uses pg library with parameterized queries to prevent SQL injection
|
|
134
|
+
*/
|
|
135
|
+
async function executeQuery(databaseUrl, sql, params = []) {
|
|
136
|
+
const client = await createDbClient(databaseUrl);
|
|
137
|
+
try {
|
|
138
|
+
const result = await client.query(sql, params);
|
|
139
|
+
return result.rows;
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
await client.end();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Execute a single-row query
|
|
147
|
+
*/
|
|
148
|
+
async function executeQuerySingle(databaseUrl, sql, params = []) {
|
|
149
|
+
const rows = await executeQuery(databaseUrl, sql, params);
|
|
150
|
+
return rows[0] ?? null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Validate that the tenants table exists in the database
|
|
154
|
+
* Provides a helpful error message if not found
|
|
155
|
+
*/
|
|
156
|
+
async function validateTenantsTableExists(databaseUrl) {
|
|
157
|
+
const result = await executeQuerySingle(databaseUrl, `SELECT EXISTS (
|
|
158
|
+
SELECT 1 FROM information_schema.tables
|
|
159
|
+
WHERE table_schema = 'public'
|
|
160
|
+
AND table_name = 'tenants'
|
|
161
|
+
) as exists`, []);
|
|
162
|
+
if (!result?.exists) {
|
|
163
|
+
throw new Error('Tenants table not found in the database.\n' +
|
|
164
|
+
'Please ensure your Prisma schema includes a Tenant model and run migrations:\n' +
|
|
165
|
+
' npx prisma migrate dev\n' +
|
|
166
|
+
'\nOr create the table manually with the required columns:\n' +
|
|
167
|
+
' id, slug, name, schema_name, status, created_at, updated_at');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// Utility Functions
|
|
172
|
+
// ============================================================================
|
|
173
|
+
/**
|
|
174
|
+
* Format date for display
|
|
175
|
+
*/
|
|
176
|
+
function formatDate(date) {
|
|
177
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
178
|
+
return d.toISOString().replace('T', ' ').slice(0, 19);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Format status with color
|
|
182
|
+
*/
|
|
183
|
+
function formatStatus(status) {
|
|
184
|
+
switch (status) {
|
|
185
|
+
case 'active':
|
|
186
|
+
return pc.green(status);
|
|
187
|
+
case 'suspended':
|
|
188
|
+
return pc.red(status);
|
|
189
|
+
case 'pending':
|
|
190
|
+
return pc.yellow(status);
|
|
191
|
+
case 'migrating':
|
|
192
|
+
return pc.cyan(status);
|
|
193
|
+
default:
|
|
194
|
+
return pc.gray(status);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Sanitize error messages to prevent credential leakage
|
|
199
|
+
*/
|
|
200
|
+
function sanitizeErrorMessage(message) {
|
|
201
|
+
// Remove connection strings
|
|
202
|
+
let sanitized = message.replace(/postgresql:\/\/[^@]+@[^\s"']+/gi, 'postgresql://***:***@***/***');
|
|
203
|
+
// Remove passwords
|
|
204
|
+
sanitized = sanitized.replace(/password[=:]\s*['"]?[^'"\s]+/gi, 'password=***');
|
|
205
|
+
return sanitized;
|
|
206
|
+
}
|
|
207
|
+
// ============================================================================
|
|
208
|
+
// tenant:create
|
|
209
|
+
// ============================================================================
|
|
210
|
+
function createTenantCreateCommand() {
|
|
211
|
+
return new Command('create')
|
|
212
|
+
.description('Create a new tenant with PostgreSQL schema')
|
|
213
|
+
.argument('<slug>', 'URL-safe tenant slug (e.g., acme-corp)')
|
|
214
|
+
.option('-n, --name <name>', 'Human-readable tenant name')
|
|
215
|
+
.option('--dry-run', 'Preview without creating', false)
|
|
216
|
+
.option('--json', 'Output as JSON', false)
|
|
217
|
+
.action(async (slug, options) => {
|
|
218
|
+
try {
|
|
219
|
+
// Validate inputs
|
|
220
|
+
validateSlugInput(slug);
|
|
221
|
+
const name = options.name ?? slug;
|
|
222
|
+
validateName(name);
|
|
223
|
+
const databaseUrl = getDatabaseUrl();
|
|
224
|
+
const schemaManager = createTenantSchemaManager({ databaseUrl });
|
|
225
|
+
const schemaName = slugToSchemaName(slug);
|
|
226
|
+
if (options.dryRun) {
|
|
227
|
+
info(`Would create tenant: ${pc.cyan(slug)}`);
|
|
228
|
+
step(`Schema name: ${pc.cyan(schemaName)}`);
|
|
229
|
+
step(`Display name: ${name}`);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
// Validate tenants table exists before proceeding
|
|
233
|
+
await validateTenantsTableExists(databaseUrl);
|
|
234
|
+
if (!options.json) {
|
|
235
|
+
info(`Creating tenant: ${pc.cyan(slug)}`);
|
|
236
|
+
}
|
|
237
|
+
const s = p.spinner();
|
|
238
|
+
s.start('Creating PostgreSQL schema...');
|
|
239
|
+
// Create schema
|
|
240
|
+
const schemaResult = await schemaManager.createSchema(slug);
|
|
241
|
+
if (!schemaResult.created) {
|
|
242
|
+
s.stop('Schema already exists');
|
|
243
|
+
warning(`Schema ${schemaName} already exists`);
|
|
244
|
+
if (options.json) {
|
|
245
|
+
console.log(JSON.stringify({ success: false, error: 'Schema already exists', schemaName }, null, 2));
|
|
246
|
+
}
|
|
247
|
+
return; // Explicit early return
|
|
248
|
+
}
|
|
249
|
+
// Schema was created, continue with migration
|
|
250
|
+
s.message('Running migrations...');
|
|
251
|
+
// Run migrations
|
|
252
|
+
const migrateResult = await schemaManager.migrateSchema(schemaName);
|
|
253
|
+
s.message('Creating tenant record...');
|
|
254
|
+
// Insert tenant record using parameterized query
|
|
255
|
+
await executeQuery(databaseUrl, `INSERT INTO tenants (id, slug, name, schema_name, status, created_at, updated_at)
|
|
256
|
+
VALUES (gen_random_uuid(), $1, $2, $3, 'active', NOW(), NOW())`, [slug, name, schemaName]);
|
|
257
|
+
s.stop('Tenant created successfully');
|
|
258
|
+
if (options.json) {
|
|
259
|
+
console.log(JSON.stringify({
|
|
260
|
+
success: true,
|
|
261
|
+
tenant: {
|
|
262
|
+
slug,
|
|
263
|
+
name,
|
|
264
|
+
schemaName,
|
|
265
|
+
status: 'active',
|
|
266
|
+
migrationsApplied: migrateResult.migrationsApplied,
|
|
267
|
+
},
|
|
268
|
+
}, null, 2));
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
success(`Created tenant ${pc.cyan(slug)}`);
|
|
272
|
+
step(`Schema: ${pc.cyan(schemaName)}`);
|
|
273
|
+
step(`Migrations applied: ${migrateResult.migrationsApplied}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
if (err instanceof InvalidSlugError) {
|
|
278
|
+
error(err.message);
|
|
279
|
+
}
|
|
280
|
+
else if (err instanceof TenantError) {
|
|
281
|
+
error(`Tenant error: ${err.message}`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
285
|
+
}
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// ============================================================================
|
|
291
|
+
// tenant:list
|
|
292
|
+
// ============================================================================
|
|
293
|
+
function createTenantListCommand() {
|
|
294
|
+
return new Command('list')
|
|
295
|
+
.description('List all tenants')
|
|
296
|
+
.option('--status <status>', 'Filter by status (active/suspended/pending)')
|
|
297
|
+
.option('--json', 'Output as JSON', false)
|
|
298
|
+
.action(async (options) => {
|
|
299
|
+
try {
|
|
300
|
+
const databaseUrl = getDatabaseUrl();
|
|
301
|
+
// Validate tenants table exists
|
|
302
|
+
await validateTenantsTableExists(databaseUrl);
|
|
303
|
+
// Build query with optional parameterized status filter
|
|
304
|
+
let sql;
|
|
305
|
+
let params;
|
|
306
|
+
if (options.status) {
|
|
307
|
+
validateStatus(options.status);
|
|
308
|
+
sql =
|
|
309
|
+
'SELECT slug, name, schema_name, status, created_at FROM tenants WHERE status = $1 ORDER BY created_at DESC';
|
|
310
|
+
params = [options.status];
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
sql =
|
|
314
|
+
'SELECT slug, name, schema_name, status, created_at FROM tenants ORDER BY created_at DESC';
|
|
315
|
+
params = [];
|
|
316
|
+
}
|
|
317
|
+
const tenants = await executeQuery(databaseUrl, sql, params);
|
|
318
|
+
if (tenants.length === 0) {
|
|
319
|
+
if (options.json) {
|
|
320
|
+
console.log(JSON.stringify({ tenants: [] }, null, 2));
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
info('No tenants found');
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (options.json) {
|
|
328
|
+
console.log(JSON.stringify({
|
|
329
|
+
tenants: tenants.map((t) => ({
|
|
330
|
+
slug: t.slug,
|
|
331
|
+
name: t.name,
|
|
332
|
+
schemaName: t.schema_name,
|
|
333
|
+
status: t.status,
|
|
334
|
+
createdAt: t.created_at,
|
|
335
|
+
})),
|
|
336
|
+
}, null, 2));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
console.log('');
|
|
340
|
+
console.log(pc.dim('┌─────────────────────┬────────────────────┬────────────┬─────────────────────┐'));
|
|
341
|
+
console.log(`│ ${pc.bold('Slug')} │ ${pc.bold('Schema')} │ ${pc.bold('Status')} │ ${pc.bold('Created')} │`);
|
|
342
|
+
console.log(pc.dim('├─────────────────────┼────────────────────┼────────────┼─────────────────────┤'));
|
|
343
|
+
for (const t of tenants) {
|
|
344
|
+
const slug = t.slug.padEnd(19);
|
|
345
|
+
const schema = t.schema_name.padEnd(18);
|
|
346
|
+
const status = formatStatus(t.status).padEnd(20); // Extra padding for color codes
|
|
347
|
+
const created = formatDate(t.created_at);
|
|
348
|
+
console.log(`│ ${slug} │ ${schema} │ ${status} │ ${created} │`);
|
|
349
|
+
}
|
|
350
|
+
console.log(pc.dim('└─────────────────────┴────────────────────┴────────────┴─────────────────────┘'));
|
|
351
|
+
console.log('');
|
|
352
|
+
info(`Total: ${tenants.length} tenant(s)`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// tenant:migrate
|
|
363
|
+
// ============================================================================
|
|
364
|
+
function createTenantMigrateCommand() {
|
|
365
|
+
return new Command('migrate')
|
|
366
|
+
.description('Run migrations on tenant schemas')
|
|
367
|
+
.argument('[slug]', 'Specific tenant to migrate')
|
|
368
|
+
.option('--all', 'Migrate all active tenants', false)
|
|
369
|
+
.option('--dry-run', 'Preview without running migrations', false)
|
|
370
|
+
.option('--json', 'Output as JSON', false)
|
|
371
|
+
.action(async (slug, options) => {
|
|
372
|
+
try {
|
|
373
|
+
const databaseUrl = getDatabaseUrl();
|
|
374
|
+
const schemaManager = createTenantSchemaManager({ databaseUrl });
|
|
375
|
+
// Validate tenants table exists
|
|
376
|
+
await validateTenantsTableExists(databaseUrl);
|
|
377
|
+
// Get tenants to migrate using parameterized queries
|
|
378
|
+
let sql;
|
|
379
|
+
let params;
|
|
380
|
+
if (slug) {
|
|
381
|
+
validateSlugInput(slug);
|
|
382
|
+
sql = 'SELECT slug, schema_name FROM tenants WHERE slug = $1';
|
|
383
|
+
params = [slug];
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
sql = "SELECT slug, schema_name FROM tenants WHERE status = 'active'";
|
|
387
|
+
params = [];
|
|
388
|
+
}
|
|
389
|
+
const tenants = await executeQuery(databaseUrl, sql, params);
|
|
390
|
+
if (tenants.length === 0) {
|
|
391
|
+
if (slug) {
|
|
392
|
+
error(`Tenant not found: ${slug}`);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
info('No active tenants to migrate');
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (options.dryRun) {
|
|
401
|
+
info('Would migrate the following tenants:');
|
|
402
|
+
for (const t of tenants) {
|
|
403
|
+
step(`${t.slug} (${t.schema_name})`);
|
|
404
|
+
}
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const results = [];
|
|
408
|
+
const s = p.spinner();
|
|
409
|
+
for (const t of tenants) {
|
|
410
|
+
s.start(`Migrating ${t.slug}...`);
|
|
411
|
+
try {
|
|
412
|
+
// Check current status before updating (prevents concurrent migrations)
|
|
413
|
+
const currentTenant = await executeQuerySingle(databaseUrl, 'SELECT status FROM tenants WHERE slug = $1', [t.slug]);
|
|
414
|
+
if (!currentTenant) {
|
|
415
|
+
s.stop(`Tenant ${t.slug} no longer exists`);
|
|
416
|
+
warning(`Skipping ${t.slug}: tenant not found`);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (currentTenant.status === 'migrating') {
|
|
420
|
+
s.stop(`Tenant ${t.slug} already migrating`);
|
|
421
|
+
warning(`Skipping ${t.slug}: migration already in progress`);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (currentTenant.status === 'suspended') {
|
|
425
|
+
s.stop(`Tenant ${t.slug} is suspended`);
|
|
426
|
+
warning(`Skipping ${t.slug}: tenant is suspended`);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
// Update status to migrating using parameterized query
|
|
430
|
+
await executeQuery(databaseUrl, 'UPDATE tenants SET status = $1 WHERE slug = $2', [
|
|
431
|
+
'migrating',
|
|
432
|
+
t.slug,
|
|
433
|
+
]);
|
|
434
|
+
const migrateResult = await schemaManager.migrateSchema(t.schema_name);
|
|
435
|
+
// Update status back to active
|
|
436
|
+
await executeQuery(databaseUrl, 'UPDATE tenants SET status = $1 WHERE slug = $2', [
|
|
437
|
+
'active',
|
|
438
|
+
t.slug,
|
|
439
|
+
]);
|
|
440
|
+
results.push({
|
|
441
|
+
slug: t.slug,
|
|
442
|
+
schemaName: t.schema_name,
|
|
443
|
+
migrationsApplied: migrateResult.migrationsApplied,
|
|
444
|
+
});
|
|
445
|
+
s.stop(`Migrated ${t.slug}: ${migrateResult.migrationsApplied} migration(s) applied`);
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
s.stop(`Failed to migrate ${t.slug}`);
|
|
449
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
450
|
+
// Restore status (try to set back to active, ignore if it fails)
|
|
451
|
+
try {
|
|
452
|
+
await executeQuery(databaseUrl, 'UPDATE tenants SET status = $1 WHERE slug = $2', [
|
|
453
|
+
'active',
|
|
454
|
+
t.slug,
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
warning(`Could not restore status for ${t.slug} - may need manual intervention`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (options.json) {
|
|
463
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
console.log('');
|
|
467
|
+
success(`Migrated ${results.length} tenant(s)`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch (err) {
|
|
471
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
472
|
+
process.exit(1);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
// ============================================================================
|
|
477
|
+
// tenant:status
|
|
478
|
+
// ============================================================================
|
|
479
|
+
function createTenantStatusCommand() {
|
|
480
|
+
return new Command('status')
|
|
481
|
+
.description('Show tenant status and schema info')
|
|
482
|
+
.argument('<slug>', 'Tenant slug')
|
|
483
|
+
.option('--json', 'Output as JSON', false)
|
|
484
|
+
.action(async (slug, options) => {
|
|
485
|
+
try {
|
|
486
|
+
validateSlugInput(slug);
|
|
487
|
+
const databaseUrl = getDatabaseUrl();
|
|
488
|
+
// Validate tenants table exists
|
|
489
|
+
await validateTenantsTableExists(databaseUrl);
|
|
490
|
+
// Use parameterized query
|
|
491
|
+
const tenant = await executeQuerySingle(databaseUrl, `SELECT id, slug, name, schema_name, status, created_at, updated_at
|
|
492
|
+
FROM tenants WHERE slug = $1`, [slug]);
|
|
493
|
+
if (!tenant) {
|
|
494
|
+
error(`Tenant not found: ${slug}`);
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
// Check if schema exists using parameterized query
|
|
498
|
+
const schemaCheck = await executeQuerySingle(databaseUrl, 'SELECT EXISTS(SELECT 1 FROM information_schema.schemata WHERE schema_name = $1) as exists', [tenant.schema_name]);
|
|
499
|
+
const tenantInfo = {
|
|
500
|
+
id: tenant.id,
|
|
501
|
+
slug: tenant.slug,
|
|
502
|
+
name: tenant.name,
|
|
503
|
+
schemaName: tenant.schema_name,
|
|
504
|
+
status: tenant.status,
|
|
505
|
+
schemaExists: schemaCheck?.exists ?? false,
|
|
506
|
+
createdAt: tenant.created_at,
|
|
507
|
+
updatedAt: tenant.updated_at,
|
|
508
|
+
};
|
|
509
|
+
if (options.json) {
|
|
510
|
+
console.log(JSON.stringify(tenantInfo, null, 2));
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
console.log('');
|
|
514
|
+
console.log(`${pc.bold('Tenant:')} ${tenant.name} (${tenant.slug})`);
|
|
515
|
+
console.log(`${pc.bold('Status:')} ${formatStatus(tenant.status)}`);
|
|
516
|
+
console.log(`${pc.bold('Schema:')} ${tenant.schema_name}`);
|
|
517
|
+
console.log(`${pc.bold('Schema exists:')} ${tenantInfo.schemaExists ? pc.green('yes') : pc.red('no')}`);
|
|
518
|
+
console.log(`${pc.bold('Created:')} ${formatDate(tenant.created_at)}`);
|
|
519
|
+
console.log(`${pc.bold('Updated:')} ${formatDate(tenant.updated_at)}`);
|
|
520
|
+
console.log('');
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
// ============================================================================
|
|
530
|
+
// tenant:suspend
|
|
531
|
+
// ============================================================================
|
|
532
|
+
function createTenantSuspendCommand() {
|
|
533
|
+
return new Command('suspend')
|
|
534
|
+
.description('Suspend a tenant (block access)')
|
|
535
|
+
.argument('<slug>', 'Tenant slug to suspend')
|
|
536
|
+
.option('-f, --force', 'Skip confirmation', false)
|
|
537
|
+
.option('--json', 'Output as JSON', false)
|
|
538
|
+
.action(async (slug, options) => {
|
|
539
|
+
try {
|
|
540
|
+
validateSlugInput(slug);
|
|
541
|
+
const databaseUrl = getDatabaseUrl();
|
|
542
|
+
// Validate tenants table exists
|
|
543
|
+
await validateTenantsTableExists(databaseUrl);
|
|
544
|
+
// Check tenant exists using parameterized query
|
|
545
|
+
const existing = await executeQuerySingle(databaseUrl, 'SELECT status FROM tenants WHERE slug = $1', [slug]);
|
|
546
|
+
if (!existing) {
|
|
547
|
+
error(`Tenant not found: ${slug}`);
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
if (existing.status === 'suspended') {
|
|
551
|
+
warning(`Tenant ${slug} is already suspended`);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
// Confirm if not forced
|
|
555
|
+
if (!options.force && !options.json) {
|
|
556
|
+
const confirmed = await p.confirm({
|
|
557
|
+
message: `Are you sure you want to suspend tenant ${pc.cyan(slug)}?`,
|
|
558
|
+
});
|
|
559
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
560
|
+
info('Cancelled');
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// Suspend using parameterized query
|
|
565
|
+
await executeQuery(databaseUrl, 'UPDATE tenants SET status = $1, updated_at = NOW() WHERE slug = $2', ['suspended', slug]);
|
|
566
|
+
if (options.json) {
|
|
567
|
+
console.log(JSON.stringify({ success: true, slug, status: 'suspended' }, null, 2));
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
success(`Suspended tenant ${pc.cyan(slug)}`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
catch (err) {
|
|
574
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
575
|
+
process.exit(1);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// ============================================================================
|
|
580
|
+
// tenant:activate
|
|
581
|
+
// ============================================================================
|
|
582
|
+
function createTenantActivateCommand() {
|
|
583
|
+
return new Command('activate')
|
|
584
|
+
.description('Activate a suspended tenant')
|
|
585
|
+
.argument('<slug>', 'Tenant slug to activate')
|
|
586
|
+
.option('--json', 'Output as JSON', false)
|
|
587
|
+
.action(async (slug, options) => {
|
|
588
|
+
try {
|
|
589
|
+
validateSlugInput(slug);
|
|
590
|
+
const databaseUrl = getDatabaseUrl();
|
|
591
|
+
// Validate tenants table exists
|
|
592
|
+
await validateTenantsTableExists(databaseUrl);
|
|
593
|
+
// Check tenant exists using parameterized query
|
|
594
|
+
const existing = await executeQuerySingle(databaseUrl, 'SELECT status FROM tenants WHERE slug = $1', [slug]);
|
|
595
|
+
if (!existing) {
|
|
596
|
+
error(`Tenant not found: ${slug}`);
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
if (existing.status === 'active') {
|
|
600
|
+
warning(`Tenant ${slug} is already active`);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
// Activate using parameterized query
|
|
604
|
+
await executeQuery(databaseUrl, 'UPDATE tenants SET status = $1, updated_at = NOW() WHERE slug = $2', ['active', slug]);
|
|
605
|
+
if (options.json) {
|
|
606
|
+
console.log(JSON.stringify({ success: true, slug, status: 'active' }, null, 2));
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
success(`Activated tenant ${pc.cyan(slug)}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
error(sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));
|
|
614
|
+
process.exit(1);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
// ============================================================================
|
|
619
|
+
// Main Command
|
|
620
|
+
// ============================================================================
|
|
621
|
+
/**
|
|
622
|
+
* Create the tenant command with subcommands
|
|
623
|
+
*/
|
|
624
|
+
export function createTenantCommand() {
|
|
625
|
+
const tenant = new Command('tenant')
|
|
626
|
+
.description('Multi-tenancy management commands')
|
|
627
|
+
.addCommand(createTenantCreateCommand())
|
|
628
|
+
.addCommand(createTenantListCommand())
|
|
629
|
+
.addCommand(createTenantMigrateCommand())
|
|
630
|
+
.addCommand(createTenantStatusCommand())
|
|
631
|
+
.addCommand(createTenantSuspendCommand())
|
|
632
|
+
.addCommand(createTenantActivateCommand());
|
|
633
|
+
return tenant;
|
|
634
|
+
}
|
|
635
|
+
//# sourceMappingURL=tenant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/commands/tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEzE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AA8CtB,+EAA+E;AAC/E,6BAA6B;AAC7B,+EAA+E;AAE/E;;GAEG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,+CAA+C;IAC/C,MAAM,eAAe,GAAG,uBAAuB,CAAC;IAChD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,8HAA8H,CAC/H,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,MAAc;IACpC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,qBAAqB,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClG,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,iEAAiE;IACjE,MAAM,eAAe,GAAG,yCAAyC,CAAC;IAClE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,sHAAsH,CACvH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E;;GAEG;AACH,SAAS,mBAAmB,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAE5B,oCAAoC;QACpC,IAAI,MAAM,CAAC,QAAQ,KAAK,aAAa,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YACzE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QAED,+DAA+D;QAC/D,MAAM,eAAe,GAAG,mBAAmB,CAAC;QAC5C,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACrE,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc;IACrB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,iDAAiD;YAC/C,6DAA6D,CAChE,CAAC;IACJ,CAAC;IACD,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+EAA+E;AAC/E,6BAA6B;AAC7B,+EAA+E;AAE/E;;;GAGG;AACH,KAAK,UAAU,cAAc,CAAC,WAAmB;IAC/C,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,oDAAoD;QACpD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,YAAY,CACzB,WAAmB,EACnB,GAAW,EACX,SAAoB,EAAE;IAEtB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAI,GAAG,EAAE,MAAM,CAAC,CAAC;QAClD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,kBAAkB,CAC/B,WAAmB,EACnB,GAAW,EACX,SAAoB,EAAE;IAEtB,MAAM,IAAI,GAAG,MAAM,YAAY,CAAI,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,0BAA0B,CAAC,WAAmB;IAC3D,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,WAAW,EACX;;;;gBAIY,EACZ,EAAE,CACH,CAAC;IAEF,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,4CAA4C;YAC1C,gFAAgF;YAChF,4BAA4B;YAC5B,6DAA6D;YAC7D,+DAA+D,CAClE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,UAAU,CAAC,IAAmB;IACrC,MAAM,CAAC,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3D,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,MAAc;IAClC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1B,KAAK,WAAW;YACd,OAAO,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxB,KAAK,SAAS;YACZ,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,KAAK,WAAW;YACd,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB;YACE,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,OAAe;IAC3C,4BAA4B;IAC5B,IAAI,SAAS,GAAG,OAAO,CAAC,OAAO,CAC7B,iCAAiC,EACjC,8BAA8B,CAC/B,CAAC;IAEF,mBAAmB;IACnB,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,gCAAgC,EAAE,cAAc,CAAC,CAAC;IAEhF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,SAAS,yBAAyB;IAChC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC;SACzB,WAAW,CAAC,4CAA4C,CAAC;SACzD,QAAQ,CAAC,QAAQ,EAAE,wCAAwC,CAAC;SAC5D,MAAM,CAAC,mBAAmB,EAAE,4BAA4B,CAAC;SACzD,MAAM,CAAC,WAAW,EAAE,0BAA0B,EAAE,KAAK,CAAC;SACtD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAA4B,EAAE,EAAE;QAC3D,IAAI,CAAC;YACH,kBAAkB;YAClB,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;YAClC,YAAY,CAAC,IAAI,CAAC,CAAC;YAEnB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YACrC,MAAM,aAAa,GAAG,yBAAyB,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;YACjE,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAE1C,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,IAAI,CAAC,wBAAwB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC9C,IAAI,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBAC5C,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;gBAC9B,OAAO;YACT,CAAC;YAED,kDAAkD;YAClD,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC;YAE9C,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBAClB,IAAI,CAAC,oBAAoB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5C,CAAC;YAED,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACtB,CAAC,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAEzC,gBAAgB;YAChB,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAE5D,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;gBAC1B,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;gBAChC,OAAO,CAAC,UAAU,UAAU,iBAAiB,CAAC,CAAC;gBAE/C,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;oBACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,EAC9D,IAAI,EACJ,CAAC,CACF,CACF,CAAC;gBACJ,CAAC;gBACD,OAAO,CAAC,wBAAwB;YAClC,CAAC;YAED,8CAA8C;YAC9C,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;YAEnC,iBAAiB;YACjB,MAAM,aAAa,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAEpE,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;YAEvC,iDAAiD;YACjD,MAAM,YAAY,CAChB,WAAW,EACX;0EACgE,EAChE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CACzB,CAAC;YAEF,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YAEtC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;oBACE,OAAO,EAAE,IAAI;oBACb,MAAM,EAAE;wBACN,IAAI;wBACJ,IAAI;wBACJ,UAAU;wBACV,MAAM,EAAE,QAAQ;wBAChB,iBAAiB,EAAE,aAAa,CAAC,iBAAiB;qBACnD;iBACF,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC3C,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBACvC,IAAI,CAAC,uBAAuB,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,gBAAgB,EAAE,CAAC;gBACpC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;iBAAM,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;gBACtC,KAAK,CAAC,iBAAiB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAChF,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,cAAc;AACd,+EAA+E;AAE/E,SAAS,uBAAuB;IAC9B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;SACvB,WAAW,CAAC,kBAAkB,CAAC;SAC/B,MAAM,CAAC,mBAAmB,EAAE,6CAA6C,CAAC;SAC1E,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YAErC,gCAAgC;YAChC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC;YAE9C,wDAAwD;YACxD,IAAI,GAAW,CAAC;YAChB,IAAI,MAAiB,CAAC;YAEtB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC/B,GAAG;oBACD,4GAA4G,CAAC;gBAC/G,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,GAAG;oBACD,0FAA0F,CAAC;gBAC7F,MAAM,GAAG,EAAE,CAAC;YACd,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YAE7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;oBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACxD,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,kBAAkB,CAAC,CAAC;gBAC3B,CAAC;gBACD,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;oBACE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC3B,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,UAAU,EAAE,CAAC,CAAC,WAAW;wBACzB,MAAM,EAAE,CAAC,CAAC,MAAM;wBAChB,SAAS,EAAE,CAAC,CAAC,UAAU;qBACxB,CAAC,CAAC;iBACJ,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,GAAG,CACJ,iFAAiF,CAClF,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAC1I,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,GAAG,CACJ,iFAAiF,CAClF,CACF,CAAC;gBAEF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBAC/B,MAAM,MAAM,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBACxC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,gCAAgC;oBAClF,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;oBACzC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,IAAI,CAAC,CAAC;gBAClE,CAAC;gBAED,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,GAAG,CACJ,iFAAiF,CAClF,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,IAAI,CAAC,UAAU,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,SAAS,0BAA0B;IACjC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CAAC,kCAAkC,CAAC;SAC/C,QAAQ,CAAC,QAAQ,EAAE,4BAA4B,CAAC;SAChD,MAAM,CAAC,OAAO,EAAE,4BAA4B,EAAE,KAAK,CAAC;SACpD,MAAM,CAAC,WAAW,EAAE,oCAAoC,EAAE,KAAK,CAAC;SAChE,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,OAA6B,EAAE,EAAE;QACxE,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YACrC,MAAM,aAAa,GAAG,yBAAyB,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;YAEjE,gCAAgC;YAChC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC;YAE9C,qDAAqD;YACrD,IAAI,GAAW,CAAC;YAChB,IAAI,MAAiB,CAAC;YAEtB,IAAI,IAAI,EAAE,CAAC;gBACT,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBACxB,GAAG,GAAG,uDAAuD,CAAC;gBAC9D,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,GAAG,GAAG,+DAA+D,CAAC;gBACtE,MAAM,GAAG,EAAE,CAAC;YACd,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,YAAY,CAChC,WAAW,EACX,GAAG,EACH,MAAM,CACP,CAAC;YAEF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,IAAI,EAAE,CAAC;oBACT,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;oBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,8BAA8B,CAAC,CAAC;oBACrC,OAAO;gBACT,CAAC;YACH,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,IAAI,CAAC,sCAAsC,CAAC,CAAC;gBAC7C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC;gBACvC,CAAC;gBACD,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAA2E,EAAE,CAAC;YAC3F,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YAEtB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;gBAElC,IAAI,CAAC;oBACH,wEAAwE;oBACxE,MAAM,aAAa,GAAG,MAAM,kBAAkB,CAC5C,WAAW,EACX,4CAA4C,EAC5C,CAAC,CAAC,CAAC,IAAI,CAAC,CACT,CAAC;oBAEF,IAAI,CAAC,aAAa,EAAE,CAAC;wBACnB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,mBAAmB,CAAC,CAAC;wBAC5C,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC;wBAChD,SAAS;oBACX,CAAC;oBAED,IAAI,aAAa,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;wBACzC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC;wBAC7C,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,iCAAiC,CAAC,CAAC;wBAC7D,SAAS;oBACX,CAAC;oBAED,IAAI,aAAa,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;wBACzC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC;wBACxC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,uBAAuB,CAAC,CAAC;wBACnD,SAAS;oBACX,CAAC;oBAED,uDAAuD;oBACvD,MAAM,YAAY,CAAC,WAAW,EAAE,gDAAgD,EAAE;wBAChF,WAAW;wBACX,CAAC,CAAC,IAAI;qBACP,CAAC,CAAC;oBAEH,MAAM,aAAa,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;oBAEvE,+BAA+B;oBAC/B,MAAM,YAAY,CAAC,WAAW,EAAE,gDAAgD,EAAE;wBAChF,QAAQ;wBACR,CAAC,CAAC,IAAI;qBACP,CAAC,CAAC;oBAEH,OAAO,CAAC,IAAI,CAAC;wBACX,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,UAAU,EAAE,CAAC,CAAC,WAAW;wBACzB,iBAAiB,EAAE,aAAa,CAAC,iBAAiB;qBACnD,CAAC,CAAC;oBAEH,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,iBAAiB,uBAAuB,CAAC,CAAC;gBACxF,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;oBACtC,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBAE9E,iEAAiE;oBACjE,IAAI,CAAC;wBACH,MAAM,YAAY,CAAC,WAAW,EAAE,gDAAgD,EAAE;4BAChF,QAAQ;4BACR,CAAC,CAAC,IAAI;yBACP,CAAC,CAAC;oBACL,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,CAAC,gCAAgC,CAAC,CAAC,IAAI,iCAAiC,CAAC,CAAC;oBACnF,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACpD,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO,CAAC,YAAY,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,SAAS,yBAAyB;IAChC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC;SACzB,WAAW,CAAC,oCAAoC,CAAC;SACjD,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;SACjC,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAA4B,EAAE,EAAE;QAC3D,IAAI,CAAC;YACH,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YAErC,gCAAgC;YAChC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC;YAE9C,0BAA0B;YAC1B,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,WAAW,EACX;wCAC8B,EAC9B,CAAC,IAAI,CAAC,CACP,CAAC;YAEF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,mDAAmD;YACnD,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAC1C,WAAW,EACX,2FAA2F,EAC3F,CAAC,MAAM,CAAC,WAAW,CAAC,CACrB,CAAC;YAEF,MAAM,UAAU,GAAG;gBACjB,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,UAAU,EAAE,MAAM,CAAC,WAAW;gBAC9B,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,YAAY,EAAE,WAAW,EAAE,MAAM,IAAI,KAAK;gBAC1C,SAAS,EAAE,MAAM,CAAC,UAAU;gBAC5B,SAAS,EAAE,MAAM,CAAC,UAAU;aAC7B,CAAC;YAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;gBACrE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACpE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;gBAC3D,OAAO,CAAC,GAAG,CACT,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBACvE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBACvE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,SAAS,0BAA0B;IACjC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CAAC,iCAAiC,CAAC;SAC9C,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAC;SAC5C,MAAM,CAAC,aAAa,EAAE,mBAAmB,EAAE,KAAK,CAAC;SACjD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAA6B,EAAE,EAAE;QAC5D,IAAI,CAAC;YACH,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YAErC,gCAAgC;YAChC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC;YAE9C,gDAAgD;YAChD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CACvC,WAAW,EACX,4CAA4C,EAC5C,CAAC,IAAI,CAAC,CACP,CAAC;YAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,IAAI,QAAQ,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBACpC,OAAO,CAAC,UAAU,IAAI,uBAAuB,CAAC,CAAC;gBAC/C,OAAO;YACT,CAAC;YAED,wBAAwB;YACxB,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACpC,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC;oBAChC,OAAO,EAAE,2CAA2C,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;iBACrE,CAAC,CAAC;gBAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oBACxC,IAAI,CAAC,WAAW,CAAC,CAAC;oBAClB,OAAO;gBACT,CAAC;YACH,CAAC;YAED,oCAAoC;YACpC,MAAM,YAAY,CAChB,WAAW,EACX,oEAAoE,EACpE,CAAC,WAAW,EAAE,IAAI,CAAC,CACpB,CAAC;YAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACrF,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,oBAAoB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,SAAS,2BAA2B;IAClC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;SAC3B,WAAW,CAAC,6BAA6B,CAAC;SAC1C,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;SAC7C,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAA8B,EAAE,EAAE;QAC7D,IAAI,CAAC;YACH,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YAErC,gCAAgC;YAChC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC;YAE9C,gDAAgD;YAChD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CACvC,WAAW,EACX,4CAA4C,EAC5C,CAAC,IAAI,CAAC,CACP,CAAC;YAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACjC,OAAO,CAAC,UAAU,IAAI,oBAAoB,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YAED,qCAAqC;YACrC,MAAM,YAAY,CAChB,WAAW,EACX,oEAAoE,EACpE,CAAC,QAAQ,EAAE,IAAI,CAAC,CACjB,CAAC;YAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAClF,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,oBAAoB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,oBAAoB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;SACjC,WAAW,CAAC,mCAAmC,CAAC;SAChD,UAAU,CAAC,yBAAyB,EAAE,CAAC;SACvC,UAAU,CAAC,uBAAuB,EAAE,CAAC;SACrC,UAAU,CAAC,0BAA0B,EAAE,CAAC;SACxC,UAAU,CAAC,yBAAyB,EAAE,CAAC;SACvC,UAAU,CAAC,0BAA0B,EAAE,CAAC;SACxC,UAAU,CAAC,2BAA2B,EAAE,CAAC,CAAC;IAE7C,OAAO,MAAM,CAAC;AAChB,CAAC"}
|