@meridianjs/project 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/dist/index.d.mts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +216 -0
- package/dist/index.mjs +190 -0
- package/package.json +43 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as _meridianjs_types from '@meridianjs/types';
|
|
2
|
+
import { MeridianContainer } from '@meridianjs/types';
|
|
3
|
+
|
|
4
|
+
declare const ProjectModuleService_base: new (container: MeridianContainer) => _meridianjs_types.IModuleService;
|
|
5
|
+
declare class ProjectModuleService extends ProjectModuleService_base {
|
|
6
|
+
private readonly container;
|
|
7
|
+
constructor(container: MeridianContainer);
|
|
8
|
+
/** Find a project by its short identifier code (e.g. "PROJ"). */
|
|
9
|
+
retrieveProjectByIdentifier(identifier: string): Promise<any | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a 3–5 character uppercase alphanumeric identifier from a project name.
|
|
12
|
+
* - Prefer at least 4 chars; if the name is only 3 letters, use those (e.g. "API" → "API").
|
|
13
|
+
* - Multiple words: acronym (first letter of each); if < 4 chars, extend with subsequent letters.
|
|
14
|
+
* - Single word 4+: first 4 (e.g. "Backend" → "BACK"). Single word 1–2: pad to 4 with "X".
|
|
15
|
+
* Examples: "My Project" → "MPYR", "API" → "API", "Backend" → "BACK", "Alpha Beta" → "ABLP".
|
|
16
|
+
*/
|
|
17
|
+
generateIdentifier(name: string): string;
|
|
18
|
+
/** List all labels for a given project. */
|
|
19
|
+
listLabelsByProject(projectId: string): Promise<any[]>;
|
|
20
|
+
/** List all milestones for a given project. */
|
|
21
|
+
listMilestonesByProject(projectId: string): Promise<any[]>;
|
|
22
|
+
/** List all statuses for a given project, ordered by position. */
|
|
23
|
+
listStatusesByProject(projectId: string): Promise<any[]>;
|
|
24
|
+
/** Update position field for each status to match the provided orderedIds index. */
|
|
25
|
+
reorderStatuses(projectId: string, orderedIds: string[]): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare const PROJECT_MODULE = "projectModuleService";
|
|
29
|
+
declare const _default: _meridianjs_types.ModuleDefinition;
|
|
30
|
+
|
|
31
|
+
export { PROJECT_MODULE, ProjectModuleService, _default as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as _meridianjs_types from '@meridianjs/types';
|
|
2
|
+
import { MeridianContainer } from '@meridianjs/types';
|
|
3
|
+
|
|
4
|
+
declare const ProjectModuleService_base: new (container: MeridianContainer) => _meridianjs_types.IModuleService;
|
|
5
|
+
declare class ProjectModuleService extends ProjectModuleService_base {
|
|
6
|
+
private readonly container;
|
|
7
|
+
constructor(container: MeridianContainer);
|
|
8
|
+
/** Find a project by its short identifier code (e.g. "PROJ"). */
|
|
9
|
+
retrieveProjectByIdentifier(identifier: string): Promise<any | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a 3–5 character uppercase alphanumeric identifier from a project name.
|
|
12
|
+
* - Prefer at least 4 chars; if the name is only 3 letters, use those (e.g. "API" → "API").
|
|
13
|
+
* - Multiple words: acronym (first letter of each); if < 4 chars, extend with subsequent letters.
|
|
14
|
+
* - Single word 4+: first 4 (e.g. "Backend" → "BACK"). Single word 1–2: pad to 4 with "X".
|
|
15
|
+
* Examples: "My Project" → "MPYR", "API" → "API", "Backend" → "BACK", "Alpha Beta" → "ABLP".
|
|
16
|
+
*/
|
|
17
|
+
generateIdentifier(name: string): string;
|
|
18
|
+
/** List all labels for a given project. */
|
|
19
|
+
listLabelsByProject(projectId: string): Promise<any[]>;
|
|
20
|
+
/** List all milestones for a given project. */
|
|
21
|
+
listMilestonesByProject(projectId: string): Promise<any[]>;
|
|
22
|
+
/** List all statuses for a given project, ordered by position. */
|
|
23
|
+
listStatusesByProject(projectId: string): Promise<any[]>;
|
|
24
|
+
/** Update position field for each status to match the provided orderedIds index. */
|
|
25
|
+
reorderStatuses(projectId: string, orderedIds: string[]): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare const PROJECT_MODULE = "projectModuleService";
|
|
29
|
+
declare const _default: _meridianjs_types.ModuleDefinition;
|
|
30
|
+
|
|
31
|
+
export { PROJECT_MODULE, ProjectModuleService, _default as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PROJECT_MODULE: () => PROJECT_MODULE,
|
|
24
|
+
ProjectModuleService: () => ProjectModuleService,
|
|
25
|
+
default: () => index_default
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var import_framework_utils7 = require("@meridianjs/framework-utils");
|
|
29
|
+
|
|
30
|
+
// src/service.ts
|
|
31
|
+
var import_framework_utils5 = require("@meridianjs/framework-utils");
|
|
32
|
+
|
|
33
|
+
// src/models/project.ts
|
|
34
|
+
var import_framework_utils = require("@meridianjs/framework-utils");
|
|
35
|
+
var Project = import_framework_utils.model.define("project", {
|
|
36
|
+
id: import_framework_utils.model.id().primaryKey(),
|
|
37
|
+
name: import_framework_utils.model.text(),
|
|
38
|
+
/** Short uppercase code used as issue identifier prefix, e.g. "PROJ" */
|
|
39
|
+
identifier: import_framework_utils.model.text(),
|
|
40
|
+
description: import_framework_utils.model.text().nullable(),
|
|
41
|
+
status: import_framework_utils.model.enum(["active", "archived", "paused"]).default("active"),
|
|
42
|
+
visibility: import_framework_utils.model.enum(["private", "public", "workspace"]).default("private"),
|
|
43
|
+
icon: import_framework_utils.model.text().nullable(),
|
|
44
|
+
color: import_framework_utils.model.text().nullable(),
|
|
45
|
+
/** Denormalized workspace reference — no FK constraint */
|
|
46
|
+
workspace_id: import_framework_utils.model.text(),
|
|
47
|
+
owner_id: import_framework_utils.model.text().nullable()
|
|
48
|
+
}, [
|
|
49
|
+
{ columns: ["workspace_id"] },
|
|
50
|
+
{ columns: ["identifier"], unique: true }
|
|
51
|
+
]);
|
|
52
|
+
var project_default = Project;
|
|
53
|
+
|
|
54
|
+
// src/models/label.ts
|
|
55
|
+
var import_framework_utils2 = require("@meridianjs/framework-utils");
|
|
56
|
+
var Label = import_framework_utils2.model.define("label", {
|
|
57
|
+
id: import_framework_utils2.model.id().primaryKey(),
|
|
58
|
+
name: import_framework_utils2.model.text(),
|
|
59
|
+
color: import_framework_utils2.model.text(),
|
|
60
|
+
project_id: import_framework_utils2.model.text()
|
|
61
|
+
});
|
|
62
|
+
var label_default = Label;
|
|
63
|
+
|
|
64
|
+
// src/models/milestone.ts
|
|
65
|
+
var import_framework_utils3 = require("@meridianjs/framework-utils");
|
|
66
|
+
var Milestone = import_framework_utils3.model.define("milestone", {
|
|
67
|
+
id: import_framework_utils3.model.id().primaryKey(),
|
|
68
|
+
name: import_framework_utils3.model.text(),
|
|
69
|
+
description: import_framework_utils3.model.text().nullable(),
|
|
70
|
+
project_id: import_framework_utils3.model.text(),
|
|
71
|
+
status: import_framework_utils3.model.enum(["open", "closed"]).default("open"),
|
|
72
|
+
due_date: import_framework_utils3.model.date().nullable()
|
|
73
|
+
});
|
|
74
|
+
var milestone_default = Milestone;
|
|
75
|
+
|
|
76
|
+
// src/models/project-status.ts
|
|
77
|
+
var import_framework_utils4 = require("@meridianjs/framework-utils");
|
|
78
|
+
var ProjectStatus = import_framework_utils4.model.define("project_status", {
|
|
79
|
+
id: import_framework_utils4.model.id().primaryKey(),
|
|
80
|
+
/** Denormalized project reference — no FK constraint */
|
|
81
|
+
project_id: import_framework_utils4.model.text(),
|
|
82
|
+
/** Display label, e.g. "In Progress" */
|
|
83
|
+
name: import_framework_utils4.model.text(),
|
|
84
|
+
/** URL-safe slug stored on Issue.status, e.g. "in_progress" */
|
|
85
|
+
key: import_framework_utils4.model.text(),
|
|
86
|
+
/** Hex color, e.g. "#6366f1" */
|
|
87
|
+
color: import_framework_utils4.model.text(),
|
|
88
|
+
/** Semantic category used for board icon and grouping */
|
|
89
|
+
category: import_framework_utils4.model.enum(["backlog", "unstarted", "started", "completed", "cancelled"]),
|
|
90
|
+
/** Zero-indexed column order */
|
|
91
|
+
position: import_framework_utils4.model.number()
|
|
92
|
+
}, [
|
|
93
|
+
{ columns: ["project_id"] }
|
|
94
|
+
]);
|
|
95
|
+
var project_status_default = ProjectStatus;
|
|
96
|
+
|
|
97
|
+
// src/service.ts
|
|
98
|
+
var ProjectModuleService = class extends (0, import_framework_utils5.MeridianService)({
|
|
99
|
+
Project: project_default,
|
|
100
|
+
Label: label_default,
|
|
101
|
+
Milestone: milestone_default,
|
|
102
|
+
ProjectStatus: project_status_default
|
|
103
|
+
}) {
|
|
104
|
+
container;
|
|
105
|
+
constructor(container) {
|
|
106
|
+
super(container);
|
|
107
|
+
this.container = container;
|
|
108
|
+
}
|
|
109
|
+
/** Find a project by its short identifier code (e.g. "PROJ"). */
|
|
110
|
+
async retrieveProjectByIdentifier(identifier) {
|
|
111
|
+
const repo = this.container.resolve("projectRepository");
|
|
112
|
+
try {
|
|
113
|
+
return await repo.findOneOrFail({ identifier: identifier.toUpperCase() });
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Generate a 3–5 character uppercase alphanumeric identifier from a project name.
|
|
120
|
+
* - Prefer at least 4 chars; if the name is only 3 letters, use those (e.g. "API" → "API").
|
|
121
|
+
* - Multiple words: acronym (first letter of each); if < 4 chars, extend with subsequent letters.
|
|
122
|
+
* - Single word 4+: first 4 (e.g. "Backend" → "BACK"). Single word 1–2: pad to 4 with "X".
|
|
123
|
+
* Examples: "My Project" → "MPYR", "API" → "API", "Backend" → "BACK", "Alpha Beta" → "ABLP".
|
|
124
|
+
*/
|
|
125
|
+
generateIdentifier(name) {
|
|
126
|
+
const words = name.trim().split(/\s+/).map((w) => w.replace(/[^A-Za-z0-9]/g, "").toUpperCase()).filter(Boolean);
|
|
127
|
+
if (words.length === 0) return "PROJ";
|
|
128
|
+
const maxLen = 5;
|
|
129
|
+
if (words.length === 1) {
|
|
130
|
+
const word = words[0];
|
|
131
|
+
if (word.length >= 4) return word.substring(0, maxLen);
|
|
132
|
+
if (word.length === 3) return word;
|
|
133
|
+
return word.padEnd(4, "X").substring(0, maxLen);
|
|
134
|
+
}
|
|
135
|
+
let acronym = words.map((w) => w[0]).join("");
|
|
136
|
+
if (acronym.length >= 4) return acronym.substring(0, maxLen);
|
|
137
|
+
let result = acronym;
|
|
138
|
+
let wi = 0;
|
|
139
|
+
let ci = 1;
|
|
140
|
+
while (result.length < 4 && wi < words.length) {
|
|
141
|
+
const word = words[wi];
|
|
142
|
+
if (ci < word.length) {
|
|
143
|
+
result += word[ci];
|
|
144
|
+
ci++;
|
|
145
|
+
} else {
|
|
146
|
+
wi++;
|
|
147
|
+
ci = 1;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
result = result.substring(0, maxLen);
|
|
151
|
+
return result.length >= 4 ? result : result.padEnd(4, "X").substring(0, maxLen);
|
|
152
|
+
}
|
|
153
|
+
/** List all labels for a given project. */
|
|
154
|
+
async listLabelsByProject(projectId) {
|
|
155
|
+
const repo = this.container.resolve("labelRepository");
|
|
156
|
+
return repo.find({ project_id: projectId });
|
|
157
|
+
}
|
|
158
|
+
/** List all milestones for a given project. */
|
|
159
|
+
async listMilestonesByProject(projectId) {
|
|
160
|
+
const repo = this.container.resolve("milestoneRepository");
|
|
161
|
+
return repo.find({ project_id: projectId });
|
|
162
|
+
}
|
|
163
|
+
/** List all statuses for a given project, ordered by position. */
|
|
164
|
+
async listStatusesByProject(projectId) {
|
|
165
|
+
const repo = this.container.resolve("projectStatusRepository");
|
|
166
|
+
return repo.find({ project_id: projectId }, { orderBy: { position: "ASC" } });
|
|
167
|
+
}
|
|
168
|
+
/** Update position field for each status to match the provided orderedIds index. */
|
|
169
|
+
async reorderStatuses(projectId, orderedIds) {
|
|
170
|
+
const repo = this.container.resolve("projectStatusRepository");
|
|
171
|
+
for (let i = 0; i < orderedIds.length; i++) {
|
|
172
|
+
const entity = await repo.findOneOrFail({ id: orderedIds[i], project_id: projectId });
|
|
173
|
+
Object.assign(entity, { position: i });
|
|
174
|
+
}
|
|
175
|
+
await repo.flush();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// src/loaders/default.ts
|
|
180
|
+
var import_framework_utils6 = require("@meridianjs/framework-utils");
|
|
181
|
+
var ProjectSchema = (0, import_framework_utils6.dmlToEntitySchema)(project_default);
|
|
182
|
+
var LabelSchema = (0, import_framework_utils6.dmlToEntitySchema)(label_default);
|
|
183
|
+
var MilestoneSchema = (0, import_framework_utils6.dmlToEntitySchema)(milestone_default);
|
|
184
|
+
var ProjectStatusSchema = (0, import_framework_utils6.dmlToEntitySchema)(project_status_default);
|
|
185
|
+
var entitySchemas = [ProjectSchema, LabelSchema, MilestoneSchema, ProjectStatusSchema];
|
|
186
|
+
async function defaultLoader({ container }) {
|
|
187
|
+
const config = container.resolve("config");
|
|
188
|
+
const { databaseUrl } = config.projectConfig;
|
|
189
|
+
const orm = await (0, import_framework_utils6.createModuleOrm)(entitySchemas, databaseUrl);
|
|
190
|
+
const em = orm.em.fork();
|
|
191
|
+
container.register({
|
|
192
|
+
projectRepository: (0, import_framework_utils6.createRepository)(em, "project"),
|
|
193
|
+
labelRepository: (0, import_framework_utils6.createRepository)(em, "label"),
|
|
194
|
+
milestoneRepository: (0, import_framework_utils6.createRepository)(em, "milestone"),
|
|
195
|
+
projectStatusRepository: (0, import_framework_utils6.createRepository)(em, "project_status"),
|
|
196
|
+
projectOrm: orm
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/index.ts
|
|
201
|
+
var PROJECT_MODULE = "projectModuleService";
|
|
202
|
+
var index_default = (0, import_framework_utils7.Module)(PROJECT_MODULE, {
|
|
203
|
+
service: ProjectModuleService,
|
|
204
|
+
models: [project_default, label_default, milestone_default, project_status_default],
|
|
205
|
+
loaders: [defaultLoader],
|
|
206
|
+
linkable: {
|
|
207
|
+
project: { tableName: "project", primaryKey: "id" },
|
|
208
|
+
label: { tableName: "label", primaryKey: "id" },
|
|
209
|
+
milestone: { tableName: "milestone", primaryKey: "id" }
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
213
|
+
0 && (module.exports = {
|
|
214
|
+
PROJECT_MODULE,
|
|
215
|
+
ProjectModuleService
|
|
216
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Module } from "@meridianjs/framework-utils";
|
|
3
|
+
|
|
4
|
+
// src/service.ts
|
|
5
|
+
import { MeridianService } from "@meridianjs/framework-utils";
|
|
6
|
+
|
|
7
|
+
// src/models/project.ts
|
|
8
|
+
import { model } from "@meridianjs/framework-utils";
|
|
9
|
+
var Project = model.define("project", {
|
|
10
|
+
id: model.id().primaryKey(),
|
|
11
|
+
name: model.text(),
|
|
12
|
+
/** Short uppercase code used as issue identifier prefix, e.g. "PROJ" */
|
|
13
|
+
identifier: model.text(),
|
|
14
|
+
description: model.text().nullable(),
|
|
15
|
+
status: model.enum(["active", "archived", "paused"]).default("active"),
|
|
16
|
+
visibility: model.enum(["private", "public", "workspace"]).default("private"),
|
|
17
|
+
icon: model.text().nullable(),
|
|
18
|
+
color: model.text().nullable(),
|
|
19
|
+
/** Denormalized workspace reference — no FK constraint */
|
|
20
|
+
workspace_id: model.text(),
|
|
21
|
+
owner_id: model.text().nullable()
|
|
22
|
+
}, [
|
|
23
|
+
{ columns: ["workspace_id"] },
|
|
24
|
+
{ columns: ["identifier"], unique: true }
|
|
25
|
+
]);
|
|
26
|
+
var project_default = Project;
|
|
27
|
+
|
|
28
|
+
// src/models/label.ts
|
|
29
|
+
import { model as model2 } from "@meridianjs/framework-utils";
|
|
30
|
+
var Label = model2.define("label", {
|
|
31
|
+
id: model2.id().primaryKey(),
|
|
32
|
+
name: model2.text(),
|
|
33
|
+
color: model2.text(),
|
|
34
|
+
project_id: model2.text()
|
|
35
|
+
});
|
|
36
|
+
var label_default = Label;
|
|
37
|
+
|
|
38
|
+
// src/models/milestone.ts
|
|
39
|
+
import { model as model3 } from "@meridianjs/framework-utils";
|
|
40
|
+
var Milestone = model3.define("milestone", {
|
|
41
|
+
id: model3.id().primaryKey(),
|
|
42
|
+
name: model3.text(),
|
|
43
|
+
description: model3.text().nullable(),
|
|
44
|
+
project_id: model3.text(),
|
|
45
|
+
status: model3.enum(["open", "closed"]).default("open"),
|
|
46
|
+
due_date: model3.date().nullable()
|
|
47
|
+
});
|
|
48
|
+
var milestone_default = Milestone;
|
|
49
|
+
|
|
50
|
+
// src/models/project-status.ts
|
|
51
|
+
import { model as model4 } from "@meridianjs/framework-utils";
|
|
52
|
+
var ProjectStatus = model4.define("project_status", {
|
|
53
|
+
id: model4.id().primaryKey(),
|
|
54
|
+
/** Denormalized project reference — no FK constraint */
|
|
55
|
+
project_id: model4.text(),
|
|
56
|
+
/** Display label, e.g. "In Progress" */
|
|
57
|
+
name: model4.text(),
|
|
58
|
+
/** URL-safe slug stored on Issue.status, e.g. "in_progress" */
|
|
59
|
+
key: model4.text(),
|
|
60
|
+
/** Hex color, e.g. "#6366f1" */
|
|
61
|
+
color: model4.text(),
|
|
62
|
+
/** Semantic category used for board icon and grouping */
|
|
63
|
+
category: model4.enum(["backlog", "unstarted", "started", "completed", "cancelled"]),
|
|
64
|
+
/** Zero-indexed column order */
|
|
65
|
+
position: model4.number()
|
|
66
|
+
}, [
|
|
67
|
+
{ columns: ["project_id"] }
|
|
68
|
+
]);
|
|
69
|
+
var project_status_default = ProjectStatus;
|
|
70
|
+
|
|
71
|
+
// src/service.ts
|
|
72
|
+
var ProjectModuleService = class extends MeridianService({
|
|
73
|
+
Project: project_default,
|
|
74
|
+
Label: label_default,
|
|
75
|
+
Milestone: milestone_default,
|
|
76
|
+
ProjectStatus: project_status_default
|
|
77
|
+
}) {
|
|
78
|
+
container;
|
|
79
|
+
constructor(container) {
|
|
80
|
+
super(container);
|
|
81
|
+
this.container = container;
|
|
82
|
+
}
|
|
83
|
+
/** Find a project by its short identifier code (e.g. "PROJ"). */
|
|
84
|
+
async retrieveProjectByIdentifier(identifier) {
|
|
85
|
+
const repo = this.container.resolve("projectRepository");
|
|
86
|
+
try {
|
|
87
|
+
return await repo.findOneOrFail({ identifier: identifier.toUpperCase() });
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generate a 3–5 character uppercase alphanumeric identifier from a project name.
|
|
94
|
+
* - Prefer at least 4 chars; if the name is only 3 letters, use those (e.g. "API" → "API").
|
|
95
|
+
* - Multiple words: acronym (first letter of each); if < 4 chars, extend with subsequent letters.
|
|
96
|
+
* - Single word 4+: first 4 (e.g. "Backend" → "BACK"). Single word 1–2: pad to 4 with "X".
|
|
97
|
+
* Examples: "My Project" → "MPYR", "API" → "API", "Backend" → "BACK", "Alpha Beta" → "ABLP".
|
|
98
|
+
*/
|
|
99
|
+
generateIdentifier(name) {
|
|
100
|
+
const words = name.trim().split(/\s+/).map((w) => w.replace(/[^A-Za-z0-9]/g, "").toUpperCase()).filter(Boolean);
|
|
101
|
+
if (words.length === 0) return "PROJ";
|
|
102
|
+
const maxLen = 5;
|
|
103
|
+
if (words.length === 1) {
|
|
104
|
+
const word = words[0];
|
|
105
|
+
if (word.length >= 4) return word.substring(0, maxLen);
|
|
106
|
+
if (word.length === 3) return word;
|
|
107
|
+
return word.padEnd(4, "X").substring(0, maxLen);
|
|
108
|
+
}
|
|
109
|
+
let acronym = words.map((w) => w[0]).join("");
|
|
110
|
+
if (acronym.length >= 4) return acronym.substring(0, maxLen);
|
|
111
|
+
let result = acronym;
|
|
112
|
+
let wi = 0;
|
|
113
|
+
let ci = 1;
|
|
114
|
+
while (result.length < 4 && wi < words.length) {
|
|
115
|
+
const word = words[wi];
|
|
116
|
+
if (ci < word.length) {
|
|
117
|
+
result += word[ci];
|
|
118
|
+
ci++;
|
|
119
|
+
} else {
|
|
120
|
+
wi++;
|
|
121
|
+
ci = 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
result = result.substring(0, maxLen);
|
|
125
|
+
return result.length >= 4 ? result : result.padEnd(4, "X").substring(0, maxLen);
|
|
126
|
+
}
|
|
127
|
+
/** List all labels for a given project. */
|
|
128
|
+
async listLabelsByProject(projectId) {
|
|
129
|
+
const repo = this.container.resolve("labelRepository");
|
|
130
|
+
return repo.find({ project_id: projectId });
|
|
131
|
+
}
|
|
132
|
+
/** List all milestones for a given project. */
|
|
133
|
+
async listMilestonesByProject(projectId) {
|
|
134
|
+
const repo = this.container.resolve("milestoneRepository");
|
|
135
|
+
return repo.find({ project_id: projectId });
|
|
136
|
+
}
|
|
137
|
+
/** List all statuses for a given project, ordered by position. */
|
|
138
|
+
async listStatusesByProject(projectId) {
|
|
139
|
+
const repo = this.container.resolve("projectStatusRepository");
|
|
140
|
+
return repo.find({ project_id: projectId }, { orderBy: { position: "ASC" } });
|
|
141
|
+
}
|
|
142
|
+
/** Update position field for each status to match the provided orderedIds index. */
|
|
143
|
+
async reorderStatuses(projectId, orderedIds) {
|
|
144
|
+
const repo = this.container.resolve("projectStatusRepository");
|
|
145
|
+
for (let i = 0; i < orderedIds.length; i++) {
|
|
146
|
+
const entity = await repo.findOneOrFail({ id: orderedIds[i], project_id: projectId });
|
|
147
|
+
Object.assign(entity, { position: i });
|
|
148
|
+
}
|
|
149
|
+
await repo.flush();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/loaders/default.ts
|
|
154
|
+
import { dmlToEntitySchema, createRepository, createModuleOrm } from "@meridianjs/framework-utils";
|
|
155
|
+
var ProjectSchema = dmlToEntitySchema(project_default);
|
|
156
|
+
var LabelSchema = dmlToEntitySchema(label_default);
|
|
157
|
+
var MilestoneSchema = dmlToEntitySchema(milestone_default);
|
|
158
|
+
var ProjectStatusSchema = dmlToEntitySchema(project_status_default);
|
|
159
|
+
var entitySchemas = [ProjectSchema, LabelSchema, MilestoneSchema, ProjectStatusSchema];
|
|
160
|
+
async function defaultLoader({ container }) {
|
|
161
|
+
const config = container.resolve("config");
|
|
162
|
+
const { databaseUrl } = config.projectConfig;
|
|
163
|
+
const orm = await createModuleOrm(entitySchemas, databaseUrl);
|
|
164
|
+
const em = orm.em.fork();
|
|
165
|
+
container.register({
|
|
166
|
+
projectRepository: createRepository(em, "project"),
|
|
167
|
+
labelRepository: createRepository(em, "label"),
|
|
168
|
+
milestoneRepository: createRepository(em, "milestone"),
|
|
169
|
+
projectStatusRepository: createRepository(em, "project_status"),
|
|
170
|
+
projectOrm: orm
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/index.ts
|
|
175
|
+
var PROJECT_MODULE = "projectModuleService";
|
|
176
|
+
var index_default = Module(PROJECT_MODULE, {
|
|
177
|
+
service: ProjectModuleService,
|
|
178
|
+
models: [project_default, label_default, milestone_default, project_status_default],
|
|
179
|
+
loaders: [defaultLoader],
|
|
180
|
+
linkable: {
|
|
181
|
+
project: { tableName: "project", primaryKey: "id" },
|
|
182
|
+
label: { tableName: "label", primaryKey: "id" },
|
|
183
|
+
milestone: { tableName: "milestone", primaryKey: "id" }
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
export {
|
|
187
|
+
PROJECT_MODULE,
|
|
188
|
+
ProjectModuleService,
|
|
189
|
+
index_default as default
|
|
190
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meridianjs/project",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Meridian project module — Project, Label, Milestone domain models",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
22
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"clean": "rm -rf dist",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@meridianjs/framework-utils": "^0.1.0",
|
|
29
|
+
"@meridianjs/types": "^0.1.0",
|
|
30
|
+
"@mikro-orm/core": "^6.4.3",
|
|
31
|
+
"@mikro-orm/postgresql": "^6.4.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"tsup": "^8.3.5",
|
|
35
|
+
"typescript": "*"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
}
|
|
43
|
+
}
|