@todu/engine 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 (57) hide show
  1. package/dist/change-observer.d.ts +15 -0
  2. package/dist/change-observer.d.ts.map +1 -0
  3. package/dist/change-observer.js +64 -0
  4. package/dist/change-observer.js.map +1 -0
  5. package/dist/habits.d.ts +6 -0
  6. package/dist/habits.d.ts.map +1 -0
  7. package/dist/habits.js +397 -0
  8. package/dist/habits.js.map +1 -0
  9. package/dist/index.d.ts +25 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +104 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/labels.d.ts +5 -0
  14. package/dist/labels.d.ts.map +1 -0
  15. package/dist/labels.js +113 -0
  16. package/dist/labels.js.map +1 -0
  17. package/dist/notes.d.ts +5 -0
  18. package/dist/notes.d.ts.map +1 -0
  19. package/dist/notes.js +163 -0
  20. package/dist/notes.js.map +1 -0
  21. package/dist/projects.d.ts +5 -0
  22. package/dist/projects.d.ts.map +1 -0
  23. package/dist/projects.js +117 -0
  24. package/dist/projects.js.map +1 -0
  25. package/dist/recurring.d.ts +23 -0
  26. package/dist/recurring.d.ts.map +1 -0
  27. package/dist/recurring.js +372 -0
  28. package/dist/recurring.js.map +1 -0
  29. package/dist/schedule.d.ts +38 -0
  30. package/dist/schedule.d.ts.map +1 -0
  31. package/dist/schedule.js +330 -0
  32. package/dist/schedule.js.map +1 -0
  33. package/dist/scheduling.d.ts +62 -0
  34. package/dist/scheduling.d.ts.map +1 -0
  35. package/dist/scheduling.js +58 -0
  36. package/dist/scheduling.js.map +1 -0
  37. package/dist/storage.d.ts +34 -0
  38. package/dist/storage.d.ts.map +1 -0
  39. package/dist/storage.js +204 -0
  40. package/dist/storage.js.map +1 -0
  41. package/dist/sync-client.d.ts +16 -0
  42. package/dist/sync-client.d.ts.map +1 -0
  43. package/dist/sync-client.js +54 -0
  44. package/dist/sync-client.js.map +1 -0
  45. package/dist/sync-server.d.ts +12 -0
  46. package/dist/sync-server.d.ts.map +1 -0
  47. package/dist/sync-server.js +21 -0
  48. package/dist/sync-server.js.map +1 -0
  49. package/dist/tasks.d.ts +18 -0
  50. package/dist/tasks.d.ts.map +1 -0
  51. package/dist/tasks.js +396 -0
  52. package/dist/tasks.js.map +1 -0
  53. package/dist/todu.d.ts +115 -0
  54. package/dist/todu.d.ts.map +1 -0
  55. package/dist/todu.js +76 -0
  56. package/dist/todu.js.map +1 -0
  57. package/package.json +52 -0
