@danypops/papyrus 0.47.2 → 0.49.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/package.json +2 -2
- package/src/artifact/artifact-scope-store.ts +37 -7
- package/src/artifact/in-memory-artifact-scope-store.ts +123 -0
- package/src/artifact/sqlite-artifact-scope-store.ts +148 -27
- package/src/cli/daemon-command.ts +48 -0
- package/src/cli/rules-command.ts +92 -0
- package/src/cli.ts +29 -4
- package/src/client.ts +6 -0
- package/src/constants.ts +4 -1
- package/src/daemon/daemon-state.ts +13 -1
- package/src/daemon/daemon.ts +25 -6
- package/src/db.ts +55 -0
- package/src/domain/project-registry.ts +58 -0
- package/src/domain/task-scope.ts +4 -14
- package/src/handlers/registry.ts +3 -1
- package/src/handlers/rules.ts +91 -4
- package/src/modules/rules.ts +29 -1
- package/src/ops.ts +1 -0
- package/src/ports/project-registry-store.ts +13 -0
- package/src/rules/rules-service.ts +105 -16
- package/src/service.ts +31 -4
- package/src/stores/in-memory-project-registry-store.ts +100 -0
- package/src/stores/sqlite-project-registry-store.ts +132 -0
- package/src/stores/sqlite-task-scope-store.ts +12 -120
- package/src/stores/task-scope-store.ts +30 -71
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { basename } from "node:path";
|
|
3
|
-
import { TASK_PROJECT_ALIAS_MAX_COUNT } from "../constants.ts";
|
|
4
1
|
import type {
|
|
5
2
|
RegisterTaskProjectInput,
|
|
6
3
|
TaskProject,
|
|
@@ -9,6 +6,8 @@ import type {
|
|
|
9
6
|
TaskViewMode,
|
|
10
7
|
TaskViewPreference,
|
|
11
8
|
} from "../domain/task-scope.ts";
|
|
9
|
+
import type { ProjectRegistryStore } from "../ports/project-registry-store.ts";
|
|
10
|
+
import { InMemoryProjectRegistryStore } from "./in-memory-project-registry-store.ts";
|
|
12
11
|
|
|
13
12
|
export interface TaskScopeStore {
|
|
14
13
|
assign(taskId: string, projectRoot: string | undefined, source: TaskScopeSource): TaskProjectScope;
|
|
@@ -21,32 +20,38 @@ export interface TaskScopeStore {
|
|
|
21
20
|
registerProject(input: RegisterTaskProjectInput): TaskProject;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return [trimmed];
|
|
32
|
-
});
|
|
33
|
-
if (aliases.length > TASK_PROJECT_ALIAS_MAX_COUNT) {
|
|
34
|
-
throw new Error(`project aliases cannot exceed ${TASK_PROJECT_ALIAS_MAX_COUNT} entries`);
|
|
35
|
-
}
|
|
36
|
-
return aliases;
|
|
37
|
-
}
|
|
38
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Task's own scope/view bookkeeping, composing a ProjectRegistryStore for the project-catalog
|
|
25
|
+
* methods (projects/matchingProjects/registerProject) rather than implementing that bookkeeping
|
|
26
|
+
* itself -- see project-registry-store.ts. Pass a shared registry instance to keep Task and a
|
|
27
|
+
* non-Task ArtifactScopeStore resolving against the exact same project identities; omitted, this
|
|
28
|
+
* store gets its own private one (matching this class's own behavior before the extraction).
|
|
29
|
+
*/
|
|
39
30
|
export class InMemoryTaskScopeStore implements TaskScopeStore {
|
|
40
31
|
private readonly scopes = new Map<string, TaskProjectScope>();
|
|
41
32
|
private readonly views = new Map<string, TaskViewPreference>();
|
|
42
|
-
private readonly
|
|
33
|
+
private readonly registry: InMemoryProjectRegistryStore;
|
|
34
|
+
|
|
35
|
+
constructor(registry?: ProjectRegistryStore) {
|
|
36
|
+
this.registry = registry instanceof InMemoryProjectRegistryStore ? registry : new InMemoryProjectRegistryStore();
|
|
37
|
+
this.registry.subscribeRootMoved((previousRoot, nextRoot) => this.onRootMoved(previousRoot, nextRoot));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private onRootMoved(previousRoot: string, nextRoot: string): void {
|
|
41
|
+
for (const [taskId, scope] of this.scopes) {
|
|
42
|
+
if (scope.projectRoot === previousRoot) this.scopes.set(taskId, { ...scope, projectRoot: nextRoot });
|
|
43
|
+
}
|
|
44
|
+
const view = this.views.get(previousRoot);
|
|
45
|
+
if (view) {
|
|
46
|
+
this.views.delete(previousRoot);
|
|
47
|
+
this.views.set(nextRoot, { ...view, projectRoot: nextRoot });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
43
50
|
|
|
44
51
|
assign(taskId: string, projectRoot: string | undefined, source: TaskScopeSource): TaskProjectScope {
|
|
45
52
|
const scope = { taskId, ...(projectRoot === undefined ? {} : { projectRoot }), source };
|
|
46
53
|
this.scopes.set(taskId, scope);
|
|
47
|
-
if (projectRoot !== undefined && !
|
|
48
|
-
this.registerProject({ projectRoot });
|
|
49
|
-
}
|
|
54
|
+
if (projectRoot !== undefined && !this.registry.byRoot(projectRoot)) this.registerProject({ projectRoot });
|
|
50
55
|
return scope;
|
|
51
56
|
}
|
|
52
57
|
|
|
@@ -73,60 +78,14 @@ export class InMemoryTaskScopeStore implements TaskScopeStore {
|
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
projects(query: string | undefined, limit: number): TaskProject[] {
|
|
76
|
-
|
|
77
|
-
return [...this.projectRows.values()]
|
|
78
|
-
.filter(
|
|
79
|
-
(project) =>
|
|
80
|
-
!needle ||
|
|
81
|
-
project.name.toLowerCase().includes(needle) ||
|
|
82
|
-
project.projectRoot.toLowerCase().includes(needle) ||
|
|
83
|
-
project.aliases.some((alias) => alias.toLowerCase().includes(needle)),
|
|
84
|
-
)
|
|
85
|
-
.sort((left, right) => left.name.localeCompare(right.name) || left.projectRoot.localeCompare(right.projectRoot))
|
|
86
|
-
.slice(0, limit);
|
|
81
|
+
return this.registry.projects(query, limit);
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
matchingProjects(reference: string): TaskProject[] {
|
|
90
|
-
|
|
91
|
-
return [...this.projectRows.values()]
|
|
92
|
-
.filter(
|
|
93
|
-
(project) =>
|
|
94
|
-
project.id.toLowerCase() === needle ||
|
|
95
|
-
project.name.toLowerCase() === needle ||
|
|
96
|
-
project.projectRoot.toLowerCase() === needle ||
|
|
97
|
-
project.aliases.some((alias) => alias.toLowerCase() === needle),
|
|
98
|
-
)
|
|
99
|
-
.slice(0, 11);
|
|
85
|
+
return this.registry.matchingProjects(reference);
|
|
100
86
|
}
|
|
101
87
|
|
|
102
88
|
registerProject(input: RegisterTaskProjectInput): TaskProject {
|
|
103
|
-
|
|
104
|
-
const byRoot = [...this.projectRows.values()].find((project) => project.projectRoot === input.projectRoot);
|
|
105
|
-
const existing = input.existingId ? this.projectRows.get(input.existingId) : byRoot;
|
|
106
|
-
const name = input.name?.trim() || existing?.name || basename(input.projectRoot) || input.projectRoot;
|
|
107
|
-
const aliases = uniqueAliases(
|
|
108
|
-
[...(existing?.aliases ?? []), ...(existing && existing.name !== name ? [existing.name] : []), ...(input.aliases ?? [])],
|
|
109
|
-
name,
|
|
110
|
-
);
|
|
111
|
-
const project: TaskProject = {
|
|
112
|
-
id: existing?.id ?? randomUUID(),
|
|
113
|
-
name,
|
|
114
|
-
aliases,
|
|
115
|
-
projectRoot: input.projectRoot,
|
|
116
|
-
createdAt: existing?.createdAt ?? now,
|
|
117
|
-
updatedAt: now,
|
|
118
|
-
};
|
|
119
|
-
if (existing && existing.projectRoot !== project.projectRoot) {
|
|
120
|
-
for (const [taskId, scope] of this.scopes) {
|
|
121
|
-
if (scope.projectRoot === existing.projectRoot) this.scopes.set(taskId, { ...scope, projectRoot: project.projectRoot });
|
|
122
|
-
}
|
|
123
|
-
const view = this.views.get(existing.projectRoot);
|
|
124
|
-
if (view) {
|
|
125
|
-
this.views.delete(existing.projectRoot);
|
|
126
|
-
this.views.set(project.projectRoot, { ...view, projectRoot: project.projectRoot });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
this.projectRows.set(project.id, project);
|
|
130
|
-
return project;
|
|
89
|
+
return this.registry.registerProject(input);
|
|
131
90
|
}
|
|
132
91
|
}
|