@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 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { DocHandle, Repo } from "@automerge/automerge-repo";
2
+ import { type CatalogDocument } from "@todu/core";
3
+ import type { LabelNamespace } from "./todu.js";
4
+ export declare function createLabelNamespace(catalog: DocHandle<CatalogDocument>, repo: Repo): LabelNamespace;
5
+ //# sourceMappingURL=labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EAcrB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,IAAI,EAAE,IAAI,GACT,cAAc,CAgHhB"}
package/dist/labels.js ADDED
@@ -0,0 +1,113 @@
1
+ import crypto from "node:crypto";
2
+ import { createLabelId, err, notFound, ok, validateCreateLabelInput, validateUpdateLabelInput, validationError, } from "@todu/core";
3
+ // ============================================================================
4
+ // Label namespace — CRUD operations on catalog document
5
+ // ============================================================================
6
+ export function createLabelNamespace(catalog, repo) {
7
+ return {
8
+ async create(input) {
9
+ const validationErr = validateCreateLabelInput(input);
10
+ if (validationErr)
11
+ return err(validationErr);
12
+ // Check name uniqueness
13
+ const doc = catalog.doc();
14
+ if (doc) {
15
+ const existing = doc.labels.find((l) => l.name.toLowerCase() === input.name.trim().toLowerCase());
16
+ if (existing) {
17
+ return err(validationError("name", `Label "${input.name.trim()}" already exists`));
18
+ }
19
+ }
20
+ const now = new Date().toISOString();
21
+ const id = createLabelId(`lbl-${crypto.randomUUID().slice(0, 8)}`);
22
+ const label = {
23
+ id,
24
+ name: input.name.trim(),
25
+ createdAt: now,
26
+ };
27
+ if (input.color !== undefined) {
28
+ label.color = input.color;
29
+ }
30
+ catalog.change((doc) => {
31
+ doc.labels.push(label);
32
+ });
33
+ return ok(label);
34
+ },
35
+ async list() {
36
+ const doc = catalog.doc();
37
+ if (!doc)
38
+ return ok([]);
39
+ return ok(doc.labels.map(cloneLabel));
40
+ },
41
+ async update(id, input) {
42
+ const validationErr = validateUpdateLabelInput(input);
43
+ if (validationErr)
44
+ return err(validationErr);
45
+ const doc = catalog.doc();
46
+ if (!doc)
47
+ return err(notFound("label", id));
48
+ const index = doc.labels.findIndex((l) => l.id === id);
49
+ if (index === -1)
50
+ return err(notFound("label", id));
51
+ // Check name uniqueness if name is changing
52
+ if (input.name !== undefined) {
53
+ const existing = doc.labels.find((l) => l.id !== id && l.name.toLowerCase() === input.name.trim().toLowerCase());
54
+ if (existing) {
55
+ return err(validationError("name", `Label "${input.name.trim()}" already exists`));
56
+ }
57
+ }
58
+ catalog.change((doc) => {
59
+ const label = doc.labels[index];
60
+ if (input.name !== undefined)
61
+ label.name = input.name.trim();
62
+ if (input.color !== undefined)
63
+ label.color = input.color;
64
+ });
65
+ const updated = catalog.doc().labels[index];
66
+ return ok(cloneLabel(updated));
67
+ },
68
+ async delete(id) {
69
+ const doc = catalog.doc();
70
+ if (!doc)
71
+ return err(notFound("label", id));
72
+ const index = doc.labels.findIndex((l) => l.id === id);
73
+ if (index === -1)
74
+ return err(notFound("label", id));
75
+ const labelName = doc.labels[index].name;
76
+ // Remove label from all tasks that reference it
77
+ for (const docId of Object.values(doc.taskListDocIds)) {
78
+ const handle = await repo.find(docId);
79
+ const listDoc = handle.doc();
80
+ if (!listDoc)
81
+ continue;
82
+ const tasksWithLabel = listDoc.tasks
83
+ .map((t, i) => ({ index: i, hasLabel: t.labels.includes(labelName) }))
84
+ .filter((t) => t.hasLabel);
85
+ if (tasksWithLabel.length > 0) {
86
+ handle.change((doc) => {
87
+ for (const { index } of tasksWithLabel) {
88
+ const task = doc.tasks[index];
89
+ const labelIdx = task.labels.indexOf(labelName);
90
+ if (labelIdx !== -1) {
91
+ task.labels.splice(labelIdx, 1);
92
+ }
93
+ }
94
+ });
95
+ }
96
+ }
97
+ // Remove from catalog
98
+ catalog.change((doc) => {
99
+ doc.labels.splice(index, 1);
100
+ });
101
+ return ok(undefined);
102
+ },
103
+ };
104
+ }
105
+ function cloneLabel(l) {
106
+ return {
107
+ id: l.id,
108
+ name: l.name,
109
+ color: l.color,
110
+ createdAt: l.createdAt,
111
+ };
112
+ }
113
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { DocHandle, Repo } from "@automerge/automerge-repo";
2
+ import { type CatalogDocument } from "@todu/core";
3
+ import type { NoteNamespace } from "./todu.js";
4
+ export declare function createNoteNamespace(catalog: DocHandle<CatalogDocument>, repo: Repo): NoteNamespace;
5
+ //# sourceMappingURL=notes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EAgBrB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAM/C,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,IAAI,EAAE,IAAI,GACT,aAAa,CA+Jf"}
package/dist/notes.js ADDED
@@ -0,0 +1,163 @@
1
+ import crypto from "node:crypto";
2
+ import { createNoteId, createNotesDocument, err, notFound, ok, validateCreateNoteInput, validateUpdateNoteInput, } from "@todu/core";
3
+ // ============================================================================
4
+ // Note namespace — CRUD on NotesDocument
5
+ // ============================================================================
6
+ export function createNoteNamespace(catalog, repo) {
7
+ /**
8
+ * Get or create the global NotesDocument.
9
+ */
10
+ async function getOrCreateNotesDoc() {
11
+ const catalogDoc = catalog.doc();
12
+ const existingDocId = catalogDoc?.notesDocId;
13
+ if (existingDocId) {
14
+ return await repo.find(existingDocId);
15
+ }
16
+ const handle = repo.create();
17
+ const template = createNotesDocument();
18
+ handle.change((doc) => {
19
+ doc.notes = template.notes;
20
+ });
21
+ catalog.change((doc) => {
22
+ doc.notesDocId = handle.documentId;
23
+ });
24
+ return handle;
25
+ }
26
+ /**
27
+ * Verify that an entity exists (task or project).
28
+ */
29
+ async function entityExists(entityType, entityId) {
30
+ const catalogDoc = catalog.doc();
31
+ if (!catalogDoc)
32
+ return false;
33
+ if (entityType === "project") {
34
+ return catalogDoc.projects.some((p) => p.id === entityId);
35
+ }
36
+ if (entityType === "task") {
37
+ for (const docId of Object.values(catalogDoc.taskListDocIds)) {
38
+ const handle = await repo.find(docId);
39
+ const doc = handle.doc();
40
+ if (doc?.tasks.some((t) => t.id === entityId))
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ // Habits will be checked when that slice lands
46
+ return true;
47
+ }
48
+ return {
49
+ async create(input) {
50
+ const validationErr = validateCreateNoteInput(input);
51
+ if (validationErr)
52
+ return err(validationErr);
53
+ // Verify entity exists if attached
54
+ if (input.entityType && input.entityId) {
55
+ const exists = await entityExists(input.entityType, input.entityId);
56
+ if (!exists) {
57
+ return err(notFound(input.entityType, input.entityId));
58
+ }
59
+ }
60
+ const now = new Date().toISOString();
61
+ const id = createNoteId(`note-${crypto.randomUUID().slice(0, 8)}`);
62
+ const note = {
63
+ id,
64
+ content: input.content.trim(),
65
+ author: input.author ?? "user",
66
+ tags: input.tags ?? [],
67
+ createdAt: now,
68
+ };
69
+ if (input.entityType !== undefined)
70
+ note.entityType = input.entityType;
71
+ if (input.entityId !== undefined)
72
+ note.entityId = input.entityId;
73
+ const notesHandle = await getOrCreateNotesDoc();
74
+ notesHandle.change((doc) => {
75
+ doc.notes.push(note);
76
+ });
77
+ return ok(note);
78
+ },
79
+ async list(filter) {
80
+ const catalogDoc = catalog.doc();
81
+ if (!catalogDoc?.notesDocId)
82
+ return ok([]);
83
+ const notesHandle = await repo.find(catalogDoc.notesDocId);
84
+ const notesDoc = notesHandle.doc();
85
+ if (!notesDoc)
86
+ return ok([]);
87
+ let notes = notesDoc.notes.map(cloneNote);
88
+ // Apply filters
89
+ if (filter?.entityType) {
90
+ notes = notes.filter((n) => n.entityType === filter.entityType);
91
+ }
92
+ if (filter?.entityId) {
93
+ notes = notes.filter((n) => n.entityId === filter.entityId);
94
+ }
95
+ if (filter?.tag) {
96
+ notes = notes.filter((n) => n.tags.includes(filter.tag));
97
+ }
98
+ if (filter?.author) {
99
+ notes = notes.filter((n) => n.author === filter.author);
100
+ }
101
+ // Sort by createdAt desc (newest first)
102
+ notes.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
103
+ return ok(notes);
104
+ },
105
+ async update(id, input) {
106
+ const validationErr = validateUpdateNoteInput(input);
107
+ if (validationErr)
108
+ return err(validationErr);
109
+ const catalogDoc = catalog.doc();
110
+ if (!catalogDoc?.notesDocId)
111
+ return err(notFound("note", id));
112
+ const notesHandle = await repo.find(catalogDoc.notesDocId);
113
+ const notesDoc = notesHandle.doc();
114
+ if (!notesDoc)
115
+ return err(notFound("note", id));
116
+ const index = notesDoc.notes.findIndex((n) => n.id === id);
117
+ if (index === -1)
118
+ return err(notFound("note", id));
119
+ notesHandle.change((doc) => {
120
+ const note = doc.notes[index];
121
+ if (input.content !== undefined)
122
+ note.content = input.content.trim();
123
+ if (input.tags !== undefined) {
124
+ // Clear and repopulate tags array for Automerge compatibility
125
+ while (note.tags.length > 0)
126
+ note.tags.pop();
127
+ for (const tag of input.tags)
128
+ note.tags.push(tag);
129
+ }
130
+ });
131
+ const updated = notesHandle.doc().notes[index];
132
+ return ok(cloneNote(updated));
133
+ },
134
+ async delete(id) {
135
+ const catalogDoc = catalog.doc();
136
+ if (!catalogDoc?.notesDocId)
137
+ return err(notFound("note", id));
138
+ const notesHandle = await repo.find(catalogDoc.notesDocId);
139
+ const notesDoc = notesHandle.doc();
140
+ if (!notesDoc)
141
+ return err(notFound("note", id));
142
+ const index = notesDoc.notes.findIndex((n) => n.id === id);
143
+ if (index === -1)
144
+ return err(notFound("note", id));
145
+ notesHandle.change((doc) => {
146
+ doc.notes.splice(index, 1);
147
+ });
148
+ return ok(undefined);
149
+ },
150
+ };
151
+ }
152
+ function cloneNote(n) {
153
+ return {
154
+ id: n.id,
155
+ content: n.content,
156
+ author: n.author,
157
+ entityType: n.entityType,
158
+ entityId: n.entityId,
159
+ tags: [...n.tags],
160
+ createdAt: n.createdAt,
161
+ };
162
+ }
163
+ //# sourceMappingURL=notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.js","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { DocHandle } from "@automerge/automerge-repo";
2
+ import { type CatalogDocument } from "@todu/core";
3
+ import type { ProjectNamespace } from "./todu.js";
4
+ export declare function createProjectNamespace(catalog: DocHandle<CatalogDocument>): ProjectNamespace;
5
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EACL,KAAK,eAAe,EAarB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAMlD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,gBAAgB,CAwG5F"}
@@ -0,0 +1,117 @@
1
+ import crypto from "node:crypto";
2
+ import { createProjectId, err, notFound, ok, validateCreateProjectInput, validateUpdateProjectInput, } from "@todu/core";
3
+ // ============================================================================
4
+ // Project namespace — CRUD operations on catalog document
5
+ // ============================================================================
6
+ export function createProjectNamespace(catalog) {
7
+ return {
8
+ async create(input) {
9
+ const validationErr = validateCreateProjectInput(input);
10
+ if (validationErr)
11
+ return err(validationErr);
12
+ const now = new Date().toISOString();
13
+ const id = createProjectId(`proj-${crypto.randomUUID().slice(0, 8)}`);
14
+ const project = {
15
+ id,
16
+ name: input.name.trim(),
17
+ status: "active",
18
+ priority: input.priority ?? "medium",
19
+ syncStrategy: "none",
20
+ createdAt: now,
21
+ updatedAt: now,
22
+ };
23
+ // Automerge doesn't allow undefined — only set optional fields if present
24
+ if (input.description !== undefined) {
25
+ project.description = input.description.trim();
26
+ }
27
+ catalog.change((doc) => {
28
+ doc.projects.push(project);
29
+ });
30
+ return ok(project);
31
+ },
32
+ async list(filter) {
33
+ const doc = catalog.doc();
34
+ if (!doc)
35
+ return ok([]);
36
+ let filtered = doc.projects.map(cloneProject);
37
+ if (filter?.status) {
38
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
39
+ filtered = filtered.filter((p) => statuses.includes(p.status));
40
+ }
41
+ if (filter?.priority) {
42
+ filtered = filtered.filter((p) => p.priority === filter.priority);
43
+ }
44
+ if (filter?.search) {
45
+ const lowerQuery = filter.search.toLowerCase();
46
+ filtered = filtered.filter((p) => p.name.toLowerCase().includes(lowerQuery));
47
+ }
48
+ return ok(filtered);
49
+ },
50
+ async get(id) {
51
+ const doc = catalog.doc();
52
+ if (!doc)
53
+ return err(notFound("project", id));
54
+ const project = doc.projects.find((p) => p.id === id);
55
+ if (!project)
56
+ return err(notFound("project", id));
57
+ return ok(cloneProject(project));
58
+ },
59
+ async update(id, input) {
60
+ const validationErr = validateUpdateProjectInput(input);
61
+ if (validationErr)
62
+ return err(validationErr);
63
+ const doc = catalog.doc();
64
+ if (!doc)
65
+ return err(notFound("project", id));
66
+ const index = doc.projects.findIndex((p) => p.id === id);
67
+ if (index === -1)
68
+ return err(notFound("project", id));
69
+ const now = new Date().toISOString();
70
+ catalog.change((doc) => {
71
+ const project = doc.projects[index];
72
+ if (input.name !== undefined)
73
+ project.name = input.name.trim();
74
+ if (input.description !== undefined)
75
+ project.description = input.description.trim();
76
+ if (input.status !== undefined)
77
+ project.status = input.status;
78
+ if (input.priority !== undefined)
79
+ project.priority = input.priority;
80
+ project.updatedAt = now;
81
+ });
82
+ // Read back the updated project
83
+ const updated = catalog.doc().projects[index];
84
+ return ok(cloneProject(updated));
85
+ },
86
+ async delete(id) {
87
+ const doc = catalog.doc();
88
+ if (!doc)
89
+ return err(notFound("project", id));
90
+ const index = doc.projects.findIndex((p) => p.id === id);
91
+ if (index === -1)
92
+ return err(notFound("project", id));
93
+ // TODO: When tasks slice lands, check for non-empty TaskListDocument
94
+ // For now, allow deletion unconditionally.
95
+ catalog.change((doc) => {
96
+ doc.projects.splice(index, 1);
97
+ });
98
+ return ok(undefined);
99
+ },
100
+ };
101
+ }
102
+ /** Clone a project out of the Automerge proxy */
103
+ function cloneProject(p) {
104
+ return {
105
+ id: p.id,
106
+ name: p.name,
107
+ description: p.description,
108
+ status: p.status,
109
+ priority: p.priority,
110
+ externalId: p.externalId,
111
+ systemId: p.systemId,
112
+ syncStrategy: p.syncStrategy,
113
+ createdAt: p.createdAt,
114
+ updatedAt: p.updatedAt,
115
+ };
116
+ }
117
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { DocHandle, Repo } from "@automerge/automerge-repo";
2
+ import { type CatalogDocument, type ProjectId, type RecurringId } from "@todu/core";
3
+ import type { RecurringNamespace } from "./todu.js";
4
+ export declare function createRecurringNamespace(catalog: DocHandle<CatalogDocument>, repo: Repo): RecurringNamespace;
5
+ export interface UpcomingOccurrence {
6
+ templateId: RecurringId;
7
+ title: string;
8
+ date: string;
9
+ projectId: ProjectId;
10
+ priority: string;
11
+ schedule: string;
12
+ }
13
+ /**
14
+ * Register the recurring template processor with the scheduling framework.
15
+ * Called once during module initialization.
16
+ */
17
+ export declare function registerRecurringProcessor(catalog: DocHandle<CatalogDocument>, repo: Repo): void;
18
+ /**
19
+ * Add a date to a template's skip list.
20
+ * Called when a generated task is deleted.
21
+ */
22
+ export declare function addToSkipList(catalog: DocHandle<CatalogDocument>, templateId: string, date: string): void;
23
+ //# sourceMappingURL=recurring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurring.d.ts","sourceRoot":"","sources":["../src/recurring.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EAQpB,KAAK,SAAS,EAEd,KAAK,WAAW,EASjB,MAAM,YAAY,CAAC;AAUpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAMpD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,IAAI,EAAE,IAAI,GACT,kBAAkB,CAiSpB;AAMD,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,WAAW,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiGD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAOhG;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,IAAI,CAaN"}