@plandesk/api 0.19.0 → 1.0.0-beta.1
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/dist/access-control.d.ts +150 -0
- package/dist/access-control.js +63 -0
- package/dist/agent-keys.d.ts +81 -0
- package/dist/agent-keys.js +181 -0
- package/dist/auth-context.d.ts +60 -0
- package/dist/auth-context.js +41 -0
- package/dist/auth.d.ts +33 -0
- package/dist/auth.js +357 -0
- package/dist/better-auth.d.ts +66 -0
- package/dist/better-auth.js +106 -0
- package/dist/github.d.ts +57 -0
- package/dist/github.js +115 -0
- package/dist/hosted-auth.d.ts +23 -0
- package/dist/hosted-auth.js +49 -0
- package/dist/identity.d.ts +38 -0
- package/dist/identity.js +163 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +12 -2
- package/dist/invitations.d.ts +94 -0
- package/dist/invitations.js +211 -0
- package/dist/organizations.d.ts +32 -0
- package/dist/organizations.js +63 -0
- package/dist/permissions.d.ts +19 -0
- package/dist/permissions.js +42 -0
- package/dist/projection.d.ts +1 -1
- package/dist/projection.js +5 -5
- package/dist/routes/agent-runs.js +3 -3
- package/dist/routes/artifacts.js +6 -6
- package/dist/routes/auth.d.ts +13 -0
- package/dist/routes/auth.js +83 -0
- package/dist/routes/canvas.js +5 -5
- package/dist/routes/comments.js +11 -11
- package/dist/routes/documents.js +10 -10
- package/dist/routes/folders.js +8 -8
- package/dist/routes/goals.js +11 -11
- package/dist/routes/notes.js +8 -8
- package/dist/routes/orgs.d.ts +17 -0
- package/dist/routes/orgs.js +241 -0
- package/dist/routes/projects.js +13 -13
- package/dist/routes/shares.js +124 -2
- package/dist/routes/submissions.js +7 -8
- package/dist/routes/tags.js +6 -6
- package/dist/routes/tasks.js +14 -3
- package/dist/routes/tokens.js +4 -4
- package/dist/serialize.d.ts +3 -3
- package/dist/server.d.ts +20 -2
- package/dist/server.js +57 -9
- package/dist/services/agent-runs.d.ts +13 -14
- package/dist/services/agent-runs.js +39 -41
- package/dist/services/artifacts.d.ts +6 -7
- package/dist/services/artifacts.js +30 -26
- package/dist/services/canvas.d.ts +11 -12
- package/dist/services/canvas.js +58 -41
- package/dist/services/comments.d.ts +11 -12
- package/dist/services/comments.js +44 -64
- package/dist/services/documents.d.ts +10 -11
- package/dist/services/documents.js +74 -55
- package/dist/services/files.d.ts +2 -1
- package/dist/services/files.js +13 -5
- package/dist/services/folders.d.ts +7 -8
- package/dist/services/folders.js +45 -33
- package/dist/services/goals.d.ts +24 -25
- package/dist/services/goals.js +60 -53
- package/dist/services/index.d.ts +2 -5
- package/dist/services/index.js +15 -20
- package/dist/services/notes.d.ts +7 -8
- package/dist/services/notes.js +35 -23
- package/dist/services/org-scope.d.ts +22 -0
- package/dist/services/org-scope.js +38 -0
- package/dist/services/projects.d.ts +12 -13
- package/dist/services/projects.js +90 -58
- package/dist/services/scope.d.ts +12 -0
- package/dist/services/scope.js +29 -0
- package/dist/services/share.d.ts +74 -10
- package/dist/services/share.js +213 -23
- package/dist/services/sync.d.ts +7 -29
- package/dist/services/sync.js +37 -142
- package/dist/services/tags.d.ts +6 -7
- package/dist/services/tags.js +34 -22
- package/dist/services/tasks.d.ts +24 -17
- package/dist/services/tasks.js +97 -58
- package/dist/services/tokens.d.ts +6 -5
- package/dist/services/tokens.js +13 -6
- package/dist/session.d.ts +5 -0
- package/dist/session.js +8 -0
- package/dist/storage/local.js +16 -10
- package/dist/storage/s3.js +11 -6
- package/dist/test-ba-org.d.ts +16 -0
- package/dist/test-ba-org.js +35 -0
- package/dist/test-helpers.d.ts +21 -5
- package/dist/test-helpers.js +45 -8
- package/dist/vercel.d.ts +3 -0
- package/dist/vercel.js +62 -0
- package/dist/worker.d.ts +44 -0
- package/dist/worker.js +101 -0
- package/package.json +8 -5
- package/web/assets/index-D29SyYFS.css +2 -0
- package/web/assets/index-tBJ_K3ur.js +358 -0
- package/web/index.html +2 -2
- package/web/assets/index-CZs-KaPG.css +0 -2
- package/web/assets/index-zUcwwGtr.js +0 -358
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { type Db } from '@plandesk/db';
|
|
2
2
|
import { type PaginationParams, type SerializedDocument, type SerializedDocumentFolderTree, type SerializedDocumentTree } from '../serialize.js';
|
|
3
|
-
import type
|
|
4
|
-
export type DocumentServiceDeps = {
|
|
3
|
+
import { type OrgScopedDeps } from './org-scope.js';
|
|
4
|
+
export type DocumentServiceDeps = OrgScopedDeps & {
|
|
5
5
|
db: Db;
|
|
6
|
-
eventBus: EventBus;
|
|
7
6
|
};
|
|
8
7
|
export type CreateDocumentInput = {
|
|
9
8
|
title: string;
|
|
@@ -25,14 +24,14 @@ export declare class InvalidDocumentError extends Error {
|
|
|
25
24
|
constructor(message: string);
|
|
26
25
|
}
|
|
27
26
|
export declare function createDocumentService(deps: DocumentServiceDeps): {
|
|
28
|
-
listTree(projectId: string, pagination?: PaginationParams): SerializedDocumentTree[] | undefined
|
|
29
|
-
listFolderTree(projectId: string): SerializedDocumentFolderTree | undefined
|
|
30
|
-
listByFolder(projectId: string, folderId: string): SerializedDocumentTree[] | undefined
|
|
31
|
-
create(projectId: string, input: CreateDocumentInput): SerializedDocument | undefined
|
|
32
|
-
get(id: string): SerializedDocument | undefined
|
|
33
|
-
update(id: string, input: UpdateDocumentInput): SerializedDocument | undefined
|
|
34
|
-
getByTask(taskId: string): SerializedDocument | undefined
|
|
35
|
-
delete(id: string): boolean
|
|
27
|
+
listTree(projectId: string, pagination?: PaginationParams): Promise<SerializedDocumentTree[] | undefined>;
|
|
28
|
+
listFolderTree(projectId: string): Promise<SerializedDocumentFolderTree | undefined>;
|
|
29
|
+
listByFolder(projectId: string, folderId: string): Promise<SerializedDocumentTree[] | undefined>;
|
|
30
|
+
create(projectId: string, input: CreateDocumentInput): Promise<SerializedDocument | undefined>;
|
|
31
|
+
get(id: string): Promise<SerializedDocument | undefined>;
|
|
32
|
+
update(id: string, input: UpdateDocumentInput): Promise<SerializedDocument | undefined>;
|
|
33
|
+
getByTask(taskId: string): Promise<SerializedDocument | undefined>;
|
|
34
|
+
delete(id: string): Promise<boolean>;
|
|
36
35
|
};
|
|
37
36
|
export type DocumentService = ReturnType<typeof createDocumentService>;
|
|
38
37
|
//# sourceMappingURL=documents.d.ts.map
|
|
@@ -1,70 +1,93 @@
|
|
|
1
|
-
import { createDocument as dbCreateDocument, deleteCommentsByTarget, deleteDocument as dbDeleteDocument, detachDocumentChildren, getDocument as dbGetDocument, getDocumentByTask as dbGetDocumentByTask, getFolderByProjectAndId, getProject, getTask, listDocuments as dbListDocuments, listFolders as dbListFolders, updateDocument as dbUpdateDocument, } from '@plandesk/db';
|
|
1
|
+
import { withTransaction, createDocument as dbCreateDocument, deleteCommentsByTarget, deleteDocument as dbDeleteDocument, detachDocumentChildren, getDocument as dbGetDocument, getDocumentByTask as dbGetDocumentByTask, getFolderByProjectAndId, getProject, getTask, listDocuments as dbListDocuments, listFolders as dbListFolders, updateDocument as dbUpdateDocument, } from '@plandesk/db';
|
|
2
2
|
import { buildDocumentTree, buildFolderTree, serializeDocument, } from '../serialize.js';
|
|
3
|
+
import { assertPermission, resolveOrgId } from './org-scope.js';
|
|
4
|
+
import { assertProjectInOrg, ProjectNotInOrgError } from './scope.js';
|
|
3
5
|
export class InvalidDocumentError extends Error {
|
|
4
6
|
constructor(message) {
|
|
5
7
|
super(message);
|
|
6
8
|
this.name = 'InvalidDocumentError';
|
|
7
9
|
}
|
|
8
10
|
}
|
|
9
|
-
function assertTaskInProject(db, projectId, taskId) {
|
|
10
|
-
const task = getTask(db, taskId);
|
|
11
|
+
async function assertTaskInProject(db, projectId, taskId) {
|
|
12
|
+
const task = await getTask(db, taskId);
|
|
11
13
|
if (!task || task.projectId !== projectId) {
|
|
12
14
|
throw new InvalidDocumentError('Task does not belong to project');
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
|
-
function assertParentInProject(db, projectId, parentId) {
|
|
16
|
-
const parent = dbGetDocument(db, parentId);
|
|
17
|
+
async function assertParentInProject(db, projectId, parentId) {
|
|
18
|
+
const parent = await dbGetDocument(db, parentId);
|
|
17
19
|
if (!parent || parent.projectId !== projectId) {
|
|
18
20
|
throw new InvalidDocumentError('Parent document does not belong to project');
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
|
-
function assertFolderInProject(db, projectId, folderId) {
|
|
22
|
-
if (!getFolderByProjectAndId(db, projectId, folderId)) {
|
|
23
|
+
async function assertFolderInProject(db, projectId, folderId) {
|
|
24
|
+
if (!(await getFolderByProjectAndId(db, projectId, folderId))) {
|
|
23
25
|
throw new InvalidDocumentError('Folder does not belong to project');
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
export function createDocumentService(deps) {
|
|
27
|
-
const { db
|
|
29
|
+
const { db } = deps;
|
|
28
30
|
return {
|
|
29
|
-
listTree(projectId, pagination = {}) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
async listTree(projectId, pagination = {}) {
|
|
32
|
+
try {
|
|
33
|
+
await assertProjectInOrg(db, projectId, resolveOrgId(deps));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
33
40
|
}
|
|
34
|
-
return buildDocumentTree(dbListDocuments(db, projectId, pagination));
|
|
41
|
+
return buildDocumentTree(await dbListDocuments(db, projectId, pagination));
|
|
35
42
|
},
|
|
36
|
-
listFolderTree(projectId) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return undefined;
|
|
43
|
+
async listFolderTree(projectId) {
|
|
44
|
+
try {
|
|
45
|
+
await assertProjectInOrg(db, projectId, resolveOrgId(deps));
|
|
40
46
|
}
|
|
41
|
-
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
return buildFolderTree(await dbListFolders(db, projectId), await dbListDocuments(db, projectId));
|
|
42
54
|
},
|
|
43
|
-
listByFolder(projectId, folderId) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return undefined;
|
|
55
|
+
async listByFolder(projectId, folderId) {
|
|
56
|
+
try {
|
|
57
|
+
await assertProjectInOrg(db, projectId, resolveOrgId(deps));
|
|
47
58
|
}
|
|
48
|
-
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
if (!(await getFolderByProjectAndId(db, projectId, folderId))) {
|
|
49
66
|
return undefined;
|
|
50
67
|
}
|
|
51
|
-
return buildDocumentTree(dbListDocuments(db, projectId, { folderId }));
|
|
68
|
+
return buildDocumentTree(await dbListDocuments(db, projectId, { folderId }));
|
|
52
69
|
},
|
|
53
|
-
create(projectId, input) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
async create(projectId, input) {
|
|
71
|
+
assertPermission(deps, 'document', 'create');
|
|
72
|
+
try {
|
|
73
|
+
await assertProjectInOrg(db, projectId, resolveOrgId(deps));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
57
80
|
}
|
|
58
81
|
if (input.linkedTaskId !== undefined && input.linkedTaskId !== null) {
|
|
59
|
-
assertTaskInProject(db, projectId, input.linkedTaskId);
|
|
82
|
+
await assertTaskInProject(db, projectId, input.linkedTaskId);
|
|
60
83
|
}
|
|
61
84
|
if (input.parentId !== undefined && input.parentId !== null) {
|
|
62
|
-
assertParentInProject(db, projectId, input.parentId);
|
|
85
|
+
await assertParentInProject(db, projectId, input.parentId);
|
|
63
86
|
}
|
|
64
87
|
if (input.folderId !== undefined && input.folderId !== null) {
|
|
65
|
-
assertFolderInProject(db, projectId, input.folderId);
|
|
88
|
+
await assertFolderInProject(db, projectId, input.folderId);
|
|
66
89
|
}
|
|
67
|
-
const document = dbCreateDocument(db, {
|
|
90
|
+
const document = await dbCreateDocument(db, {
|
|
68
91
|
projectId,
|
|
69
92
|
title: input.title,
|
|
70
93
|
body: input.body,
|
|
@@ -73,65 +96,61 @@ export function createDocumentService(deps) {
|
|
|
73
96
|
folderId: input.folderId,
|
|
74
97
|
linkedTaskId: input.linkedTaskId,
|
|
75
98
|
});
|
|
76
|
-
eventBus.emit({
|
|
77
|
-
type: 'document_created',
|
|
78
|
-
documentId: document.id,
|
|
79
|
-
projectId,
|
|
80
|
-
});
|
|
81
99
|
return serializeDocument(document);
|
|
82
100
|
},
|
|
83
|
-
get(id) {
|
|
84
|
-
const document = dbGetDocument(db, id);
|
|
101
|
+
async get(id) {
|
|
102
|
+
const document = await dbGetDocument(db, id);
|
|
85
103
|
if (!document) {
|
|
86
104
|
return undefined;
|
|
87
105
|
}
|
|
88
106
|
return serializeDocument(document);
|
|
89
107
|
},
|
|
90
|
-
update(id, input) {
|
|
91
|
-
|
|
108
|
+
async update(id, input) {
|
|
109
|
+
assertPermission(deps, 'document', 'update');
|
|
110
|
+
const existing = await dbGetDocument(db, id);
|
|
92
111
|
if (!existing) {
|
|
93
112
|
return undefined;
|
|
94
113
|
}
|
|
95
114
|
if (input.linkedTaskId !== undefined && input.linkedTaskId !== null) {
|
|
96
|
-
assertTaskInProject(db, existing.projectId, input.linkedTaskId);
|
|
115
|
+
await assertTaskInProject(db, existing.projectId, input.linkedTaskId);
|
|
97
116
|
}
|
|
98
117
|
if (input.parentId !== undefined && input.parentId !== null) {
|
|
99
118
|
if (input.parentId === id) {
|
|
100
119
|
throw new InvalidDocumentError('Document cannot be its own parent');
|
|
101
120
|
}
|
|
102
|
-
assertParentInProject(db, existing.projectId, input.parentId);
|
|
121
|
+
await assertParentInProject(db, existing.projectId, input.parentId);
|
|
103
122
|
}
|
|
104
123
|
if (input.folderId !== undefined && input.folderId !== null) {
|
|
105
|
-
assertFolderInProject(db, existing.projectId, input.folderId);
|
|
124
|
+
await assertFolderInProject(db, existing.projectId, input.folderId);
|
|
106
125
|
}
|
|
107
|
-
const document = dbUpdateDocument(db, id, input);
|
|
126
|
+
const document = await dbUpdateDocument(db, id, input);
|
|
108
127
|
if (!document) {
|
|
109
128
|
return undefined;
|
|
110
129
|
}
|
|
111
130
|
return serializeDocument(document);
|
|
112
131
|
},
|
|
113
|
-
getByTask(taskId) {
|
|
114
|
-
const task = getTask(db, taskId);
|
|
132
|
+
async getByTask(taskId) {
|
|
133
|
+
const task = await getTask(db, taskId);
|
|
115
134
|
if (!task) {
|
|
116
135
|
return undefined;
|
|
117
136
|
}
|
|
118
|
-
const document = dbGetDocumentByTask(db, taskId);
|
|
137
|
+
const document = await dbGetDocumentByTask(db, taskId);
|
|
119
138
|
if (!document) {
|
|
120
139
|
return undefined;
|
|
121
140
|
}
|
|
122
141
|
return serializeDocument(document);
|
|
123
142
|
},
|
|
124
|
-
delete(id) {
|
|
125
|
-
|
|
143
|
+
async delete(id) {
|
|
144
|
+
assertPermission(deps, 'document', 'delete');
|
|
145
|
+
const existing = await dbGetDocument(db, id);
|
|
126
146
|
if (!existing) {
|
|
127
147
|
return false;
|
|
128
148
|
}
|
|
129
|
-
db
|
|
130
|
-
detachDocumentChildren(tx, id);
|
|
131
|
-
deleteCommentsByTarget(tx, 'document', id);
|
|
132
|
-
dbDeleteDocument(tx, id);
|
|
149
|
+
await withTransaction(db, async (tx) => {
|
|
150
|
+
await detachDocumentChildren(tx, id);
|
|
151
|
+
await deleteCommentsByTarget(tx, 'document', id);
|
|
152
|
+
await dbDeleteDocument(tx, id);
|
|
133
153
|
});
|
|
134
|
-
eventBus.emit({ type: 'canvas_updated', projectId: existing.projectId });
|
|
135
154
|
return true;
|
|
136
155
|
},
|
|
137
156
|
};
|
package/dist/services/files.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Db } from '@plandesk/db';
|
|
2
2
|
import type { StorageAdapter, StorageResolveResult } from '../storage/adapter.js';
|
|
3
|
-
|
|
3
|
+
import { type OrgScopedDeps } from './org-scope.js';
|
|
4
|
+
export type FileServiceDeps = OrgScopedDeps & {
|
|
4
5
|
db: Db;
|
|
5
6
|
storage: StorageAdapter;
|
|
6
7
|
};
|
package/dist/services/files.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { getFile
|
|
1
|
+
import { getFile } from '@plandesk/db';
|
|
2
|
+
import { assertPermission, resolveOrgId } from './org-scope.js';
|
|
3
|
+
import { assertProjectInOrg, ProjectNotInOrgError } from './scope.js';
|
|
2
4
|
export function createFileService(deps) {
|
|
3
5
|
const { db, storage } = deps;
|
|
4
6
|
return {
|
|
5
7
|
async create(input) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
assertPermission(deps, 'document', 'create');
|
|
9
|
+
try {
|
|
10
|
+
await assertProjectInOrg(db, input.projectId, resolveOrgId(deps));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
9
17
|
}
|
|
10
18
|
const { id, url } = await storage.put({
|
|
11
19
|
projectId: input.projectId,
|
|
@@ -16,7 +24,7 @@ export function createFileService(deps) {
|
|
|
16
24
|
// Content-addressed dedup means the persisted row may reflect an
|
|
17
25
|
// earlier upload of the same bytes — read it back rather than echoing
|
|
18
26
|
// this call's input, so the response always matches what is stored.
|
|
19
|
-
const file = getFile(db, id);
|
|
27
|
+
const file = await getFile(db, input.projectId, id);
|
|
20
28
|
if (!file) {
|
|
21
29
|
throw new Error(`Storage adapter did not persist file metadata for ${id}`);
|
|
22
30
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { type Db } from '@plandesk/db';
|
|
2
2
|
import { type SerializedFolder } from '../serialize.js';
|
|
3
|
-
import type
|
|
4
|
-
export type FolderServiceDeps = {
|
|
3
|
+
import { type OrgScopedDeps } from './org-scope.js';
|
|
4
|
+
export type FolderServiceDeps = OrgScopedDeps & {
|
|
5
5
|
db: Db;
|
|
6
|
-
eventBus: EventBus;
|
|
7
6
|
};
|
|
8
7
|
export type CreateFolderInput = {
|
|
9
8
|
name: string;
|
|
@@ -17,11 +16,11 @@ export declare class InvalidFolderError extends Error {
|
|
|
17
16
|
constructor(message: string);
|
|
18
17
|
}
|
|
19
18
|
export declare function createFolderService(deps: FolderServiceDeps): {
|
|
20
|
-
list(projectId: string): SerializedFolder[] | undefined
|
|
21
|
-
create(projectId: string, input: CreateFolderInput): SerializedFolder | undefined
|
|
22
|
-
get(id: string): SerializedFolder | undefined
|
|
23
|
-
update(id: string, input: UpdateFolderInput): SerializedFolder | undefined
|
|
24
|
-
delete(id: string): boolean
|
|
19
|
+
list(projectId: string): Promise<SerializedFolder[] | undefined>;
|
|
20
|
+
create(projectId: string, input: CreateFolderInput): Promise<SerializedFolder | undefined>;
|
|
21
|
+
get(id: string): Promise<SerializedFolder | undefined>;
|
|
22
|
+
update(id: string, input: UpdateFolderInput): Promise<SerializedFolder | undefined>;
|
|
23
|
+
delete(id: string): Promise<boolean>;
|
|
25
24
|
};
|
|
26
25
|
export type FolderService = ReturnType<typeof createFolderService>;
|
|
27
26
|
//# sourceMappingURL=folders.d.ts.map
|
package/dist/services/folders.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { createFolder as dbCreateFolder, deleteFolder as dbDeleteFolder, getFolder as dbGetFolder, getProject, listFolders as dbListFolders, moveDocumentsToFolder, reparentChildFolders, updateFolder as dbUpdateFolder, } from '@plandesk/db';
|
|
1
|
+
import { withTransaction, createFolder as dbCreateFolder, deleteFolder as dbDeleteFolder, getFolder as dbGetFolder, getProject, listFolders as dbListFolders, moveDocumentsToFolder, reparentChildFolders, updateFolder as dbUpdateFolder, } from '@plandesk/db';
|
|
2
2
|
import { serializeFolder } from '../serialize.js';
|
|
3
|
+
import { assertPermission, resolveOrgId } from './org-scope.js';
|
|
4
|
+
import { assertProjectInOrg, ProjectNotInOrgError } from './scope.js';
|
|
3
5
|
export class InvalidFolderError extends Error {
|
|
4
6
|
constructor(message) {
|
|
5
7
|
super(message);
|
|
@@ -11,13 +13,13 @@ function assertNonEmptyName(name) {
|
|
|
11
13
|
throw new InvalidFolderError('Folder name must not be empty');
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
|
-
function assertParentInProject(db, projectId, parentFolderId) {
|
|
15
|
-
const parent = dbGetFolder(db, parentFolderId);
|
|
16
|
+
async function assertParentInProject(db, projectId, parentFolderId) {
|
|
17
|
+
const parent = await dbGetFolder(db, parentFolderId);
|
|
16
18
|
if (!parent || parent.projectId !== projectId) {
|
|
17
19
|
throw new InvalidFolderError('Parent folder does not belong to project');
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
|
-
function assertNoCycle(db, folderId, newParentFolderId) {
|
|
22
|
+
async function assertNoCycle(db, folderId, newParentFolderId) {
|
|
21
23
|
const visited = new Set();
|
|
22
24
|
let current = newParentFolderId;
|
|
23
25
|
while (current !== null) {
|
|
@@ -28,45 +30,56 @@ function assertNoCycle(db, folderId, newParentFolderId) {
|
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
32
|
visited.add(current);
|
|
31
|
-
current = dbGetFolder(db, current)?.parentFolderId ?? null;
|
|
33
|
+
current = (await dbGetFolder(db, current))?.parentFolderId ?? null;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
export function createFolderService(deps) {
|
|
35
|
-
const { db
|
|
37
|
+
const { db } = deps;
|
|
36
38
|
return {
|
|
37
|
-
list(projectId) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
async list(projectId) {
|
|
40
|
+
try {
|
|
41
|
+
await assertProjectInOrg(db, projectId, resolveOrgId(deps));
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
41
48
|
}
|
|
42
|
-
return dbListFolders(db, projectId).map(serializeFolder);
|
|
49
|
+
return (await dbListFolders(db, projectId)).map(serializeFolder);
|
|
43
50
|
},
|
|
44
|
-
create(projectId, input) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
async create(projectId, input) {
|
|
52
|
+
assertPermission(deps, 'document', 'create');
|
|
53
|
+
try {
|
|
54
|
+
await assertProjectInOrg(db, projectId, resolveOrgId(deps));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof ProjectNotInOrgError) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
48
61
|
}
|
|
49
62
|
assertNonEmptyName(input.name);
|
|
50
63
|
if (input.parentFolderId !== undefined && input.parentFolderId !== null) {
|
|
51
|
-
assertParentInProject(db, projectId, input.parentFolderId);
|
|
64
|
+
await assertParentInProject(db, projectId, input.parentFolderId);
|
|
52
65
|
}
|
|
53
|
-
const folder = dbCreateFolder(db, {
|
|
66
|
+
const folder = await dbCreateFolder(db, {
|
|
54
67
|
projectId,
|
|
55
68
|
name: input.name,
|
|
56
69
|
parentFolderId: input.parentFolderId,
|
|
57
70
|
});
|
|
58
|
-
eventBus.emit({ type: 'folder_created', folderId: folder.id, projectId });
|
|
59
71
|
return serializeFolder(folder);
|
|
60
72
|
},
|
|
61
|
-
get(id) {
|
|
62
|
-
const folder = dbGetFolder(db, id);
|
|
73
|
+
async get(id) {
|
|
74
|
+
const folder = await dbGetFolder(db, id);
|
|
63
75
|
if (!folder) {
|
|
64
76
|
return undefined;
|
|
65
77
|
}
|
|
66
78
|
return serializeFolder(folder);
|
|
67
79
|
},
|
|
68
|
-
update(id, input) {
|
|
69
|
-
|
|
80
|
+
async update(id, input) {
|
|
81
|
+
assertPermission(deps, 'document', 'update');
|
|
82
|
+
const existing = await dbGetFolder(db, id);
|
|
70
83
|
if (!existing) {
|
|
71
84
|
return undefined;
|
|
72
85
|
}
|
|
@@ -77,29 +90,28 @@ export function createFolderService(deps) {
|
|
|
77
90
|
if (input.parentFolderId === id) {
|
|
78
91
|
throw new InvalidFolderError('Folder cannot be its own parent');
|
|
79
92
|
}
|
|
80
|
-
assertParentInProject(db, existing.projectId, input.parentFolderId);
|
|
81
|
-
assertNoCycle(db, id, input.parentFolderId);
|
|
93
|
+
await assertParentInProject(db, existing.projectId, input.parentFolderId);
|
|
94
|
+
await assertNoCycle(db, id, input.parentFolderId);
|
|
82
95
|
}
|
|
83
|
-
const folder = dbUpdateFolder(db, id, input);
|
|
96
|
+
const folder = await dbUpdateFolder(db, id, input);
|
|
84
97
|
if (!folder) {
|
|
85
98
|
return undefined;
|
|
86
99
|
}
|
|
87
|
-
eventBus.emit({ type: 'folder_updated', folderId: folder.id, projectId: folder.projectId });
|
|
88
100
|
return serializeFolder(folder);
|
|
89
101
|
},
|
|
90
|
-
delete(id) {
|
|
91
|
-
|
|
102
|
+
async delete(id) {
|
|
103
|
+
assertPermission(deps, 'document', 'delete');
|
|
104
|
+
const existing = await dbGetFolder(db, id);
|
|
92
105
|
if (!existing) {
|
|
93
106
|
return false;
|
|
94
107
|
}
|
|
95
108
|
// Never orphan: children folders and contained documents move to the
|
|
96
109
|
// deleted folder's parent (or root when the folder was at root).
|
|
97
|
-
db
|
|
98
|
-
reparentChildFolders(tx, id, existing.parentFolderId);
|
|
99
|
-
moveDocumentsToFolder(tx, id, existing.parentFolderId);
|
|
100
|
-
dbDeleteFolder(tx, id);
|
|
110
|
+
await withTransaction(db, async (tx) => {
|
|
111
|
+
await reparentChildFolders(tx, id, existing.parentFolderId);
|
|
112
|
+
await moveDocumentsToFolder(tx, id, existing.parentFolderId);
|
|
113
|
+
await dbDeleteFolder(tx, id);
|
|
101
114
|
});
|
|
102
|
-
eventBus.emit({ type: 'folder_updated', folderId: id, projectId: existing.projectId });
|
|
103
115
|
return true;
|
|
104
116
|
},
|
|
105
117
|
};
|
package/dist/services/goals.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type Db, type GoalStatus } from '@plandesk/db';
|
|
2
|
-
import type
|
|
3
|
-
export type GoalServiceDeps = {
|
|
2
|
+
import { type OrgScopedDeps } from './org-scope.js';
|
|
3
|
+
export type GoalServiceDeps = OrgScopedDeps & {
|
|
4
4
|
db: Db;
|
|
5
|
-
eventBus: EventBus;
|
|
6
5
|
};
|
|
7
6
|
export declare class InvalidGoalTransitionError extends Error {
|
|
8
7
|
constructor(message: string);
|
|
@@ -79,11 +78,11 @@ export declare function evaluateEvidence(surface: VerificationSurface, evidence:
|
|
|
79
78
|
detail?: string;
|
|
80
79
|
};
|
|
81
80
|
export declare function createGoalService(deps: GoalServiceDeps): {
|
|
82
|
-
create(projectId: string, input: CreateGoalInput): {
|
|
81
|
+
create(projectId: string, input: CreateGoalInput): Promise<{
|
|
83
82
|
id: string;
|
|
84
83
|
project_id: string;
|
|
85
84
|
objective: string;
|
|
86
|
-
status: "
|
|
85
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
87
86
|
verification_surface: string | null;
|
|
88
87
|
constraints: string | null;
|
|
89
88
|
boundaries: string | null;
|
|
@@ -98,15 +97,15 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
98
97
|
} | null;
|
|
99
98
|
created_at: string;
|
|
100
99
|
updated_at: string;
|
|
101
|
-
} | undefined
|
|
102
|
-
get(goalId: string): {
|
|
100
|
+
} | undefined>;
|
|
101
|
+
get(goalId: string): Promise<{
|
|
103
102
|
cycle_tasks: {
|
|
104
103
|
tags?: import("../serialize.js").SerializedTag[] | undefined;
|
|
105
104
|
id: string;
|
|
106
105
|
project_id: string;
|
|
107
106
|
goal_id: string;
|
|
108
107
|
label: string;
|
|
109
|
-
status: "
|
|
108
|
+
status: "in_progress" | "done" | "todo" | "scope" | "backlog";
|
|
110
109
|
description: string | null;
|
|
111
110
|
x: number;
|
|
112
111
|
y: number;
|
|
@@ -118,7 +117,7 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
118
117
|
id: string;
|
|
119
118
|
project_id: string;
|
|
120
119
|
objective: string;
|
|
121
|
-
status: "
|
|
120
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
122
121
|
verification_surface: string | null;
|
|
123
122
|
constraints: string | null;
|
|
124
123
|
boundaries: string | null;
|
|
@@ -133,12 +132,12 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
133
132
|
} | null;
|
|
134
133
|
created_at: string;
|
|
135
134
|
updated_at: string;
|
|
136
|
-
} | undefined
|
|
137
|
-
listByProject(projectId: string): {
|
|
135
|
+
} | undefined>;
|
|
136
|
+
listByProject(projectId: string): Promise<{
|
|
138
137
|
id: string;
|
|
139
138
|
project_id: string;
|
|
140
139
|
objective: string;
|
|
141
|
-
status: "
|
|
140
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
142
141
|
verification_surface: string | null;
|
|
143
142
|
constraints: string | null;
|
|
144
143
|
boundaries: string | null;
|
|
@@ -153,12 +152,12 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
153
152
|
} | null;
|
|
154
153
|
created_at: string;
|
|
155
154
|
updated_at: string;
|
|
156
|
-
}[] | undefined
|
|
157
|
-
update(goalId: string, input: UpdateGoalInput): {
|
|
155
|
+
}[] | undefined>;
|
|
156
|
+
update(goalId: string, input: UpdateGoalInput): Promise<{
|
|
158
157
|
id: string;
|
|
159
158
|
project_id: string;
|
|
160
159
|
objective: string;
|
|
161
|
-
status: "
|
|
160
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
162
161
|
verification_surface: string | null;
|
|
163
162
|
constraints: string | null;
|
|
164
163
|
boundaries: string | null;
|
|
@@ -173,12 +172,12 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
173
172
|
} | null;
|
|
174
173
|
created_at: string;
|
|
175
174
|
updated_at: string;
|
|
176
|
-
} | undefined
|
|
177
|
-
pause(goalId: string): {
|
|
175
|
+
} | undefined>;
|
|
176
|
+
pause(goalId: string): Promise<{
|
|
178
177
|
id: string;
|
|
179
178
|
project_id: string;
|
|
180
179
|
objective: string;
|
|
181
|
-
status: "
|
|
180
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
182
181
|
verification_surface: string | null;
|
|
183
182
|
constraints: string | null;
|
|
184
183
|
boundaries: string | null;
|
|
@@ -193,12 +192,12 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
193
192
|
} | null;
|
|
194
193
|
created_at: string;
|
|
195
194
|
updated_at: string;
|
|
196
|
-
} | undefined
|
|
197
|
-
resume(goalId: string): {
|
|
195
|
+
} | undefined>;
|
|
196
|
+
resume(goalId: string): Promise<{
|
|
198
197
|
id: string;
|
|
199
198
|
project_id: string;
|
|
200
199
|
objective: string;
|
|
201
|
-
status: "
|
|
200
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
202
201
|
verification_surface: string | null;
|
|
203
202
|
constraints: string | null;
|
|
204
203
|
boundaries: string | null;
|
|
@@ -213,12 +212,12 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
213
212
|
} | null;
|
|
214
213
|
created_at: string;
|
|
215
214
|
updated_at: string;
|
|
216
|
-
} | undefined
|
|
217
|
-
complete(goalId: string, evidence?: VerificationEvidence): {
|
|
215
|
+
} | undefined>;
|
|
216
|
+
complete(goalId: string, evidence?: VerificationEvidence): Promise<{
|
|
218
217
|
id: string;
|
|
219
218
|
project_id: string;
|
|
220
219
|
objective: string;
|
|
221
|
-
status: "
|
|
220
|
+
status: "paused" | "active" | "complete" | "blocked";
|
|
222
221
|
verification_surface: string | null;
|
|
223
222
|
constraints: string | null;
|
|
224
223
|
boundaries: string | null;
|
|
@@ -233,7 +232,7 @@ export declare function createGoalService(deps: GoalServiceDeps): {
|
|
|
233
232
|
} | null;
|
|
234
233
|
created_at: string;
|
|
235
234
|
updated_at: string;
|
|
236
|
-
} | undefined
|
|
235
|
+
} | undefined>;
|
|
237
236
|
};
|
|
238
237
|
export type GoalService = ReturnType<typeof createGoalService>;
|
|
239
238
|
//# sourceMappingURL=goals.d.ts.map
|