@meridianjs/project 0.1.1 → 0.1.3
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/README.md +116 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +20 -0
- package/dist/index.mjs +20 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @meridianjs/project
|
|
2
|
+
|
|
3
|
+
Project module for MeridianJS. Manages projects, labels, milestones, and custom project statuses. The Kanban board columns and issue status fields are driven entirely by `ProjectStatus` records — not hard-coded enums.
|
|
4
|
+
|
|
5
|
+
Auto-loaded by `@meridianjs/meridian` — you do not need to add this to `modules[]` yourself.
|
|
6
|
+
|
|
7
|
+
## Service: `projectModuleService`
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const svc = req.scope.resolve("projectModuleService") as any
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Auto-generated CRUD
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Projects
|
|
17
|
+
await svc.listProjects(filters?, options?)
|
|
18
|
+
await svc.listAndCountProjects(filters?, options?)
|
|
19
|
+
await svc.retrieveProject(id)
|
|
20
|
+
await svc.createProject(data)
|
|
21
|
+
await svc.updateProject(id, data)
|
|
22
|
+
await svc.deleteProject(id)
|
|
23
|
+
|
|
24
|
+
// Labels
|
|
25
|
+
await svc.listLabels(filters?)
|
|
26
|
+
await svc.createLabel(data)
|
|
27
|
+
await svc.updateLabel(id, data)
|
|
28
|
+
await svc.deleteLabel(id)
|
|
29
|
+
|
|
30
|
+
// Milestones
|
|
31
|
+
await svc.listMilestones(filters?)
|
|
32
|
+
await svc.createMilestone(data)
|
|
33
|
+
await svc.updateMilestone(id, data)
|
|
34
|
+
await svc.deleteMilestone(id)
|
|
35
|
+
|
|
36
|
+
// Project Statuses
|
|
37
|
+
await svc.listProjectStatuses(filters?)
|
|
38
|
+
await svc.retrieveProjectStatus(id)
|
|
39
|
+
await svc.createProjectStatus(data)
|
|
40
|
+
await svc.updateProjectStatus(id, data)
|
|
41
|
+
await svc.deleteProjectStatus(id)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Custom Methods
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Find a project by its short identifier (e.g. "SITE", "APP")
|
|
48
|
+
const project = await svc.retrieveProjectByIdentifier("SITE")
|
|
49
|
+
|
|
50
|
+
// Auto-generate an identifier from a project name
|
|
51
|
+
const identifier = svc.generateIdentifier("Website Redesign") // → "WEBS"
|
|
52
|
+
|
|
53
|
+
// List all labels for a project
|
|
54
|
+
const labels = await svc.listLabelsByProject(projectId)
|
|
55
|
+
|
|
56
|
+
// List all statuses for a project, ordered by position
|
|
57
|
+
const statuses = await svc.listStatusesByProject(projectId)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Data Models
|
|
61
|
+
|
|
62
|
+
### Project
|
|
63
|
+
|
|
64
|
+
| Field | Type | Description |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `id` | `uuid` | Primary key |
|
|
67
|
+
| `name` | `text` | Project name |
|
|
68
|
+
| `identifier` | `text` | Short uppercase code (e.g. `"SITE"`) |
|
|
69
|
+
| `description` | `text` | Optional description |
|
|
70
|
+
| `color` | `text` | Hex colour for UI |
|
|
71
|
+
| `workspace_id` | `text` | Owning workspace |
|
|
72
|
+
| `status` | `text` | `"active"` \| `"archived"` \| `"paused"` |
|
|
73
|
+
| `visibility` | `text` | `"private"` \| `"public"` \| `"workspace"` |
|
|
74
|
+
| `created_at` | `datetime` | — |
|
|
75
|
+
| `updated_at` | `datetime` | — |
|
|
76
|
+
|
|
77
|
+
### ProjectStatus
|
|
78
|
+
|
|
79
|
+
Custom Kanban columns. Created automatically by `createProjectWorkflow` with 6 defaults, then fully managed via the statuses API.
|
|
80
|
+
|
|
81
|
+
| Field | Type | Description |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `id` | `uuid` | Primary key |
|
|
84
|
+
| `project_id` | `text` | Owning project |
|
|
85
|
+
| `name` | `text` | Display name (e.g. `"In Progress"`) |
|
|
86
|
+
| `key` | `text` | Machine key (e.g. `"in_progress"`) |
|
|
87
|
+
| `color` | `text` | Hex colour for the Kanban column |
|
|
88
|
+
| `category` | `text` | `"backlog"` \| `"unstarted"` \| `"started"` \| `"completed"` \| `"cancelled"` |
|
|
89
|
+
| `position` | `number` | Column order |
|
|
90
|
+
|
|
91
|
+
### Default Statuses (seeded on project creation)
|
|
92
|
+
|
|
93
|
+
| Name | Key | Category |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| Backlog | `backlog` | `backlog` |
|
|
96
|
+
| Todo | `todo` | `unstarted` |
|
|
97
|
+
| In Progress | `in_progress` | `started` |
|
|
98
|
+
| In Review | `in_review` | `started` |
|
|
99
|
+
| Done | `done` | `completed` |
|
|
100
|
+
| Cancelled | `cancelled` | `cancelled` |
|
|
101
|
+
|
|
102
|
+
## API Routes
|
|
103
|
+
|
|
104
|
+
| Method | Path | Description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `GET/POST` | `/admin/projects` | List / create projects |
|
|
107
|
+
| `GET/PUT/DELETE` | `/admin/projects/:id` | Get / update / delete project |
|
|
108
|
+
| `GET/POST` | `/admin/projects/:id/statuses` | List / create custom statuses |
|
|
109
|
+
| `PUT/DELETE` | `/admin/projects/:id/statuses/:statusId` | Update / delete a status |
|
|
110
|
+
| `POST` | `/admin/projects/:id/statuses/reorder` | Reorder columns |
|
|
111
|
+
| `GET/POST` | `/admin/projects/:id/labels` | Manage project labels |
|
|
112
|
+
| `GET/POST` | `/admin/projects/:id/milestones` | Manage milestones |
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -21,6 +21,12 @@ declare class ProjectModuleService extends ProjectModuleService_base {
|
|
|
21
21
|
listMilestonesByProject(projectId: string): Promise<any[]>;
|
|
22
22
|
/** List all statuses for a given project, ordered by position. */
|
|
23
23
|
listStatusesByProject(projectId: string): Promise<any[]>;
|
|
24
|
+
/** Generate a random share token and set visibility to "public". */
|
|
25
|
+
generateShareToken(projectId: string): Promise<any>;
|
|
26
|
+
/** Remove the share token and set visibility back to "private". */
|
|
27
|
+
revokeShareToken(projectId: string): Promise<any>;
|
|
28
|
+
/** Find a project by share token. Returns null if not found or not public. */
|
|
29
|
+
retrieveProjectByShareToken(token: string): Promise<any | null>;
|
|
24
30
|
/** Update position field for each status to match the provided orderedIds index. */
|
|
25
31
|
reorderStatuses(projectId: string, orderedIds: string[]): Promise<void>;
|
|
26
32
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ declare class ProjectModuleService extends ProjectModuleService_base {
|
|
|
21
21
|
listMilestonesByProject(projectId: string): Promise<any[]>;
|
|
22
22
|
/** List all statuses for a given project, ordered by position. */
|
|
23
23
|
listStatusesByProject(projectId: string): Promise<any[]>;
|
|
24
|
+
/** Generate a random share token and set visibility to "public". */
|
|
25
|
+
generateShareToken(projectId: string): Promise<any>;
|
|
26
|
+
/** Remove the share token and set visibility back to "private". */
|
|
27
|
+
revokeShareToken(projectId: string): Promise<any>;
|
|
28
|
+
/** Find a project by share token. Returns null if not found or not public. */
|
|
29
|
+
retrieveProjectByShareToken(token: string): Promise<any | null>;
|
|
24
30
|
/** Update position field for each status to match the provided orderedIds index. */
|
|
25
31
|
reorderStatuses(projectId: string, orderedIds: string[]): Promise<void>;
|
|
26
32
|
}
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ var import_framework_utils7 = require("@meridianjs/framework-utils");
|
|
|
29
29
|
|
|
30
30
|
// src/service.ts
|
|
31
31
|
var import_framework_utils5 = require("@meridianjs/framework-utils");
|
|
32
|
+
var import_crypto = require("crypto");
|
|
32
33
|
|
|
33
34
|
// src/models/project.ts
|
|
34
35
|
var import_framework_utils = require("@meridianjs/framework-utils");
|
|
@@ -45,6 +46,7 @@ var Project = import_framework_utils.model.define("project", {
|
|
|
45
46
|
/** Denormalized workspace reference — no FK constraint */
|
|
46
47
|
workspace_id: import_framework_utils.model.text(),
|
|
47
48
|
owner_id: import_framework_utils.model.text().nullable(),
|
|
49
|
+
share_token: import_framework_utils.model.text().nullable(),
|
|
48
50
|
/** Arbitrary key/value storage for custom integrations */
|
|
49
51
|
metadata: import_framework_utils.model.json().nullable()
|
|
50
52
|
}, [
|
|
@@ -169,6 +171,24 @@ var ProjectModuleService = class extends (0, import_framework_utils5.MeridianSer
|
|
|
169
171
|
const repo = this.container.resolve("projectStatusRepository");
|
|
170
172
|
return repo.find({ project_id: projectId }, { orderBy: { position: "ASC" } });
|
|
171
173
|
}
|
|
174
|
+
/** Generate a random share token and set visibility to "public". */
|
|
175
|
+
async generateShareToken(projectId) {
|
|
176
|
+
const token = (0, import_crypto.randomBytes)(32).toString("hex");
|
|
177
|
+
return this.updateProject(projectId, { share_token: token, visibility: "public" });
|
|
178
|
+
}
|
|
179
|
+
/** Remove the share token and set visibility back to "private". */
|
|
180
|
+
async revokeShareToken(projectId) {
|
|
181
|
+
return this.updateProject(projectId, { share_token: null, visibility: "private" });
|
|
182
|
+
}
|
|
183
|
+
/** Find a project by share token. Returns null if not found or not public. */
|
|
184
|
+
async retrieveProjectByShareToken(token) {
|
|
185
|
+
const repo = this.container.resolve("projectRepository");
|
|
186
|
+
try {
|
|
187
|
+
return await repo.findOneOrFail({ share_token: token, visibility: "public" });
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
172
192
|
/** Update position field for each status to match the provided orderedIds index. */
|
|
173
193
|
async reorderStatuses(projectId, orderedIds) {
|
|
174
194
|
const repo = this.container.resolve("projectStatusRepository");
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { Module } from "@meridianjs/framework-utils";
|
|
|
3
3
|
|
|
4
4
|
// src/service.ts
|
|
5
5
|
import { MeridianService } from "@meridianjs/framework-utils";
|
|
6
|
+
import { randomBytes } from "crypto";
|
|
6
7
|
|
|
7
8
|
// src/models/project.ts
|
|
8
9
|
import { model } from "@meridianjs/framework-utils";
|
|
@@ -19,6 +20,7 @@ var Project = model.define("project", {
|
|
|
19
20
|
/** Denormalized workspace reference — no FK constraint */
|
|
20
21
|
workspace_id: model.text(),
|
|
21
22
|
owner_id: model.text().nullable(),
|
|
23
|
+
share_token: model.text().nullable(),
|
|
22
24
|
/** Arbitrary key/value storage for custom integrations */
|
|
23
25
|
metadata: model.json().nullable()
|
|
24
26
|
}, [
|
|
@@ -143,6 +145,24 @@ var ProjectModuleService = class extends MeridianService({
|
|
|
143
145
|
const repo = this.container.resolve("projectStatusRepository");
|
|
144
146
|
return repo.find({ project_id: projectId }, { orderBy: { position: "ASC" } });
|
|
145
147
|
}
|
|
148
|
+
/** Generate a random share token and set visibility to "public". */
|
|
149
|
+
async generateShareToken(projectId) {
|
|
150
|
+
const token = randomBytes(32).toString("hex");
|
|
151
|
+
return this.updateProject(projectId, { share_token: token, visibility: "public" });
|
|
152
|
+
}
|
|
153
|
+
/** Remove the share token and set visibility back to "private". */
|
|
154
|
+
async revokeShareToken(projectId) {
|
|
155
|
+
return this.updateProject(projectId, { share_token: null, visibility: "private" });
|
|
156
|
+
}
|
|
157
|
+
/** Find a project by share token. Returns null if not found or not public. */
|
|
158
|
+
async retrieveProjectByShareToken(token) {
|
|
159
|
+
const repo = this.container.resolve("projectRepository");
|
|
160
|
+
try {
|
|
161
|
+
return await repo.findOneOrFail({ share_token: token, visibility: "public" });
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
146
166
|
/** Update position field for each status to match the provided orderedIds index. */
|
|
147
167
|
async reorderStatuses(projectId, orderedIds) {
|
|
148
168
|
const repo = this.container.resolve("projectStatusRepository");
|