@jamesaphoenix/tx 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.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/cli.js +868 -0
- package/dist/cli.js.map +1 -0
- package/dist/db.js +70 -0
- package/dist/db.js.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/id.js +19 -0
- package/dist/id.js.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/layer.js +20 -0
- package/dist/layer.js.map +1 -0
- package/dist/mcp/server.js +453 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/repo/dep-repo.js +104 -0
- package/dist/repo/dep-repo.js.map +1 -0
- package/dist/repo/task-repo.js +140 -0
- package/dist/repo/task-repo.js.map +1 -0
- package/dist/schema.js +37 -0
- package/dist/schema.js.map +1 -0
- package/dist/schemas/sync.js +55 -0
- package/dist/schemas/sync.js.map +1 -0
- package/dist/services/dep-service.js +34 -0
- package/dist/services/dep-service.js.map +1 -0
- package/dist/services/hierarchy-service.js +66 -0
- package/dist/services/hierarchy-service.js.map +1 -0
- package/dist/services/ready-service.js +70 -0
- package/dist/services/ready-service.js.map +1 -0
- package/dist/services/score-service.js +82 -0
- package/dist/services/score-service.js.map +1 -0
- package/dist/services/sync-service.js +244 -0
- package/dist/services/sync-service.js.map +1 -0
- package/dist/services/task-service.js +201 -0
- package/dist/services/task-service.js.map +1 -0
- package/migrations/001_initial.sql +57 -0
- package/package.json +52 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { SqliteClient } from "../db.js";
|
|
3
|
+
import { DatabaseError } from "../errors.js";
|
|
4
|
+
import { rowToTask } from "../schema.js";
|
|
5
|
+
export class TaskRepository extends Context.Tag("TaskRepository")() {
|
|
6
|
+
}
|
|
7
|
+
export const TaskRepositoryLive = Layer.effect(TaskRepository, Effect.gen(function* () {
|
|
8
|
+
const db = yield* SqliteClient;
|
|
9
|
+
return {
|
|
10
|
+
findById: (id) => Effect.try({
|
|
11
|
+
try: () => {
|
|
12
|
+
const row = db.prepare("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
13
|
+
return row ? rowToTask(row) : null;
|
|
14
|
+
},
|
|
15
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
16
|
+
}),
|
|
17
|
+
findByIds: (ids) => Effect.try({
|
|
18
|
+
try: () => {
|
|
19
|
+
if (ids.length === 0)
|
|
20
|
+
return [];
|
|
21
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
22
|
+
const rows = db.prepare(`SELECT * FROM tasks WHERE id IN (${placeholders})`).all(...ids);
|
|
23
|
+
return rows.map(rowToTask);
|
|
24
|
+
},
|
|
25
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
26
|
+
}),
|
|
27
|
+
findAll: (filter) => Effect.try({
|
|
28
|
+
try: () => {
|
|
29
|
+
const conditions = [];
|
|
30
|
+
const params = [];
|
|
31
|
+
if (filter?.status) {
|
|
32
|
+
if (Array.isArray(filter.status)) {
|
|
33
|
+
const placeholders = filter.status.map(() => "?").join(",");
|
|
34
|
+
conditions.push(`status IN (${placeholders})`);
|
|
35
|
+
params.push(...filter.status);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
conditions.push("status = ?");
|
|
39
|
+
params.push(filter.status);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (filter?.parentId !== undefined) {
|
|
43
|
+
if (filter.parentId === null) {
|
|
44
|
+
conditions.push("parent_id IS NULL");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
conditions.push("parent_id = ?");
|
|
48
|
+
params.push(filter.parentId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
52
|
+
const limit = filter?.limit ? `LIMIT ${filter.limit}` : "";
|
|
53
|
+
const sql = `SELECT * FROM tasks ${where} ORDER BY score DESC, created_at ASC ${limit}`;
|
|
54
|
+
const rows = db.prepare(sql).all(...params);
|
|
55
|
+
return rows.map(rowToTask);
|
|
56
|
+
},
|
|
57
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
58
|
+
}),
|
|
59
|
+
findByParent: (parentId) => Effect.try({
|
|
60
|
+
try: () => {
|
|
61
|
+
const rows = parentId === null
|
|
62
|
+
? db.prepare("SELECT * FROM tasks WHERE parent_id IS NULL ORDER BY score DESC").all()
|
|
63
|
+
: db.prepare("SELECT * FROM tasks WHERE parent_id = ? ORDER BY score DESC").all(parentId);
|
|
64
|
+
return rows.map(rowToTask);
|
|
65
|
+
},
|
|
66
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
67
|
+
}),
|
|
68
|
+
getChildIds: (id) => Effect.try({
|
|
69
|
+
try: () => {
|
|
70
|
+
const rows = db.prepare("SELECT id FROM tasks WHERE parent_id = ?").all(id);
|
|
71
|
+
return rows.map(r => r.id);
|
|
72
|
+
},
|
|
73
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
74
|
+
}),
|
|
75
|
+
getChildIdsForMany: (ids) => Effect.try({
|
|
76
|
+
try: () => {
|
|
77
|
+
const result = new Map();
|
|
78
|
+
if (ids.length === 0)
|
|
79
|
+
return result;
|
|
80
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
81
|
+
const rows = db.prepare(`SELECT id, parent_id FROM tasks WHERE parent_id IN (${placeholders})`).all(...ids);
|
|
82
|
+
// Initialize all requested IDs with empty arrays
|
|
83
|
+
for (const id of ids) {
|
|
84
|
+
result.set(id, []);
|
|
85
|
+
}
|
|
86
|
+
// Group by parent_id
|
|
87
|
+
for (const row of rows) {
|
|
88
|
+
const existing = result.get(row.parent_id) ?? [];
|
|
89
|
+
result.set(row.parent_id, [...existing, row.id]);
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
},
|
|
93
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
94
|
+
}),
|
|
95
|
+
insert: (task) => Effect.try({
|
|
96
|
+
try: () => {
|
|
97
|
+
db.prepare(`INSERT INTO tasks (id, title, description, status, parent_id, score, created_at, updated_at, completed_at, metadata)
|
|
98
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(task.id, task.title, task.description, task.status, task.parentId, task.score, task.createdAt.toISOString(), task.updatedAt.toISOString(), task.completedAt?.toISOString() ?? null, JSON.stringify(task.metadata));
|
|
99
|
+
},
|
|
100
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
101
|
+
}),
|
|
102
|
+
update: (task) => Effect.try({
|
|
103
|
+
try: () => {
|
|
104
|
+
db.prepare(`UPDATE tasks SET
|
|
105
|
+
title = ?, description = ?, status = ?, parent_id = ?,
|
|
106
|
+
score = ?, updated_at = ?, completed_at = ?, metadata = ?
|
|
107
|
+
WHERE id = ?`).run(task.title, task.description, task.status, task.parentId, task.score, task.updatedAt.toISOString(), task.completedAt?.toISOString() ?? null, JSON.stringify(task.metadata), task.id);
|
|
108
|
+
},
|
|
109
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
110
|
+
}),
|
|
111
|
+
remove: (id) => Effect.try({
|
|
112
|
+
try: () => {
|
|
113
|
+
db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
|
|
114
|
+
},
|
|
115
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
116
|
+
}),
|
|
117
|
+
count: (filter) => Effect.try({
|
|
118
|
+
try: () => {
|
|
119
|
+
const conditions = [];
|
|
120
|
+
const params = [];
|
|
121
|
+
if (filter?.status) {
|
|
122
|
+
if (Array.isArray(filter.status)) {
|
|
123
|
+
const placeholders = filter.status.map(() => "?").join(",");
|
|
124
|
+
conditions.push(`status IN (${placeholders})`);
|
|
125
|
+
params.push(...filter.status);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
conditions.push("status = ?");
|
|
129
|
+
params.push(filter.status);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
133
|
+
const result = db.prepare(`SELECT COUNT(*) as cnt FROM tasks ${where}`).get(...params);
|
|
134
|
+
return result.cnt;
|
|
135
|
+
},
|
|
136
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
137
|
+
})
|
|
138
|
+
};
|
|
139
|
+
}));
|
|
140
|
+
//# sourceMappingURL=task-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-repo.js","sourceRoot":"","sources":["../../src/repo/task-repo.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Plain TypeScript types for the bootstrap.
|
|
2
|
+
// Can be migrated to Effect Schema.Class later for runtime validation.
|
|
3
|
+
export const TASK_STATUSES = [
|
|
4
|
+
"backlog", "ready", "planning", "active",
|
|
5
|
+
"blocked", "review", "human_needs_to_review", "done"
|
|
6
|
+
];
|
|
7
|
+
// Status transition validation
|
|
8
|
+
const VALID_TRANSITIONS = {
|
|
9
|
+
backlog: ["ready", "planning", "active", "blocked", "done"],
|
|
10
|
+
ready: ["planning", "active", "blocked", "done"],
|
|
11
|
+
planning: ["ready", "active", "blocked", "done"],
|
|
12
|
+
active: ["blocked", "review", "done"],
|
|
13
|
+
blocked: ["backlog", "ready", "planning", "active"],
|
|
14
|
+
review: ["active", "human_needs_to_review", "done"],
|
|
15
|
+
human_needs_to_review: ["active", "review", "done"],
|
|
16
|
+
done: ["backlog"]
|
|
17
|
+
};
|
|
18
|
+
export const isValidTransition = (from, to) => VALID_TRANSITIONS[from]?.includes(to) ?? false;
|
|
19
|
+
export const isValidStatus = (s) => TASK_STATUSES.includes(s);
|
|
20
|
+
export const rowToTask = (row) => ({
|
|
21
|
+
id: row.id,
|
|
22
|
+
title: row.title,
|
|
23
|
+
description: row.description,
|
|
24
|
+
status: row.status,
|
|
25
|
+
parentId: row.parent_id,
|
|
26
|
+
score: row.score,
|
|
27
|
+
createdAt: new Date(row.created_at),
|
|
28
|
+
updatedAt: new Date(row.updated_at),
|
|
29
|
+
completedAt: row.completed_at ? new Date(row.completed_at) : null,
|
|
30
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
31
|
+
});
|
|
32
|
+
export const rowToDependency = (row) => ({
|
|
33
|
+
blockerId: row.blocker_id,
|
|
34
|
+
blockedId: row.blocked_id,
|
|
35
|
+
createdAt: new Date(row.created_at)
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,uEAAuE;AAEvE,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ;IACxC,SAAS,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM;CAC5C,CAAA;AA4DV,+BAA+B;AAC/B,MAAM,iBAAiB,GAAqC;IAC1D,OAAO,EAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;IAC1E,KAAK,EAAmB,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;IACjE,QAAQ,EAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;IAC9D,MAAM,EAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC;IACrD,OAAO,EAAiB,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC;IAClE,MAAM,EAAkB,CAAC,QAAQ,EAAE,uBAAuB,EAAE,MAAM,CAAC;IACnE,qBAAqB,EAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;IACpD,IAAI,EAAoB,CAAC,SAAS,CAAC;CACpC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAgB,EAAE,EAAc,EAAW,EAAE,CAC7E,iBAAiB,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,KAAK,CAAA;AAEhD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAS,EAAmB,EAAE,CAC1D,aAAa,CAAC,QAAQ,CAAC,CAAe,CAAC,CAAA;AAgBzC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAY,EAAQ,EAAE,CAAC,CAAC;IAChD,EAAE,EAAE,GAAG,CAAC,EAAY;IACpB,KAAK,EAAE,GAAG,CAAC,KAAK;IAChB,WAAW,EAAE,GAAG,CAAC,WAAW;IAC5B,MAAM,EAAE,GAAG,CAAC,MAAoB;IAChC,QAAQ,EAAE,GAAG,CAAC,SAA0B;IACxC,KAAK,EAAE,GAAG,CAAC,KAAK;IAChB,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;IACnC,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;IACnC,WAAW,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;IACjE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;CAC3C,CAAC,CAAA;AASF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAkB,EAAkB,EAAE,CAAC,CAAC;IACtE,SAAS,EAAE,GAAG,CAAC,UAAoB;IACnC,SAAS,EAAE,GAAG,CAAC,UAAoB;IACnC,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;CACpC,CAAC,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Effect Schema definitions for JSONL sync operations.
|
|
2
|
+
// See DD-009 for specification details.
|
|
3
|
+
import { Schema } from "effect";
|
|
4
|
+
import { TASK_STATUSES } from "../schema.js";
|
|
5
|
+
// Schema version - v=1 for all sync operations
|
|
6
|
+
export const SyncVersion = Schema.Literal(1);
|
|
7
|
+
// TaskId schema - matches tx-[a-z0-9]{6,8} pattern
|
|
8
|
+
export const TaskIdSchema = Schema.String.pipe(Schema.pattern(/^tx-[a-z0-9]{6,8}$/));
|
|
9
|
+
// TaskStatus schema - matches the status lifecycle
|
|
10
|
+
export const TaskStatusSchema = Schema.Literal(...TASK_STATUSES);
|
|
11
|
+
// ISO 8601 timestamp pattern (basic validation)
|
|
12
|
+
const IsoTimestamp = Schema.String.pipe(Schema.pattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/));
|
|
13
|
+
// Task data embedded in upsert operations
|
|
14
|
+
export const TaskDataSchema = Schema.Struct({
|
|
15
|
+
title: Schema.String,
|
|
16
|
+
description: Schema.String,
|
|
17
|
+
status: TaskStatusSchema,
|
|
18
|
+
score: Schema.Number.pipe(Schema.int()),
|
|
19
|
+
parentId: Schema.NullOr(TaskIdSchema),
|
|
20
|
+
metadata: Schema.Record({ key: Schema.String, value: Schema.Unknown })
|
|
21
|
+
});
|
|
22
|
+
// Task upsert operation
|
|
23
|
+
export const TaskUpsertOp = Schema.Struct({
|
|
24
|
+
v: SyncVersion,
|
|
25
|
+
op: Schema.Literal("upsert"),
|
|
26
|
+
ts: IsoTimestamp,
|
|
27
|
+
id: TaskIdSchema,
|
|
28
|
+
data: TaskDataSchema
|
|
29
|
+
});
|
|
30
|
+
// Task delete operation (tombstone)
|
|
31
|
+
export const TaskDeleteOp = Schema.Struct({
|
|
32
|
+
v: SyncVersion,
|
|
33
|
+
op: Schema.Literal("delete"),
|
|
34
|
+
ts: IsoTimestamp,
|
|
35
|
+
id: TaskIdSchema
|
|
36
|
+
});
|
|
37
|
+
// Dependency add operation
|
|
38
|
+
export const DepAddOp = Schema.Struct({
|
|
39
|
+
v: SyncVersion,
|
|
40
|
+
op: Schema.Literal("dep_add"),
|
|
41
|
+
ts: IsoTimestamp,
|
|
42
|
+
blockerId: TaskIdSchema,
|
|
43
|
+
blockedId: TaskIdSchema
|
|
44
|
+
});
|
|
45
|
+
// Dependency remove operation
|
|
46
|
+
export const DepRemoveOp = Schema.Struct({
|
|
47
|
+
v: SyncVersion,
|
|
48
|
+
op: Schema.Literal("dep_remove"),
|
|
49
|
+
ts: IsoTimestamp,
|
|
50
|
+
blockerId: TaskIdSchema,
|
|
51
|
+
blockedId: TaskIdSchema
|
|
52
|
+
});
|
|
53
|
+
// Union of all sync operations
|
|
54
|
+
export const SyncOperation = Schema.Union(TaskUpsertOp, TaskDeleteOp, DepAddOp, DepRemoveOp);
|
|
55
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/schemas/sync.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,wCAAwC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,+CAA+C;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAE5C,mDAAmD;AACnD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAC5C,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CACrC,CAAA;AAED,mDAAmD;AACnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,CAAA;AAEhE,gDAAgD;AAChD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CACrC,MAAM,CAAC,OAAO,CAAC,sCAAsC,CAAC,CACvD,CAAA;AAED,0CAA0C;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;CACvE,CAAC,CAAA;AAEF,wBAAwB;AACxB,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;IACxC,CAAC,EAAE,WAAW;IACd,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC5B,EAAE,EAAE,YAAY;IAChB,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,cAAc;CACrB,CAAC,CAAA;AAGF,oCAAoC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;IACxC,CAAC,EAAE,WAAW;IACd,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC5B,EAAE,EAAE,YAAY;IAChB,EAAE,EAAE,YAAY;CACjB,CAAC,CAAA;AAGF,2BAA2B;AAC3B,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IACpC,CAAC,EAAE,WAAW;IACd,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;IAC7B,EAAE,EAAE,YAAY;IAChB,SAAS,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;CACxB,CAAC,CAAA;AAGF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;IACvC,CAAC,EAAE,WAAW;IACd,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;IAChC,EAAE,EAAE,YAAY;IAChB,SAAS,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;CACxB,CAAC,CAAA;AAGF,+BAA+B;AAC/B,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CACvC,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,WAAW,CACZ,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { DependencyRepository } from "../repo/dep-repo.js";
|
|
3
|
+
import { TaskRepository } from "../repo/task-repo.js";
|
|
4
|
+
import { ValidationError, CircularDependencyError, TaskNotFoundError } from "../errors.js";
|
|
5
|
+
export class DependencyService extends Context.Tag("DependencyService")() {
|
|
6
|
+
}
|
|
7
|
+
export const DependencyServiceLive = Layer.effect(DependencyService, Effect.gen(function* () {
|
|
8
|
+
const depRepo = yield* DependencyRepository;
|
|
9
|
+
const taskRepo = yield* TaskRepository;
|
|
10
|
+
return {
|
|
11
|
+
addBlocker: (taskId, blockerId) => Effect.gen(function* () {
|
|
12
|
+
if (taskId === blockerId) {
|
|
13
|
+
return yield* Effect.fail(new ValidationError({ reason: "A task cannot block itself" }));
|
|
14
|
+
}
|
|
15
|
+
const task = yield* taskRepo.findById(taskId);
|
|
16
|
+
if (!task) {
|
|
17
|
+
return yield* Effect.fail(new TaskNotFoundError({ id: taskId }));
|
|
18
|
+
}
|
|
19
|
+
const blocker = yield* taskRepo.findById(blockerId);
|
|
20
|
+
if (!blocker) {
|
|
21
|
+
return yield* Effect.fail(new TaskNotFoundError({ id: blockerId }));
|
|
22
|
+
}
|
|
23
|
+
// Cycle detection: check if there's already a path from taskId to blockerId
|
|
24
|
+
// (i.e., blockerId is transitively blocked by taskId)
|
|
25
|
+
const wouldCycle = yield* depRepo.hasPath(blockerId, taskId);
|
|
26
|
+
if (wouldCycle) {
|
|
27
|
+
return yield* Effect.fail(new CircularDependencyError({ taskId, blockerId }));
|
|
28
|
+
}
|
|
29
|
+
yield* depRepo.insert(blockerId, taskId);
|
|
30
|
+
}),
|
|
31
|
+
removeBlocker: (taskId, blockerId) => depRepo.remove(blockerId, taskId)
|
|
32
|
+
};
|
|
33
|
+
}));
|
|
34
|
+
//# sourceMappingURL=dep-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dep-service.js","sourceRoot":"","sources":["../../src/services/dep-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,iBAAiB,EAAiB,MAAM,cAAc,CAAA;AAGzG,MAAM,OAAO,iBAAkB,SAAQ,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAMpE;CAAG;AAEN,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC,MAAM,CAC/C,iBAAiB,EACjB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAA;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAA;IAEtC,OAAO;QACL,UAAU,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAChC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC,CAAC,CAAA;YAC1F,CAAC;YAED,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;YAClE,CAAC;YAED,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;YACnD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAA;YACrE,CAAC;YAED,4EAA4E;YAC5E,sDAAsD;YACtD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;YAC5D,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,uBAAuB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAA;YAC/E,CAAC;YAED,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAC1C,CAAC,CAAC;QAEJ,aAAa,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CACnC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC;KACpC,CAAA;AACH,CAAC,CAAC,CACH,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { TaskRepository } from "../repo/task-repo.js";
|
|
3
|
+
import { TaskNotFoundError } from "../errors.js";
|
|
4
|
+
export class HierarchyService extends Context.Tag("HierarchyService")() {
|
|
5
|
+
}
|
|
6
|
+
export const HierarchyServiceLive = Layer.effect(HierarchyService, Effect.gen(function* () {
|
|
7
|
+
const taskRepo = yield* TaskRepository;
|
|
8
|
+
const buildTree = (task) => Effect.gen(function* () {
|
|
9
|
+
const childTasks = yield* taskRepo.findByParent(task.id);
|
|
10
|
+
const childTrees = [];
|
|
11
|
+
for (const child of childTasks) {
|
|
12
|
+
childTrees.push(yield* buildTree(child));
|
|
13
|
+
}
|
|
14
|
+
return { task, children: childTrees };
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
getChildren: (id) => Effect.gen(function* () {
|
|
18
|
+
const task = yield* taskRepo.findById(id);
|
|
19
|
+
if (!task) {
|
|
20
|
+
return yield* Effect.fail(new TaskNotFoundError({ id }));
|
|
21
|
+
}
|
|
22
|
+
return yield* taskRepo.findByParent(id);
|
|
23
|
+
}),
|
|
24
|
+
getAncestors: (id) => Effect.gen(function* () {
|
|
25
|
+
const task = yield* taskRepo.findById(id);
|
|
26
|
+
if (!task) {
|
|
27
|
+
return yield* Effect.fail(new TaskNotFoundError({ id }));
|
|
28
|
+
}
|
|
29
|
+
const ancestors = [];
|
|
30
|
+
let currentId = task.parentId;
|
|
31
|
+
while (currentId !== null) {
|
|
32
|
+
const parent = yield* taskRepo.findById(currentId);
|
|
33
|
+
if (!parent)
|
|
34
|
+
break;
|
|
35
|
+
ancestors.push(parent);
|
|
36
|
+
currentId = parent.parentId;
|
|
37
|
+
}
|
|
38
|
+
return ancestors;
|
|
39
|
+
}),
|
|
40
|
+
getTree: (id) => Effect.gen(function* () {
|
|
41
|
+
const task = yield* taskRepo.findById(id);
|
|
42
|
+
if (!task) {
|
|
43
|
+
return yield* Effect.fail(new TaskNotFoundError({ id }));
|
|
44
|
+
}
|
|
45
|
+
return yield* buildTree(task);
|
|
46
|
+
}),
|
|
47
|
+
getDepth: (id) => Effect.gen(function* () {
|
|
48
|
+
const task = yield* taskRepo.findById(id);
|
|
49
|
+
if (!task) {
|
|
50
|
+
return yield* Effect.fail(new TaskNotFoundError({ id }));
|
|
51
|
+
}
|
|
52
|
+
let depth = 0;
|
|
53
|
+
let currentId = task.parentId;
|
|
54
|
+
while (currentId !== null) {
|
|
55
|
+
const parent = yield* taskRepo.findById(currentId);
|
|
56
|
+
if (!parent)
|
|
57
|
+
break;
|
|
58
|
+
depth++;
|
|
59
|
+
currentId = parent.parentId;
|
|
60
|
+
}
|
|
61
|
+
return depth;
|
|
62
|
+
}),
|
|
63
|
+
getRoots: () => taskRepo.findByParent(null)
|
|
64
|
+
};
|
|
65
|
+
}));
|
|
66
|
+
//# sourceMappingURL=hierarchy-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hierarchy-service.js","sourceRoot":"","sources":["../../src/services/hierarchy-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { TaskRepository } from "../repo/task-repo.js";
|
|
3
|
+
import { DependencyRepository } from "../repo/dep-repo.js";
|
|
4
|
+
export class ReadyService extends Context.Tag("ReadyService")() {
|
|
5
|
+
}
|
|
6
|
+
export const ReadyServiceLive = Layer.effect(ReadyService, Effect.gen(function* () {
|
|
7
|
+
const taskRepo = yield* TaskRepository;
|
|
8
|
+
const depRepo = yield* DependencyRepository;
|
|
9
|
+
return {
|
|
10
|
+
getReady: (limit = 100) => Effect.gen(function* () {
|
|
11
|
+
const candidates = yield* taskRepo.findAll({
|
|
12
|
+
status: ["backlog", "ready", "planning"]
|
|
13
|
+
});
|
|
14
|
+
const ready = [];
|
|
15
|
+
for (const task of candidates) {
|
|
16
|
+
const blockerIds = yield* depRepo.getBlockerIds(task.id);
|
|
17
|
+
const blockingIds = yield* depRepo.getBlockingIds(task.id);
|
|
18
|
+
const childIds = yield* taskRepo.getChildIds(task.id);
|
|
19
|
+
if (blockerIds.length === 0) {
|
|
20
|
+
ready.push({
|
|
21
|
+
...task,
|
|
22
|
+
blockedBy: [],
|
|
23
|
+
blocks: blockingIds,
|
|
24
|
+
children: childIds,
|
|
25
|
+
isReady: true
|
|
26
|
+
});
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const blockers = yield* taskRepo.findByIds(blockerIds);
|
|
30
|
+
const allDone = blockers.every(b => b.status === "done");
|
|
31
|
+
if (allDone) {
|
|
32
|
+
ready.push({
|
|
33
|
+
...task,
|
|
34
|
+
blockedBy: blockerIds,
|
|
35
|
+
blocks: blockingIds,
|
|
36
|
+
children: childIds,
|
|
37
|
+
isReady: true
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
ready.sort((a, b) => b.score - a.score);
|
|
42
|
+
return ready.slice(0, limit);
|
|
43
|
+
}),
|
|
44
|
+
isReady: (id) => Effect.gen(function* () {
|
|
45
|
+
const task = yield* taskRepo.findById(id);
|
|
46
|
+
if (!task)
|
|
47
|
+
return false;
|
|
48
|
+
if (!["backlog", "ready", "planning"].includes(task.status))
|
|
49
|
+
return false;
|
|
50
|
+
const blockerIds = yield* depRepo.getBlockerIds(id);
|
|
51
|
+
if (blockerIds.length === 0)
|
|
52
|
+
return true;
|
|
53
|
+
const blockers = yield* taskRepo.findByIds(blockerIds);
|
|
54
|
+
return blockers.every(b => b.status === "done");
|
|
55
|
+
}),
|
|
56
|
+
getBlockers: (id) => Effect.gen(function* () {
|
|
57
|
+
const blockerIds = yield* depRepo.getBlockerIds(id);
|
|
58
|
+
if (blockerIds.length === 0)
|
|
59
|
+
return [];
|
|
60
|
+
return yield* taskRepo.findByIds(blockerIds);
|
|
61
|
+
}),
|
|
62
|
+
getBlocking: (id) => Effect.gen(function* () {
|
|
63
|
+
const blockingIds = yield* depRepo.getBlockingIds(id);
|
|
64
|
+
if (blockingIds.length === 0)
|
|
65
|
+
return [];
|
|
66
|
+
return yield* taskRepo.findByIds(blockingIds);
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
}));
|
|
70
|
+
//# sourceMappingURL=ready-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ready-service.js","sourceRoot":"","sources":["../../src/services/ready-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { TaskRepository } from "../repo/task-repo.js";
|
|
3
|
+
import { DependencyRepository } from "../repo/dep-repo.js";
|
|
4
|
+
import { TaskNotFoundError } from "../errors.js";
|
|
5
|
+
import { HierarchyService } from "./hierarchy-service.js";
|
|
6
|
+
export class ScoreService extends Context.Tag("ScoreService")() {
|
|
7
|
+
}
|
|
8
|
+
export const ScoreServiceLive = Layer.effect(ScoreService, Effect.gen(function* () {
|
|
9
|
+
const taskRepo = yield* TaskRepository;
|
|
10
|
+
const depRepo = yield* DependencyRepository;
|
|
11
|
+
const hierarchySvc = yield* HierarchyService;
|
|
12
|
+
const computeBreakdown = (task, blockingCount, depth) => {
|
|
13
|
+
// Base score from DB
|
|
14
|
+
const baseScore = task.score;
|
|
15
|
+
// Blocking bonus: +25 per task this task blocks
|
|
16
|
+
const blockingBonus = blockingCount * 25;
|
|
17
|
+
// Age bonus: old tasks shouldn't rot
|
|
18
|
+
const ageMs = Date.now() - task.createdAt.getTime();
|
|
19
|
+
const ageHours = ageMs / (1000 * 60 * 60);
|
|
20
|
+
let ageBonus = 0;
|
|
21
|
+
if (ageHours > 48) {
|
|
22
|
+
ageBonus = 100;
|
|
23
|
+
}
|
|
24
|
+
else if (ageHours > 24) {
|
|
25
|
+
ageBonus = 50;
|
|
26
|
+
}
|
|
27
|
+
// Depth penalty: prefer root tasks over deep subtasks
|
|
28
|
+
const depthPenalty = depth * 10;
|
|
29
|
+
// Blocked status penalty: blocked tasks should not be prioritized
|
|
30
|
+
const blockedPenalty = task.status === "blocked" ? 1000 : 0;
|
|
31
|
+
// Final calculation
|
|
32
|
+
const finalScore = baseScore + blockingBonus + ageBonus - depthPenalty - blockedPenalty;
|
|
33
|
+
return {
|
|
34
|
+
baseScore,
|
|
35
|
+
blockingBonus,
|
|
36
|
+
blockingCount,
|
|
37
|
+
ageBonus,
|
|
38
|
+
ageHours: Math.floor(ageHours),
|
|
39
|
+
depthPenalty,
|
|
40
|
+
depth,
|
|
41
|
+
blockedPenalty,
|
|
42
|
+
finalScore
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const getTaskContext = (task) => Effect.gen(function* () {
|
|
46
|
+
// Get how many tasks this task blocks
|
|
47
|
+
const blockingIds = yield* depRepo.getBlockingIds(task.id);
|
|
48
|
+
const blockingCount = blockingIds.length;
|
|
49
|
+
// Get depth in hierarchy
|
|
50
|
+
const depth = yield* hierarchySvc.getDepth(task.id).pipe(Effect.catchTag("TaskNotFoundError", () => Effect.succeed(0)));
|
|
51
|
+
return { blockingCount, depth };
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
calculate: (task) => Effect.gen(function* () {
|
|
55
|
+
const ctx = yield* getTaskContext(task);
|
|
56
|
+
const breakdown = computeBreakdown(task, ctx.blockingCount, ctx.depth);
|
|
57
|
+
return breakdown.finalScore;
|
|
58
|
+
}),
|
|
59
|
+
calculateById: (id) => Effect.gen(function* () {
|
|
60
|
+
const task = yield* taskRepo.findById(id);
|
|
61
|
+
if (!task) {
|
|
62
|
+
return yield* Effect.fail(new TaskNotFoundError({ id }));
|
|
63
|
+
}
|
|
64
|
+
const ctx = yield* getTaskContext(task);
|
|
65
|
+
const breakdown = computeBreakdown(task, ctx.blockingCount, ctx.depth);
|
|
66
|
+
return breakdown.finalScore;
|
|
67
|
+
}),
|
|
68
|
+
getBreakdown: (task) => Effect.gen(function* () {
|
|
69
|
+
const ctx = yield* getTaskContext(task);
|
|
70
|
+
return computeBreakdown(task, ctx.blockingCount, ctx.depth);
|
|
71
|
+
}),
|
|
72
|
+
getBreakdownById: (id) => Effect.gen(function* () {
|
|
73
|
+
const task = yield* taskRepo.findById(id);
|
|
74
|
+
if (!task) {
|
|
75
|
+
return yield* Effect.fail(new TaskNotFoundError({ id }));
|
|
76
|
+
}
|
|
77
|
+
const ctx = yield* getTaskContext(task);
|
|
78
|
+
return computeBreakdown(task, ctx.blockingCount, ctx.depth);
|
|
79
|
+
})
|
|
80
|
+
};
|
|
81
|
+
}));
|
|
82
|
+
//# sourceMappingURL=score-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score-service.js","sourceRoot":"","sources":["../../src/services/score-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAiB,MAAM,cAAc,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAkBzD,MAAM,OAAO,YAAa,SAAQ,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAuB1D;CAAG;AAEN,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAC1C,YAAY,EACZ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAA;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAA;IAC3C,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAA;IAE5C,MAAM,gBAAgB,GAAG,CACvB,IAAU,EACV,aAAqB,EACrB,KAAa,EACG,EAAE;QAClB,qBAAqB;QACrB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAA;QAE5B,gDAAgD;QAChD,MAAM,aAAa,GAAG,aAAa,GAAG,EAAE,CAAA;QAExC,qCAAqC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAA;QACnD,MAAM,QAAQ,GAAG,KAAK,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;QACzC,IAAI,QAAQ,GAAG,CAAC,CAAA;QAChB,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;YAClB,QAAQ,GAAG,GAAG,CAAA;QAChB,CAAC;aAAM,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;YACzB,QAAQ,GAAG,EAAE,CAAA;QACf,CAAC;QAED,sDAAsD;QACtD,MAAM,YAAY,GAAG,KAAK,GAAG,EAAE,CAAA;QAE/B,kEAAkE;QAClE,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAE3D,oBAAoB;QACpB,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,YAAY,GAAG,cAAc,CAAA;QAEvF,OAAO;YACL,SAAS;YACT,aAAa;YACb,aAAa;YACb,QAAQ;YACR,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC9B,YAAY;YACZ,KAAK;YACL,cAAc;YACd,UAAU;SACX,CAAA;IACH,CAAC,CAAA;IAED,MAAM,cAAc,GAAG,CAAC,IAAU,EAAE,EAAE,CACpC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,sCAAsC;QACtC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC1D,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAA;QAExC,yBAAyB;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CACtD,MAAM,CAAC,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAC9D,CAAA;QAED,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAA;IACjC,CAAC,CAAC,CAAA;IAEJ,OAAO;QACL,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAClB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;YACvC,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;YACtE,OAAO,SAAS,CAAC,UAAU,CAAA;QAC7B,CAAC,CAAC;QAEJ,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,CACpB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;YACzC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;YAC1D,CAAC;YACD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;YACvC,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;YACtE,OAAO,SAAS,CAAC,UAAU,CAAA;QAC7B,CAAC,CAAC;QAEJ,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CACrB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;YACvC,OAAO,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;QAC7D,CAAC,CAAC;QAEJ,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE,CACvB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;YACzC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;YAC1D,CAAC;YACD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;YACvC,OAAO,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;QAC7D,CAAC,CAAC;KACL,CAAA;AACH,CAAC,CAAC,CACH,CAAA"}
|