{"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/record-lines.ts","../../src/seed/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\n/**\n * Resolve the 1-based physical line a JSONL record occupies.\n *\n * The seed reader drops blank and whitespace-only lines before indexing its\n * records, so a record's index is not its line number once a file contains\n * one. Reported locations have to point at the line a reader would open.\n * @param content - Raw JSONL file contents\n * @param recordIndex - 0-based index among the file's non-blank lines\n * @returns 1-based physical line, or undefined when the file holds no such record\n */\nexport function physicalLineOfRecord(content: string, recordIndex: number): number | undefined {\n  const lines = content.split(\"\\n\");\n  let remaining = recordIndex;\n  for (const [index, line] of lines.entries()) {\n    if (line.trim().length === 0) continue;\n    if (remaining === 0) return index + 1;\n    remaining -= 1;\n  }\n  return undefined;\n}\n\n/**\n * Locate the first reported error in its own file.\n *\n * Errors are reported per record, so the index is mapped back to a physical\n * line; a file that cannot be re-read yields the file alone rather than a\n * guessed line.\n * @param errors - Validation errors in the order the validator reported them\n * @returns Location of the first error, or undefined when there is none\n */\nexport async function firstErrorLocation(\n  errors: readonly ValidationErrorDetail[],\n): Promise<{ file: string; line?: number } | undefined> {\n  const first = errors[0];\n  if (!first) return undefined;\n  try {\n    const content = await readFile(first.file, \"utf-8\");\n    return { file: first.file, line: physicalLineOfRecord(content, first.rowIndex) };\n  } catch {\n    return { file: first.file };\n  }\n}\n","/**\n * Seed integration module for generated seed code.\n *\n * Re-exports `@toiroakr/lines-db` through a single import path\n * to avoid phantom dependency issues with pnpm, and provides\n * seed-specific utility functions used by the code generator.\n */\n\nimport { readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { LinesDB, ErrorFormatter, findSchemaFile } from \"@toiroakr/lines-db\";\n// `pathe`, not `node:path`: the file paths reported back are printed and returned\n// to the caller, and these stay separator-stable across platforms.\nimport { basename, dirname, join } from \"pathe\";\nimport { firstErrorLocation } from \"./record-lines\";\nimport type { JsonObject, ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\nexport { defineSchema } from \"@toiroakr/lines-db\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\n/** Fields `fillSeedData` writes when the caller names none. */\nconst DEFAULT_FILL_FIELDS = [\"id\"];\n\ntype SeedDataTarget = {\n  dataDir: string;\n  tableName?: string;\n};\n\ntype ValidateSeedDataOptions = {\n  /** Resolved absolute path to a data directory or a .jsonl file */\n  path: string;\n  /** Show verbose error output */\n  verbose?: boolean;\n};\n\ntype ValidateSeedResult =\n  | { valid: true; output: string }\n  | {\n      valid: false;\n      output: string;\n      error: string;\n      /** Where the first reported error sits, for tooling that links to source. */\n      location?: { file: string; line?: number };\n    };\n\n/**\n * A JSONL seed file that received values.\n */\ntype FilledSeedFile = {\n  /** Name of the seeded table, matching the JSONL file name */\n  table: string;\n  /** Absolute path to the updated JSONL file */\n  file: string;\n  /** Fields that were written back to the file */\n  fields: string[];\n  /** How many rows were missing at least one of those fields */\n  count: number;\n};\n\ntype FillSeedDataOptions = {\n  /** Resolved absolute path to a data directory or a .jsonl file */\n  path: string;\n  /** Fields to fill. Defaults to `id`. */\n  fields?: readonly string[];\n};\n\ntype FillSeedDataResult = {\n  output: string;\n  filled: FilledSeedFile[];\n};\n\nasync function resolveSeedDataTarget(resolvedPath: string): Promise<SeedDataTarget> {\n  const stats = await stat(resolvedPath);\n  if (stats.isDirectory()) {\n    return { dataDir: resolvedPath };\n  }\n  if (stats.isFile() && resolvedPath.endsWith(\".jsonl\")) {\n    return { dataDir: dirname(resolvedPath), tableName: basename(resolvedPath, \".jsonl\") };\n  }\n  throw new Error(`Invalid path: ${resolvedPath}. Must be a directory or .jsonl file.`);\n}\n\nasync function listSeedTables(dataDir: string): Promise<string[]> {\n  const entries = await readdir(dataDir);\n  return entries\n    .filter((entry) => entry.endsWith(\".jsonl\"))\n    .map((entry) => basename(entry, \".jsonl\"))\n    .toSorted();\n}\n\nfunction formatWarnings(warnings: string[]): string[] {\n  if (warnings.length === 0) {\n    return [];\n  }\n  return [...warnings.map((warning) => `⚠ ${warning}`), \"\"];\n}\n\nfunction formatValidationErrors(errors: ValidationErrorDetail[], verbose: boolean): string {\n  const formatter = new ErrorFormatter({ verbose });\n  const errorLines: string[] = [];\n  const errorsByFile = new Map<string, ValidationErrorDetail[]>();\n  for (const error of errors) {\n    const fileErrors = errorsByFile.get(error.file) || [];\n    fileErrors.push(error);\n    errorsByFile.set(error.file, fileErrors);\n  }\n  for (const [file, fileErrors] of errorsByFile) {\n    errorLines.push(formatter.formatErrorHeader(fileErrors.length, file));\n    errorLines.push(\"\");\n    const validationErrors = fileErrors.filter(\n      (e) => e.type !== \"foreignKey\" || !e.foreignKeyError,\n    );\n    const foreignKeyErrors = fileErrors.filter((e) => e.type === \"foreignKey\" && e.foreignKeyError);\n    if (validationErrors.length > 0) {\n      errorLines.push(\n        formatter.formatValidationErrors(\n          validationErrors.map((e) => ({\n            file: e.file,\n            rowIndex: e.rowIndex,\n            issues: e.issues,\n          })),\n        ),\n      );\n    }\n    for (const fkError of foreignKeyErrors) {\n      if (fkError.foreignKeyError) {\n        errorLines.push(\n          formatter.formatForeignKeyError({\n            file: fkError.file,\n            rowIndex: fkError.rowIndex,\n            column: fkError.foreignKeyError.column,\n            value: fkError.foreignKeyError.value,\n            referencedTable: fkError.foreignKeyError.referencedTable,\n            referencedColumn: fkError.foreignKeyError.referencedColumn,\n          }),\n        );\n      }\n    }\n    errorLines.push(\"\");\n  }\n\n  return errorLines.join(\"\\n\");\n}\n\n/**\n * Validate JSONL seed data against schema definitions.\n * Resolves the given path (directory or `.jsonl` file), validates the rows it\n * holds, and returns formatted output and error messages.\n * @param options - Validation options including path and verbose flag\n * @returns Validation result with output messages and optional error details\n */\nexport async function validateSeedData(\n  options: ValidateSeedDataOptions,\n): Promise<ValidateSeedResult> {\n  const { path: resolvedPath, verbose = false } = options;\n  const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n\n  const db = LinesDB.create({ dataDir });\n  let result;\n  try {\n    result = await db.initialize({ tableName, detailedValidate: true });\n  } finally {\n    await db.close();\n  }\n\n  const outputLines = formatWarnings(result.warnings);\n\n  if (result.valid) {\n    outputLines.push(\"✓ All records are valid\");\n    return { valid: true, output: outputLines.join(\"\\n\") };\n  }\n\n  return {\n    valid: false,\n    output: outputLines.join(\"\\n\"),\n    error: formatValidationErrors(result.errors, verbose),\n    location: await firstErrorLocation(result.errors),\n  };\n}\n\n// A row's own value for a field, or undefined when the row has none.\nfunction ownValue(row: Record<string, unknown>, field: string): unknown {\n  return Object.hasOwn(row, field) ? row[field] : undefined;\n}\n\n// Not `row[field] = value`: a field named `__proto__` goes through the inherited\n// setter, which leaves no own property for the serializer to read back.\nfunction setField(row: JsonObject, field: string, value: JsonObject[string]): void {\n  Object.defineProperty(row, field, {\n    value,\n    enumerable: true,\n    writable: true,\n    configurable: true,\n  });\n}\n\nfunction isBlank(value: unknown): boolean {\n  if (value === undefined || value === null) {\n    return true;\n  }\n  if (typeof value !== \"object\" || Array.isArray(value)) {\n    return false;\n  }\n  // A nested field the row never had comes back as an object whose keys carry no\n  // value of their own, and writing that into the line fills nothing in.\n  return Object.values(value).every(isBlank);\n}\n\n/** A JSONL line, kept as text so an untouched line is written back verbatim. */\ntype SeedLine = {\n  text: string;\n  /** Line separator the file used after this line, kept so CRLF survives. */\n  eol: string;\n  row: JsonObject | undefined;\n};\n\nfunction splitLines(content: string): SeedLine[] {\n  if (content === \"\") {\n    return [];\n  }\n  return content.split(\"\\n\").map((raw, index, all) => {\n    const eol = index === all.length - 1 ? \"\" : \"\\n\";\n    const text = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n    const carriage = raw.endsWith(\"\\r\") ? \"\\r\" : \"\";\n    let row: JsonObject | undefined;\n    if (text.trim() !== \"\") {\n      try {\n        const parsed: unknown = JSON.parse(text);\n        row =\n          parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n            ? (parsed as JsonObject)\n            : undefined;\n      } catch {\n        row = undefined;\n      }\n    }\n    return { text, eol: `${carriage}${eol}`, row };\n  });\n}\n\n// Keys go in the order the hook produced them, which is the order the table\n// declares its fields. Keys the table does not declare follow the declared ones.\nfunction serializeRow(row: JsonObject, fieldOrder: string[]): string {\n  const rank = new Map(fieldOrder.map((field, index) => [field, index]));\n  const rankOf = (key: string): number => rank.get(key) ?? fieldOrder.length;\n  return JSON.stringify(\n    Object.fromEntries(Object.entries(row).toSorted(([a], [b]) => rankOf(a) - rankOf(b))),\n  );\n}\n\ntype SeedHook = (row: unknown) => Record<string, unknown>;\n\nasync function loadSeedHook(dataDir: string, table: string): Promise<SeedHook | undefined> {\n  const schemaPath = await findSchemaFile(dataDir, table);\n  if (!schemaPath) {\n    return undefined;\n  }\n  const loaded: unknown = await import(pathToFileURL(schemaPath).href);\n  const hook = (loaded as { hook?: unknown }).hook;\n  if (typeof hook !== \"function\") {\n    throw new Error(\n      `${schemaPath} does not export \\`hook\\`. Run \\`tailor generate\\` to regenerate the seed schema files.`,\n    );\n  }\n  return hook as SeedHook;\n}\n\n/**\n * Fill in the values a record gets on create for the JSONL seed data rows that\n * are missing them, so a row can be referenced by `id` or carry a timestamp\n * before it is ever seeded.\n *\n * The values come from the table's own create-time behavior — its `id`, its field\n * defaults, and its create hooks — applied to each row on its own. Nothing is\n * validated, so a row can be filled while the data around it is still\n * incomplete: that is what lets you get the ids you need in order to write the\n * rows that reference them. Run `validateSeedData` when the data is ready.\n *\n * Only the named fields are written, and only into a row that has no value for\n * them, so a value already in the file is never replaced. A line that gains\n * nothing is written back exactly as it was, byte for byte; a line that does get\n * a value is re-serialized with its keys in the order the table declares its\n * fields, so a filled-in `id` lands at the front. A field the table gives no\n * value to — one it does not declare, or one the platform assigns such as a\n * serial field — is skipped, so one field list covers a whole data directory.\n *\n * The values are read from the schema files generated next to the data, and all\n * of them are read before anything is written: a file that predates the current\n * generator stops the run with nothing filled in anywhere.\n * @param options - Fill options including path and fields\n * @returns Which files received which fields\n */\nexport async function fillSeedData(options: FillSeedDataOptions): Promise<FillSeedDataResult> {\n  const { path: resolvedPath, fields = DEFAULT_FILL_FIELDS } = options;\n  if (fields.length === 0) {\n    throw new Error(\"No fields to fill. Name at least one field.\");\n  }\n  const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n  const tables = tableName ? [tableName] : await listSeedTables(dataDir);\n\n  const warnings: string[] = [];\n  const filled: FilledSeedFile[] = [];\n  const producedFields = new Set<string>();\n\n  // Every hook loads before anything is written, so a schema file that predates\n  // `tailor generate` stops the run instead of leaving half the files filled.\n  const hooks: { table: string; hook: SeedHook }[] = [];\n  for (const table of tables) {\n    const hook = await loadSeedHook(dataDir, table);\n    if (!hook) {\n      warnings.push(`No schema file for ${table}, so nothing can be filled in there`);\n      continue;\n    }\n    hooks.push({ table, hook });\n  }\n\n  // Every line is decided before any file is written, so a hook that throws on\n  // one table cannot leave another one already rewritten.\n  const writes: { file: string; content: string }[] = [];\n  for (const { table, hook } of hooks) {\n    const file = join(dataDir, `${table}.jsonl`);\n    const lines = splitLines(await readFile(file, \"utf-8\"));\n\n    const written = new Set<string>();\n    const unreadable: number[] = [];\n    let count = 0;\n    let fieldOrder: string[] = [];\n    for (const [index, line] of lines.entries()) {\n      const row = line.row;\n      if (!row) {\n        if (line.text.trim() !== \"\") {\n          unreadable.push(index + 1);\n        }\n        continue;\n      }\n      const hooked = hook(row);\n      fieldOrder = Object.keys(hooked);\n      const gained = fields.filter((field) => {\n        const value = ownValue(hooked, field);\n        if (isBlank(value)) {\n          return false;\n        }\n        producedFields.add(field);\n        return isBlank(ownValue(row, field));\n      });\n      if (gained.length === 0) {\n        continue;\n      }\n      for (const field of gained) {\n        setField(row, field, hooked[field] as JsonObject[string]);\n        written.add(field);\n      }\n      line.text = serializeRow(row, fieldOrder);\n      count += 1;\n    }\n\n    if (unreadable.length > 0) {\n      warnings.push(\n        `${file}: line(s) ${unreadable.join(\", \")} are not JSON objects, so nothing was filled in there`,\n      );\n    }\n\n    if (count === 0) {\n      continue;\n    }\n    writes.push({ file, content: lines.map((line) => `${line.text}${line.eol}`).join(\"\") });\n    filled.push({ table, file, fields: [...written], count });\n  }\n\n  for (const { file, content } of writes) {\n    await writeFile(file, content);\n  }\n\n  const unproducedFields = fields.filter((field) => !producedFields.has(field));\n  if (tables.length > 0 && unproducedFields.length > 0) {\n    warnings.push(`No seed data produces a value for: ${unproducedFields.join(\", \")}`);\n  }\n\n  const outputLines = formatWarnings(warnings);\n  outputLines.push(\n    filled.length === 0\n      ? \"\\u2713 Nothing to fill\"\n      : filled\n          .map(\n            ({ file, fields: tableFields, count }) =>\n              `\\u2713 ${file}: filled ${tableFields.join(\", \")} in ${count} row(s)`,\n          )\n          .join(\"\\n\"),\n  );\n\n  return { output: outputLines.join(\"\\n\"), filled };\n}\n"],"mappings":"0RAaA,SAAgB,qBAAqB,EAAiB,EAAyC,CAC7F,IAAM,EAAQ,EAAQ,MAAM;CAAI,EAC5B,EAAY,EAChB,IAAK,GAAM,CAAC,EAAO,KAAS,EAAM,QAAQ,EACpC,KAAK,KAAK,CAAC,CAAC,SAAW,EAC3B,IAAI,IAAc,EAAG,OAAO,EAAQ,EACpC,GADoC,CAIxC,CAWA,eAAsB,mBACpB,EACsD,CACtD,IAAM,EAAQ,EAAO,GAChB,KACL,GAAI,CACF,IAAM,EAAU,MAAM,EAAS,EAAM,KAAM,OAAO,EAClD,MAAO,CAAE,KAAM,EAAM,KAAM,KAAM,qBAAqB,EAAS,EAAM,QAAQ,CAAE,CACjF,MAAQ,CACN,MAAO,CAAE,KAAM,EAAM,IAAK,CAC5B,CACF,CCvBA,MAAM,EAAsB,CAAC,IAAI,EAkDjC,eAAe,sBAAsB,EAA+C,CAClF,IAAM,EAAQ,MAAM,EAAK,CAAY,EACrC,GAAI,EAAM,YAAY,EACpB,MAAO,CAAE,QAAS,CAAa,EAEjC,GAAI,EAAM,OAAO,GAAK,EAAa,SAAS,QAAQ,EAClD,MAAO,CAAE,QAAS,EAAQ,CAAY,EAAG,UAAW,EAAS,EAAc,QAAQ,CAAE,EAEvF,MAAU,MAAM,iBAAiB,EAAa,sCAAsC,CACtF,CAEA,eAAe,eAAe,EAAoC,CAEhE,OAAO,MADe,EAAQ,CAAO,EAAA,CAElC,OAAQ,GAAU,EAAM,SAAS,QAAQ,CAAC,CAAC,CAC3C,IAAK,GAAU,EAAS,EAAO,QAAQ,CAAC,CAAC,CACzC,SAAS,CACd,CAEA,SAAS,eAAe,EAA8B,CAIpD,OAHI,EAAS,SAAW,EACf,CAAC,EAEH,CAAC,GAAG,EAAS,IAAK,GAAY,KAAK,GAAS,EAAG,EAAE,CAC1D,CAEA,SAAS,uBAAuB,EAAiC,EAA0B,CACzF,IAAM,EAAY,IAAI,EAAe,CAAE,SAAQ,CAAC,EAC1C,EAAuB,CAAC,EACxB,EAAe,IAAI,IACzB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAa,EAAa,IAAI,EAAM,IAAI,GAAK,CAAC,EACpD,EAAW,KAAK,CAAK,EACrB,EAAa,IAAI,EAAM,KAAM,CAAU,CACzC,CACA,IAAK,GAAM,CAAC,EAAM,KAAe,EAAc,CAC7C,EAAW,KAAK,EAAU,kBAAkB,EAAW,OAAQ,CAAI,CAAC,EACpE,EAAW,KAAK,EAAE,EAClB,IAAM,EAAmB,EAAW,OACjC,GAAM,EAAE,OAAS,cAAgB,CAAC,EAAE,eACvC,EACM,EAAmB,EAAW,OAAQ,GAAM,EAAE,OAAS,cAAgB,EAAE,eAAe,EAC1F,EAAiB,OAAS,GAC5B,EAAW,KACT,EAAU,uBACR,EAAiB,IAAK,IAAO,CAC3B,KAAM,EAAE,KACR,SAAU,EAAE,SACZ,OAAQ,EAAE,MACZ,EAAE,CACJ,CACF,EAEF,IAAK,IAAM,KAAW,EAChB,EAAQ,iBACV,EAAW,KACT,EAAU,sBAAsB,CAC9B,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,OAAQ,EAAQ,gBAAgB,OAChC,MAAO,EAAQ,gBAAgB,MAC/B,gBAAiB,EAAQ,gBAAgB,gBACzC,iBAAkB,EAAQ,gBAAgB,gBAC5C,CAAC,CACH,EAGJ,EAAW,KAAK,EAAE,CACpB,CAEA,OAAO,EAAW,KAAK;CAAI,CAC7B,CASA,eAAsB,iBACpB,EAC6B,CAC7B,GAAM,CAAE,KAAM,EAAc,UAAU,IAAU,EAC1C,CAAE,UAAS,aAAc,MAAM,sBAAsB,CAAY,EAEjE,EAAK,EAAQ,OAAO,CAAE,SAAQ,CAAC,EACjC,EACJ,GAAI,CACF,EAAS,MAAM,EAAG,WAAW,CAAE,YAAW,iBAAkB,EAAK,CAAC,CACpE,QAAU,CACR,MAAM,EAAG,MAAM,CACjB,CAEA,IAAM,EAAc,eAAe,EAAO,QAAQ,EAOlD,OALI,EAAO,OACT,EAAY,KAAK,yBAAyB,EACnC,CAAE,MAAO,GAAM,OAAQ,EAAY,KAAK;CAAI,CAAE,GAGhD,CACL,MAAO,GACP,OAAQ,EAAY,KAAK;CAAI,EAC7B,MAAO,uBAAuB,EAAO,OAAQ,CAAO,EACpD,SAAU,MAAM,mBAAmB,EAAO,MAAM,CAClD,CACF,CAGA,SAAS,SAAS,EAA8B,EAAwB,CACtE,OAAO,OAAO,OAAO,EAAK,CAAK,EAAI,EAAI,GAAS,IAAA,EAClD,CAIA,SAAS,SAAS,EAAiB,EAAe,EAAiC,CACjF,OAAO,eAAe,EAAK,EAAO,CAChC,QACA,WAAY,GACZ,SAAU,GACV,aAAc,EAChB,CAAC,CACH,CAEA,SAAS,QAAQ,EAAyB,CASxC,OARI,GAAiC,KAC5B,GAEL,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAC3C,GAIF,OAAO,OAAO,CAAK,CAAC,CAAC,MAAM,OAAO,CAC3C,CAUA,SAAS,WAAW,EAA6B,CAI/C,OAHI,IAAY,GACP,CAAC,EAEH,EAAQ,MAAM;CAAI,CAAC,CAAC,KAAK,EAAK,EAAO,IAAQ,CAClD,IAAM,EAAM,IAAU,EAAI,OAAS,EAAI,GAAK;EACtC,EAAO,EAAI,SAAS,IAAI,EAAI,EAAI,MAAM,EAAG,EAAE,EAAI,EAC/C,EAAW,EAAI,SAAS,IAAI,EAAI,KAAO,GACzC,EACJ,GAAI,EAAK,KAAK,IAAM,GAClB,GAAI,CACF,IAAM,EAAkB,KAAK,MAAM,CAAI,EACvC,EACqB,OAAO,GAAW,UAArC,GAAiD,CAAC,MAAM,QAAQ,CAAM,EACjE,EACD,IAAA,EACR,MAAQ,CACN,EAAM,IAAA,EACR,CAEF,MAAO,CAAE,OAAM,IAAK,GAAG,IAAW,IAAO,KAAI,CAC/C,CAAC,CACH,CAIA,SAAS,aAAa,EAAiB,EAA8B,CACnE,IAAM,EAAO,IAAI,IAAI,EAAW,KAAK,EAAO,IAAU,CAAC,EAAO,CAAK,CAAC,CAAC,EAC/D,OAAU,GAAwB,EAAK,IAAI,CAAG,GAAK,EAAW,OACpE,OAAO,KAAK,UACV,OAAO,YAAY,OAAO,QAAQ,CAAG,CAAC,CAAC,UAAU,CAAC,GAAI,CAAC,KAAO,OAAO,CAAC,EAAI,OAAO,CAAC,CAAC,CAAC,CACtF,CACF,CAIA,eAAe,aAAa,EAAiB,EAA8C,CACzF,IAAM,EAAa,MAAM,EAAe,EAAS,CAAK,EACtD,GAAI,CAAC,EACH,OAGF,IAAM,GAAQ,MADgB,OAAO,EAAc,CAAU,CAAC,CAAC,MAAA,CACnB,KAC5C,GAAI,OAAO,GAAS,WAClB,MAAU,MACR,GAAG,EAAW,wFAChB,EAEF,OAAO,CACT,CA2BA,eAAsB,aAAa,EAA2D,CAC5F,GAAM,CAAE,KAAM,EAAc,SAAS,GAAwB,EAC7D,GAAI,EAAO,SAAW,EACpB,MAAU,MAAM,6CAA6C,EAE/D,GAAM,CAAE,UAAS,aAAc,MAAM,sBAAsB,CAAY,EACjE,EAAS,EAAY,CAAC,CAAS,EAAI,MAAM,eAAe,CAAO,EAE/D,EAAqB,CAAC,EACtB,EAA2B,CAAC,EAC5B,EAAiB,IAAI,IAIrB,EAA6C,CAAC,EACpD,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,MAAM,aAAa,EAAS,CAAK,EAC9C,GAAI,CAAC,EAAM,CACT,EAAS,KAAK,sBAAsB,EAAM,oCAAoC,EAC9E,QACF,CACA,EAAM,KAAK,CAAE,QAAO,MAAK,CAAC,CAC5B,CAIA,IAAM,EAA8C,CAAC,EACrD,IAAK,GAAM,CAAE,QAAO,UAAU,EAAO,CACnC,IAAM,EAAO,EAAK,EAAS,GAAG,EAAM,OAAO,EACrC,EAAQ,WAAW,MAAM,EAAS,EAAM,OAAO,CAAC,EAEhD,EAAU,IAAI,IACd,EAAuB,CAAC,EAC1B,EAAQ,EACR,EAAuB,CAAC,EAC5B,IAAK,GAAM,CAAC,EAAO,KAAS,EAAM,QAAQ,EAAG,CAC3C,IAAM,EAAM,EAAK,IACjB,GAAI,CAAC,EAAK,CACJ,EAAK,KAAK,KAAK,IAAM,IACvB,EAAW,KAAK,EAAQ,CAAC,EAE3B,QACF,CACA,IAAM,EAAS,EAAK,CAAG,EACvB,EAAa,OAAO,KAAK,CAAM,EAC/B,IAAM,EAAS,EAAO,OAAQ,GAE5B,CAAI,QADU,SAAS,EAAQ,CACf,CAAC,IAGjB,EAAe,IAAI,CAAK,EACjB,QAAQ,SAAS,EAAK,CAAK,CAAC,EACpC,EACG,KAAO,SAAW,EAGtB,KAAK,IAAM,KAAS,EAClB,SAAS,EAAK,EAAO,EAAO,EAA4B,EACxD,EAAQ,IAAI,CAAK,EAEnB,EAAK,KAAO,aAAa,EAAK,CAAU,EACxC,GAAS,CAFT,CAGF,CAEI,EAAW,OAAS,GACtB,EAAS,KACP,GAAG,EAAK,YAAY,EAAW,KAAK,IAAI,EAAE,sDAC5C,EAGE,IAAU,IAGd,EAAO,KAAK,CAAE,OAAM,QAAS,EAAM,IAAK,GAAS,GAAG,EAAK,OAAO,EAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CAAE,CAAC,EACtF,EAAO,KAAK,CAAE,QAAO,OAAM,OAAQ,CAAC,GAAG,CAAO,EAAG,OAAM,CAAC,EAC1D,CAEA,IAAK,GAAM,CAAE,OAAM,aAAa,EAC9B,MAAM,EAAU,EAAM,CAAO,EAG/B,IAAM,EAAmB,EAAO,OAAQ,GAAU,CAAC,EAAe,IAAI,CAAK,CAAC,EACxE,EAAO,OAAS,GAAK,EAAiB,OAAS,GACjD,EAAS,KAAK,sCAAsC,EAAiB,KAAK,IAAI,GAAG,EAGnF,IAAM,EAAc,eAAe,CAAQ,EAY3C,OAXA,EAAY,KACV,EAAO,SAAW,EACd,oBACA,EACG,KACE,CAAE,OAAM,OAAQ,EAAa,WAC5B,UAAU,EAAK,WAAW,EAAY,KAAK,IAAI,EAAE,MAAM,EAAM,QACjE,CAAC,CACA,KAAK;CAAI,CAClB,EAEO,CAAE,OAAQ,EAAY,KAAK;CAAI,EAAG,QAAO,CAClD"}