kanbo-cli 0.1.0

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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/better-sqlite3-fU3G4xnX.cjs +2254 -0
  4. package/dist/better-sqlite3-fU3G4xnX.cjs.map +1 -0
  5. package/dist/cli.cjs +42949 -0
  6. package/dist/cli.cjs.map +1 -0
  7. package/dist/dist-Cd6wg8pp.cjs +942 -0
  8. package/dist/dist-Cd6wg8pp.cjs.map +1 -0
  9. package/dist/drizzle-postgres/0000_board.sql +135 -0
  10. package/dist/drizzle-postgres/0001_history_order.sql +2 -0
  11. package/dist/drizzle-postgres/0002_issue_pull_requests.sql +16 -0
  12. package/dist/drizzle-postgres/0003_milestone_start_date.sql +1 -0
  13. package/dist/drizzle-postgres/0004_status_entry_rules.sql +1 -0
  14. package/dist/drizzle-postgres/0005_run_external_session_ref.sql +1 -0
  15. package/dist/drizzle-postgres/README.md +61 -0
  16. package/dist/drizzle-postgres/meta/0000_snapshot.json +1078 -0
  17. package/dist/drizzle-postgres/meta/0001_snapshot.json +1090 -0
  18. package/dist/drizzle-postgres/meta/0002_snapshot.json +1239 -0
  19. package/dist/drizzle-postgres/meta/0003_snapshot.json +1245 -0
  20. package/dist/drizzle-postgres/meta/0004_snapshot.json +1251 -0
  21. package/dist/drizzle-postgres/meta/0005_snapshot.json +1257 -0
  22. package/dist/drizzle-postgres/meta/_journal.json +48 -0
  23. package/dist/drizzle-sqlite/0000_board.sql +133 -0
  24. package/dist/drizzle-sqlite/0001_issue_pull_requests.sql +15 -0
  25. package/dist/drizzle-sqlite/0002_milestone_start_date.sql +1 -0
  26. package/dist/drizzle-sqlite/0003_status_entry_rules.sql +1 -0
  27. package/dist/drizzle-sqlite/0004_run_external_session_ref.sql +1 -0
  28. package/dist/drizzle-sqlite/README.md +67 -0
  29. package/dist/drizzle-sqlite/meta/0000_snapshot.json +946 -0
  30. package/dist/drizzle-sqlite/meta/0001_snapshot.json +1054 -0
  31. package/dist/drizzle-sqlite/meta/0002_snapshot.json +1061 -0
  32. package/dist/drizzle-sqlite/meta/0003_snapshot.json +1068 -0
  33. package/dist/drizzle-sqlite/meta/0004_snapshot.json +1075 -0
  34. package/dist/drizzle-sqlite/meta/_journal.json +41 -0
  35. package/dist/lib/board-store-B16YarvX.d.mts +281 -0
  36. package/dist/lib/board-store-BHHo_ZlR.d.cts +281 -0
  37. package/dist/lib/index.cjs +340 -0
  38. package/dist/lib/index.cjs.map +1 -0
  39. package/dist/lib/index.d.cts +1608 -0
  40. package/dist/lib/index.d.mts +1608 -0
  41. package/dist/lib/index.mjs +284 -0
  42. package/dist/lib/index.mjs.map +1 -0
  43. package/dist/lib/manifest-C0EcwwgO.mjs +7866 -0
  44. package/dist/lib/manifest-C0EcwwgO.mjs.map +1 -0
  45. package/dist/lib/manifest-CpZOcB_6.cjs +8272 -0
  46. package/dist/lib/manifest-CpZOcB_6.cjs.map +1 -0
  47. package/dist/lib/mcp/index.cjs +184 -0
  48. package/dist/lib/mcp/index.cjs.map +1 -0
  49. package/dist/lib/mcp/index.d.cts +346 -0
  50. package/dist/lib/mcp/index.d.mts +346 -0
  51. package/dist/lib/mcp/index.mjs +173 -0
  52. package/dist/lib/mcp/index.mjs.map +1 -0
  53. package/dist/lib/migrate-BhYi9cXw.mjs +656 -0
  54. package/dist/lib/migrate-BhYi9cXw.mjs.map +1 -0
  55. package/dist/lib/migrate-DnMjTCvu.cjs +709 -0
  56. package/dist/lib/migrate-DnMjTCvu.cjs.map +1 -0
  57. package/dist/lib/migrations-path-CZxVOgeD.cjs +109 -0
  58. package/dist/lib/migrations-path-CZxVOgeD.cjs.map +1 -0
  59. package/dist/lib/migrations-path-Ciwan537.mjs +80 -0
  60. package/dist/lib/migrations-path-Ciwan537.mjs.map +1 -0
  61. package/dist/lib/postgres/index.cjs +56 -0
  62. package/dist/lib/postgres/index.cjs.map +1 -0
  63. package/dist/lib/postgres/index.d.cts +3403 -0
  64. package/dist/lib/postgres/index.d.mts +3403 -0
  65. package/dist/lib/postgres/index.mjs +35 -0
  66. package/dist/lib/postgres/index.mjs.map +1 -0
  67. package/dist/lib/roles.sql-B9rt-x4o.cjs +1033 -0
  68. package/dist/lib/roles.sql-B9rt-x4o.cjs.map +1 -0
  69. package/dist/lib/roles.sql-CvyCGt38.mjs +920 -0
  70. package/dist/lib/roles.sql-CvyCGt38.mjs.map +1 -0
  71. package/dist/lib/schema-epoch-ZyumVL_A.d.cts +16 -0
  72. package/dist/lib/schema-epoch-ZyumVL_A.d.mts +16 -0
  73. package/dist/lib/schema-fsHqNAip.d.cts +3612 -0
  74. package/dist/lib/schema-fsHqNAip.d.mts +3612 -0
  75. package/dist/lib/sqlite/index.cjs +23 -0
  76. package/dist/lib/sqlite/index.d.cts +175 -0
  77. package/dist/lib/sqlite/index.d.mts +175 -0
  78. package/dist/lib/sqlite/index.mjs +4 -0
  79. package/dist/lib/sqlite/schema.cjs +266 -0
  80. package/dist/lib/sqlite/schema.cjs.map +1 -0
  81. package/dist/lib/sqlite/schema.d.cts +2 -0
  82. package/dist/lib/sqlite/schema.d.mts +2 -0
  83. package/dist/lib/sqlite/schema.mjs +256 -0
  84. package/dist/lib/sqlite/schema.mjs.map +1 -0
  85. package/dist/lib/tool-names-9cmCn0gP.d.cts +112 -0
  86. package/dist/lib/tool-names-Dxg1UTUl.d.mts +112 -0
  87. package/dist/page/board.css +477 -0
  88. package/dist/page/board.js +2 -0
  89. package/drizzle-postgres/0000_board.sql +135 -0
  90. package/drizzle-postgres/0001_history_order.sql +2 -0
  91. package/drizzle-postgres/0002_issue_pull_requests.sql +16 -0
  92. package/drizzle-postgres/0003_milestone_start_date.sql +1 -0
  93. package/drizzle-postgres/0004_status_entry_rules.sql +1 -0
  94. package/drizzle-postgres/0005_run_external_session_ref.sql +1 -0
  95. package/drizzle-postgres/README.md +61 -0
  96. package/drizzle-postgres/meta/0000_snapshot.json +1078 -0
  97. package/drizzle-postgres/meta/0001_snapshot.json +1090 -0
  98. package/drizzle-postgres/meta/0002_snapshot.json +1239 -0
  99. package/drizzle-postgres/meta/0003_snapshot.json +1245 -0
  100. package/drizzle-postgres/meta/0004_snapshot.json +1251 -0
  101. package/drizzle-postgres/meta/0005_snapshot.json +1257 -0
  102. package/drizzle-postgres/meta/_journal.json +48 -0
  103. package/drizzle-sqlite/0000_board.sql +133 -0
  104. package/drizzle-sqlite/0001_issue_pull_requests.sql +15 -0
  105. package/drizzle-sqlite/0002_milestone_start_date.sql +1 -0
  106. package/drizzle-sqlite/0003_status_entry_rules.sql +1 -0
  107. package/drizzle-sqlite/0004_run_external_session_ref.sql +1 -0
  108. package/drizzle-sqlite/README.md +67 -0
  109. package/drizzle-sqlite/meta/0000_snapshot.json +946 -0
  110. package/drizzle-sqlite/meta/0001_snapshot.json +1054 -0
  111. package/drizzle-sqlite/meta/0002_snapshot.json +1061 -0
  112. package/drizzle-sqlite/meta/0003_snapshot.json +1068 -0
  113. package/drizzle-sqlite/meta/0004_snapshot.json +1075 -0
  114. package/drizzle-sqlite/meta/_journal.json +41 -0
  115. package/package.json +147 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-BhYi9cXw.mjs","names":[],"sources":["../../src/sqlite/transaction.ts","../../src/sqlite/board-store.sqlite.ts","../../src/sqlite/open-database.ts","../../src/sqlite/migrate.ts"],"sourcesContent":["import type { Database as SqliteConnection } from 'better-sqlite3'\nimport { sql } from 'drizzle-orm'\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\n\n/**\n * The database handle itself — what the host hands the board — and not an open\n * transaction on it: the helper reaches for the driver connection, which a\n * drizzle `SQLiteTransaction` does not carry.\n *\n * The schema parameter is `Record<string, unknown>` rather than any particular\n * schema, which is what lets one type stand for all three of the files a board\n * can live in (ruling 5-3): a handle drizzle was given a host app's whole schema\n * satisfies it, and so does one given only the board's own nine tables. The\n * board never reaches for `database.query`, so the relational builder those\n * generics shape is not something this package gives up by asking for less —\n * `src/sqlite/database-type.test.ts` is the probe that says both handles still\n * arrive here.\n */\nexport type SqliteDatabase = BetterSQLite3Database<Record<string, unknown>>\n\n/**\n * The better-sqlite3 handle Drizzle keeps beside the query builder. `drizzle()`\n * attaches it as `$client` (`drizzle-orm/better-sqlite3/driver.js`), but the\n * handle is declared as a `BetterSQLite3Database`, a class that does not\n * declare `$client` — which is why the assertion below restates the shape by\n * hand. Its `inTransaction` is the only honest answer to \"is a\n * transaction already open\", including one this module did not open.\n */\nfunction readSqliteConnection(database: SqliteDatabase): SqliteConnection {\n return (database as SqliteDatabase & { $client: SqliteConnection }).$client\n}\n\n/** The statements that open, keep and undo one transaction scope. */\ninterface TransactionScope {\n begin: string\n commit: string\n undo: string\n /** A savepoint has to be released after its rollback; the outer scope has not. */\n releaseAfterUndo: string | null\n}\n\n/**\n * One fixed name for every nested scope, the way better-sqlite3 names its own\n * (`lib/methods/transaction.js`). `release` and `rollback to` address the\n * innermost savepoint carrying the name, and these scopes are opened and closed\n * in stack order, so each call reaches its own — a counter in the name would\n * only repeat what SQLite already tracks, and would collide the moment two\n * scopes nested through different code paths.\n */\nconst SAVEPOINT_NAME = 'kanbo_tx'\n\n/**\n * How the outer `begin` takes its write lock. `deferred` — the SQLite default —\n * waits for the first write; `immediate` takes the lock up front, so two\n * processes racing for the same file fail fast instead of half-way through.\n * A nested scope is a savepoint and takes no lock of its own, so the mode only\n * reaches the outer statement.\n */\nexport type SqliteTransactionMode = 'deferred' | 'immediate'\n\nfunction readTransactionScope(connection: SqliteConnection, mode: SqliteTransactionMode): TransactionScope {\n if (!connection.inTransaction) {\n return {\n begin: mode === 'immediate' ? 'begin immediate' : 'begin',\n commit: 'commit',\n undo: 'rollback',\n releaseAfterUndo: null,\n }\n }\n return {\n begin: `savepoint ${SAVEPOINT_NAME}`,\n commit: `release ${SAVEPOINT_NAME}`,\n undo: `rollback to ${SAVEPOINT_NAME}`,\n releaseAfterUndo: `release ${SAVEPOINT_NAME}`,\n }\n}\n\n/**\n * Run `fn` inside one SQLite transaction that is allowed to await.\n *\n * `database.transaction(...)` cannot serve here: it commits the moment its\n * callback returns, and an async callback returns a pending promise, so the\n * transaction would close before the work inside it ran. The statements are\n * issued by hand instead, and the driver's own `inTransaction` flag picks which\n * — the same branch better-sqlite3 takes. With nothing open: `begin`, then\n * `commit`, or `rollback` when `fn` throws. With a transaction already open —\n * another call to this helper, or one a module opened through\n * `database.transaction` — a `savepoint` … `release` pair, undone by\n * `rollback to` followed by its own `release`. A second `begin` would throw,\n * which is why the flag decides and not a private counter. When SQLite has\n * already aborted the transaction itself the undo is skipped: it would throw\n * over the caller's error.\n *\n * **The callback must be the only database-touching async chain in flight —\n * no `Promise.all` sibling, no lock hand-off, no fire-and-forget promise\n * started before it — and inside it, only await operations that resolve\n * without I/O: store methods, synchronous database writes.** Even a resolved\n * promise yields to the microtask queue first, so a sibling chain already\n * queued in the same macrotask still runs inside this open transaction; a\n * chain started in a separate macrotask is unaffected. Awaiting anything that\n * truly suspends — a timer, the filesystem, the network — hands the\n * connection to whatever runs next, and its writes land in this transaction\n * too.\n */\nexport async function runSqliteTransaction<T>(\n database: SqliteDatabase,\n fn: () => Promise<T>,\n options?: { mode?: SqliteTransactionMode },\n): Promise<T> {\n const connection = readSqliteConnection(database)\n const scope = readTransactionScope(connection, options?.mode ?? 'deferred')\n\n database.run(sql.raw(scope.begin))\n try {\n const result = await fn()\n database.run(sql.raw(scope.commit))\n return result\n }\n catch (error) {\n if (connection.inTransaction) {\n database.run(sql.raw(scope.undo))\n if (scope.releaseAfterUndo) {\n database.run(sql.raw(scope.releaseAfterUndo))\n }\n }\n throw error\n }\n}\n","import { and, asc, desc, eq, getTableColumns, inArray, isNotNull, or, sql } from 'drizzle-orm'\n\nimport type {\n BoardActiveRun,\n BoardRunProjection,\n BoardRunProjectionScope,\n BoardStore,\n BoardTransactionMode,\n IssueCommentStore,\n IssueContextRefStore,\n IssueFieldChangeStore,\n IssueMilestoneStore,\n IssuePullRequestStore,\n IssueRelationStore,\n IssueRowStore,\n IssueRunStore,\n IssueStatusStore,\n KanbanMetaStore,\n} from '../board-store'\nimport {\n issueComments,\n issueFieldChanges,\n issueMilestones,\n issuePullRequests,\n issueRelations,\n issueRuns,\n issues,\n issueStatuses,\n kanbanMeta,\n} from './schema'\nimport type { SqliteDatabase } from './transaction'\nimport { runSqliteTransaction } from './transaction'\n\n/**\n * One `BoardStore` group with the `Promise` taken off every method: better-sqlite3\n * is synchronous, so the core returns rows directly and the facade below is what\n * adds the promises back.\n */\ntype SyncStoreGroup<TGroup> = {\n [TMethod in keyof TGroup]: TGroup[TMethod] extends (...args: infer TArgs) => Promise<infer TResult>\n ? (...args: TArgs) => TResult\n : never\n}\n\n/**\n * The synchronous core of the SQLite store — the methods `BoardStore` declares,\n * resolved rather than promised.\n *\n * Every statement in the module runs through this core; the store returned by\n * `createSqliteBoardStore` is a thin async wrapper over it. Transactions are not\n * part of it: only the facade offers one, because only an awaitable transaction\n * can hold a `BoardStore` callback open.\n */\ninterface SqliteBoardStoreCore {\n statuses: SyncStoreGroup<IssueStatusStore>\n milestones: SyncStoreGroup<IssueMilestoneStore>\n issues: SyncStoreGroup<IssueRowStore>\n comments: SyncStoreGroup<IssueCommentStore>\n runs: SyncStoreGroup<IssueRunStore>\n relations: SyncStoreGroup<IssueRelationStore>\n contextRefs: SyncStoreGroup<IssueContextRefStore>\n fieldChanges: SyncStoreGroup<IssueFieldChangeStore>\n pullRequests: SyncStoreGroup<IssuePullRequestStore>\n meta: SyncStoreGroup<KanbanMetaStore>\n}\n\n/**\n * The order two runs of one card are in when they started in the same second.\n *\n * `started_at` counts whole seconds and a run id is a random UUID, so the column\n * pair the attempt number is built on cannot separate two launches a second\n * apart — the row the board wrote second has to be the second attempt. SQLite\n * hands that out already: `rowid` rises with every insert. A store on another\n * engine answers the same question with whatever its own insert order is.\n */\nconst RUN_INSERTION_ORDER = sql`${issueRuns}.rowid asc`\n\n/**\n * The same tie-break for the two tables that record what happened to a card.\n *\n * `created_at` counts whole seconds here too, and a person approving a card and\n * an agent commenting on it inside the same second must come back in the order\n * the board wrote them: `readApproval` takes the *last* comment to decide\n * whether the card was approved or returned.\n */\nconst COMMENT_INSERTION_ORDER = sql`${issueComments}.rowid asc`\nconst FIELD_CHANGE_INSERTION_ORDER = sql`${issueFieldChanges}.rowid asc`\n\n/** And for a card's pull-request links, which list in the order they were made. */\nconst PULL_REQUEST_INSERTION_ORDER = sql`${issuePullRequests}.rowid asc`\n\n/**\n * The run facts of every card in scope, in one statement.\n *\n * The window functions do the grouping the caller would otherwise do with a\n * second query per card: `count(*)` counts the card's runs on every one of its\n * rows, and `row_number()` ranks them so that a `running` run wins, then the\n * newest start, then the row written last — the tie-break two runs started in\n * the same second need. Reading only rank 1 therefore leaves one row per card, carrying\n * the run the board should show — and `state` on that row says whether it is\n * still going, which is what turns it into an `activeRun` or into `null`.\n */\nfunction selectRunProjection(database: SqliteDatabase, scope: BoardRunProjectionScope): BoardRunProjection[] {\n if ('issueIds' in scope && scope.issueIds.length === 0) {\n return []\n }\n\n const cards = 'workspaceId' in scope\n ? sql`select ${issues.id} from ${issues} where ${issues.workspaceId} = ${scope.workspaceId}`\n : sql`values ${sql.join(scope.issueIds.map(issueId => sql`(${issueId})`), sql`, `)}`\n\n const rows = database.all<{\n issueId: string\n attemptCount: number\n id: string\n agentId: string | null\n agentName: string\n state: BoardActiveRun['state']\n branch: string | null\n executionMode: BoardActiveRun['executionMode']\n chatSessionId: string | null\n startedAt: number\n }>(sql`\n select\n ranked.issue_id as issueId,\n ranked.attempt_count as attemptCount,\n ranked.id as id,\n ranked.agent_id as agentId,\n ranked.agent_name as agentName,\n ranked.state as state,\n ranked.branch as branch,\n ranked.execution_mode as executionMode,\n ranked.chat_session_id as chatSessionId,\n ranked.started_at as startedAt\n from (\n select\n run.*,\n count(*) over (partition by run.issue_id) as attempt_count,\n row_number() over (\n partition by run.issue_id\n order by (run.state = 'running') desc, run.started_at desc, run.rowid desc\n ) as card_rank\n from ${issueRuns} as run\n where run.issue_id in (${cards})\n ) as ranked\n where ranked.card_rank = 1\n `)\n\n return rows.map(row => ({\n issueId: row.issueId,\n attemptCount: row.attemptCount,\n activeRun: row.state === 'running'\n ? {\n id: row.id,\n agentId: row.agentId,\n agentName: row.agentName,\n state: row.state,\n branch: row.branch,\n executionMode: row.executionMode,\n chatSessionId: row.chatSessionId,\n startedAt: row.startedAt,\n }\n : null,\n }))\n}\n\n/**\n * Bind the store's statements to one SQLite handle. `resolveDatabase` is called\n * per statement rather than captured, so the store follows a host that swaps its\n * database out (a server that resets its infrastructure between tests, say).\n */\nfunction createSqliteBoardStoreCore(resolveDatabase: () => SqliteDatabase): SqliteBoardStoreCore {\n return {\n statuses: {\n listByWorkspace: workspaceId => resolveDatabase()\n .select()\n .from(issueStatuses)\n .where(eq(issueStatuses.workspaceId, workspaceId))\n .orderBy(issueStatuses.order)\n .all(),\n listAllInBoardOrder: () => resolveDatabase()\n .select()\n .from(issueStatuses)\n .orderBy(issueStatuses.workspaceId, issueStatuses.order)\n .all(),\n listAll: () => resolveDatabase().select().from(issueStatuses).all(),\n findById: statusId => resolveDatabase()\n .select()\n .from(issueStatuses)\n .where(eq(issueStatuses.id, statusId))\n .get() ?? null,\n findInWorkspace: (workspaceId, statusId) => resolveDatabase()\n .select()\n .from(issueStatuses)\n .where(sql`${issueStatuses.workspaceId} = ${workspaceId} AND ${issueStatuses.id} = ${statusId}`)\n .get() ?? null,\n countByWorkspace: (workspaceId) => {\n const row = resolveDatabase()\n .select({ count: sql<number>`count(*)` })\n .from(issueStatuses)\n .where(eq(issueStatuses.workspaceId, workspaceId))\n .get()\n return row?.count ?? 0\n },\n create: values => resolveDatabase().insert(issueStatuses).values(values).returning().get(),\n update: (statusId, patch) => {\n resolveDatabase().update(issueStatuses).set(patch).where(eq(issueStatuses.id, statusId)).run()\n },\n delete: (statusId) => {\n resolveDatabase().delete(issueStatuses).where(eq(issueStatuses.id, statusId)).run()\n },\n },\n\n milestones: {\n listNewestFirst: (workspaceId) => {\n const predicates = [\n workspaceId ? eq(issueMilestones.workspaceId, workspaceId) : undefined,\n ].filter(predicate => predicate !== undefined)\n return resolveDatabase()\n .select()\n .from(issueMilestones)\n .where(predicates.length > 0 ? and(...predicates) : undefined)\n .orderBy(desc(issueMilestones.createdAt))\n .all()\n },\n listByWorkspace: workspaceId => resolveDatabase()\n .select()\n .from(issueMilestones)\n .where(eq(issueMilestones.workspaceId, workspaceId))\n .all(),\n listAll: () => resolveDatabase().select().from(issueMilestones).all(),\n findById: milestoneId => resolveDatabase()\n .select()\n .from(issueMilestones)\n .where(eq(issueMilestones.id, milestoneId))\n .get() ?? null,\n findInWorkspace: (workspaceId, milestoneId) => resolveDatabase()\n .select()\n .from(issueMilestones)\n .where(and(eq(issueMilestones.id, milestoneId), eq(issueMilestones.workspaceId, workspaceId)))\n .get() ?? null,\n create: values => resolveDatabase().insert(issueMilestones).values(values).returning().get(),\n update: (milestoneId, patch) => {\n resolveDatabase().update(issueMilestones).set(patch).where(eq(issueMilestones.id, milestoneId)).run()\n },\n delete: (milestoneId) => {\n resolveDatabase().delete(issueMilestones).where(eq(issueMilestones.id, milestoneId)).run()\n },\n },\n\n issues: {\n listInBoardOrder: (workspaceId) => {\n const predicates = [\n workspaceId ? eq(issues.workspaceId, workspaceId) : undefined,\n ].filter(predicate => predicate !== undefined)\n return resolveDatabase()\n .select()\n .from(issues)\n .where(predicates.length > 0 ? and(...predicates) : undefined)\n // Board position is authoritative; creation time only breaks ties between\n // issues that have never been dragged (all of which share order 0).\n .orderBy(issues.order, desc(issues.createdAt))\n .all()\n },\n listNewestFirst: () => resolveDatabase().select().from(issues).orderBy(desc(issues.createdAt)).all(),\n listAll: () => resolveDatabase().select().from(issues).all(),\n listByWorkspace: workspaceId => resolveDatabase()\n .select()\n .from(issues)\n .where(eq(issues.workspaceId, workspaceId))\n .all(),\n listByIds: issueIds => resolveDatabase().select().from(issues).where(inArray(issues.id, issueIds)).all(),\n findById: issueId => resolveDatabase().select().from(issues).where(eq(issues.id, issueId)).get() ?? null,\n findInWorkspace: (workspaceId, issueId) => resolveDatabase()\n .select()\n .from(issues)\n .where(and(eq(issues.id, issueId), eq(issues.workspaceId, workspaceId)))\n .get() ?? null,\n findByNumber: (workspaceId, number) => resolveDatabase()\n .select()\n .from(issues)\n .where(and(eq(issues.workspaceId, workspaceId), eq(issues.number, number)))\n .get() ?? null,\n maxNumber: (workspaceId) => {\n const row = resolveDatabase()\n .select({ maxNum: sql<number>`coalesce(max(${issues.number}), 0)` })\n .from(issues)\n .where(eq(issues.workspaceId, workspaceId))\n .get()\n return row?.maxNum ?? 0\n },\n maxOrder: (workspaceId) => {\n const row = resolveDatabase()\n .select({ maxOrder: sql<number>`coalesce(max(${issues.order}), 0)` })\n .from(issues)\n .where(eq(issues.workspaceId, workspaceId))\n .get()\n return row?.maxOrder ?? 0\n },\n create: values => resolveDatabase().insert(issues).values(values).returning().get(),\n update: (issueId, patch) => {\n resolveDatabase().update(issues).set(patch).where(eq(issues.id, issueId)).run()\n },\n updateMany: (issueIds, patch) => resolveDatabase()\n .update(issues)\n .set(patch)\n .where(sql`${issues.id} IN (${sql.join(issueIds.map(issueId => sql`${issueId}`), sql`, `)})`)\n .run()\n .changes,\n clearMilestoneReferences: (milestoneId, updatedAt) => {\n resolveDatabase()\n .update(issues)\n .set({ milestoneId: null, updatedAt })\n .where(eq(issues.milestoneId, milestoneId))\n .run()\n },\n clearParentReferences: (parentIssueId, updatedAt) => {\n resolveDatabase()\n .update(issues)\n .set({ parentIssueId: null, updatedAt })\n .where(eq(issues.parentIssueId, parentIssueId))\n .run()\n },\n clearDelegateReferences: (delegate, updatedAt) => resolveDatabase()\n .update(issues)\n .set({ delegateAgentId: null, delegateProviderTargetId: null, updatedAt })\n .where('agentId' in delegate\n ? eq(issues.delegateAgentId, delegate.agentId)\n : eq(issues.delegateProviderTargetId, delegate.providerTargetId))\n .run()\n .changes,\n delete: (issueId) => {\n resolveDatabase().delete(issues).where(eq(issues.id, issueId)).run()\n },\n },\n\n comments: {\n listByIssue: issueId => resolveDatabase()\n .select()\n .from(issueComments)\n .where(eq(issueComments.issueId, issueId))\n .orderBy(asc(issueComments.createdAt), COMMENT_INSERTION_ORDER)\n .all(),\n findById: commentId => resolveDatabase()\n .select()\n .from(issueComments)\n .where(eq(issueComments.id, commentId))\n .get() ?? null,\n findByDedupeKey: (issueId, dedupeKey) => resolveDatabase()\n .select()\n .from(issueComments)\n .where(and(eq(issueComments.issueId, issueId), eq(issueComments.dedupeKey, dedupeKey)))\n .get() ?? null,\n // `substr` rather than `like`: a key may carry `_` or `%`, which `like`\n // would read as wildcards.\n listByDedupeKeyPrefix: (issueId, prefix) => resolveDatabase()\n .select()\n .from(issueComments)\n .where(and(\n eq(issueComments.issueId, issueId),\n sql`substr(${issueComments.dedupeKey}, 1, ${prefix.length}) = ${prefix}`,\n ))\n .orderBy(asc(issueComments.createdAt), COMMENT_INSERTION_ORDER)\n .all(),\n create: values => resolveDatabase().insert(issueComments).values(values).returning().get(),\n createOnce: (values) => {\n const database = resolveDatabase()\n // `do nothing` returns no row when the key is already taken, and the\n // read below then finds whichever row holds it.\n const inserted = database.insert(issueComments).values(values).onConflictDoNothing().returning().get()\n if (inserted) {\n return inserted\n }\n const existing = database\n .select()\n .from(issueComments)\n .where(and(eq(issueComments.issueId, values.issueId), eq(issueComments.dedupeKey, values.dedupeKey)))\n .get()\n if (!existing) {\n // The insert was refused, and not by the dedupe index: some other\n // constraint on the row said no. Returning nothing here would hand the\n // caller an `IssueComment` that does not exist.\n throw new Error(\n `issue comment for ${values.issueId} was refused, and no comment carries dedupe key ${values.dedupeKey}`,\n )\n }\n return existing\n },\n delete: (commentId) => {\n resolveDatabase().delete(issueComments).where(eq(issueComments.id, commentId)).run()\n },\n },\n\n runs: {\n listByIssue: issueId => resolveDatabase()\n .select()\n .from(issueRuns)\n .where(eq(issueRuns.issueId, issueId))\n .orderBy(asc(issueRuns.startedAt), RUN_INSERTION_ORDER)\n .all(),\n listRunning: issueId => resolveDatabase()\n .select()\n .from(issueRuns)\n .where(and(eq(issueRuns.issueId, issueId), eq(issueRuns.state, 'running')))\n .orderBy(asc(issueRuns.startedAt), RUN_INSERTION_ORDER)\n .all(),\n listRunningWithSession: () => resolveDatabase()\n .select()\n .from(issueRuns)\n .where(and(eq(issueRuns.state, 'running'), isNotNull(issueRuns.chatSessionId)))\n .orderBy(asc(issueRuns.startedAt), RUN_INSERTION_ORDER)\n .all(),\n findById: runId => resolveDatabase().select().from(issueRuns).where(eq(issueRuns.id, runId)).get() ?? null,\n findByChatSessionId: chatSessionId => resolveDatabase()\n .select()\n .from(issueRuns)\n .where(eq(issueRuns.chatSessionId, chatSessionId))\n .orderBy(desc(issueRuns.startedAt), sql`${issueRuns}.rowid desc`)\n .get() ?? null,\n countByIssue: (issueId) => {\n const row = resolveDatabase()\n .select({ count: sql<number>`count(*)` })\n .from(issueRuns)\n .where(eq(issueRuns.issueId, issueId))\n .get()\n return row?.count ?? 0\n },\n create: values => resolveDatabase().insert(issueRuns).values(values).returning().get(),\n update: (runId, patch) => {\n resolveDatabase().update(issueRuns).set(patch).where(eq(issueRuns.id, runId)).run()\n },\n project: scope => selectRunProjection(resolveDatabase(), scope),\n },\n\n relations: {\n listByIssue: issueId => resolveDatabase()\n .select()\n .from(issueRelations)\n .where(or(eq(issueRelations.sourceIssueId, issueId), eq(issueRelations.targetIssueId, issueId)))\n .all(),\n findById: relationId => resolveDatabase()\n .select()\n .from(issueRelations)\n .where(eq(issueRelations.id, relationId))\n .get() ?? null,\n findMatching: edge => resolveDatabase()\n .select()\n .from(issueRelations)\n .where(and(\n eq(issueRelations.type, edge.type),\n edge.type === 'relates_to'\n ? or(\n and(\n eq(issueRelations.sourceIssueId, edge.sourceIssueId),\n eq(issueRelations.targetIssueId, edge.targetIssueId),\n ),\n and(\n eq(issueRelations.sourceIssueId, edge.targetIssueId),\n eq(issueRelations.targetIssueId, edge.sourceIssueId),\n ),\n )\n : and(\n eq(issueRelations.sourceIssueId, edge.sourceIssueId),\n eq(issueRelations.targetIssueId, edge.targetIssueId),\n ),\n ))\n .get() ?? null,\n create: values => resolveDatabase().insert(issueRelations).values(values).returning().get(),\n delete: (relationId) => {\n resolveDatabase().delete(issueRelations).where(eq(issueRelations.id, relationId)).run()\n },\n deleteByIssue: (issueId) => {\n resolveDatabase()\n .delete(issueRelations)\n .where(or(eq(issueRelations.sourceIssueId, issueId), eq(issueRelations.targetIssueId, issueId)))\n .run()\n },\n },\n\n contextRefs: {\n replace: (issueId, contextRefs, updatedAt) => {\n resolveDatabase().update(issues).set({ contextRefs, updatedAt }).where(eq(issues.id, issueId)).run()\n },\n },\n\n fieldChanges: {\n listByIssue: issueId => resolveDatabase()\n .select()\n .from(issueFieldChanges)\n .where(eq(issueFieldChanges.issueId, issueId))\n .orderBy(asc(issueFieldChanges.createdAt), FIELD_CHANGE_INSERTION_ORDER)\n .all(),\n listByIssueField: (issueId, field) => resolveDatabase()\n .select()\n .from(issueFieldChanges)\n .where(and(eq(issueFieldChanges.issueId, issueId), eq(issueFieldChanges.field, field)))\n .orderBy(asc(issueFieldChanges.createdAt), FIELD_CHANGE_INSERTION_ORDER)\n .all(),\n create: (values) => {\n resolveDatabase().insert(issueFieldChanges).values(values).run()\n },\n },\n\n pullRequests: {\n listByIssue: issueId => resolveDatabase()\n .select()\n .from(issuePullRequests)\n .where(eq(issuePullRequests.issueId, issueId))\n .orderBy(asc(issuePullRequests.createdAt), PULL_REQUEST_INSERTION_ORDER)\n .all(),\n listLinked: () => resolveDatabase()\n .select({ ...getTableColumns(issuePullRequests), workspaceId: issues.workspaceId })\n .from(issuePullRequests)\n .innerJoin(issues, eq(issues.id, issuePullRequests.issueId))\n .orderBy(asc(issuePullRequests.createdAt), PULL_REQUEST_INSERTION_ORDER)\n .all(),\n link: (values) => {\n const database = resolveDatabase()\n // `do nothing` returns no row when the card already names the pull\n // request, and the read below then finds the link that does.\n const inserted = database.insert(issuePullRequests).values(values).onConflictDoNothing().returning().get()\n if (inserted) {\n return inserted\n }\n const existing = database\n .select()\n .from(issuePullRequests)\n .where(and(\n eq(issuePullRequests.issueId, values.issueId),\n eq(issuePullRequests.owner, values.owner),\n eq(issuePullRequests.repo, values.repo),\n eq(issuePullRequests.number, values.number),\n ))\n .get()\n if (!existing) {\n // Refused, and not by the one index that makes a link idempotent.\n throw new Error(\n `pull request link for ${values.issueId} was refused, and the card names no ${values.owner}/${values.repo}#${values.number}`,\n )\n }\n return existing\n },\n unlink: (issueId, linkId) => resolveDatabase()\n .delete(issuePullRequests)\n .where(and(eq(issuePullRequests.id, linkId), eq(issuePullRequests.issueId, issueId)))\n .returning()\n .get() ?? null,\n },\n\n meta: {\n read: key => resolveDatabase().select().from(kanbanMeta).where(eq(kanbanMeta.key, key)).get() ?? null,\n bumpRevision: (key, updatedAt) => {\n resolveDatabase()\n .insert(kanbanMeta)\n .values({ key, revision: 1, updatedAt })\n .onConflictDoUpdate({\n target: kanbanMeta.key,\n set: { revision: sql`${kanbanMeta.revision} + 1`, updatedAt },\n })\n .run()\n },\n },\n }\n}\n\n/**\n * `BoardStore` backed by SQLite: every method resolves immediately, because the\n * core underneath it is synchronous.\n *\n * `database` is a function rather than a handle so the store follows whatever\n * the host currently has open — a server passes its single connection\n * accessor, a standalone reader passes its own.\n */\nexport function createSqliteBoardStore(options: { database: () => SqliteDatabase }): BoardStore {\n const core = createSqliteBoardStoreCore(options.database)\n\n const store: BoardStore = {\n statuses: {\n listByWorkspace: async workspaceId => core.statuses.listByWorkspace(workspaceId),\n listAllInBoardOrder: async () => core.statuses.listAllInBoardOrder(),\n listAll: async () => core.statuses.listAll(),\n findById: async statusId => core.statuses.findById(statusId),\n findInWorkspace: async (workspaceId, statusId) => core.statuses.findInWorkspace(workspaceId, statusId),\n countByWorkspace: async workspaceId => core.statuses.countByWorkspace(workspaceId),\n create: async values => core.statuses.create(values),\n update: async (statusId, patch) => core.statuses.update(statusId, patch),\n delete: async statusId => core.statuses.delete(statusId),\n },\n\n milestones: {\n listNewestFirst: async workspaceId => core.milestones.listNewestFirst(workspaceId),\n listByWorkspace: async workspaceId => core.milestones.listByWorkspace(workspaceId),\n listAll: async () => core.milestones.listAll(),\n findById: async milestoneId => core.milestones.findById(milestoneId),\n findInWorkspace: async (workspaceId, milestoneId) => core.milestones.findInWorkspace(workspaceId, milestoneId),\n create: async values => core.milestones.create(values),\n update: async (milestoneId, patch) => core.milestones.update(milestoneId, patch),\n delete: async milestoneId => core.milestones.delete(milestoneId),\n },\n\n issues: {\n listInBoardOrder: async workspaceId => core.issues.listInBoardOrder(workspaceId),\n listNewestFirst: async () => core.issues.listNewestFirst(),\n listAll: async () => core.issues.listAll(),\n listByWorkspace: async workspaceId => core.issues.listByWorkspace(workspaceId),\n listByIds: async issueIds => core.issues.listByIds(issueIds),\n findById: async issueId => core.issues.findById(issueId),\n findInWorkspace: async (workspaceId, issueId) => core.issues.findInWorkspace(workspaceId, issueId),\n findByNumber: async (workspaceId, number) => core.issues.findByNumber(workspaceId, number),\n maxNumber: async workspaceId => core.issues.maxNumber(workspaceId),\n maxOrder: async workspaceId => core.issues.maxOrder(workspaceId),\n create: async values => core.issues.create(values),\n update: async (issueId, patch) => core.issues.update(issueId, patch),\n updateMany: async (issueIds, patch) => core.issues.updateMany(issueIds, patch),\n clearMilestoneReferences: async (milestoneId, updatedAt) => core.issues.clearMilestoneReferences(milestoneId, updatedAt),\n clearParentReferences: async (parentIssueId, updatedAt) => core.issues.clearParentReferences(parentIssueId, updatedAt),\n clearDelegateReferences: async (delegate, updatedAt) => core.issues.clearDelegateReferences(delegate, updatedAt),\n delete: async issueId => core.issues.delete(issueId),\n },\n\n comments: {\n listByIssue: async issueId => core.comments.listByIssue(issueId),\n findById: async commentId => core.comments.findById(commentId),\n findByDedupeKey: async (issueId, dedupeKey) => core.comments.findByDedupeKey(issueId, dedupeKey),\n listByDedupeKeyPrefix: async (issueId, prefix) => core.comments.listByDedupeKeyPrefix(issueId, prefix),\n create: async values => core.comments.create(values),\n createOnce: async values => core.comments.createOnce(values),\n delete: async commentId => core.comments.delete(commentId),\n },\n\n runs: {\n listByIssue: async issueId => core.runs.listByIssue(issueId),\n listRunning: async issueId => core.runs.listRunning(issueId),\n listRunningWithSession: async () => core.runs.listRunningWithSession(),\n findById: async runId => core.runs.findById(runId),\n findByChatSessionId: async chatSessionId => core.runs.findByChatSessionId(chatSessionId),\n countByIssue: async issueId => core.runs.countByIssue(issueId),\n create: async values => core.runs.create(values),\n update: async (runId, patch) => core.runs.update(runId, patch),\n project: async scope => core.runs.project(scope),\n },\n\n relations: {\n listByIssue: async issueId => core.relations.listByIssue(issueId),\n findById: async relationId => core.relations.findById(relationId),\n findMatching: async edge => core.relations.findMatching(edge),\n create: async values => core.relations.create(values),\n delete: async relationId => core.relations.delete(relationId),\n deleteByIssue: async issueId => core.relations.deleteByIssue(issueId),\n },\n\n contextRefs: {\n replace: async (issueId, contextRefs, updatedAt) => core.contextRefs.replace(issueId, contextRefs, updatedAt),\n },\n\n fieldChanges: {\n listByIssue: async issueId => core.fieldChanges.listByIssue(issueId),\n listByIssueField: async (issueId, field) => core.fieldChanges.listByIssueField(issueId, field),\n create: async values => core.fieldChanges.create(values),\n },\n\n pullRequests: {\n listByIssue: async issueId => core.pullRequests.listByIssue(issueId),\n listLinked: async () => core.pullRequests.listLinked(),\n link: async values => core.pullRequests.link(values),\n unlink: async (issueId, linkId) => core.pullRequests.unlink(issueId, linkId),\n },\n\n meta: {\n read: async key => core.meta.read(key),\n bumpRevision: async (key, updatedAt) => core.meta.bumpRevision(key, updatedAt),\n },\n\n /**\n * Run `fn` inside one SQLite transaction, through the driver-level helper in\n * `./transaction.ts`.\n *\n * The caller rule lives there in full; in short: `fn` must be the only\n * database-touching async chain in flight, and inside it only await\n * operations that resolve without I/O — store methods, synchronous database\n * writes.\n */\n transaction: async <T>(fn: (tx: BoardStore) => Promise<T>, transactionOptions?: { mode?: BoardTransactionMode }): Promise<T> => {\n // One connection backs the whole store, so it is already the transactional view.\n return await runSqliteTransaction(options.database(), () => fn(store), transactionOptions)\n },\n }\n\n return store\n}\n","import { createRequire } from 'node:module'\n\nimport type { Database as SqliteConnection, Options as SqliteConnectionOptions } from 'better-sqlite3'\nimport { sql } from 'drizzle-orm'\nimport { getTableConfig } from 'drizzle-orm/sqlite-core'\n\nimport { BoardError } from '../domain/errors'\nimport { BOARD_SCHEMA_EPOCH, BOARD_TABLES_ADDED_WITHIN_EPOCH } from '../schema-epoch'\nimport { boardSqliteSchema } from './schema'\nimport type { SqliteDatabase } from './transaction'\n\nexport { BOARD_SCHEMA_EPOCH }\n\nconst require = createRequire(import.meta.url)\n\n/** The tables a board file must already carry for this package to read it. */\nconst REQUIRED_BOARD_TABLES = ['issue_runs', 'kanban_meta'] as const\n\n/** One open board file: the drizzle handle, the driver beneath it, and the way to let go. */\nexport interface BoardDatabase {\n database: SqliteDatabase\n connection: SqliteConnection\n close: () => void\n}\n\n/** What a caller is told when the native module a board file needs is not installed. */\nconst BETTER_SQLITE3_MISSING_MESSAGE = 'Install better-sqlite3 to use a board file'\n\n/**\n * `better-sqlite3` is an optional peer dependency (ruling 5): a Postgres-only\n * or capabilities-only install carries no native module, and importing\n * `kanbo/sqlite` for a type or for `runSqliteTransaction` should not\n * demand one either. So the require happens here, the one place a board file\n * is actually opened, rather than at the top of this module.\n */\nfunction loadBetterSqlite3(): new (path: string, options?: SqliteConnectionOptions) => SqliteConnection {\n try {\n return require('better-sqlite3')\n }\n catch (error) {\n throw new Error(BETTER_SQLITE3_MISSING_MESSAGE, { cause: error })\n }\n}\n\n/**\n * Drizzle's own better-sqlite3 driver, and why it is not imported at the top of\n * this file like every other import in the package.\n *\n * `drizzle-orm/better-sqlite3` imports the native module eagerly — it has an\n * overload where `drizzle()` constructs the connection itself — so a static\n * import of it makes `better-sqlite3` required by everything that can reach\n * this module, `kanbo capabilities` and `kanbo/mcp` included, and the\n * friendly message above becomes unreachable. A dynamic import keeps the whole\n * chain behind the one call that actually opens a file, which is what makes the\n * optional peer honest. `createRequire` would be the smaller change and is the\n * wrong one: it would load drizzle's CJS copy beside the ESM one this package\n * already holds, and it would resolve from `node_modules`, which the packaged\n * `dist/cli.cjs` deliberately does not have beside it.\n *\n * The module is remembered after the first load, so a command that opens two\n * board files pays for it once.\n */\nlet sqliteDriver: typeof import('drizzle-orm/better-sqlite3') | null = null\n\nasync function loadSqliteDriver(): Promise<typeof import('drizzle-orm/better-sqlite3')> {\n sqliteDriver ??= await import('drizzle-orm/better-sqlite3')\n return sqliteDriver\n}\n\n/**\n * Open a board file with the runtime pragmas a long-running server sets on its\n * own database, so a board read from a terminal behaves the same as one read\n * from inside an app. Migrations are not applied here: the file's owner applies\n * them (`kanbo init --file`, `kanbo migrate`, or the app the database belongs to).\n *\n * Asynchronous because the driver beneath it is loaded on demand — see\n * `loadSqliteDriver`. The native module is asked for first, so an install\n * without it hears the sentence above rather than a module-resolution stack\n * from inside drizzle.\n */\nexport async function openBoardDatabase(path: string): Promise<BoardDatabase> {\n const Database = loadBetterSqlite3()\n const { drizzle } = await loadSqliteDriver()\n const connection = new Database(path)\n connection.pragma('foreign_keys = ON')\n connection.pragma('journal_mode = WAL')\n connection.pragma('busy_timeout = 5000')\n // NORMAL is the recommended durability tradeoff with WAL (vs FULL fsync on every commit).\n connection.pragma('synchronous = NORMAL')\n return {\n database: drizzle(connection, { schema: boardSqliteSchema }),\n connection,\n close: () => connection.close(),\n }\n}\n\n/**\n * Every column this build expects to find, read off the schema itself — the\n * same idea as the Postgres guard's, so a column a later migration added\n * (`issue_milestones.start_date`) is looked for without being listed again.\n */\nfunction expectedColumns(): string[] {\n return Object.values(boardSqliteSchema).flatMap((table) => {\n const config = getTableConfig(table)\n return config.columns.map(column => `${config.name}.${column.name}`)\n })\n}\n\n/**\n * Refuse a board file this build cannot speak for, before any statement runs\n * against it: one with no board in it (`missingTables`), one whose board a\n * later migration has since added a table to (`missingAddedTables`) or a\n * column to (`missingColumns`), and one stamped with another epoch. An app\n * that migrates its own database on start need not call this; it is the guard a\n * standalone reader puts in front of a file it did not create.\n */\nexport function assertBoardSchema(database: SqliteDatabase): void {\n const present = new Set(database\n .all<{ name: string }>(sql`select name from sqlite_master where type = 'table'`)\n .map(row => row.name))\n const missing = REQUIRED_BOARD_TABLES.filter(table => !present.has(table))\n if (missing.length > 0) {\n throw new BoardError('board_schema_outdated', { missingTables: missing })\n }\n const missingAddedTables = BOARD_TABLES_ADDED_WITHIN_EPOCH.filter(table => !present.has(table))\n if (missingAddedTables.length > 0) {\n throw new BoardError('board_schema_outdated', { missingAddedTables })\n }\n const columns = new Set(database\n .all<{ tableName: string, columnName: string }>(sql`\n select m.name as tableName, p.name as columnName\n from sqlite_master m join pragma_table_info(m.name) p\n where m.type = 'table'`)\n .map(row => `${row.tableName}.${row.columnName}`))\n const missingColumns = expectedColumns().filter(column => !columns.has(column))\n if (missingColumns.length > 0) {\n throw new BoardError('board_schema_outdated', { missingColumns })\n }\n\n const row = database.get<{ revision: number } | undefined>(\n sql`select revision from kanban_meta where key = 'schema_epoch'`,\n )\n if (row?.revision !== BOARD_SCHEMA_EPOCH) {\n throw new BoardError('board_schema_outdated', { expected: BOARD_SCHEMA_EPOCH, found: row?.revision ?? null })\n }\n}\n\n/**\n * Who created this board file's tables from nothing: this package\n * (`kanbo init --file`), or a host app whose own database the board lives in.\n */\nexport type BoardFileOwner = 'kanbo' | 'host'\n\n/**\n * Which of the two ever wrote this file's board, going by the `kanban_meta`\n * marker `kanbo init --file` leaves behind (ruling 5-4).\n *\n * Absent reads as a host database — every file this package has never\n * touched, and a file this build cannot make sense of at all: `assertBoardSchema` is what says a board is missing or too old,\n * not this, so a table or a row that is not there yet is read as \"not this\n * package's file\" rather than an error of its own.\n */\nexport function readBoardFileOwner(database: SqliteDatabase): BoardFileOwner {\n try {\n const row = database.get<{ key: string } | undefined>(\n sql`select key from kanban_meta where key = 'file_owner'`,\n )\n return row ? 'kanbo' : 'host'\n }\n catch {\n return 'host'\n }\n}\n\n/**\n * Leave the marker that tells every later reader this file is this package's\n * own, not a host app's. `kanbo init --file` calls this once, right after\n * migrating; `insert or ignore` is what makes calling it again on the same\n * file a no-op rather than a conflict.\n */\nexport function markBoardFileOwnedByKanbo(database: SqliteDatabase): void {\n database.run(sql`insert or ignore into kanban_meta (key, revision) values ('file_owner', 1)`)\n}\n\n/** What a file already holds, before a board is opened on it or written into it. */\nexport interface BoardFileContents {\n /** No tables at all — a path nothing has been created in yet, or a zero-byte file. */\n empty: boolean\n /** Whose board the tables are, by the `file_owner` marker (ruling 5-4). Says nothing about an empty file. */\n owner: BoardFileOwner\n}\n\n/**\n * Look at a file on a connection of its own and hand it back closed, so a\n * caller can decide what to do with it before opening it for real.\n *\n * Two callers, two different decisions off the same two facts: the command line\n * asks whose file it is to know whether a host app's `workspaces` row may be\n * looked up in it, and a host app asks before it writes a board into it, because\n * a file with tables that are not this package's is somebody else's database\n * and migrating it would contaminate it (ruling 5-8).\n *\n * A path that is not a SQLite database at all — a README, a half-downloaded\n * file, a directory — throws, and that is deliberately not the same answer as\n * \"a database with somebody else's tables in it\": one is a mistake about which\n * path to use and the other is a mistake about which file is a board. No pragma\n * is set and nothing is written, so a file that turns out not to be ours is\n * left exactly as it was found, journal mode included.\n */\nexport function readBoardFileContents(path: string): BoardFileContents {\n const Database = loadBetterSqlite3()\n const connection = new Database(path, { fileMustExist: true })\n try {\n const tables = connection.prepare('select count(*) as count from sqlite_master where type = \\'table\\'').get() as { count: number }\n if (tables.count === 0) {\n return { empty: true, owner: 'host' }\n }\n return { empty: false, owner: readMarkedOwner(connection) }\n }\n finally {\n connection.close()\n }\n}\n\n/** The marker, read straight off the driver: the same question `readBoardFileOwner` asks a drizzle handle. */\nfunction readMarkedOwner(connection: SqliteConnection): BoardFileOwner {\n try {\n return connection.prepare('select key from kanban_meta where key = \\'file_owner\\'').get() ? 'kanbo' : 'host'\n }\n catch {\n return 'host'\n }\n}\n","import { dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { migrate } from 'drizzle-orm/better-sqlite3/migrator'\n\nimport { hasMigrationJournal, resolveBoardMigrationsPath } from '../migrations-path'\nimport type { SqliteDatabase } from './transaction'\n\n/** The folder's name, wherever it has been put. */\nconst MIGRATIONS_DIRECTORY = 'drizzle-sqlite'\n\n/**\n * The board's SQLite migrations for the file that is running right now — the\n * ones that build a board *file of this package's own*. The two layouts this\n * has to survive are `src/migrations-path.ts`'s subject.\n */\nexport function getBoardSqliteMigrationsPath(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n return resolveBoardMigrationsPath(here, MIGRATIONS_DIRECTORY, hasMigrationJournal)\n}\n\n/**\n * Create or update a board file this package owns.\n *\n * A host app's own database is not this: there the board is nine tables among\n * the app's own, and the app's migration runner migrates the whole file — which\n * may take two passes, because a migration that rebuilds a table has to drop\n * it with foreign keys off. Nothing here rebuilds anything — `drizzle-sqlite/` starts at the\n * board as it is now and will only ever be appended to — so drizzle's own\n * migrator is the whole of what this needs, and a file that has been migrated\n * before is a no-op by `__drizzle_migrations`.\n *\n * What the file holds afterwards is the board and nothing else: no\n * `workspaces`, no `agents`, no `provider_targets`. Every reference out of the\n * board is a plain text column for exactly that reason (ruling 5-1).\n */\nexport function migrateBoardFile(database: SqliteDatabase): void {\n migrate(database, { migrationsFolder: getBoardSqliteMigrationsPath() })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,SAAS,qBAAqB,UAA4C;CACxE,OAAQ,SAA4D;AACtE;;;;;;;;;AAmBA,MAAM,iBAAiB;AAWvB,SAAS,qBAAqB,YAA8B,MAA+C;CACzG,IAAI,CAAC,WAAW,eACd,OAAO;EACL,OAAO,SAAS,cAAc,oBAAoB;EAClD,QAAQ;EACR,MAAM;EACN,kBAAkB;CACpB;CAEF,OAAO;EACL,OAAO,aAAa;EACpB,QAAQ,WAAW;EACnB,MAAM,eAAe;EACrB,kBAAkB,WAAW;CAC/B;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,eAAsB,qBACpB,UACA,IACA,SACY;CACZ,MAAM,aAAa,qBAAqB,QAAQ;CAChD,MAAM,QAAQ,qBAAqB,YAAY,SAAS,QAAQ,UAAU;CAE1E,SAAS,IAAI,IAAI,IAAI,MAAM,KAAK,CAAC;CACjC,IAAI;EACF,MAAM,SAAS,MAAM,GAAG;EACxB,SAAS,IAAI,IAAI,IAAI,MAAM,MAAM,CAAC;EAClC,OAAO;CACT,SACO,OAAO;EACZ,IAAI,WAAW,eAAe;GAC5B,SAAS,IAAI,IAAI,IAAI,MAAM,IAAI,CAAC;GAChC,IAAI,MAAM,kBACR,SAAS,IAAI,IAAI,IAAI,MAAM,gBAAgB,CAAC;EAEhD;EACA,MAAM;CACR;AACF;;;;;;;;;;;;ACpDA,MAAM,sBAAsB,GAAG,GAAG,UAAU;;;;;;;;;AAU5C,MAAM,0BAA0B,GAAG,GAAG,cAAc;AACpD,MAAM,+BAA+B,GAAG,GAAG,kBAAkB;;AAG7D,MAAM,+BAA+B,GAAG,GAAG,kBAAkB;;;;;;;;;;;;AAa7D,SAAS,oBAAoB,UAA0B,OAAsD;CAC3G,IAAI,cAAc,SAAS,MAAM,SAAS,WAAW,GACnD,OAAO,CAAC;CAGV,MAAM,QAAQ,iBAAiB,QAC3B,GAAG,UAAU,OAAO,GAAG,QAAQ,OAAO,SAAS,OAAO,YAAY,KAAK,MAAM,gBAC7E,GAAG,UAAU,IAAI,KAAK,MAAM,SAAS,KAAI,YAAW,GAAG,IAAI,QAAQ,EAAE,GAAG,GAAG,IAAI;CAuCnF,OArCa,SAAS,IAWnB,GAAG;;;;;;;;;;;;;;;;;;;;aAoBK,UAAU;+BACQ,MAAM;;;GAKzB,CAAC,CAAC,KAAI,SAAQ;EACtB,SAAS,IAAI;EACb,cAAc,IAAI;EAClB,WAAW,IAAI,UAAU,YACrB;GACE,IAAI,IAAI;GACR,SAAS,IAAI;GACb,WAAW,IAAI;GACf,OAAO,IAAI;GACX,QAAQ,IAAI;GACZ,eAAe,IAAI;GACnB,eAAe,IAAI;GACnB,WAAW,IAAI;EACjB,IACA;CACN,EAAE;AACJ;;;;;;AAOA,SAAS,2BAA2B,iBAA6D;CAC/F,OAAO;EACL,UAAU;GACR,kBAAiB,gBAAe,gBAAgB,CAAC,CAC9C,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,GAAG,cAAc,aAAa,WAAW,CAAC,CAAC,CACjD,QAAQ,cAAc,KAAK,CAAC,CAC5B,IAAI;GACP,2BAA2B,gBAAgB,CAAC,CACzC,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,QAAQ,cAAc,aAAa,cAAc,KAAK,CAAC,CACvD,IAAI;GACP,eAAe,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,IAAI;GAClE,WAAU,aAAY,gBAAgB,CAAC,CACpC,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,GAAG,cAAc,IAAI,QAAQ,CAAC,CAAC,CACrC,IAAI,KAAK;GACZ,kBAAkB,aAAa,aAAa,gBAAgB,CAAC,CAC1D,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,GAAG,GAAG,cAAc,YAAY,KAAK,YAAY,OAAO,cAAc,GAAG,KAAK,UAAU,CAAC,CAC/F,IAAI,KAAK;GACZ,mBAAmB,gBAAgB;IAMjC,OALY,gBAAgB,CAAC,CAC1B,OAAO,EAAE,OAAO,GAAW,WAAW,CAAC,CAAC,CACxC,KAAK,aAAa,CAAC,CACnB,MAAM,GAAG,cAAc,aAAa,WAAW,CAAC,CAAC,CACjD,IACM,CAAC,EAAE,SAAS;GACvB;GACA,SAAQ,WAAU,gBAAgB,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;GACzF,SAAS,UAAU,UAAU;IAC3B,gBAAgB,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,cAAc,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI;GAC/F;GACA,SAAS,aAAa;IACpB,gBAAgB,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,GAAG,cAAc,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI;GACpF;EACF;EAEA,YAAY;GACV,kBAAkB,gBAAgB;IAChC,MAAM,aAAa,CACjB,cAAc,GAAG,gBAAgB,aAAa,WAAW,IAAI,KAAA,CAC/D,CAAC,CAAC,QAAO,cAAa,cAAc,KAAA,CAAS;IAC7C,OAAO,gBAAgB,CAAC,CACrB,OAAO,CAAC,CACR,KAAK,eAAe,CAAC,CACrB,MAAM,WAAW,SAAS,IAAI,IAAI,GAAG,UAAU,IAAI,KAAA,CAAS,CAAC,CAC7D,QAAQ,KAAK,gBAAgB,SAAS,CAAC,CAAC,CACxC,IAAI;GACT;GACA,kBAAiB,gBAAe,gBAAgB,CAAC,CAC9C,OAAO,CAAC,CACR,KAAK,eAAe,CAAC,CACrB,MAAM,GAAG,gBAAgB,aAAa,WAAW,CAAC,CAAC,CACnD,IAAI;GACP,eAAe,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,IAAI;GACpE,WAAU,gBAAe,gBAAgB,CAAC,CACvC,OAAO,CAAC,CACR,KAAK,eAAe,CAAC,CACrB,MAAM,GAAG,gBAAgB,IAAI,WAAW,CAAC,CAAC,CAC1C,IAAI,KAAK;GACZ,kBAAkB,aAAa,gBAAgB,gBAAgB,CAAC,CAC7D,OAAO,CAAC,CACR,KAAK,eAAe,CAAC,CACrB,MAAM,IAAI,GAAG,gBAAgB,IAAI,WAAW,GAAG,GAAG,gBAAgB,aAAa,WAAW,CAAC,CAAC,CAAC,CAC7F,IAAI,KAAK;GACZ,SAAQ,WAAU,gBAAgB,CAAC,CAAC,OAAO,eAAe,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;GAC3F,SAAS,aAAa,UAAU;IAC9B,gBAAgB,CAAC,CAAC,OAAO,eAAe,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,gBAAgB,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI;GACtG;GACA,SAAS,gBAAgB;IACvB,gBAAgB,CAAC,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,GAAG,gBAAgB,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI;GAC3F;EACF;EAEA,QAAQ;GACN,mBAAmB,gBAAgB;IACjC,MAAM,aAAa,CACjB,cAAc,GAAG,OAAO,aAAa,WAAW,IAAI,KAAA,CACtD,CAAC,CAAC,QAAO,cAAa,cAAc,KAAA,CAAS;IAC7C,OAAO,gBAAgB,CAAC,CACrB,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,WAAW,SAAS,IAAI,IAAI,GAAG,UAAU,IAAI,KAAA,CAAS,CAAC,CAG7D,QAAQ,OAAO,OAAO,KAAK,OAAO,SAAS,CAAC,CAAC,CAC7C,IAAI;GACT;GACA,uBAAuB,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,QAAQ,KAAK,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI;GACnG,eAAe,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI;GAC3D,kBAAiB,gBAAe,gBAAgB,CAAC,CAC9C,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,CAC1C,IAAI;GACP,YAAW,aAAY,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,MAAM,QAAQ,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI;GACvG,WAAU,YAAW,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK;GACpG,kBAAkB,aAAa,YAAY,gBAAgB,CAAC,CACzD,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,GAAG,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,CAAC,CACvE,IAAI,KAAK;GACZ,eAAe,aAAa,WAAW,gBAAgB,CAAC,CACrD,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,IAAI,GAAG,OAAO,aAAa,WAAW,GAAG,GAAG,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,CAC1E,IAAI,KAAK;GACZ,YAAY,gBAAgB;IAM1B,OALY,gBAAgB,CAAC,CAC1B,OAAO,EAAE,QAAQ,GAAW,gBAAgB,OAAO,OAAO,OAAO,CAAC,CAAC,CACnE,KAAK,MAAM,CAAC,CACZ,MAAM,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,CAC1C,IACM,CAAC,EAAE,UAAU;GACxB;GACA,WAAW,gBAAgB;IAMzB,OALY,gBAAgB,CAAC,CAC1B,OAAO,EAAE,UAAU,GAAW,gBAAgB,OAAO,MAAM,OAAO,CAAC,CAAC,CACpE,KAAK,MAAM,CAAC,CACZ,MAAM,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,CAC1C,IACM,CAAC,EAAE,YAAY;GAC1B;GACA,SAAQ,WAAU,gBAAgB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;GAClF,SAAS,SAAS,UAAU;IAC1B,gBAAgB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI;GAChF;GACA,aAAa,UAAU,UAAU,gBAAgB,CAAC,CAC/C,OAAO,MAAM,CAAC,CACd,IAAI,KAAK,CAAC,CACV,MAAM,GAAG,GAAG,OAAO,GAAG,OAAO,IAAI,KAAK,SAAS,KAAI,YAAW,GAAG,GAAG,SAAS,GAAG,GAAG,IAAI,EAAE,EAAE,CAAC,CAC5F,IAAI,CAAC,CACL;GACH,2BAA2B,aAAa,cAAc;IACpD,gBAAgB,CAAC,CACd,OAAO,MAAM,CAAC,CACd,IAAI;KAAE,aAAa;KAAM;IAAU,CAAC,CAAC,CACrC,MAAM,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,CAC1C,IAAI;GACT;GACA,wBAAwB,eAAe,cAAc;IACnD,gBAAgB,CAAC,CACd,OAAO,MAAM,CAAC,CACd,IAAI;KAAE,eAAe;KAAM;IAAU,CAAC,CAAC,CACvC,MAAM,GAAG,OAAO,eAAe,aAAa,CAAC,CAAC,CAC9C,IAAI;GACT;GACA,0BAA0B,UAAU,cAAc,gBAAgB,CAAC,CAChE,OAAO,MAAM,CAAC,CACd,IAAI;IAAE,iBAAiB;IAAM,0BAA0B;IAAM;GAAU,CAAC,CAAC,CACzE,MAAM,aAAa,WAChB,GAAG,OAAO,iBAAiB,SAAS,OAAO,IAC3C,GAAG,OAAO,0BAA0B,SAAS,gBAAgB,CAAC,CAAC,CAClE,IAAI,CAAC,CACL;GACH,SAAS,YAAY;IACnB,gBAAgB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI;GACrE;EACF;EAEA,UAAU;GACR,cAAa,YAAW,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,GAAG,cAAc,SAAS,OAAO,CAAC,CAAC,CACzC,QAAQ,IAAI,cAAc,SAAS,GAAG,uBAAuB,CAAC,CAC9D,IAAI;GACP,WAAU,cAAa,gBAAgB,CAAC,CACrC,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,GAAG,cAAc,IAAI,SAAS,CAAC,CAAC,CACtC,IAAI,KAAK;GACZ,kBAAkB,SAAS,cAAc,gBAAgB,CAAC,CACvD,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,IAAI,GAAG,cAAc,SAAS,OAAO,GAAG,GAAG,cAAc,WAAW,SAAS,CAAC,CAAC,CAAC,CACtF,IAAI,KAAK;GAGZ,wBAAwB,SAAS,WAAW,gBAAgB,CAAC,CAC1D,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,IACL,GAAG,cAAc,SAAS,OAAO,GACjC,GAAG,UAAU,cAAc,UAAU,OAAO,OAAO,OAAO,MAAM,QAClE,CAAC,CAAC,CACD,QAAQ,IAAI,cAAc,SAAS,GAAG,uBAAuB,CAAC,CAC9D,IAAI;GACP,SAAQ,WAAU,gBAAgB,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;GACzF,aAAa,WAAW;IACtB,MAAM,WAAW,gBAAgB;IAGjC,MAAM,WAAW,SAAS,OAAO,aAAa,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;IACrG,IAAI,UACF,OAAO;IAET,MAAM,WAAW,SACd,OAAO,CAAC,CACR,KAAK,aAAa,CAAC,CACnB,MAAM,IAAI,GAAG,cAAc,SAAS,OAAO,OAAO,GAAG,GAAG,cAAc,WAAW,OAAO,SAAS,CAAC,CAAC,CAAC,CACpG,IAAI;IACP,IAAI,CAAC,UAIH,MAAM,IAAI,MACR,qBAAqB,OAAO,QAAQ,kDAAkD,OAAO,WAC/F;IAEF,OAAO;GACT;GACA,SAAS,cAAc;IACrB,gBAAgB,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,GAAG,cAAc,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI;GACrF;EACF;EAEA,MAAM;GACJ,cAAa,YAAW,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,MAAM,GAAG,UAAU,SAAS,OAAO,CAAC,CAAC,CACrC,QAAQ,IAAI,UAAU,SAAS,GAAG,mBAAmB,CAAC,CACtD,IAAI;GACP,cAAa,YAAW,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,MAAM,IAAI,GAAG,UAAU,SAAS,OAAO,GAAG,GAAG,UAAU,OAAO,SAAS,CAAC,CAAC,CAAC,CAC1E,QAAQ,IAAI,UAAU,SAAS,GAAG,mBAAmB,CAAC,CACtD,IAAI;GACP,8BAA8B,gBAAgB,CAAC,CAC5C,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,MAAM,IAAI,GAAG,UAAU,OAAO,SAAS,GAAG,UAAU,UAAU,aAAa,CAAC,CAAC,CAAC,CAC9E,QAAQ,IAAI,UAAU,SAAS,GAAG,mBAAmB,CAAC,CACtD,IAAI;GACP,WAAU,UAAS,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM,GAAG,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK;GACtG,sBAAqB,kBAAiB,gBAAgB,CAAC,CACpD,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,MAAM,GAAG,UAAU,eAAe,aAAa,CAAC,CAAC,CACjD,QAAQ,KAAK,UAAU,SAAS,GAAG,GAAG,GAAG,UAAU,YAAY,CAAC,CAChE,IAAI,KAAK;GACZ,eAAe,YAAY;IAMzB,OALY,gBAAgB,CAAC,CAC1B,OAAO,EAAE,OAAO,GAAW,WAAW,CAAC,CAAC,CACxC,KAAK,SAAS,CAAC,CACf,MAAM,GAAG,UAAU,SAAS,OAAO,CAAC,CAAC,CACrC,IACM,CAAC,EAAE,SAAS;GACvB;GACA,SAAQ,WAAU,gBAAgB,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;GACrF,SAAS,OAAO,UAAU;IACxB,gBAAgB,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI;GACpF;GACA,UAAS,UAAS,oBAAoB,gBAAgB,GAAG,KAAK;EAChE;EAEA,WAAW;GACT,cAAa,YAAW,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,cAAc,CAAC,CACpB,MAAM,GAAG,GAAG,eAAe,eAAe,OAAO,GAAG,GAAG,eAAe,eAAe,OAAO,CAAC,CAAC,CAAC,CAC/F,IAAI;GACP,WAAU,eAAc,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,cAAc,CAAC,CACpB,MAAM,GAAG,eAAe,IAAI,UAAU,CAAC,CAAC,CACxC,IAAI,KAAK;GACZ,eAAc,SAAQ,gBAAgB,CAAC,CACpC,OAAO,CAAC,CACR,KAAK,cAAc,CAAC,CACpB,MAAM,IACL,GAAG,eAAe,MAAM,KAAK,IAAI,GACjC,KAAK,SAAS,eACV,GACE,IACE,GAAG,eAAe,eAAe,KAAK,aAAa,GACnD,GAAG,eAAe,eAAe,KAAK,aAAa,CACrD,GACA,IACE,GAAG,eAAe,eAAe,KAAK,aAAa,GACnD,GAAG,eAAe,eAAe,KAAK,aAAa,CACrD,CACF,IACA,IACE,GAAG,eAAe,eAAe,KAAK,aAAa,GACnD,GAAG,eAAe,eAAe,KAAK,aAAa,CACrD,CACN,CAAC,CAAC,CACD,IAAI,KAAK;GACZ,SAAQ,WAAU,gBAAgB,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;GAC1F,SAAS,eAAe;IACtB,gBAAgB,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,GAAG,eAAe,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI;GACxF;GACA,gBAAgB,YAAY;IAC1B,gBAAgB,CAAC,CACd,OAAO,cAAc,CAAC,CACtB,MAAM,GAAG,GAAG,eAAe,eAAe,OAAO,GAAG,GAAG,eAAe,eAAe,OAAO,CAAC,CAAC,CAAC,CAC/F,IAAI;GACT;EACF;EAEA,aAAa,EACX,UAAU,SAAS,aAAa,cAAc;GAC5C,gBAAgB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI;IAAE;IAAa;GAAU,CAAC,CAAC,CAAC,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI;EACrG,EACF;EAEA,cAAc;GACZ,cAAa,YAAW,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,iBAAiB,CAAC,CACvB,MAAM,GAAG,kBAAkB,SAAS,OAAO,CAAC,CAAC,CAC7C,QAAQ,IAAI,kBAAkB,SAAS,GAAG,4BAA4B,CAAC,CACvE,IAAI;GACP,mBAAmB,SAAS,UAAU,gBAAgB,CAAC,CACpD,OAAO,CAAC,CACR,KAAK,iBAAiB,CAAC,CACvB,MAAM,IAAI,GAAG,kBAAkB,SAAS,OAAO,GAAG,GAAG,kBAAkB,OAAO,KAAK,CAAC,CAAC,CAAC,CACtF,QAAQ,IAAI,kBAAkB,SAAS,GAAG,4BAA4B,CAAC,CACvE,IAAI;GACP,SAAS,WAAW;IAClB,gBAAgB,CAAC,CAAC,OAAO,iBAAiB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI;GACjE;EACF;EAEA,cAAc;GACZ,cAAa,YAAW,gBAAgB,CAAC,CACtC,OAAO,CAAC,CACR,KAAK,iBAAiB,CAAC,CACvB,MAAM,GAAG,kBAAkB,SAAS,OAAO,CAAC,CAAC,CAC7C,QAAQ,IAAI,kBAAkB,SAAS,GAAG,4BAA4B,CAAC,CACvE,IAAI;GACP,kBAAkB,gBAAgB,CAAC,CAChC,OAAO;IAAE,GAAG,gBAAgB,iBAAiB;IAAG,aAAa,OAAO;GAAY,CAAC,CAAC,CAClF,KAAK,iBAAiB,CAAC,CACvB,UAAU,QAAQ,GAAG,OAAO,IAAI,kBAAkB,OAAO,CAAC,CAAC,CAC3D,QAAQ,IAAI,kBAAkB,SAAS,GAAG,4BAA4B,CAAC,CACvE,IAAI;GACP,OAAO,WAAW;IAChB,MAAM,WAAW,gBAAgB;IAGjC,MAAM,WAAW,SAAS,OAAO,iBAAiB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI;IACzG,IAAI,UACF,OAAO;IAET,MAAM,WAAW,SACd,OAAO,CAAC,CACR,KAAK,iBAAiB,CAAC,CACvB,MAAM,IACL,GAAG,kBAAkB,SAAS,OAAO,OAAO,GAC5C,GAAG,kBAAkB,OAAO,OAAO,KAAK,GACxC,GAAG,kBAAkB,MAAM,OAAO,IAAI,GACtC,GAAG,kBAAkB,QAAQ,OAAO,MAAM,CAC5C,CAAC,CAAC,CACD,IAAI;IACP,IAAI,CAAC,UAEH,MAAM,IAAI,MACR,yBAAyB,OAAO,QAAQ,sCAAsC,OAAO,MAAM,GAAG,OAAO,KAAK,GAAG,OAAO,QACtH;IAEF,OAAO;GACT;GACA,SAAS,SAAS,WAAW,gBAAgB,CAAC,CAC3C,OAAO,iBAAiB,CAAC,CACzB,MAAM,IAAI,GAAG,kBAAkB,IAAI,MAAM,GAAG,GAAG,kBAAkB,SAAS,OAAO,CAAC,CAAC,CAAC,CACpF,UAAU,CAAC,CACX,IAAI,KAAK;EACd;EAEA,MAAM;GACJ,OAAM,QAAO,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,MAAM,GAAG,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK;GACjG,eAAe,KAAK,cAAc;IAChC,gBAAgB,CAAC,CACd,OAAO,UAAU,CAAC,CAClB,OAAO;KAAE;KAAK,UAAU;KAAG;IAAU,CAAC,CAAC,CACvC,mBAAmB;KAClB,QAAQ,WAAW;KACnB,KAAK;MAAE,UAAU,GAAG,GAAG,WAAW,SAAS;MAAO;KAAU;IAC9D,CAAC,CAAC,CACD,IAAI;GACT;EACF;CACF;AACF;;;;;;;;;AAUA,SAAgB,uBAAuB,SAAyD;CAC9F,MAAM,OAAO,2BAA2B,QAAQ,QAAQ;CAExD,MAAM,QAAoB;EACxB,UAAU;GACR,iBAAiB,OAAM,gBAAe,KAAK,SAAS,gBAAgB,WAAW;GAC/E,qBAAqB,YAAY,KAAK,SAAS,oBAAoB;GACnE,SAAS,YAAY,KAAK,SAAS,QAAQ;GAC3C,UAAU,OAAM,aAAY,KAAK,SAAS,SAAS,QAAQ;GAC3D,iBAAiB,OAAO,aAAa,aAAa,KAAK,SAAS,gBAAgB,aAAa,QAAQ;GACrG,kBAAkB,OAAM,gBAAe,KAAK,SAAS,iBAAiB,WAAW;GACjF,QAAQ,OAAM,WAAU,KAAK,SAAS,OAAO,MAAM;GACnD,QAAQ,OAAO,UAAU,UAAU,KAAK,SAAS,OAAO,UAAU,KAAK;GACvE,QAAQ,OAAM,aAAY,KAAK,SAAS,OAAO,QAAQ;EACzD;EAEA,YAAY;GACV,iBAAiB,OAAM,gBAAe,KAAK,WAAW,gBAAgB,WAAW;GACjF,iBAAiB,OAAM,gBAAe,KAAK,WAAW,gBAAgB,WAAW;GACjF,SAAS,YAAY,KAAK,WAAW,QAAQ;GAC7C,UAAU,OAAM,gBAAe,KAAK,WAAW,SAAS,WAAW;GACnE,iBAAiB,OAAO,aAAa,gBAAgB,KAAK,WAAW,gBAAgB,aAAa,WAAW;GAC7G,QAAQ,OAAM,WAAU,KAAK,WAAW,OAAO,MAAM;GACrD,QAAQ,OAAO,aAAa,UAAU,KAAK,WAAW,OAAO,aAAa,KAAK;GAC/E,QAAQ,OAAM,gBAAe,KAAK,WAAW,OAAO,WAAW;EACjE;EAEA,QAAQ;GACN,kBAAkB,OAAM,gBAAe,KAAK,OAAO,iBAAiB,WAAW;GAC/E,iBAAiB,YAAY,KAAK,OAAO,gBAAgB;GACzD,SAAS,YAAY,KAAK,OAAO,QAAQ;GACzC,iBAAiB,OAAM,gBAAe,KAAK,OAAO,gBAAgB,WAAW;GAC7E,WAAW,OAAM,aAAY,KAAK,OAAO,UAAU,QAAQ;GAC3D,UAAU,OAAM,YAAW,KAAK,OAAO,SAAS,OAAO;GACvD,iBAAiB,OAAO,aAAa,YAAY,KAAK,OAAO,gBAAgB,aAAa,OAAO;GACjG,cAAc,OAAO,aAAa,WAAW,KAAK,OAAO,aAAa,aAAa,MAAM;GACzF,WAAW,OAAM,gBAAe,KAAK,OAAO,UAAU,WAAW;GACjE,UAAU,OAAM,gBAAe,KAAK,OAAO,SAAS,WAAW;GAC/D,QAAQ,OAAM,WAAU,KAAK,OAAO,OAAO,MAAM;GACjD,QAAQ,OAAO,SAAS,UAAU,KAAK,OAAO,OAAO,SAAS,KAAK;GACnE,YAAY,OAAO,UAAU,UAAU,KAAK,OAAO,WAAW,UAAU,KAAK;GAC7E,0BAA0B,OAAO,aAAa,cAAc,KAAK,OAAO,yBAAyB,aAAa,SAAS;GACvH,uBAAuB,OAAO,eAAe,cAAc,KAAK,OAAO,sBAAsB,eAAe,SAAS;GACrH,yBAAyB,OAAO,UAAU,cAAc,KAAK,OAAO,wBAAwB,UAAU,SAAS;GAC/G,QAAQ,OAAM,YAAW,KAAK,OAAO,OAAO,OAAO;EACrD;EAEA,UAAU;GACR,aAAa,OAAM,YAAW,KAAK,SAAS,YAAY,OAAO;GAC/D,UAAU,OAAM,cAAa,KAAK,SAAS,SAAS,SAAS;GAC7D,iBAAiB,OAAO,SAAS,cAAc,KAAK,SAAS,gBAAgB,SAAS,SAAS;GAC/F,uBAAuB,OAAO,SAAS,WAAW,KAAK,SAAS,sBAAsB,SAAS,MAAM;GACrG,QAAQ,OAAM,WAAU,KAAK,SAAS,OAAO,MAAM;GACnD,YAAY,OAAM,WAAU,KAAK,SAAS,WAAW,MAAM;GAC3D,QAAQ,OAAM,cAAa,KAAK,SAAS,OAAO,SAAS;EAC3D;EAEA,MAAM;GACJ,aAAa,OAAM,YAAW,KAAK,KAAK,YAAY,OAAO;GAC3D,aAAa,OAAM,YAAW,KAAK,KAAK,YAAY,OAAO;GAC3D,wBAAwB,YAAY,KAAK,KAAK,uBAAuB;GACrE,UAAU,OAAM,UAAS,KAAK,KAAK,SAAS,KAAK;GACjD,qBAAqB,OAAM,kBAAiB,KAAK,KAAK,oBAAoB,aAAa;GACvF,cAAc,OAAM,YAAW,KAAK,KAAK,aAAa,OAAO;GAC7D,QAAQ,OAAM,WAAU,KAAK,KAAK,OAAO,MAAM;GAC/C,QAAQ,OAAO,OAAO,UAAU,KAAK,KAAK,OAAO,OAAO,KAAK;GAC7D,SAAS,OAAM,UAAS,KAAK,KAAK,QAAQ,KAAK;EACjD;EAEA,WAAW;GACT,aAAa,OAAM,YAAW,KAAK,UAAU,YAAY,OAAO;GAChE,UAAU,OAAM,eAAc,KAAK,UAAU,SAAS,UAAU;GAChE,cAAc,OAAM,SAAQ,KAAK,UAAU,aAAa,IAAI;GAC5D,QAAQ,OAAM,WAAU,KAAK,UAAU,OAAO,MAAM;GACpD,QAAQ,OAAM,eAAc,KAAK,UAAU,OAAO,UAAU;GAC5D,eAAe,OAAM,YAAW,KAAK,UAAU,cAAc,OAAO;EACtE;EAEA,aAAa,EACX,SAAS,OAAO,SAAS,aAAa,cAAc,KAAK,YAAY,QAAQ,SAAS,aAAa,SAAS,EAC9G;EAEA,cAAc;GACZ,aAAa,OAAM,YAAW,KAAK,aAAa,YAAY,OAAO;GACnE,kBAAkB,OAAO,SAAS,UAAU,KAAK,aAAa,iBAAiB,SAAS,KAAK;GAC7F,QAAQ,OAAM,WAAU,KAAK,aAAa,OAAO,MAAM;EACzD;EAEA,cAAc;GACZ,aAAa,OAAM,YAAW,KAAK,aAAa,YAAY,OAAO;GACnE,YAAY,YAAY,KAAK,aAAa,WAAW;GACrD,MAAM,OAAM,WAAU,KAAK,aAAa,KAAK,MAAM;GACnD,QAAQ,OAAO,SAAS,WAAW,KAAK,aAAa,OAAO,SAAS,MAAM;EAC7E;EAEA,MAAM;GACJ,MAAM,OAAM,QAAO,KAAK,KAAK,KAAK,GAAG;GACrC,cAAc,OAAO,KAAK,cAAc,KAAK,KAAK,aAAa,KAAK,SAAS;EAC/E;;;;;;;;;;EAWA,aAAa,OAAU,IAAoC,uBAAqE;GAE9H,OAAO,MAAM,qBAAqB,QAAQ,SAAS,SAAS,GAAG,KAAK,GAAG,kBAAkB;EAC3F;CACF;CAEA,OAAO;AACT;;;ACpqBA,MAAM,UAAU,cAAc,YAAY,GAAG;;AAG7C,MAAM,wBAAwB,CAAC,cAAc,aAAa;;AAU1D,MAAM,iCAAiC;;;;;;;;AASvC,SAAS,oBAA+F;CACtG,IAAI;EACF,OAAO,QAAQ,gBAAgB;CACjC,SACO,OAAO;EACZ,MAAM,IAAI,MAAM,gCAAgC,EAAE,OAAO,MAAM,CAAC;CAClE;AACF;;;;;;;;;;;;;;;;;;;AAoBA,IAAI,eAAmE;AAEvE,eAAe,mBAAyE;CACtF,iBAAiB,MAAM,OAAO;CAC9B,OAAO;AACT;;;;;;;;;;;;AAaA,eAAsB,kBAAkB,MAAsC;CAC5E,MAAM,WAAW,kBAAkB;CACnC,MAAM,EAAE,YAAY,MAAM,iBAAiB;CAC3C,MAAM,aAAa,IAAI,SAAS,IAAI;CACpC,WAAW,OAAO,mBAAmB;CACrC,WAAW,OAAO,oBAAoB;CACtC,WAAW,OAAO,qBAAqB;CAEvC,WAAW,OAAO,sBAAsB;CACxC,OAAO;EACL,UAAU,QAAQ,YAAY,EAAE,QAAQ,kBAAkB,CAAC;EAC3D;EACA,aAAa,WAAW,MAAM;CAChC;AACF;;;;;;AAOA,SAAS,kBAA4B;CACnC,OAAO,OAAO,OAAO,iBAAiB,CAAC,CAAC,SAAS,UAAU;EACzD,MAAM,SAAS,eAAe,KAAK;EACnC,OAAO,OAAO,QAAQ,KAAI,WAAU,GAAG,OAAO,KAAK,GAAG,OAAO,MAAM;CACrE,CAAC;AACH;;;;;;;;;AAUA,SAAgB,kBAAkB,UAAgC;CAChE,MAAM,UAAU,IAAI,IAAI,SACrB,IAAsB,GAAG,qDAAqD,CAAC,CAC/E,KAAI,QAAO,IAAI,IAAI,CAAC;CACvB,MAAM,UAAU,sBAAsB,QAAO,UAAS,CAAC,QAAQ,IAAI,KAAK,CAAC;CACzE,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,WAAW,yBAAyB,EAAE,eAAe,QAAQ,CAAC;CAE1E,MAAM,qBAAqB,gCAAgC,QAAO,UAAS,CAAC,QAAQ,IAAI,KAAK,CAAC;CAC9F,IAAI,mBAAmB,SAAS,GAC9B,MAAM,IAAI,WAAW,yBAAyB,EAAE,mBAAmB,CAAC;CAEtE,MAAM,UAAU,IAAI,IAAI,SACrB,IAA+C,GAAG;;;6BAG1B,CAAC,CACzB,KAAI,QAAO,GAAG,IAAI,UAAU,GAAG,IAAI,YAAY,CAAC;CACnD,MAAM,iBAAiB,gBAAgB,CAAC,CAAC,QAAO,WAAU,CAAC,QAAQ,IAAI,MAAM,CAAC;CAC9E,IAAI,eAAe,SAAS,GAC1B,MAAM,IAAI,WAAW,yBAAyB,EAAE,eAAe,CAAC;CAGlE,MAAM,MAAM,SAAS,IACnB,GAAG,6DACL;CACA,IAAI,KAAK,aAAA,GACP,MAAM,IAAI,WAAW,yBAAyB;EAAE,UAAA;EAA8B,OAAO,KAAK,YAAY;CAAK,CAAC;AAEhH;;;;;;;;;;AAiBA,SAAgB,mBAAmB,UAA0C;CAC3E,IAAI;EAIF,OAHY,SAAS,IACnB,GAAG,sDAEI,IAAI,UAAU;CACzB,QACM;EACJ,OAAO;CACT;AACF;;;;;;;AAQA,SAAgB,0BAA0B,UAAgC;CACxE,SAAS,IAAI,GAAG,4EAA4E;AAC9F;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,sBAAsB,MAAiC;CAErE,MAAM,aAAa,KADF,kBACa,GAAE,MAAM,EAAE,eAAe,KAAK,CAAC;CAC7D,IAAI;EAEF,IADe,WAAW,QAAQ,kEAAoE,CAAC,CAAC,IAC/F,CAAC,CAAC,UAAU,GACnB,OAAO;GAAE,OAAO;GAAM,OAAO;EAAO;EAEtC,OAAO;GAAE,OAAO;GAAO,OAAO,gBAAgB,UAAU;EAAE;CAC5D,UACQ;EACN,WAAW,MAAM;CACnB;AACF;;AAGA,SAAS,gBAAgB,YAA8C;CACrE,IAAI;EACF,OAAO,WAAW,QAAQ,sDAAwD,CAAC,CAAC,IAAI,IAAI,UAAU;CACxG,QACM;EACJ,OAAO;CACT;AACF;;;;AC/NA,MAAM,uBAAuB;;;;;;AAO7B,SAAgB,+BAAuC;CACrD,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;CACnD,OAAO,2BAA2B,MAAM,sBAAsB,mBAAmB;AACnF;;;;;;;;;;;;;;;;AAiBA,SAAgB,iBAAiB,UAAgC;CAC/D,QAAQ,UAAU,EAAE,kBAAkB,6BAA6B,EAAE,CAAC;AACxE"}