everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 24.6 kB
Source Map (JSON)
{"version":3,"file":"run-migrations.mjs","names":[],"sources":["../../src/db/run-migrations.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { sql } from \"drizzle-orm\";\nimport { Effect, Option, Schedule, Schema } from \"effect\";\nimport {\n DUPLICATE_OBJECT_SQLSTATES,\n extractExpectedTables,\n getMigrationStorage,\n isConcurrentDdlUniqueViolation,\n isRetryableMigrationExecutionError,\n type MigrationStorage,\n toSqlArray,\n visitCauses,\n} from \"./core\";\nimport { DatabaseError } from \"./errors\";\n\nexport interface Migration {\n idx: number;\n when: number;\n tag: string;\n hash: string;\n sql: string[];\n}\n\n/**\n * Minimal structural view of a drizzle PgDatabase — the concrete per-workspace\n * `PgDatabase<PgQueryResultHKT, TSchema>` values satisfy this.\n */\nexport interface MigrationDatabase {\n execute(query: unknown): Promise<unknown>;\n transaction<T>(fn: (tx: { execute(query: unknown): Promise<unknown> }) => Promise<T>): Promise<T>;\n}\n\nexport interface LoadedMigrations {\n migrations: Migration[];\n source: \"virtual\" | \"disk\";\n}\n\nconst migrationJournalSchema = Schema.Struct({\n entries: Schema.Array(\n Schema.Struct({\n idx: Schema.Finite,\n when: Schema.Finite,\n tag: Schema.String,\n }),\n ),\n});\n\nexport interface DriftReport {\n status: \"healthy\" | \"empty\" | \"untracked-existing-schema\" | \"drift-safe-repair\" | \"drift-manual\";\n expectedTables: string[];\n missingTables: string[];\n appliedHashes: number;\n localHashes: number;\n storage: MigrationStorage;\n}\n\nexport interface MigrationReport {\n applied: number;\n total: number;\n storage: MigrationStorage;\n schema: string | undefined;\n}\n\nexport interface RunMigrationsOptions {\n schemaName?: string;\n journal?: MigrationStorage;\n duplicateSqlStates?: readonly string[];\n}\n\nconst DEFAULT_DUPLICATE_SQLSTATES: readonly string[] = DUPLICATE_OBJECT_SQLSTATES;\n\nfunction normalizeRows<T>(result: unknown): T[] {\n if (Array.isArray(result)) return result as T[];\n if (result && typeof result === \"object\" && \"rows\" in result) {\n return (result as { rows: T[] }).rows;\n }\n return [];\n}\n\ntype QueryExecutor = { execute(query: unknown): Promise<unknown> };\n\nfunction isDuplicateObjectError(error: unknown, codes: readonly string[]): boolean {\n return visitCauses(error, 6, (link) => {\n const code = (link as { code?: unknown }).code;\n return typeof code === \"string\" && codes.includes(code);\n });\n}\n\n/**\n * Whether a failed migration statement should be treated as duplicate-DDL\n * tolerance: either an explicit duplicate-object class, or the concurrent-DDL\n * catalog collision (SQLSTATE 23505 on a pg_catalog unique index) that two\n * booting processes can produce when they run the same `CREATE TABLE` against\n * a fresh database at the same time.\n */\nfunction isTolerableDuplicateDdl(error: unknown, codes: readonly string[]): boolean {\n return isDuplicateObjectError(error, codes) || isConcurrentDdlUniqueViolation(error);\n}\n\n/**\n * Check which of the given expected tables already exist in the target schema.\n * The schema name is bound as a query parameter.\n */\nasync function existingTablesIn(\n executor: QueryExecutor,\n tables: string[],\n schemaName?: string,\n): Promise<Set<string>> {\n if (tables.length === 0) return new Set<string>();\n const schema = schemaName ?? \"public\";\n const result = await executor.execute(sql`\n SELECT table_name FROM information_schema.tables\n WHERE table_schema = ${schema}\n AND table_name = ANY(${sql.raw(toSqlArray(tables))})\n `);\n return new Set(normalizeRows<{ table_name: string }>(result).map((r) => r.table_name));\n}\n\n/** Read applied hashes from the migration journal. */\nasync function readAppliedHashesIn(\n executor: QueryExecutor,\n ref: ReturnType<typeof sql.raw>,\n): Promise<Set<string>> {\n const result = await executor.execute(sql`SELECT hash FROM ${ref}`);\n return new Set(normalizeRows<{ hash: string }>(result).map((r) => r.hash));\n}\n\nexport interface LoadMigrationsOptions {\n /** Directory containing the workspace's drizzle `migrations/` folder for the disk fallback. */\n fromDir?: string;\n /**\n * Bundler-provided virtual-module loader (e.g. `virtual:drizzle-migrations.sql`\n * registered by rspack). Lives at the call site — the shared package must not\n * name the virtual module, which only exists inside plugin bundles.\n */\n virtual?: () => Promise<{ default?: Migration[] }>;\n}\n\nexport function loadMigrations(\n opts: LoadMigrationsOptions = {},\n): Effect.Effect<LoadedMigrations, DatabaseError> {\n return Effect.gen(function* () {\n if (opts.virtual) {\n const mod = yield* Effect.tryPromise({\n try: () => opts.virtual!(),\n catch: (cause) => new DatabaseError({ stage: \"load\", cause }),\n }).pipe(Effect.option);\n const migrations = Option.getOrUndefined(mod)?.default;\n if (migrations?.length) {\n yield* Effect.logInfo(\n `[Database] Loaded ${migrations.length} migration(s) from virtual module`,\n );\n return { migrations, source: \"virtual\" as const };\n }\n yield* Effect.logDebug(\"[Database] Virtual migrations unavailable, loading from disk\");\n }\n\n const diskMigrations = Option.getOrUndefined(\n yield* loadMigrationsFromDisk(opts.fromDir).pipe(Effect.option),\n );\n if (diskMigrations) {\n yield* Effect.logInfo(`[Database] Loaded ${diskMigrations.length} migration(s) from disk`);\n return { migrations: diskMigrations, source: \"disk\" as const };\n }\n\n yield* Effect.logWarning(\"[Database] No migrations found from virtual or disk\");\n return { migrations: [], source: \"disk\" as const };\n });\n}\n\nfunction loadMigrationsFromDisk(fromDir?: string): Effect.Effect<Migration[], DatabaseError> {\n return Effect.gen(function* () {\n const migrationsDir = resolve(fromDir ?? import.meta.dirname, \"migrations\");\n const journalPath = join(migrationsDir, \"meta\", \"_journal.json\");\n\n if (!existsSync(journalPath)) {\n return yield* new DatabaseError({\n stage: \"load\",\n cause: new Error(\n `Migrations journal not found at ${journalPath}. Run \\`db:generate\\` first.`,\n ),\n });\n }\n\n const journalContents = yield* Effect.try({\n try: () => readFileSync(journalPath, \"utf8\"),\n catch: (cause) => new DatabaseError({ stage: \"load\", cause }),\n });\n const json = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Json))(\n journalContents,\n ).pipe(Effect.mapError((cause) => new DatabaseError({ stage: \"load\", cause })));\n const journal = yield* Schema.decodeUnknownEffect(migrationJournalSchema)(json).pipe(\n Effect.mapError((cause) => new DatabaseError({ stage: \"load\", cause })),\n );\n\n return yield* Effect.forEach(journal.entries, (entry) =>\n Effect.try({\n try: () => {\n const sqlPath = join(migrationsDir, `${entry.tag}.sql`);\n if (!existsSync(sqlPath)) {\n throw new Error(`Migration SQL file not found: ${sqlPath}`);\n }\n const raw = readFileSync(sqlPath, \"utf8\");\n const sqlStatements = raw\n .split(\"--> statement-breakpoint\")\n .map((statement) => statement.trim());\n const hash = createHash(\"sha256\").update(raw).digest(\"hex\");\n return { idx: entry.idx, when: entry.when, tag: entry.tag, hash, sql: sqlStatements };\n },\n catch: (cause) => new DatabaseError({ stage: \"load\", cause }),\n }),\n );\n });\n}\n\nfunction journalRef(s: MigrationStorage): ReturnType<typeof sql> {\n return sql.raw(`\"${s.schema}\".\"${s.table}\"`);\n}\n\n/**\n * Apply one statement under a savepoint, tolerating duplicate DDL: either an\n * explicit duplicate-object class or the concurrent-DDL catalog collision a\n * non-participant migrator (drizzle-kit) can still produce.\n */\nasync function applyWithSavepoint(\n tx: QueryExecutor,\n query: ReturnType<typeof sql>,\n migrationTag: string,\n savepoint: string,\n duplicateSqlStates: readonly string[],\n): Promise<void> {\n await tx.execute(sql.raw(`SAVEPOINT ${savepoint}`));\n try {\n await tx.execute(query);\n } catch (cause) {\n if (isTolerableDuplicateDdl(cause, duplicateSqlStates)) {\n await tx.execute(sql.raw(`ROLLBACK TO SAVEPOINT ${savepoint}`));\n return;\n }\n throw new DatabaseError({ stage: \"migration\", migrationTag, cause });\n }\n await tx.execute(sql.raw(`RELEASE SAVEPOINT ${savepoint}`));\n}\n\ninterface LockedRunArgs {\n sorted: Migration[];\n journal: MigrationStorage;\n ref: ReturnType<typeof sql.raw>;\n schemaName: string | undefined;\n duplicateSqlStates: readonly string[];\n}\n\n/**\n * The whole migration run inside ONE transaction that opens with a\n * journal-scoped `pg_advisory_xact_lock`. Drizzle's `db.transaction` pins a\n * single pooled client for the callback, so the lock, the journal read, the\n * DDL, and the journal inserts all share one session; the lock releases at\n * COMMIT/ROLLBACK or on connection death — a crashed migrator can never\n * orphan it. A concurrent migrator blocks (bounded by the driver's\n * `lock_timeout`), then reads the winner's committed journal rows and applies\n * nothing.\n */\nasync function runLockedTransaction(\n db: MigrationDatabase,\n args: LockedRunArgs,\n): Promise<{ appliedTags: string[]; logs: string[] }> {\n const { sorted, journal, ref, schemaName, duplicateSqlStates } = args;\n const appliedTags: string[] = [];\n const logs: string[] = [];\n\n await db.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT pg_advisory_xact_lock(hashtextextended(${`${journal.schema}.${journal.table}`}, 0))`,\n );\n\n await applyWithSavepoint(\n tx,\n sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(`\"${journal.schema}\"`)}`,\n \"init-schema\",\n \"sp_init_schema\",\n duplicateSqlStates,\n );\n await applyWithSavepoint(\n tx,\n sql`\n CREATE TABLE IF NOT EXISTS ${ref} (\n id SERIAL PRIMARY KEY,\n hash text NOT NULL,\n created_at bigint\n )\n `,\n \"init-table\",\n \"sp_init_table\",\n duplicateSqlStates,\n );\n if (schemaName) {\n await applyWithSavepoint(\n tx,\n sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(`\"${schemaName}\"`)}`,\n \"init-data-schema\",\n \"sp_init_data_schema\",\n duplicateSqlStates,\n );\n }\n\n const appliedHashes = await readAppliedHashesIn(tx, ref);\n\n for (const migration of sorted) {\n const isApplied =\n appliedHashes.has(migration.hash) || appliedHashes.has(migration.hash.slice(0, 12));\n if (isApplied) continue;\n\n // Preflight: if this migration's expected tables already exist, record it\n // as applied rather than crashing on a duplicate DDL error.\n const expectedTables = extractExpectedTables([migration]);\n if (expectedTables.length > 0) {\n const existing = await existingTablesIn(tx, expectedTables, schemaName);\n const missingTables = expectedTables.filter((t) => !existing.has(t));\n if (missingTables.length === 0) {\n logs.push(\n `[Database] All tables for migration ${migration.tag} already exist — ` +\n `recording as applied without replaying DDL`,\n );\n await tx.execute(\n sql`INSERT INTO ${ref} (hash, created_at) VALUES (${migration.hash}, ${migration.when})`,\n );\n appliedHashes.add(migration.hash);\n appliedTags.push(migration.tag);\n continue;\n }\n if (missingTables.length < expectedTables.length) {\n logs.push(\n `[Database] Partial table overlap for migration ${migration.tag}: ` +\n `${expectedTables.length - missingTables.length} table(s) exist but not all. ` +\n `Applying migration — existing tables: ${expectedTables.filter((t) => existing.has(t)).join(\", \")}`,\n );\n }\n }\n\n logs.push(`[Database] Applying migration: ${migration.tag}`);\n\n for (const [i, statement] of migration.sql.entries()) {\n const stmt = schemaName ? statement.replace(/\"public\"\\./g, \"\") : statement;\n await applyWithSavepoint(tx, sql.raw(stmt), migration.tag, `stmt_${i}`, duplicateSqlStates);\n }\n await tx.execute(\n sql`INSERT INTO ${ref} (hash, created_at) VALUES (${migration.hash}, ${migration.when})`,\n );\n appliedTags.push(migration.tag);\n }\n });\n\n return { appliedTags, logs };\n}\n\n/**\n * Apply pending migrations to the target database.\n *\n * Namespace model: `opts.schemaName` (`plugin_<slug>`) scopes data tables; the\n * journal lives in `opts.journal` (default the shared `drizzle.__drizzle_migrations`).\n * Undefined `schemaName` means public-schema (api) or dedicated-DB (auth) topology.\n *\n * Concurrency: the run serializes on a journal-scoped Postgres advisory\n * transaction lock, so the dev double-boot (plugin dev-server + the host's\n * in-process load), parallel test files, and overlapping production replicas\n * converge instead of colliding in pg_catalog. SAVEPOINT-based duplicate-DDL\n * tolerance stays as defense-in-depth for non-participants (drizzle-kit).\n *\n * Reliability floor: hash-tracked idempotence with a preflight that records\n * fully-overlapped migrations as applied, everything in one locked\n * transaction, retried on deadlock/serialization/lock-timeout/connection\n * states — a failed attempt rolls back cleanly and re-runs.\n */\nexport function runMigrations(\n db: MigrationDatabase,\n migrations: Migration[],\n opts: RunMigrationsOptions = {},\n): Effect.Effect<MigrationReport, DatabaseError> {\n return Effect.gen(function* () {\n const sorted = [...migrations].sort((a, b) => a.idx - b.idx);\n const journal = opts.journal ?? getMigrationStorage();\n const schemaName = opts.schemaName;\n const duplicateSqlStates = opts.duplicateSqlStates ?? DEFAULT_DUPLICATE_SQLSTATES;\n\n const { appliedTags, logs } = yield* Effect.retry(\n Effect.tryPromise({\n try: () =>\n runLockedTransaction(db, {\n sorted,\n journal,\n ref: journalRef(journal),\n schemaName,\n duplicateSqlStates,\n }),\n catch: (cause) =>\n cause instanceof DatabaseError\n ? cause\n : new DatabaseError({ stage: \"migration\", migrationTag: \"run\", cause }),\n }),\n {\n schedule: Schedule.spaced(\"500 millis\"),\n times: 3,\n while: isRetryableMigrationExecutionError,\n },\n );\n\n for (const line of logs) yield* Effect.logInfo(line);\n\n return {\n applied: appliedTags.length,\n total: sorted.length,\n storage: journal,\n schema: schemaName,\n };\n });\n}\n\n/**\n * Detect drift between the local migration set and the database journal.\n *\n * Pass an explicit `journal` resolved from the caller's workspace for reliable\n * slug derivation; the default relies on `process.env.npm_package_name`, which is\n * unreliable under bundlers and Module Federation remotes.\n */\nexport function detectDrift(\n db: MigrationDatabase,\n migrations: Migration[],\n journal?: MigrationStorage,\n schemaName?: string,\n): Effect.Effect<DriftReport, DatabaseError> {\n return Effect.gen(function* () {\n const storage = journal ?? getMigrationStorage();\n const expectedTables = extractExpectedTables(migrations);\n const ref = journalRef(storage);\n\n // Lenient by design: drift detection reports on whatever it can read —\n // a failed journal read just means \"nothing tracked yet\".\n const appliedHashes = yield* Effect.tryPromise({\n try: () => readAppliedHashesIn(db, ref),\n catch: (cause) =>\n new DatabaseError({ stage: \"migration\", migrationTag: \"read-applied\", cause }),\n }).pipe(Effect.orElseSucceed(() => new Set<string>()));\n const appliedCount = appliedHashes.size;\n\n if (expectedTables.length === 0) {\n return {\n status: \"empty\",\n expectedTables: [],\n missingTables: [],\n appliedHashes: appliedCount,\n localHashes: migrations.length,\n storage,\n };\n }\n\n const existing = yield* Effect.tryPromise({\n try: () => existingTablesIn(db, expectedTables, schemaName),\n catch: (cause) =>\n new DatabaseError({ stage: \"migration\", migrationTag: \"preflight-table-check\", cause }),\n }).pipe(Effect.orElseSucceed(() => new Set<string>()));\n const missingTables = expectedTables.filter((t) => !existing.has(t));\n\n if (appliedCount === 0 && missingTables.length === 0) {\n // Journal is empty but all expected tables already exist.\n return {\n status: \"untracked-existing-schema\",\n expectedTables,\n missingTables: [],\n appliedHashes: 0,\n localHashes: migrations.length,\n storage,\n };\n }\n\n if (missingTables.length === 0) {\n return {\n status: \"healthy\",\n expectedTables,\n missingTables: [],\n appliedHashes: appliedCount,\n localHashes: migrations.length,\n storage,\n };\n }\n\n if (missingTables.length === expectedTables.length) {\n return {\n status: \"drift-safe-repair\",\n expectedTables,\n missingTables,\n appliedHashes: appliedCount,\n localHashes: migrations.length,\n storage,\n };\n }\n\n return {\n status: \"drift-manual\",\n expectedTables,\n missingTables,\n appliedHashes: appliedCount,\n localHashes: migrations.length,\n storage,\n };\n });\n}\n"],"mappings":";;;;;;;;;AAuCA,MAAM,yBAAyB,OAAO,OAAO,EAC3C,SAAS,OAAO,MACd,OAAO,OAAO;CACZ,KAAK,OAAO;CACZ,MAAM,OAAO;CACb,KAAK,OAAO;AACd,CAAC,CACH,EACF,CAAC;AAwBD,MAAM,8BAAiD;AAEvD,SAAS,cAAiB,QAAsB;CAC9C,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO;CAClC,IAAI,UAAU,OAAO,WAAW,YAAY,UAAU,QACpD,OAAQ,OAAyB;CAEnC,OAAO,CAAC;AACV;AAIA,SAAS,uBAAuB,OAAgB,OAAmC;CACjF,OAAO,YAAY,OAAO,IAAI,SAAS;EACrC,MAAM,OAAQ,KAA4B;EAC1C,OAAO,OAAO,SAAS,YAAY,MAAM,SAAS,IAAI;CACxD,CAAC;AACH;;;;;;;;AASA,SAAS,wBAAwB,OAAgB,OAAmC;CAClF,OAAO,uBAAuB,OAAO,KAAK,KAAK,+BAA+B,KAAK;AACrF;;;;;AAMA,eAAe,iBACb,UACA,QACA,YACsB;CACtB,IAAI,OAAO,WAAW,GAAG,uBAAO,IAAI,IAAY;CAChD,MAAM,SAAS,cAAc;CAC7B,MAAM,SAAS,MAAM,SAAS,QAAQ,GAAG;;2BAEhB,OAAO;6BACL,IAAI,IAAI,WAAW,MAAM,CAAC,EAAE;GACtD;CACD,OAAO,IAAI,IAAI,cAAsC,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,UAAU,CAAC;AACvF;;AAGA,eAAe,oBACb,UACA,KACsB;CACtB,MAAM,SAAS,MAAM,SAAS,QAAQ,GAAG,oBAAoB,KAAK;CAClE,OAAO,IAAI,IAAI,cAAgC,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;AAC3E;AAaA,SAAgB,eACd,OAA8B,CAAC,GACiB;CAChD,OAAO,OAAO,IAAI,aAAa;EAC7B,IAAI,KAAK,SAAS;GAChB,MAAM,MAAM,OAAO,OAAO,WAAW;IACnC,WAAW,KAAK,QAAS;IACzB,QAAQ,UAAU,IAAI,cAAc;KAAE,OAAO;KAAQ;IAAM,CAAC;GAC9D,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM;GACrB,MAAM,aAAa,OAAO,eAAe,GAAG,CAAC,EAAE;GAC/C,IAAI,YAAY,QAAQ;IACtB,OAAO,OAAO,QACZ,qBAAqB,WAAW,OAAO,kCACzC;IACA,OAAO;KAAE;KAAY,QAAQ;IAAmB;GAClD;GACA,OAAO,OAAO,SAAS,8DAA8D;EACvF;EAEA,MAAM,iBAAiB,OAAO,eAC5B,OAAO,uBAAuB,KAAK,OAAO,CAAC,CAAC,KAAK,OAAO,MAAM,CAChE;EACA,IAAI,gBAAgB;GAClB,OAAO,OAAO,QAAQ,qBAAqB,eAAe,OAAO,wBAAwB;GACzF,OAAO;IAAE,YAAY;IAAgB,QAAQ;GAAgB;EAC/D;EAEA,OAAO,OAAO,WAAW,qDAAqD;EAC9E,OAAO;GAAE,YAAY,CAAC;GAAG,QAAQ;EAAgB;CACnD,CAAC;AACH;AAEA,SAAS,uBAAuB,SAA6D;CAC3F,OAAO,OAAO,IAAI,aAAa;EAC7B,MAAM,gBAAgB,QAAQ,WAAW,YAAY,SAAS,YAAY;EAC1E,MAAM,cAAc,KAAK,eAAe,QAAQ,eAAe;EAE/D,IAAI,CAAC,WAAW,WAAW,GACzB,OAAO,OAAO,IAAI,cAAc;GAC9B,OAAO;GACP,uBAAO,IAAI,MACT,mCAAmC,YAAY,6BACjD;EACF,CAAC;EAGH,MAAM,kBAAkB,OAAO,OAAO,IAAI;GACxC,WAAW,aAAa,aAAa,MAAM;GAC3C,QAAQ,UAAU,IAAI,cAAc;IAAE,OAAO;IAAQ;GAAM,CAAC;EAC9D,CAAC;EACD,MAAM,OAAO,OAAO,OAAO,aAAa,OAAO,eAAe,OAAO,IAAI,CAAC,CAAC,CACzE,eACF,CAAC,CAAC,KAAK,OAAO,UAAU,UAAU,IAAI,cAAc;GAAE,OAAO;GAAQ;EAAM,CAAC,CAAC,CAAC;EAC9E,MAAM,UAAU,OAAO,OAAO,oBAAoB,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,KAC9E,OAAO,UAAU,UAAU,IAAI,cAAc;GAAE,OAAO;GAAQ;EAAM,CAAC,CAAC,CACxE;EAEA,OAAO,OAAO,OAAO,QAAQ,QAAQ,UAAU,UAC7C,OAAO,IAAI;GACT,WAAW;IACT,MAAM,UAAU,KAAK,eAAe,GAAG,MAAM,IAAI,KAAK;IACtD,IAAI,CAAC,WAAW,OAAO,GACrB,MAAM,IAAI,MAAM,iCAAiC,SAAS;IAE5D,MAAM,MAAM,aAAa,SAAS,MAAM;IACxC,MAAM,gBAAgB,IACnB,MAAM,0BAA0B,CAAC,CACjC,KAAK,cAAc,UAAU,KAAK,CAAC;IACtC,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,KAAK;IAC1D,OAAO;KAAE,KAAK,MAAM;KAAK,MAAM,MAAM;KAAM,KAAK,MAAM;KAAK;KAAM,KAAK;IAAc;GACtF;GACA,QAAQ,UAAU,IAAI,cAAc;IAAE,OAAO;IAAQ;GAAM,CAAC;EAC9D,CAAC,CACH;CACF,CAAC;AACH;AAEA,SAAS,WAAW,GAA6C;CAC/D,OAAO,IAAI,IAAI,IAAI,EAAE,OAAO,KAAK,EAAE,MAAM,EAAE;AAC7C;;;;;;AAOA,eAAe,mBACb,IACA,OACA,cACA,WACA,oBACe;CACf,MAAM,GAAG,QAAQ,IAAI,IAAI,aAAa,WAAW,CAAC;CAClD,IAAI;EACF,MAAM,GAAG,QAAQ,KAAK;CACxB,SAAS,OAAO;EACd,IAAI,wBAAwB,OAAO,kBAAkB,GAAG;GACtD,MAAM,GAAG,QAAQ,IAAI,IAAI,yBAAyB,WAAW,CAAC;GAC9D;EACF;EACA,MAAM,IAAI,cAAc;GAAE,OAAO;GAAa;GAAc;EAAM,CAAC;CACrE;CACA,MAAM,GAAG,QAAQ,IAAI,IAAI,qBAAqB,WAAW,CAAC;AAC5D;;;;;;;;;;;AAoBA,eAAe,qBACb,IACA,MACoD;CACpD,MAAM,EAAE,QAAQ,SAAS,KAAK,YAAY,uBAAuB;CACjE,MAAM,cAAwB,CAAC;CAC/B,MAAM,OAAiB,CAAC;CAExB,MAAM,GAAG,YAAY,OAAO,OAAO;EACjC,MAAM,GAAG,QACP,GAAG,iDAAiD,GAAG,QAAQ,OAAO,GAAG,QAAQ,QAAQ,MAC3F;EAEA,MAAM,mBACJ,IACA,GAAG,+BAA+B,IAAI,IAAI,IAAI,QAAQ,OAAO,EAAE,KAC/D,eACA,kBACA,kBACF;EACA,MAAM,mBACJ,IACA,GAAG;qCAC4B,IAAI;;;;;SAMnC,cACA,iBACA,kBACF;EACA,IAAI,YACF,MAAM,mBACJ,IACA,GAAG,+BAA+B,IAAI,IAAI,IAAI,WAAW,EAAE,KAC3D,oBACA,uBACA,kBACF;EAGF,MAAM,gBAAgB,MAAM,oBAAoB,IAAI,GAAG;EAEvD,KAAK,MAAM,aAAa,QAAQ;GAG9B,IADE,cAAc,IAAI,UAAU,IAAI,KAAK,cAAc,IAAI,UAAU,KAAK,MAAM,GAAG,EAAE,CAAC,GACrE;GAIf,MAAM,iBAAiB,sBAAsB,CAAC,SAAS,CAAC;GACxD,IAAI,eAAe,SAAS,GAAG;IAC7B,MAAM,WAAW,MAAM,iBAAiB,IAAI,gBAAgB,UAAU;IACtE,MAAM,gBAAgB,eAAe,QAAQ,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;IACnE,IAAI,cAAc,WAAW,GAAG;KAC9B,KAAK,KACH,uCAAuC,UAAU,IAAI,4DAEvD;KACA,MAAM,GAAG,QACP,GAAG,eAAe,IAAI,8BAA8B,UAAU,KAAK,IAAI,UAAU,KAAK,EACxF;KACA,cAAc,IAAI,UAAU,IAAI;KAChC,YAAY,KAAK,UAAU,GAAG;KAC9B;IACF;IACA,IAAI,cAAc,SAAS,eAAe,QACxC,KAAK,KACH,kDAAkD,UAAU,IAAI,IAC3D,eAAe,SAAS,cAAc,OAAO,qEACP,eAAe,QAAQ,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GACpG;GAEJ;GAEA,KAAK,KAAK,kCAAkC,UAAU,KAAK;GAE3D,KAAK,MAAM,CAAC,GAAG,cAAc,UAAU,IAAI,QAAQ,GAAG;IACpD,MAAM,OAAO,aAAa,UAAU,QAAQ,eAAe,EAAE,IAAI;IACjE,MAAM,mBAAmB,IAAI,IAAI,IAAI,IAAI,GAAG,UAAU,KAAK,QAAQ,KAAK,kBAAkB;GAC5F;GACA,MAAM,GAAG,QACP,GAAG,eAAe,IAAI,8BAA8B,UAAU,KAAK,IAAI,UAAU,KAAK,EACxF;GACA,YAAY,KAAK,UAAU,GAAG;EAChC;CACF,CAAC;CAED,OAAO;EAAE;EAAa;CAAK;AAC7B;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,cACd,IACA,YACA,OAA6B,CAAC,GACiB;CAC/C,OAAO,OAAO,IAAI,aAAa;EAC7B,MAAM,SAAS,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;EAC3D,MAAM,UAAU,KAAK,WAAW,oBAAoB;EACpD,MAAM,aAAa,KAAK;EACxB,MAAM,qBAAqB,KAAK,sBAAsB;EAEtD,MAAM,EAAE,aAAa,SAAS,OAAO,OAAO,MAC1C,OAAO,WAAW;GAChB,WACE,qBAAqB,IAAI;IACvB;IACA;IACA,KAAK,WAAW,OAAO;IACvB;IACA;GACF,CAAC;GACH,QAAQ,UACN,iBAAiB,gBACb,QACA,IAAI,cAAc;IAAE,OAAO;IAAa,cAAc;IAAO;GAAM,CAAC;EAC5E,CAAC,GACD;GACE,UAAU,SAAS,OAAO,YAAY;GACtC,OAAO;GACP,OAAO;EACT,CACF;EAEA,KAAK,MAAM,QAAQ,MAAM,OAAO,OAAO,QAAQ,IAAI;EAEnD,OAAO;GACL,SAAS,YAAY;GACrB,OAAO,OAAO;GACd,SAAS;GACT,QAAQ;EACV;CACF,CAAC;AACH;;;;;;;;AASA,SAAgB,YACd,IACA,YACA,SACA,YAC2C;CAC3C,OAAO,OAAO,IAAI,aAAa;EAC7B,MAAM,UAAU,WAAW,oBAAoB;EAC/C,MAAM,iBAAiB,sBAAsB,UAAU;EACvD,MAAM,MAAM,WAAW,OAAO;EAS9B,MAAM,gBAAe,OALQ,OAAO,WAAW;GAC7C,WAAW,oBAAoB,IAAI,GAAG;GACtC,QAAQ,UACN,IAAI,cAAc;IAAE,OAAO;IAAa,cAAc;IAAgB;GAAM,CAAC;EACjF,CAAC,CAAC,CAAC,KAAK,OAAO,oCAAoB,IAAI,IAAY,CAAC,CAAC,EACnB,CAAC;EAEnC,IAAI,eAAe,WAAW,GAC5B,OAAO;GACL,QAAQ;GACR,gBAAgB,CAAC;GACjB,eAAe,CAAC;GAChB,eAAe;GACf,aAAa,WAAW;GACxB;EACF;EAGF,MAAM,WAAW,OAAO,OAAO,WAAW;GACxC,WAAW,iBAAiB,IAAI,gBAAgB,UAAU;GAC1D,QAAQ,UACN,IAAI,cAAc;IAAE,OAAO;IAAa,cAAc;IAAyB;GAAM,CAAC;EAC1F,CAAC,CAAC,CAAC,KAAK,OAAO,oCAAoB,IAAI,IAAY,CAAC,CAAC;EACrD,MAAM,gBAAgB,eAAe,QAAQ,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;EAEnE,IAAI,iBAAiB,KAAK,cAAc,WAAW,GAEjD,OAAO;GACL,QAAQ;GACR;GACA,eAAe,CAAC;GAChB,eAAe;GACf,aAAa,WAAW;GACxB;EACF;EAGF,IAAI,cAAc,WAAW,GAC3B,OAAO;GACL,QAAQ;GACR;GACA,eAAe,CAAC;GAChB,eAAe;GACf,aAAa,WAAW;GACxB;EACF;EAGF,IAAI,cAAc,WAAW,eAAe,QAC1C,OAAO;GACL,QAAQ;GACR;GACA;GACA,eAAe;GACf,aAAa,WAAW;GACxB;EACF;EAGF,OAAO;GACL,QAAQ;GACR;GACA;GACA,eAAe;GACf,aAAa,WAAW;GACxB;EACF;CACF,CAAC;AACH"}