@sascha384/tic 1.34.0 → 2.0.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/.claude-plugin/plugin.json +1 -1
- package/dist/backends/availability.js +4 -2
- package/dist/backends/availability.js.map +1 -1
- package/dist/backends/factory.d.ts +5 -3
- package/dist/backends/factory.js +28 -43
- package/dist/backends/factory.js.map +1 -1
- package/dist/backends/files/hash.d.ts +1 -0
- package/dist/backends/files/hash.js +5 -0
- package/dist/backends/files/hash.js.map +1 -0
- package/dist/backends/files/index.d.ts +48 -0
- package/dist/backends/files/index.js +174 -0
- package/dist/backends/files/index.js.map +1 -0
- package/dist/backends/files/sync.d.ts +13 -0
- package/dist/backends/files/sync.js +69 -0
- package/dist/backends/files/sync.js.map +1 -0
- package/dist/backends/jira/config.d.ts +1 -1
- package/dist/backends/jira/config.js +6 -9
- package/dist/backends/jira/config.js.map +1 -1
- package/dist/backends/types.d.ts +12 -0
- package/dist/backends/types.js +5 -1
- package/dist/backends/types.js.map +1 -1
- package/dist/cli/commands/config.js +27 -14
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/init.js +10 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts +4 -4
- package/dist/cli/commands/mcp.js +16 -25
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/index.js +16 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/commands.d.ts +2 -0
- package/dist/commands.js +33 -0
- package/dist/commands.js.map +1 -1
- package/dist/components/Header.js +9 -2
- package/dist/components/Header.js.map +1 -1
- package/dist/components/HelpScreen.js +3 -0
- package/dist/components/HelpScreen.js.map +1 -1
- package/dist/components/OverlayPanel.d.ts +2 -1
- package/dist/components/OverlayPanel.js +14 -1
- package/dist/components/OverlayPanel.js.map +1 -1
- package/dist/components/Settings.js +6 -11
- package/dist/components/Settings.js.map +1 -1
- package/dist/components/StatusScreen.js +29 -4
- package/dist/components/StatusScreen.js.map +1 -1
- package/dist/components/WorkItemForm.js +6 -9
- package/dist/components/WorkItemForm.js.map +1 -1
- package/dist/components/WorkItemList.js +353 -36
- package/dist/components/WorkItemList.js.map +1 -1
- package/dist/filters.d.ts +28 -0
- package/dist/filters.js +47 -0
- package/dist/filters.js.map +1 -0
- package/dist/implement.js +4 -56
- package/dist/implement.js.map +1 -1
- package/dist/index.js +20 -8
- package/dist/index.js.map +1 -1
- package/dist/storage/config.d.ts +61 -0
- package/dist/storage/config.js +309 -0
- package/dist/storage/config.js.map +1 -0
- package/dist/storage/db.d.ts +11 -0
- package/dist/storage/db.js +34 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/index.d.ts +73 -0
- package/dist/storage/index.js +966 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/mappers.d.ts +35 -0
- package/dist/storage/mappers.js +70 -0
- package/dist/storage/mappers.js.map +1 -0
- package/dist/storage/schema.d.ts +1844 -0
- package/dist/storage/schema.js +197 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/syncQueue.d.ts +13 -0
- package/dist/storage/syncQueue.js +98 -0
- package/dist/storage/syncQueue.js.map +1 -0
- package/dist/storage/undo.d.ts +22 -0
- package/dist/storage/undo.js +129 -0
- package/dist/storage/undo.js.map +1 -0
- package/dist/stores/backendDataStore.d.ts +4 -1
- package/dist/stores/backendDataStore.js +61 -40
- package/dist/stores/backendDataStore.js.map +1 -1
- package/dist/stores/configStore.d.ts +3 -1
- package/dist/stores/configStore.js +25 -65
- package/dist/stores/configStore.js.map +1 -1
- package/dist/stores/filterStore.d.ts +13 -0
- package/dist/stores/filterStore.js +38 -0
- package/dist/stores/filterStore.js.map +1 -0
- package/dist/stores/listViewStore.d.ts +1 -0
- package/dist/stores/listViewStore.js +1 -0
- package/dist/stores/listViewStore.js.map +1 -1
- package/dist/stores/uiStore.d.ts +8 -0
- package/dist/stores/uiStore.js.map +1 -1
- package/dist/stores/undoStore.d.ts +4 -0
- package/dist/stores/undoStore.js +32 -0
- package/dist/stores/undoStore.js.map +1 -1
- package/dist/sync/SyncManager.d.ts +5 -4
- package/dist/sync/SyncManager.js +129 -36
- package/dist/sync/SyncManager.js.map +1 -1
- package/dist/sync/types.d.ts +25 -1
- package/package.json +5 -1
- package/dist/backends/local/config.d.ts +0 -23
- package/dist/backends/local/config.js +0 -42
- package/dist/backends/local/config.js.map +0 -1
- package/dist/backends/local/index.d.ts +0 -45
- package/dist/backends/local/index.js +0 -291
- package/dist/backends/local/index.js.map +0 -1
- package/dist/sync/queue.d.ts +0 -12
- package/dist/sync/queue.js +0 -56
- package/dist/sync/queue.js.map +0 -1
|
@@ -3,7 +3,8 @@ import { execFile } from 'node:child_process';
|
|
|
3
3
|
* Maps each backend to the CLI binary it requires, or null if no CLI is needed.
|
|
4
4
|
*/
|
|
5
5
|
export const BACKEND_CLI = {
|
|
6
|
-
|
|
6
|
+
none: null,
|
|
7
|
+
filesystem: null,
|
|
7
8
|
github: 'gh',
|
|
8
9
|
gitlab: 'glab',
|
|
9
10
|
azure: 'az',
|
|
@@ -29,7 +30,8 @@ export async function checkBackendAvailability(backend) {
|
|
|
29
30
|
*/
|
|
30
31
|
export async function checkAllBackendAvailability() {
|
|
31
32
|
const backends = [
|
|
32
|
-
'
|
|
33
|
+
'none',
|
|
34
|
+
'filesystem',
|
|
33
35
|
'github',
|
|
34
36
|
'gitlab',
|
|
35
37
|
'azure',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"availability.js","sourceRoot":"","sources":["../../src/backends/availability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAuC;IAC7D,
|
|
1
|
+
{"version":3,"file":"availability.js","sourceRoot":"","sources":["../../src/backends/availability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAuC;IAC7D,IAAI,EAAE,IAAI;IACV,UAAU,EAAE,IAAI;IAChB,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,IAAI;IACX,IAAI,EAAE,IAAI;CACX,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAoB;IAEpB,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,QAAQ,CACpB,MAAM,EACN,CAAC,WAAW,CAAC,EACb,EAAE,OAAO,EAAE,IAAI,EAAE,EACjB,CAAC,KAAK,EAAE,EAAE;YACR,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CACF,CAAC;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAG/C,MAAM,QAAQ,GAAkB;QAC9B,MAAM;QACN,YAAY;QACZ,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,MAAM;KACP,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,wBAAwB,CAAC,CAAC,CAAC,CAAU,CAAC,CAC3E,CAAC;IACF,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAiC,CAAC;AACrE,CAAC"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Backend } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { SyncQueueAdapter } from '../sync/types.js';
|
|
3
3
|
import { SyncManager } from '../sync/SyncManager.js';
|
|
4
|
-
export declare const VALID_BACKENDS: readonly ["
|
|
4
|
+
export declare const VALID_BACKENDS: readonly ["none", "filesystem", "github", "gitlab", "azure", "jira"];
|
|
5
5
|
export type BackendType = (typeof VALID_BACKENDS)[number];
|
|
6
6
|
export declare function detectBackend(root: string): BackendType;
|
|
7
7
|
export declare function createBackend(root: string): Promise<Backend>;
|
|
8
|
+
export declare function createRemoteBackend(root: string, backendType: string): Promise<Backend | null>;
|
|
8
9
|
export interface BackendSetup {
|
|
9
|
-
backend:
|
|
10
|
+
backend: Backend;
|
|
10
11
|
syncManager: SyncManager | null;
|
|
12
|
+
queue: SyncQueueAdapter | null;
|
|
11
13
|
}
|
|
12
14
|
export declare function createBackendWithSync(root: string): Promise<BackendSetup>;
|
package/dist/backends/factory.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import {
|
|
2
|
+
import { Storage } from '../storage/index.js';
|
|
3
|
+
import { SyncQueue } from '../storage/syncQueue.js';
|
|
3
4
|
import { configStore } from '../stores/configStore.js';
|
|
4
5
|
import { SyncManager } from '../sync/SyncManager.js';
|
|
5
|
-
import { SyncQueueStore } from '../sync/queue.js';
|
|
6
6
|
export const VALID_BACKENDS = [
|
|
7
|
-
'
|
|
7
|
+
'none',
|
|
8
|
+
'filesystem',
|
|
8
9
|
'github',
|
|
9
10
|
'gitlab',
|
|
10
11
|
'azure',
|
|
@@ -29,16 +30,24 @@ export function detectBackend(root) {
|
|
|
29
30
|
catch {
|
|
30
31
|
// Not a git repo or git not available
|
|
31
32
|
}
|
|
32
|
-
return '
|
|
33
|
+
return 'none';
|
|
33
34
|
}
|
|
34
35
|
export async function createBackend(root) {
|
|
36
|
+
const primary = Storage.create(root);
|
|
37
|
+
configStore.getState().setDatabase(primary.getDatabase());
|
|
35
38
|
if (!configStore.getState().loaded) {
|
|
36
39
|
await configStore.getState().init(root);
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
return primary;
|
|
42
|
+
}
|
|
43
|
+
export async function createRemoteBackend(root, backendType) {
|
|
44
|
+
switch (backendType) {
|
|
45
|
+
case 'none':
|
|
46
|
+
return null;
|
|
47
|
+
case 'filesystem': {
|
|
48
|
+
const { FilesBackend } = await import('./files/index.js');
|
|
49
|
+
return new FilesBackend(root);
|
|
50
|
+
}
|
|
42
51
|
case 'github': {
|
|
43
52
|
const { GitHubBackend } = await import('./github/index.js');
|
|
44
53
|
return new GitHubBackend(root);
|
|
@@ -56,47 +65,23 @@ export async function createBackend(root) {
|
|
|
56
65
|
return JiraBackend.create(root);
|
|
57
66
|
}
|
|
58
67
|
default:
|
|
59
|
-
|
|
68
|
+
return null;
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
export async function createBackendWithSync(root) {
|
|
72
|
+
const primary = Storage.create(root);
|
|
73
|
+
configStore.getState().setDatabase(primary.getDatabase());
|
|
63
74
|
if (!configStore.getState().loaded) {
|
|
64
75
|
await configStore.getState().init(root);
|
|
65
76
|
}
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let remote;
|
|
74
|
-
switch (backendType) {
|
|
75
|
-
case 'github': {
|
|
76
|
-
const { GitHubBackend } = await import('./github/index.js');
|
|
77
|
-
remote = new GitHubBackend(root);
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
case 'gitlab': {
|
|
81
|
-
const { GitLabBackend } = await import('./gitlab/index.js');
|
|
82
|
-
remote = new GitLabBackend(root);
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
case 'azure': {
|
|
86
|
-
const { AzureDevOpsBackend } = await import('./ado/index.js');
|
|
87
|
-
remote = new AzureDevOpsBackend(root);
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
case 'jira': {
|
|
91
|
-
const { JiraBackend } = await import('./jira/index.js');
|
|
92
|
-
remote = await JiraBackend.create(root);
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
default:
|
|
96
|
-
throw new Error(`Unknown backend "${backendType}". Valid backends: ${VALID_BACKENDS.join(', ')}`);
|
|
77
|
+
const config = configStore.getState().config;
|
|
78
|
+
const remote = await createRemoteBackend(root, config.backend ?? 'none');
|
|
79
|
+
let syncManager = null;
|
|
80
|
+
let queue = null;
|
|
81
|
+
if (remote) {
|
|
82
|
+
queue = new SyncQueue(primary.getDatabase());
|
|
83
|
+
syncManager = new SyncManager(primary, remote, queue);
|
|
97
84
|
}
|
|
98
|
-
|
|
99
|
-
const syncManager = new SyncManager(local, remote, queueStore);
|
|
100
|
-
return { backend: local, syncManager };
|
|
85
|
+
return { backend: primary, syncManager, queue };
|
|
101
86
|
}
|
|
102
87
|
//# sourceMappingURL=factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/backends/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/backends/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function contentHash(content: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../../src/backends/files/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BaseBackend } from '../types.js';
|
|
2
|
+
import type { BackendCapabilities, SyncableBackend } from '../types.js';
|
|
3
|
+
import type { WorkItem, NewWorkItem, NewComment, Comment, Template } from '../../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* FilesBackend is a filesystem-based sync destination.
|
|
6
|
+
*
|
|
7
|
+
* It delegates all I/O to the existing local/items.ts and local/templates.ts
|
|
8
|
+
* modules. Unlike LocalBackend, it does NOT:
|
|
9
|
+
* - Cache items (caching is the primary backend's job)
|
|
10
|
+
* - Validate relationships (that's the primary backend's job)
|
|
11
|
+
* - Manage config (config lives in the primary/SQLite)
|
|
12
|
+
* - Manage next_id (IDs come from the primary)
|
|
13
|
+
* - Support soft-delete (that's a primary backend concept)
|
|
14
|
+
*
|
|
15
|
+
* It implements SyncableBackend so it can be used as a sync destination
|
|
16
|
+
* with ID-preserving imports via importWorkItem.
|
|
17
|
+
*/
|
|
18
|
+
export declare class FilesBackend extends BaseBackend implements SyncableBackend {
|
|
19
|
+
private root;
|
|
20
|
+
constructor(root: string);
|
|
21
|
+
getRoot(): string;
|
|
22
|
+
getCapabilities(): BackendCapabilities;
|
|
23
|
+
getStatuses(): Promise<string[]>;
|
|
24
|
+
getIterations(): Promise<string[]>;
|
|
25
|
+
getWorkItemTypes(): Promise<string[]>;
|
|
26
|
+
getAssignees(): Promise<string[]>;
|
|
27
|
+
getLabels(): Promise<string[]>;
|
|
28
|
+
getCurrentIteration(): Promise<string>;
|
|
29
|
+
setCurrentIteration(_name: string): Promise<void>;
|
|
30
|
+
listWorkItems(_iteration?: string): Promise<WorkItem[]>;
|
|
31
|
+
getWorkItem(id: string): Promise<WorkItem>;
|
|
32
|
+
createWorkItem(_data: NewWorkItem): Promise<WorkItem>;
|
|
33
|
+
updateWorkItem(id: string, data: Partial<WorkItem>): Promise<WorkItem>;
|
|
34
|
+
deleteWorkItem(id: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Write a complete WorkItem preserving its existing ID.
|
|
37
|
+
* Used during sync to replicate items from primary to files.
|
|
38
|
+
*/
|
|
39
|
+
importWorkItem(item: WorkItem): Promise<WorkItem>;
|
|
40
|
+
addComment(workItemId: string, comment: NewComment): Promise<Comment>;
|
|
41
|
+
getItemUrl(id: string): string;
|
|
42
|
+
openItem(_id: string): Promise<void>;
|
|
43
|
+
listTemplates(): Promise<Template[]>;
|
|
44
|
+
getTemplate(slug: string): Promise<Template>;
|
|
45
|
+
createTemplate(template: Template): Promise<Template>;
|
|
46
|
+
updateTemplate(oldSlug: string, template: Template): Promise<Template>;
|
|
47
|
+
deleteTemplate(slug: string): Promise<void>;
|
|
48
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { BaseBackend, UnsupportedOperationError } from '../types.js';
|
|
4
|
+
import { readWorkItem, writeWorkItem, deleteWorkItem as removeWorkItemFile, listItemFiles, parseWorkItemFile, } from '../local/items.js';
|
|
5
|
+
import { listTemplates as listTemplateFiles, readTemplate, writeTemplate, deleteTemplate as removeTemplateFile, slugifyTemplateName, } from '../local/templates.js';
|
|
6
|
+
/**
|
|
7
|
+
* FilesBackend is a filesystem-based sync destination.
|
|
8
|
+
*
|
|
9
|
+
* It delegates all I/O to the existing local/items.ts and local/templates.ts
|
|
10
|
+
* modules. Unlike LocalBackend, it does NOT:
|
|
11
|
+
* - Cache items (caching is the primary backend's job)
|
|
12
|
+
* - Validate relationships (that's the primary backend's job)
|
|
13
|
+
* - Manage config (config lives in the primary/SQLite)
|
|
14
|
+
* - Manage next_id (IDs come from the primary)
|
|
15
|
+
* - Support soft-delete (that's a primary backend concept)
|
|
16
|
+
*
|
|
17
|
+
* It implements SyncableBackend so it can be used as a sync destination
|
|
18
|
+
* with ID-preserving imports via importWorkItem.
|
|
19
|
+
*/
|
|
20
|
+
export class FilesBackend extends BaseBackend {
|
|
21
|
+
root;
|
|
22
|
+
constructor(root) {
|
|
23
|
+
super(0); // no TTL cache
|
|
24
|
+
this.root = root;
|
|
25
|
+
}
|
|
26
|
+
getRoot() {
|
|
27
|
+
return this.root;
|
|
28
|
+
}
|
|
29
|
+
getCapabilities() {
|
|
30
|
+
return {
|
|
31
|
+
relationships: true,
|
|
32
|
+
customTypes: true,
|
|
33
|
+
customStatuses: true,
|
|
34
|
+
iterations: true,
|
|
35
|
+
comments: true,
|
|
36
|
+
fields: {
|
|
37
|
+
priority: true,
|
|
38
|
+
assignee: true,
|
|
39
|
+
labels: true,
|
|
40
|
+
parent: true,
|
|
41
|
+
dependsOn: true,
|
|
42
|
+
},
|
|
43
|
+
templates: true,
|
|
44
|
+
templateFields: {
|
|
45
|
+
type: true,
|
|
46
|
+
status: true,
|
|
47
|
+
priority: true,
|
|
48
|
+
assignee: true,
|
|
49
|
+
labels: true,
|
|
50
|
+
iteration: true,
|
|
51
|
+
parent: true,
|
|
52
|
+
dependsOn: true,
|
|
53
|
+
description: true,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// --- Metadata methods: return empty since metadata is the primary's concern ---
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
59
|
+
async getStatuses() {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
63
|
+
async getIterations() {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
67
|
+
async getWorkItemTypes() {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
71
|
+
async getAssignees() {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
75
|
+
async getLabels() {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
79
|
+
async getCurrentIteration() {
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
83
|
+
async setCurrentIteration(_name) {
|
|
84
|
+
// no-op: iterations are managed by the primary backend
|
|
85
|
+
}
|
|
86
|
+
// --- Work item CRUD ---
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
88
|
+
async listWorkItems(_iteration) {
|
|
89
|
+
const files = await listItemFiles(this.root);
|
|
90
|
+
const items = await Promise.all(files.map(async (f) => {
|
|
91
|
+
const raw = await fs.readFile(f, 'utf-8');
|
|
92
|
+
return parseWorkItemFile(raw);
|
|
93
|
+
}));
|
|
94
|
+
// No iteration filtering — that's the primary's job
|
|
95
|
+
return items;
|
|
96
|
+
}
|
|
97
|
+
async getWorkItem(id) {
|
|
98
|
+
return readWorkItem(this.root, id);
|
|
99
|
+
}
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/require-await
|
|
101
|
+
async createWorkItem(_data) {
|
|
102
|
+
throw new UnsupportedOperationError('createWorkItem', 'FilesBackend');
|
|
103
|
+
}
|
|
104
|
+
async updateWorkItem(id, data) {
|
|
105
|
+
const existing = await this.getWorkItem(id);
|
|
106
|
+
const updated = {
|
|
107
|
+
...existing,
|
|
108
|
+
...data,
|
|
109
|
+
id, // preserve ID
|
|
110
|
+
updated: new Date().toISOString(),
|
|
111
|
+
};
|
|
112
|
+
await writeWorkItem(this.root, updated);
|
|
113
|
+
return updated;
|
|
114
|
+
}
|
|
115
|
+
async deleteWorkItem(id) {
|
|
116
|
+
await removeWorkItemFile(this.root, id);
|
|
117
|
+
// No relationship cleanup — that's the primary's job
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Write a complete WorkItem preserving its existing ID.
|
|
121
|
+
* Used during sync to replicate items from primary to files.
|
|
122
|
+
*/
|
|
123
|
+
async importWorkItem(item) {
|
|
124
|
+
await writeWorkItem(this.root, item);
|
|
125
|
+
return item;
|
|
126
|
+
}
|
|
127
|
+
// --- Comments ---
|
|
128
|
+
async addComment(workItemId, comment) {
|
|
129
|
+
const item = await this.getWorkItem(workItemId);
|
|
130
|
+
const newComment = {
|
|
131
|
+
author: comment.author,
|
|
132
|
+
date: new Date().toISOString(),
|
|
133
|
+
body: comment.body,
|
|
134
|
+
};
|
|
135
|
+
item.comments.push(newComment);
|
|
136
|
+
item.updated = new Date().toISOString();
|
|
137
|
+
await writeWorkItem(this.root, item);
|
|
138
|
+
return newComment;
|
|
139
|
+
}
|
|
140
|
+
// --- Item URL ---
|
|
141
|
+
getItemUrl(id) {
|
|
142
|
+
return path.resolve(this.root, '.tic', 'items', `${id}.md`);
|
|
143
|
+
}
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
145
|
+
async openItem(_id) {
|
|
146
|
+
// no-op: FilesBackend is a sync destination, not interactive
|
|
147
|
+
}
|
|
148
|
+
// --- Templates ---
|
|
149
|
+
async listTemplates() {
|
|
150
|
+
return listTemplateFiles(this.root);
|
|
151
|
+
}
|
|
152
|
+
async getTemplate(slug) {
|
|
153
|
+
return readTemplate(this.root, slug);
|
|
154
|
+
}
|
|
155
|
+
async createTemplate(template) {
|
|
156
|
+
const slug = slugifyTemplateName(template.name);
|
|
157
|
+
const t = { ...template, slug };
|
|
158
|
+
await writeTemplate(this.root, t);
|
|
159
|
+
return t;
|
|
160
|
+
}
|
|
161
|
+
async updateTemplate(oldSlug, template) {
|
|
162
|
+
const newSlug = slugifyTemplateName(template.name);
|
|
163
|
+
if (oldSlug !== newSlug) {
|
|
164
|
+
await removeTemplateFile(this.root, oldSlug);
|
|
165
|
+
}
|
|
166
|
+
const t = { ...template, slug: newSlug };
|
|
167
|
+
await writeTemplate(this.root, t);
|
|
168
|
+
return t;
|
|
169
|
+
}
|
|
170
|
+
async deleteTemplate(slug) {
|
|
171
|
+
await removeTemplateFile(this.root, slug);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/backends/files/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TicDatabase } from '../../storage/db.js';
|
|
2
|
+
/** Compute content hashes for all .tic/items/*.md files on disk. */
|
|
3
|
+
export declare function computeFileHashes(root: string): Promise<Map<string, string>>;
|
|
4
|
+
/** Detect which files changed, were added, or were deleted since last sync. */
|
|
5
|
+
export declare function detectChanges(db: TicDatabase, root: string): Promise<{
|
|
6
|
+
changed: string[];
|
|
7
|
+
added: string[];
|
|
8
|
+
deleted: string[];
|
|
9
|
+
}>;
|
|
10
|
+
/** Update the stored hash for an item (upsert). */
|
|
11
|
+
export declare function updateSyncState(db: TicDatabase, itemId: string, hash: string): void;
|
|
12
|
+
/** Remove the stored hash for an item. */
|
|
13
|
+
export declare function removeSyncState(db: TicDatabase, itemId: string): void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { eq } from 'drizzle-orm';
|
|
4
|
+
import * as s from '../../storage/schema.js';
|
|
5
|
+
import { listItemFiles } from '../local/items.js';
|
|
6
|
+
import { contentHash } from './hash.js';
|
|
7
|
+
/**
|
|
8
|
+
* Extract the item ID from a full file path like `/foo/.tic/items/42.md`.
|
|
9
|
+
*/
|
|
10
|
+
function idFromPath(filePath) {
|
|
11
|
+
return path.basename(filePath, '.md');
|
|
12
|
+
}
|
|
13
|
+
/** Compute content hashes for all .tic/items/*.md files on disk. */
|
|
14
|
+
export async function computeFileHashes(root) {
|
|
15
|
+
const files = await listItemFiles(root);
|
|
16
|
+
const hashes = new Map();
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const id = idFromPath(file);
|
|
19
|
+
const raw = await fs.readFile(file, 'utf-8');
|
|
20
|
+
hashes.set(id, contentHash(raw));
|
|
21
|
+
}
|
|
22
|
+
return hashes;
|
|
23
|
+
}
|
|
24
|
+
/** Detect which files changed, were added, or were deleted since last sync. */
|
|
25
|
+
export async function detectChanges(db, root) {
|
|
26
|
+
const currentHashes = await computeFileHashes(root);
|
|
27
|
+
// Get stored hashes from the file_sync_state table
|
|
28
|
+
const rows = db.select().from(s.fileSyncState).all();
|
|
29
|
+
const storedHashes = new Map();
|
|
30
|
+
for (const row of rows) {
|
|
31
|
+
storedHashes.set(row.itemId, row.hash);
|
|
32
|
+
}
|
|
33
|
+
const changed = [];
|
|
34
|
+
const added = [];
|
|
35
|
+
const deleted = [];
|
|
36
|
+
// Check current files against stored
|
|
37
|
+
for (const [id, hash] of currentHashes) {
|
|
38
|
+
const stored = storedHashes.get(id);
|
|
39
|
+
if (!stored) {
|
|
40
|
+
added.push(id);
|
|
41
|
+
}
|
|
42
|
+
else if (stored !== hash) {
|
|
43
|
+
changed.push(id);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Check stored against current for deletions
|
|
47
|
+
for (const [id] of storedHashes) {
|
|
48
|
+
if (!currentHashes.has(id)) {
|
|
49
|
+
deleted.push(id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { changed, added, deleted };
|
|
53
|
+
}
|
|
54
|
+
/** Update the stored hash for an item (upsert). */
|
|
55
|
+
export function updateSyncState(db, itemId, hash) {
|
|
56
|
+
const now = new Date().toISOString();
|
|
57
|
+
db.insert(s.fileSyncState)
|
|
58
|
+
.values({ itemId, hash, syncedAt: now })
|
|
59
|
+
.onConflictDoUpdate({
|
|
60
|
+
target: s.fileSyncState.itemId,
|
|
61
|
+
set: { hash, syncedAt: now },
|
|
62
|
+
})
|
|
63
|
+
.run();
|
|
64
|
+
}
|
|
65
|
+
/** Remove the stored hash for an item. */
|
|
66
|
+
export function removeSyncState(db, itemId) {
|
|
67
|
+
db.delete(s.fileSyncState).where(eq(s.fileSyncState.itemId, itemId)).run();
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../src/backends/files/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { configStore } from '../../stores/configStore.js';
|
|
2
|
-
|
|
3
|
-
export async function readJiraConfig(
|
|
4
|
-
|
|
5
|
-
const config = configStore.getState().loaded
|
|
6
|
-
? configStore.getState().config
|
|
7
|
-
: await readConfig(root);
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
|
|
3
|
+
export async function readJiraConfig(_root) {
|
|
4
|
+
const config = configStore.getState().config;
|
|
8
5
|
if (!config.jira) {
|
|
9
|
-
throw new Error('Jira backend requires "jira" configuration
|
|
6
|
+
throw new Error('Jira backend requires "jira" configuration. Set it via the Settings screen or "tic config".');
|
|
10
7
|
}
|
|
11
8
|
if (!config.jira.site) {
|
|
12
|
-
throw new Error('Jira backend requires "jira.site"
|
|
9
|
+
throw new Error('Jira backend requires "jira.site". Set it via the Settings screen or "tic config".');
|
|
13
10
|
}
|
|
14
11
|
if (!config.jira.project) {
|
|
15
|
-
throw new Error('Jira backend requires "jira.project"
|
|
12
|
+
throw new Error('Jira backend requires "jira.project". Set it via the Settings screen or "tic config".');
|
|
16
13
|
}
|
|
17
14
|
return config.jira;
|
|
18
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/backends/jira/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/backends/jira/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAQ1D,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,uFAAuF,CACxF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|
package/dist/backends/types.d.ts
CHANGED
|
@@ -57,10 +57,22 @@ export interface Backend {
|
|
|
57
57
|
updateTemplate(oldSlug: string, template: Template): Promise<Template>;
|
|
58
58
|
deleteTemplate(slug: string): Promise<void>;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Backend that supports importing work items with a specific ID.
|
|
62
|
+
* Used by SyncManager to write remote items to the primary backend
|
|
63
|
+
* preserving their original IDs, and to rename local items when
|
|
64
|
+
* the remote assigns a different ID during push.
|
|
65
|
+
*/
|
|
66
|
+
export interface SyncableBackend extends Backend {
|
|
67
|
+
/** Write a complete WorkItem preserving its existing id. */
|
|
68
|
+
importWorkItem(item: WorkItem): Promise<WorkItem>;
|
|
69
|
+
}
|
|
70
|
+
export declare function isSyncableBackend(backend: Backend): backend is SyncableBackend;
|
|
60
71
|
export interface SoftDeleteBackend extends Backend {
|
|
61
72
|
softDeleteWorkItem(id: string): Promise<void>;
|
|
62
73
|
restoreWorkItem(id: string): Promise<void>;
|
|
63
74
|
permanentlyDeleteWorkItem(id: string): Promise<void>;
|
|
75
|
+
cleanupTrash(): Promise<void>;
|
|
64
76
|
}
|
|
65
77
|
export declare function isSoftDeleteBackend(backend: Backend): backend is SoftDeleteBackend;
|
|
66
78
|
export declare abstract class BaseBackend implements Backend {
|
package/dist/backends/types.js
CHANGED
|
@@ -5,10 +5,14 @@ export class UnsupportedOperationError extends Error {
|
|
|
5
5
|
this.name = 'UnsupportedOperationError';
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
+
export function isSyncableBackend(backend) {
|
|
9
|
+
return 'importWorkItem' in backend;
|
|
10
|
+
}
|
|
8
11
|
export function isSoftDeleteBackend(backend) {
|
|
9
12
|
return ('softDeleteWorkItem' in backend &&
|
|
10
13
|
'restoreWorkItem' in backend &&
|
|
11
|
-
'permanentlyDeleteWorkItem' in backend
|
|
14
|
+
'permanentlyDeleteWorkItem' in backend &&
|
|
15
|
+
'cleanupTrash' in backend);
|
|
12
16
|
}
|
|
13
17
|
export class BaseBackend {
|
|
14
18
|
cache;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6B1C,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,YAAY,SAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,GAAG,SAAS,4BAA4B,OAAO,UAAU,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6B1C,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,YAAY,SAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,GAAG,SAAS,4BAA4B,OAAO,UAAU,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AA0CD,MAAM,UAAU,iBAAiB,CAC/B,OAAgB;IAEhB,OAAO,gBAAgB,IAAI,OAAO,CAAC;AACrC,CAAC;AASD,MAAM,UAAU,mBAAmB,CACjC,OAAgB;IAEhB,OAAO,CACL,oBAAoB,IAAI,OAAO;QAC/B,iBAAiB,IAAI,OAAO;QAC5B,2BAA2B,IAAI,OAAO;QACtC,cAAc,IAAI,OAAO,CAC1B,CAAC;AACJ,CAAC;AAED,MAAM,OAAgB,WAAW;IACrB,KAAK,CAAe;IAE9B,YAAY,GAAG,GAAG,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAiCD,KAAK,CAAC,WAAW,CAAC,EAAU;QAC1B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QACxC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,EAAU;QAC5B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QACxC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;IAES,KAAK,CAAC,cAAc,CAAC,SAAkB;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAClD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAES,KAAK,CAAC,qBAAqB;QACnC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,QAAQ;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAES,KAAK,CAAC,kBAAkB;QAChC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAES,iBAAiB;QACzB,mDAAmD;IACrD,CAAC;IAES,eAAe;QACvB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACxB,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,IAAiB;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,oBAAoB,CACxB,EAAU,EACV,IAAuB;QAEvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,EAAU;QACnC,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAES,cAAc,CAAC,IAA0B;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAE1C,IACE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ;YACrB,IAAI,CAAC,QAAQ,KAAK,SAAS;YAC3B,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAC1B,CAAC;YACD,MAAM,IAAI,yBAAyB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;YACnE,MAAM,IAAI,yBAAyB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,yBAAyB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC7D,CAAC;QACD,IACE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM;YACnB,IAAI,CAAC,MAAM,KAAK,SAAS;YACzB,IAAI,CAAC,MAAM,KAAK,IAAI,EACpB,CAAC;YACD,MAAM,IAAI,yBAAyB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC7D,CAAC;QACD,IACE,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS;YACtB,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EACzB,CAAC;YACD,MAAM,IAAI,yBAAyB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAES,eAAe,CAAC,UAAmB,EAAE,SAAiB;QAC9D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,yBAAyB,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;CACF"}
|