@intentface/latch-drizzle 0.9.1

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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/adapter.d.ts +18 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +33 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/adapter.pg.d.ts +10 -0
  8. package/dist/adapter.pg.d.ts.map +1 -0
  9. package/dist/adapter.pg.js +18 -0
  10. package/dist/adapter.pg.js.map +1 -0
  11. package/dist/adapter.shared.d.ts +55 -0
  12. package/dist/adapter.shared.d.ts.map +1 -0
  13. package/dist/adapter.shared.js +527 -0
  14. package/dist/adapter.shared.js.map +1 -0
  15. package/dist/agent-store.d.ts +34 -0
  16. package/dist/agent-store.d.ts.map +1 -0
  17. package/dist/agent-store.js +236 -0
  18. package/dist/agent-store.js.map +1 -0
  19. package/dist/agent-store.smoke.d.ts +2 -0
  20. package/dist/agent-store.smoke.d.ts.map +1 -0
  21. package/dist/agent-store.smoke.js +77 -0
  22. package/dist/agent-store.smoke.js.map +1 -0
  23. package/dist/index.d.ts +24 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +22 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/limit-store.d.ts +5 -0
  28. package/dist/limit-store.d.ts.map +1 -0
  29. package/dist/limit-store.js +7 -0
  30. package/dist/limit-store.js.map +1 -0
  31. package/dist/limit-store.pg.d.ts +5 -0
  32. package/dist/limit-store.pg.d.ts.map +1 -0
  33. package/dist/limit-store.pg.js +7 -0
  34. package/dist/limit-store.pg.js.map +1 -0
  35. package/dist/limit-store.shared.d.ts +14 -0
  36. package/dist/limit-store.shared.d.ts.map +1 -0
  37. package/dist/limit-store.shared.js +60 -0
  38. package/dist/limit-store.shared.js.map +1 -0
  39. package/dist/limit-store.smoke.shared.d.ts +7 -0
  40. package/dist/limit-store.smoke.shared.d.ts.map +1 -0
  41. package/dist/limit-store.smoke.shared.js +33 -0
  42. package/dist/limit-store.smoke.shared.js.map +1 -0
  43. package/dist/memory-job-store.d.ts +5 -0
  44. package/dist/memory-job-store.d.ts.map +1 -0
  45. package/dist/memory-job-store.js +7 -0
  46. package/dist/memory-job-store.js.map +1 -0
  47. package/dist/memory-job-store.pg.d.ts +5 -0
  48. package/dist/memory-job-store.pg.d.ts.map +1 -0
  49. package/dist/memory-job-store.pg.js +7 -0
  50. package/dist/memory-job-store.pg.js.map +1 -0
  51. package/dist/memory-job-store.shared.d.ts +23 -0
  52. package/dist/memory-job-store.shared.d.ts.map +1 -0
  53. package/dist/memory-job-store.shared.js +294 -0
  54. package/dist/memory-job-store.shared.js.map +1 -0
  55. package/dist/memory-job-store.smoke.shared.d.ts +18 -0
  56. package/dist/memory-job-store.smoke.shared.d.ts.map +1 -0
  57. package/dist/memory-job-store.smoke.shared.js +149 -0
  58. package/dist/memory-job-store.smoke.shared.js.map +1 -0
  59. package/dist/migrate.d.ts +10 -0
  60. package/dist/migrate.d.ts.map +1 -0
  61. package/dist/migrate.js +202 -0
  62. package/dist/migrate.js.map +1 -0
  63. package/dist/migrate.pg.d.ts +3 -0
  64. package/dist/migrate.pg.d.ts.map +1 -0
  65. package/dist/migrate.pg.js +120 -0
  66. package/dist/migrate.pg.js.map +1 -0
  67. package/dist/run-lease.smoke.shared.d.ts +13 -0
  68. package/dist/run-lease.smoke.shared.d.ts.map +1 -0
  69. package/dist/run-lease.smoke.shared.js +65 -0
  70. package/dist/run-lease.smoke.shared.js.map +1 -0
  71. package/dist/schedule.smoke.shared.d.ts +16 -0
  72. package/dist/schedule.smoke.shared.d.ts.map +1 -0
  73. package/dist/schedule.smoke.shared.js +104 -0
  74. package/dist/schedule.smoke.shared.js.map +1 -0
  75. package/dist/schema.d.ts +2348 -0
  76. package/dist/schema.d.ts.map +1 -0
  77. package/dist/schema.js +225 -0
  78. package/dist/schema.js.map +1 -0
  79. package/dist/schema.pg.d.ts +2215 -0
  80. package/dist/schema.pg.d.ts.map +1 -0
  81. package/dist/schema.pg.js +197 -0
  82. package/dist/schema.pg.js.map +1 -0
  83. package/dist/secret-store.d.ts +11 -0
  84. package/dist/secret-store.d.ts.map +1 -0
  85. package/dist/secret-store.js +11 -0
  86. package/dist/secret-store.js.map +1 -0
  87. package/dist/secret-store.pg.d.ts +6 -0
  88. package/dist/secret-store.pg.d.ts.map +1 -0
  89. package/dist/secret-store.pg.js +7 -0
  90. package/dist/secret-store.pg.js.map +1 -0
  91. package/dist/secret-store.shared.d.ts +25 -0
  92. package/dist/secret-store.shared.d.ts.map +1 -0
  93. package/dist/secret-store.shared.js +67 -0
  94. package/dist/secret-store.shared.js.map +1 -0
  95. package/dist/smoke.d.ts +2 -0
  96. package/dist/smoke.d.ts.map +1 -0
  97. package/dist/smoke.js +108 -0
  98. package/dist/smoke.js.map +1 -0
  99. package/dist/smoke.pg.d.ts +2 -0
  100. package/dist/smoke.pg.d.ts.map +1 -0
  101. package/dist/smoke.pg.js +107 -0
  102. package/dist/smoke.pg.js.map +1 -0
  103. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Intentface
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @intentface/latch-drizzle
2
+
3
+ The persistence tier for Latch — a `StorageAdapter` and `SecretStore` implemented on **Drizzle** (libsql/SQLite first-class, Postgres alongside).
4
+
5
+ ## What it does
6
+
7
+ - **`createSqliteAdapter` / (pg variant)** — the `StorageAdapter` the runtime persists through: chats, messages (the `AppMessage` source of truth), runs (status, leases, usage, cost incl. cached-token split), usage ledger, and schedules. Owner-keyed for org/user isolation (the `owner` fn derives the key from your `Principal`).
8
+ - **`createSqliteSecretStore`** — the encrypted record store behind the core `Vault` (it never sees plaintext; the Vault wraps/unwraps).
9
+ - **`migrate(client)`** — creates the `latch_*` tables (`CREATE TABLE IF NOT EXISTS`) plus idempotent `ALTER`s for columns added later (timezone, cached-token counts, …). Safe to run at startup; no migration tooling required.
10
+
11
+ Tables: `latch_chats`, `latch_messages`, `latch_runs`, `latch_usage`, `latch_secrets`, `latch_schedules`.
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { createClient } from "@libsql/client";
17
+ import { createSqliteAdapter, createSqliteSecretStore, migrate } from "@intentface/latch-drizzle";
18
+
19
+ const client = createClient({ url: DATABASE_URL, authToken: TURSO_AUTH_TOKEN });
20
+ await migrate(client);
21
+
22
+ const owner = (p: Principal) => (p.userId ? [p.orgId, p.userId] : [p.orgId]);
23
+ const storage = createSqliteAdapter<Principal>(client, { owner });
24
+ ```
25
+
26
+ **Owner-key contract.** `owner` must be a pure projection of the *stable* part
27
+ of your principal — durable ids from your database, never per-session state
28
+ (an "active org" that can be null on some requests will silently split one
29
+ user's data across two keys). The key is derived once, at root-resource
30
+ creation; child rows inherit it. Never re-derive it from a live principal in
31
+ a different request context (OAuth callbacks, webhooks, cron) — pin identity
32
+ into the flow/run and rebuild it via `reconstructPrincipal` instead.
33
+
34
+ ## Where it fits
35
+
36
+ The T1 (persistence) tier under `@intentface/latch-core`. Swap it for another `StorageAdapter` implementation behind the same contract.
@@ -0,0 +1,18 @@
1
+ import type { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core";
2
+ import type { StorageAdapter } from "@intentface/latch-core";
3
+ export { isUniqueViolation } from "./adapter.shared.js";
4
+ export interface SqliteAdapterOptions<P> {
5
+ /**
6
+ * Derive the ownership key for a principal — the isolation boundary. Coarser
7
+ * (fewer segments) = more sharing; finer = more isolation. Must be
8
+ * server-derived from the authenticated principal, never client input.
9
+ */
10
+ owner: (principal: P) => string[];
11
+ }
12
+ /**
13
+ * A `StorageAdapter` on a Drizzle SQLite database (libsql / better-sqlite3 /
14
+ * Turso). The caller owns the connection — pass a `drizzle(...)` instance — so
15
+ * the adapter never opens or dialect-binds one itself.
16
+ */
17
+ export declare function createSqliteAdapter<P>(db: BaseSQLiteDatabase<"sync" | "async", any, any>, opts: SqliteAdapterOptions<P>): StorageAdapter<P>;
18
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAI7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC;;;;OAIG;IACH,KAAK,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,EAAE,EAAE,kBAAkB,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,EAClD,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC5B,cAAc,CAAC,CAAC,CAAC,CAyBnB"}
@@ -0,0 +1,33 @@
1
+ import * as schema from "./schema.js";
2
+ import { makeStorageAdapter } from "./adapter.shared.js";
3
+ export { isUniqueViolation } from "./adapter.shared.js";
4
+ /**
5
+ * A `StorageAdapter` on a Drizzle SQLite database (libsql / better-sqlite3 /
6
+ * Turso). The caller owns the connection — pass a `drizzle(...)` instance — so
7
+ * the adapter never opens or dialect-binds one itself.
8
+ */
9
+ export function createSqliteAdapter(db, opts) {
10
+ return makeStorageAdapter(db, schema, {
11
+ owner: opts.owner,
12
+ // No interactive transaction: this adapter also serves synchronous drivers
13
+ // (better-sqlite3), whose transaction API rejects an async callback — and
14
+ // libsql opens a transaction on a NEW connection, which for an in-memory
15
+ // database is a new, empty database. Each INSERT computes its own seq
16
+ // atomically and the UNIQUE (chat_id, seq) index backstops a true race.
17
+ withTx: (fn) => fn(db),
18
+ // Atomicity where it matters (`openTurn`): drizzle's `batch` runs a list of
19
+ // independent statements in one transaction on libsql/Turso (local, memory
20
+ // and remote alike). better-sqlite3 has no batch → sequential fallback.
21
+ ...(typeof db.batch === "function"
22
+ ? {
23
+ batch: (stmts) => db.batch(stmts),
24
+ }
25
+ : {
26
+ // Synchronous driver (better-sqlite3, bun:sqlite): drizzle's sync
27
+ // `transaction` with the builders' `.run()` / `.get()` is a real
28
+ // transaction — `openTurn` uses it, so it is atomic here too.
29
+ syncTx: (fn) => db.transaction(fn),
30
+ }),
31
+ });
32
+ }
33
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAWxD;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,EAAkD,EAClD,IAA6B;IAE7B,OAAO,kBAAkB,CAAI,EAAE,EAAE,MAAM,EAAE;QACvC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,2EAA2E;QAC3E,0EAA0E;QAC1E,yEAAyE;QACzE,sEAAsE;QACtE,wEAAwE;QACxE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACtB,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,GAAG,CAAC,OAAQ,EAAqC,CAAC,KAAK,KAAK,UAAU;YACpE,CAAC,CAAC;gBACE,KAAK,EAAE,CAAC,KAAgB,EAAE,EAAE,CACzB,EAA6D,CAAC,KAAK,CAAC,KAAc,CAAC;aACvF;YACH,CAAC,CAAC;gBACE,kEAAkE;gBAClE,iEAAiE;gBACjE,8DAA8D;gBAC9D,MAAM,EAAE,CAAK,EAAsB,EAAE,EAAE,CACpC,EAA+D,CAAC,WAAW,CAAC,EAAE,CAAC;aACnF,CAAC;KACP,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { PgDatabase } from "drizzle-orm/pg-core";
2
+ import type { StorageAdapter } from "@intentface/latch-core";
3
+ import type { SqliteAdapterOptions } from "./adapter.js";
4
+ /**
5
+ * A `StorageAdapter` backed by Postgres. Pass a Drizzle pg database built from any
6
+ * driver — node-postgres / postgres-js in production, or PGlite for tests.
7
+ * Same contract + same query logic as the SQLite adapter (different dialect schema).
8
+ */
9
+ export declare function createPostgresAdapter<P>(db: PgDatabase<any, any, any>, opts: SqliteAdapterOptions<P>): StorageAdapter<P>;
10
+ //# sourceMappingURL=adapter.pg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.pg.d.ts","sourceRoot":"","sources":["../src/adapter.pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC7B,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC5B,cAAc,CAAC,CAAC,CAAC,CASnB"}
@@ -0,0 +1,18 @@
1
+ import * as schema from "./schema.pg.js";
2
+ import { makeStorageAdapter } from "./adapter.shared.js";
3
+ /**
4
+ * A `StorageAdapter` backed by Postgres. Pass a Drizzle pg database built from any
5
+ * driver — node-postgres / postgres-js in production, or PGlite for tests.
6
+ * Same contract + same query logic as the SQLite adapter (different dialect schema).
7
+ */
8
+ export function createPostgresAdapter(db, opts) {
9
+ return makeStorageAdapter(db, schema, {
10
+ owner: opts.owner,
11
+ // One transaction per message batch: the max(seq) reads and the inserts
12
+ // commit (or roll back) together, so a crash mid-batch can't leave a
13
+ // partial write and a concurrent appender can't interleave into the same
14
+ // seq range.
15
+ withTx: (fn) => db.transaction(fn),
16
+ });
17
+ }
18
+ //# sourceMappingURL=adapter.pg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.pg.js","sourceRoot":"","sources":["../src/adapter.pg.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,EAA6B,EAC7B,IAA6B;IAE7B,OAAO,kBAAkB,CAAI,EAAE,EAAE,MAAM,EAAE;QACvC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,wEAAwE;QACxE,qEAAqE;QACrE,yEAAyE;QACzE,aAAa;QACb,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;KACnC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { type StorageAdapter } from "@intentface/latch-core";
2
+ /**
3
+ * Dialect-shared `StorageAdapter` implementation. The SQLite and Postgres entry
4
+ * points (`adapter.ts`, `adapter.pg.ts`) pass their dialect's schema tables and
5
+ * a `withTx` — the only two things that differ. Every query below uses the
6
+ * common query-builder surface (insert / onConflictDoUpdate / update /
7
+ * returning), which both dialects support. Rows are typed structurally (`any`)
8
+ * for the same reason `memory-job-store.shared.ts` does: the two schemas are
9
+ * shape-identical but distinct types.
10
+ */
11
+ /** The tables the adapter touches, in either dialect's schema. */
12
+ export interface AdapterSchema {
13
+ chats: any;
14
+ messages: any;
15
+ runs: any;
16
+ usage: any;
17
+ schedules: any;
18
+ }
19
+ export interface SharedAdapterOptions<P> {
20
+ /**
21
+ * Derive the ownership key for a principal — the isolation boundary. Coarser
22
+ * (fewer segments) = more sharing; finer = more isolation. Must be
23
+ * server-derived from the authenticated principal, never client input.
24
+ */
25
+ owner: (principal: P) => string[];
26
+ /**
27
+ * Run a message batch as one unit. Postgres: `db.transaction`. SQLite:
28
+ * sequential on the same handle (synchronous drivers can't take the async
29
+ * callback).
30
+ */
31
+ withTx: <T>(fn: (tx: any) => Promise<T>) => Promise<T>;
32
+ /**
33
+ * Run independent statements in ONE transaction, returning each statement's
34
+ * result in order (drizzle `db.batch`). Preferred by `openTurn` where the
35
+ * dialect offers it and `withTx` isn't a real transaction (libsql).
36
+ */
37
+ batch?: (stmts: any[]) => Promise<any[]>;
38
+ /** A synchronous transaction (sync SQLite drivers): the builders' `.run()`/`.get()` inside it are atomic. */
39
+ syncTx?: <T>(fn: (tx: any) => T) => T;
40
+ }
41
+ /**
42
+ * True if a driver error is a primary-key / unique-constraint violation. Walks
43
+ * the cause chain: drizzle wraps driver errors in a "Failed query: …" error and
44
+ * the constraint code lives on the cause (libsql `SQLITE_CONSTRAINT_UNIQUE`, pg
45
+ * `23505`), two levels down for libsql.
46
+ */
47
+ export declare function isUniqueViolation(error: unknown): boolean;
48
+ /**
49
+ * How long a NON-durable (T1) active run may stay active before a new turn in
50
+ * its chat supersedes it. T1 has no heartbeat, so without this a crashed T1
51
+ * turn would block its chat forever. Durable runs use their lease instead.
52
+ */
53
+ export declare const STALE_RUN_MS: number;
54
+ export declare function makeStorageAdapter<P>(db: any, s: AdapterSchema, opts: SharedAdapterOptions<P>): StorageAdapter<P>;
55
+ //# sourceMappingURL=adapter.shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.shared.d.ts","sourceRoot":"","sources":["../src/adapter.shared.ts"],"names":[],"mappings":"AAcA,OAAO,EAQL,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;GAQG;AAEH,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,KAAK,EAAE,GAAG,CAAC;IACX,SAAS,EAAE,GAAG,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC;;;;OAIG;IACH,KAAK,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC;IAClC;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzC,6GAA6G;IAC7G,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;CACvC;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUzD;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAc,CAAC;AAOxC,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,EAAE,EAAE,GAAG,EACP,CAAC,EAAE,aAAa,EAChB,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC5B,cAAc,CAAC,CAAC,CAAC,CAujBnB"}