@@ -0,0 +1,204 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { Repo } from "@automerge/automerge-repo";
4
+ import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs";
5
+ import { CATALOG_DOC_KEY, createEmptyCatalog, SCHEMA_VERSION, } from "@todu/core";
6
+ // ============================================================================
7
+ // Storage layer — Automerge repo + document management
8
+ // ============================================================================
9
+ /** Sync message throttle interval in Automerge (see helpers/throttle.js) */
10
+ const SYNC_THROTTLE_MS = 100;
11
+ /** Extra time after sync message generation for WebSocket delivery */
12
+ const SYNC_DELIVERY_MS = 20;
13
+ /**
14
+ * Wait for pending sync messages to be generated and delivered.
15
+ *
16
+ * Automerge batches sync messages on a ~100ms throttle. After a mutation,
17
+ * we wait for the "generate-sync-message" event (confirming the change was
18
+ * packaged for sending), then yield briefly for the WebSocket to deliver
19
+ * the bytes. If no sync message arrives within the throttle window,
20
+ * there's nothing pending — return immediately.
21
+ */
22
+ async function waitForSyncFlush(repo, documentId) {
23
+ return new Promise((resolve) => {
24
+ const timeout = setTimeout(() => {
25
+ repo.off("doc-metrics", onMetrics);
26
+ resolve();
27
+ }, SYNC_THROTTLE_MS + SYNC_DELIVERY_MS);
28
+ function onMetrics(m) {
29
+ if (m.type === "generate-sync-message" && m.documentId === documentId) {
30
+ repo.off("doc-metrics", onMetrics);
31
+ clearTimeout(timeout);
32
+ // Yield for WebSocket to deliver the bytes
33
+ setTimeout(resolve, SYNC_DELIVERY_MS);
34
+ }
35
+ }
36
+ repo.on("doc-metrics", onMetrics);
37
+ });
38
+ }
39
+ /**
40
+ * Initialize storage: create data directory, set up Automerge repo,
41
+ * and load or create the catalog document.
42
+ */
43
+ export async function initStorage(storagePath) {
44
+ // Ensure data directory exists
45
+ fs.mkdirSync(storagePath, { recursive: true });
46
+ // Create Automerge repo with filesystem storage
47
+ const repo = new Repo({
48
+ storage: new NodeFSStorageAdapter(storagePath),
49
+ });
50
+ // Load or create catalog document
51
+ const catalog = await loadOrCreateCatalog(repo, storagePath);
52
+ return {
53
+ repo,
54
+ catalog,
55
+ ephemeral: false,
56
+ async close() {
57
+ await repo.flush();
58
+ await repo.shutdown();
59
+ },
60
+ };
61
+ }
62
+ /**
63
+ * Initialize ephemeral storage with no filesystem persistence.
64
+ * Used by CLI when syncing with a running Electron instance.
65
+ *
66
+ * Creates an in-memory Automerge repo and reads the catalog document ID
67
+ * from the marker file (written by the persistent owner).
68
+ *
69
+ * IMPORTANT: The caller must connect a sync adapter to the repo BEFORE
70
+ * calling `findCatalog()`. The ephemeral repo has no local data — it
71
+ * needs a sync peer to provide the document contents.
72
+ */
73
+ export async function initEphemeralStorage(storagePath) {
74
+ // Read the catalog document ID from the marker file
75
+ const markerPath = path.join(storagePath, `${CATALOG_DOC_KEY}.id`);
76
+ if (!fs.existsSync(markerPath)) {
77
+ throw new Error("No catalog marker found. Run the Electron app or CLI standalone first to create data.");
78
+ }
79
+ const docId = fs.readFileSync(markerPath, "utf-8").trim();
80
+ // Create repo with no storage — purely in-memory
81
+ const repo = new Repo({});
82
+ return {
83
+ repo,
84
+ ephemeral: true,
85
+ async close() {
86
+ try {
87
+ await repo.shutdown();
88
+ }
89
+ catch {
90
+ // Safe to ignore — adapters may already be disconnected
91
+ }
92
+ },
93
+ async findCatalog() {
94
+ // Now that sync is connected, find the catalog document.
95
+ // Allow "requesting" state so find() doesn't fail while waiting
96
+ // for the sync peer to deliver the document data.
97
+ const catalog = await repo.find(docId, {
98
+ allowableStates: ["ready", "requesting", "loading"],
99
+ });
100
+ // Wait for the document to actually be ready (populated via sync)
101
+ await catalog.whenReady();
102
+ return {
103
+ repo,
104
+ catalog,
105
+ ephemeral: true,
106
+ async close() {
107
+ // Wait for any pending mutations to be synced to the server.
108
+ // Automerge throttles sync messages (~100ms batches), so after
109
+ // a change() call the sync message won't be sent immediately.
110
+ // We listen for the "generate-sync-message" event to know the
111
+ // message was queued, then yield briefly for the WebSocket to
112
+ // deliver the bytes before shutting down.
113
+ await waitForSyncFlush(repo, docId);
114
+ try {
115
+ await repo.shutdown();
116
+ }
117
+ catch {
118
+ // Safe to ignore — adapters may already be disconnected
119
+ }
120
+ },
121
+ };
122
+ },
123
+ };
124
+ }
125
+ /**
126
+ * Load existing catalog document or create a new one.
127
+ * Uses a marker file to store the document ID between sessions.
128
+ */
129
+ async function loadOrCreateCatalog(repo, storagePath) {
130
+ const markerPath = path.join(storagePath, `${CATALOG_DOC_KEY}.id`);
131
+ // Try to load existing catalog
132
+ if (fs.existsSync(markerPath)) {
133
+ const docId = fs.readFileSync(markerPath, "utf-8").trim();
134
+ const handle = await repo.find(docId);
135
+ migrateCatalog(handle);
136
+ return handle;
137
+ }
138
+ // Create new catalog
139
+ const handle = repo.create();
140
+ handle.change((doc) => {
141
+ const empty = createEmptyCatalog();
142
+ doc.version = empty.version;
143
+ doc.projects = empty.projects;
144
+ doc.labels = empty.labels;
145
+ doc.recurringTemplates = empty.recurringTemplates;
146
+ doc.habits = empty.habits;
147
+ doc.habitLogDocIds = empty.habitLogDocIds;
148
+ doc.taskListDocIds = empty.taskListDocIds;
149
+ doc.settings = empty.settings;
150
+ });
151
+ // Save document ID for next session
152
+ fs.writeFileSync(markerPath, handle.documentId, "utf-8");
153
+ return handle;
154
+ }
155
+ /**
156
+ * Migrate an existing catalog document to the current schema.
157
+ * Backfills any missing fields that were added in later versions.
158
+ * This ensures engine code can always assume catalog fields exist.
159
+ */
160
+ function migrateCatalog(handle) {
161
+ const doc = handle.doc();
162
+ if (!doc)
163
+ return;
164
+ const defaults = createEmptyCatalog();
165
+ let needsMigration = false;
166
+ // Check for missing fields
167
+ if (!Array.isArray(doc.projects))
168
+ needsMigration = true;
169
+ if (!Array.isArray(doc.labels))
170
+ needsMigration = true;
171
+ if (!Array.isArray(doc.recurringTemplates))
172
+ needsMigration = true;
173
+ if (!Array.isArray(doc.habits))
174
+ needsMigration = true;
175
+ if (doc.taskListDocIds === undefined || doc.taskListDocIds === null)
176
+ needsMigration = true;
177
+ if (doc.habitLogDocIds === undefined || doc.habitLogDocIds === null)
178
+ needsMigration = true;
179
+ if (doc.settings === undefined || doc.settings === null)
180
+ needsMigration = true;
181
+ if (doc.version === undefined || doc.version === null)
182
+ needsMigration = true;
183
+ if (!needsMigration)
184
+ return;
185
+ handle.change((d) => {
186
+ if (!Array.isArray(d.projects))
187
+ d.projects = defaults.projects;
188
+ if (!Array.isArray(d.labels))
189
+ d.labels = defaults.labels;
190
+ if (!Array.isArray(d.recurringTemplates))
191
+ d.recurringTemplates = defaults.recurringTemplates;
192
+ if (!Array.isArray(d.habits))
193
+ d.habits = defaults.habits;
194
+ if (d.taskListDocIds === undefined || d.taskListDocIds === null)
195
+ d.taskListDocIds = defaults.taskListDocIds;
196
+ if (d.habitLogDocIds === undefined || d.habitLogDocIds === null)
197
+ d.habitLogDocIds = defaults.habitLogDocIds;
198
+ if (d.settings === undefined || d.settings === null)
199
+ d.settings = defaults.settings;
200
+ if (d.version === undefined || d.version === null)
201
+ d.version = SCHEMA_VERSION;
202
+ });
203
+ }
204
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Repo } from "@automerge/automerge-repo";
2
+ export declare const DEFAULT_SYNC_URL = "ws://127.0.0.1:24377";
3
+ /**
4
+ * Add a sync client adapter to a Repo, connecting to a remote sync server.
5
+ * Waits for the adapter to be ready before returning.
6
+ * Returns a disconnect function.
7
+ *
8
+ * @param retryInterval - Retry interval in ms if connection fails (default: 500ms for local)
9
+ */
10
+ export declare function connectSyncClient(repo: Repo, url?: string, retryInterval?: number): Promise<() => void>;
11
+ /**
12
+ * Check if a sync server is reachable at the given URL.
13
+ * Returns true if the connection succeeds within the timeout.
14
+ */
15
+ export declare function isSyncServerAvailable(url?: string, timeoutMs?: number): Promise<boolean>;
16
+ //# sourceMappingURL=sync-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../src/sync-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAGtD,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,MAAyB,EAC9B,aAAa,SAAM,GAClB,OAAO,CAAC,MAAM,IAAI,CAAC,CAcrB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,GAAE,MAAyB,EAC9B,SAAS,SAAM,GACd,OAAO,CAAC,OAAO,CAAC,CA2BlB"}
@@ -0,0 +1,54 @@
1
+ import { WebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket";
2
+ export const DEFAULT_SYNC_URL = "ws://127.0.0.1:24377";
3
+ /**
4
+ * Add a sync client adapter to a Repo, connecting to a remote sync server.
5
+ * Waits for the adapter to be ready before returning.
6
+ * Returns a disconnect function.
7
+ *
8
+ * @param retryInterval - Retry interval in ms if connection fails (default: 500ms for local)
9
+ */
10
+ export async function connectSyncClient(repo, url = DEFAULT_SYNC_URL, retryInterval = 500) {
11
+ const adapter = new WebSocketClientAdapter(url, retryInterval);
12
+ repo.networkSubsystem.addNetworkAdapter(adapter);
13
+ // Wait for the WebSocket connection to establish
14
+ await adapter.whenReady();
15
+ return () => {
16
+ try {
17
+ adapter.disconnect();
18
+ }
19
+ catch {
20
+ // WebSocket may already be closed — safe to ignore
21
+ }
22
+ };
23
+ }
24
+ /**
25
+ * Check if a sync server is reachable at the given URL.
26
+ * Returns true if the connection succeeds within the timeout.
27
+ */
28
+ export async function isSyncServerAvailable(url = DEFAULT_SYNC_URL, timeoutMs = 200) {
29
+ return new Promise((resolve) => {
30
+ const timer = setTimeout(() => {
31
+ resolve(false);
32
+ }, timeoutMs);
33
+ try {
34
+ // Use dynamic import for ws since it's CJS
35
+ import("ws").then(({ default: WebSocket }) => {
36
+ const ws = new WebSocket(url);
37
+ ws.onopen = () => {
38
+ clearTimeout(timer);
39
+ ws.close();
40
+ resolve(true);
41
+ };
42
+ ws.onerror = () => {
43
+ clearTimeout(timer);
44
+ resolve(false);
45
+ };
46
+ });
47
+ }
48
+ catch {
49
+ clearTimeout(timer);
50
+ resolve(false);
51
+ }
52
+ });
53
+ }
54
+ //# sourceMappingURL=sync-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-client.js","sourceRoot":"","sources":["../src/sync-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AAErF,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAU,EACV,GAAG,GAAW,gBAAgB,EAC9B,aAAa,GAAG,GAAG,EACE;IACrB,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC/D,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEjD,iDAAiD;IACjD,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;IAE1B,OAAO,GAAG,EAAE,CAAC;QACX,IAAI,CAAC;YACH,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,qDAAmD;QACrD,CAAC;IAAA,CACF,CAAC;AAAA,CACH;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,GAAG,GAAW,gBAAgB,EAC9B,SAAS,GAAG,GAAG,EACG;IAClB,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,CAAC;QAAA,CAChB,EAAE,SAAS,CAAC,CAAC;QAEd,IAAI,CAAC;YACH,2CAA2C;YAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;gBAC5C,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;gBAE9B,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;oBAChB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,EAAE,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAA,CACf,CAAC;gBAEF,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;oBACjB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,KAAK,CAAC,CAAC;gBAAA,CAChB,CAAC;YAAA,CACH,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACJ"}
@@ -0,0 +1,12 @@
1
+ import type { Repo } from "@automerge/automerge-repo";
2
+ export declare const DEFAULT_SYNC_PORT = 24377;
3
+ export interface SyncServer {
4
+ port: number;
5
+ close(): Promise<void>;
6
+ }
7
+ /**
8
+ * Start a WebSocket sync server and attach it to an Automerge Repo.
9
+ * Other Repo instances (CLI, other devices) can connect and sync documents.
10
+ */
11
+ export declare function startSyncServer(repo: Repo, port?: number): SyncServer;
12
+ //# sourceMappingURL=sync-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-server.d.ts","sourceRoot":"","sources":["../src/sync-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAKtD,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAE,MAA0B,GAAG,UAAU,CAaxF"}
@@ -0,0 +1,21 @@
1
+ import { WebSocketServerAdapter } from "@automerge/automerge-repo-network-websocket";
2
+ import { WebSocketServer } from "ws";
3
+ export const DEFAULT_SYNC_PORT = 24377;
4
+ /**
5
+ * Start a WebSocket sync server and attach it to an Automerge Repo.
6
+ * Other Repo instances (CLI, other devices) can connect and sync documents.
7
+ */
8
+ export function startSyncServer(repo, port = DEFAULT_SYNC_PORT) {
9
+ const wss = new WebSocketServer({ host: "127.0.0.1", port });
10
+ const adapter = new WebSocketServerAdapter(wss);
11
+ repo.networkSubsystem.addNetworkAdapter(adapter);
12
+ return {
13
+ port,
14
+ close() {
15
+ return new Promise((resolve) => {
16
+ wss.close(() => resolve());
17
+ });
18
+ },
19
+ };
20
+ }
21
+ //# sourceMappingURL=sync-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-server.js","sourceRoot":"","sources":["../src/sync-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAErC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAOvC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU,EAAE,IAAI,GAAW,iBAAiB,EAAc;IACxF,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,GAAoC,CAAC,CAAC;IACjF,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEjD,OAAO;QACL,IAAI;QACJ,KAAK,GAAG;YACN,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;gBACpC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAAA,CAC5B,CAAC,CAAC;QAAA,CACJ;KACF,CAAC;AAAA,CACH"}
@@ -0,0 +1,18 @@
1
+ import type { DocHandle, Repo } from "@automerge/automerge-repo";
2
+ import { type CatalogDocument, type CreateTaskInput, type Result, type TaskId, type TaskWithDetail } from "@todu/core";
3
+ import type { TaskNamespace } from "./todu.js";
4
+ /**
5
+ * Extended task namespace with internal methods for use by recurring templates.
6
+ * The public TaskNamespace is a subset of this.
7
+ */
8
+ export interface InternalTaskNamespace extends TaskNamespace {
9
+ /**
10
+ * Create a task with a specific ID and optional templateId.
11
+ * Used by recurring template task generation.
12
+ */
13
+ create(input: CreateTaskInput, taskId?: TaskId, templateId?: string): Promise<Result<TaskWithDetail>>;
14
+ /** Expose repo for recurring template task lookup */
15
+ _repo: Repo;
16
+ }
17
+ export declare function createTaskNamespace(catalog: DocHandle<CatalogDocument>, repo: Repo): InternalTaskNamespace;
18
+ //# sourceMappingURL=tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../src/tasks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EAQpB,KAAK,MAAM,EAIX,KAAK,MAAM,EAIX,KAAK,cAAc,EAKpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAM/C;;;GAGG;AACH,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D;;;OAGG;IACH,MAAM,CACJ,KAAK,EAAE,eAAe,EACtB,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IAEnC,qDAAqD;IACrD,KAAK,EAAE,IAAI,CAAC;CACb;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,IAAI,EAAE,IAAI,GACT,qBAAqB,CAsYvB"}