@nowline/export-msproj 0.2.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.js ADDED
@@ -0,0 +1,369 @@
1
+ // MS Project XML exporter — lossy projection of the Nowline AST onto
2
+ // Microsoft Project's import schema.
3
+ //
4
+ // Spec: specs/handoffs/m2c.md § 8.
5
+ // Decisions:
6
+ // - Resolution 6: Standard calendar block (Mon–Fri, 8h, fixed UIDs 1/2).
7
+ // - Resolution 9: single stderr summary line on lossy drops; never an error.
8
+ // - Lossy export policy: `--strict` does not escalate.
9
+ //
10
+ // Determinism: no `new Date()`. Anchoring date comes from `options.startDate`
11
+ // or `inputs.today`; calendar UIDs are fixed; Tasks numbered sequentially.
12
+ import { displayLabel, getProp, getProps, hasProp, roadmapTitle } from '@nowline/export-core';
13
+ import { buildCalendarsBlock, STANDARD_RESOURCE_CALENDAR_UID } from './calendar.js';
14
+ import { durationToMsProjMinutes, minutesToMsProjDuration } from './duration.js';
15
+ import { escapeXml, tag } from './xml.js';
16
+ const PROJECT_XMLNS = 'http://schemas.microsoft.com/project';
17
+ export function exportMsProjXml(inputs, options = {}) {
18
+ const ast = inputs.ast;
19
+ const drops = {
20
+ labels: 0,
21
+ footnote: ast.roadmapEntries.filter((e) => e.$type === 'FootnoteDeclaration').length,
22
+ bracket: 0,
23
+ style: 0,
24
+ progress: 0,
25
+ before: 0,
26
+ description: 0,
27
+ };
28
+ const projectName = escapeXml(options.projectName ?? roadmapTitle(ast.roadmapDecl ?? undefined));
29
+ const startDate = resolveStartDate(options.startDate, inputs.today);
30
+ // Resources
31
+ const resources = collectResources(ast);
32
+ // Tasks (walk roadmap entries in source order)
33
+ const tasks = collectTasks(ast, drops, startDate);
34
+ // Predecessor lookup uses Nowline ids → task UIDs.
35
+ const idToUid = new Map();
36
+ for (const t of tasks) {
37
+ if (t.nowlineId)
38
+ idToUid.set(t.nowlineId, t.uid);
39
+ }
40
+ const idToUidResource = new Map();
41
+ for (const r of resources) {
42
+ if (r.nowlineId)
43
+ idToUidResource.set(r.nowlineId, r.uid);
44
+ }
45
+ // Emit XML
46
+ const lines = [];
47
+ lines.push('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
48
+ lines.push(`<Project xmlns="${PROJECT_XMLNS}">`);
49
+ lines.push(` <Name>${projectName}</Name>`);
50
+ lines.push(` <Title>${projectName}</Title>`);
51
+ lines.push(` <StartDate>${startDate}T08:00:00</StartDate>`);
52
+ lines.push(' <ScheduleFromStart>1</ScheduleFromStart>');
53
+ lines.push(' <CalendarUID>1</CalendarUID>');
54
+ lines.push(buildCalendarsBlock());
55
+ // Tasks block
56
+ lines.push(' <Tasks>');
57
+ for (const t of tasks) {
58
+ emitTask(t, idToUid, lines);
59
+ }
60
+ lines.push(' </Tasks>');
61
+ // Resources block
62
+ if (resources.length > 0) {
63
+ lines.push(' <Resources>');
64
+ for (const r of resources) {
65
+ emitResource(r, lines);
66
+ }
67
+ lines.push(' </Resources>');
68
+ // Assignments — owners on items.
69
+ const assignments = collectAssignments(tasks, idToUidResource);
70
+ if (assignments.length > 0) {
71
+ lines.push(' <Assignments>');
72
+ for (const a of assignments) {
73
+ emitAssignment(a, lines);
74
+ }
75
+ lines.push(' </Assignments>');
76
+ }
77
+ }
78
+ lines.push('</Project>');
79
+ // Lossy summary
80
+ const summary = formatDrops(drops);
81
+ if (summary) {
82
+ const sink = options.onLossy ?? ((msg) => process.stderr.write(`${msg}\n`));
83
+ sink(summary);
84
+ }
85
+ return lines.join('\n');
86
+ }
87
+ // ---------- Tasks ----------
88
+ function collectTasks(ast, drops, startDate) {
89
+ const tasks = [];
90
+ const ctx = { uid: 1, id: 1 };
91
+ for (const entry of ast.roadmapEntries) {
92
+ if (entry.$type === 'SwimlaneDeclaration') {
93
+ const lane = entry;
94
+ const summaryUid = ctx.uid;
95
+ tasks.push({
96
+ uid: ctx.uid++,
97
+ id: ctx.id++,
98
+ name: displayLabel(lane),
99
+ outlineLevel: 1,
100
+ isSummary: true,
101
+ isMilestone: false,
102
+ durationMinutes: 0,
103
+ predecessors: [],
104
+ nowlineId: lane.name,
105
+ ownerRefs: getProps(lane, 'owner'),
106
+ });
107
+ for (const child of lane.content) {
108
+ walkSwimlaneChild(child, 2, ctx, drops, tasks, startDate);
109
+ }
110
+ // Summary spans all child rows — implicit in MSProject by id ranges,
111
+ // but we don't bother computing FinishDate / actuals.
112
+ void summaryUid;
113
+ }
114
+ else if (entry.$type === 'MilestoneDeclaration') {
115
+ const m = entry;
116
+ tasks.push({
117
+ uid: ctx.uid++,
118
+ id: ctx.id++,
119
+ name: displayLabel(m),
120
+ outlineLevel: 1,
121
+ isSummary: false,
122
+ isMilestone: true,
123
+ durationMinutes: 0,
124
+ predecessors: getProps(m, 'depends'),
125
+ nowlineId: m.name,
126
+ ownerRefs: [],
127
+ startsAt: getProp(m, 'date'),
128
+ });
129
+ if (hasProp(m, 'style'))
130
+ drops.style += 1;
131
+ }
132
+ else if (entry.$type === 'AnchorDeclaration') {
133
+ const a = entry;
134
+ tasks.push({
135
+ uid: ctx.uid++,
136
+ id: ctx.id++,
137
+ name: displayLabel(a),
138
+ outlineLevel: 1,
139
+ isSummary: false,
140
+ isMilestone: true, // Anchors → milestones in MS Project
141
+ durationMinutes: 0,
142
+ predecessors: [],
143
+ nowlineId: a.name,
144
+ ownerRefs: [],
145
+ startsAt: getProp(a, 'date'),
146
+ });
147
+ }
148
+ }
149
+ return tasks;
150
+ }
151
+ function walkSwimlaneChild(child, outline, ctx, drops, tasks, startDate) {
152
+ if (child.$type === 'ItemDeclaration') {
153
+ emitTaskRow(child, outline, ctx, drops, tasks);
154
+ }
155
+ else if (child.$type === 'GroupBlock') {
156
+ const group = child;
157
+ tasks.push({
158
+ uid: ctx.uid++,
159
+ id: ctx.id++,
160
+ name: displayLabel(group),
161
+ outlineLevel: outline,
162
+ isSummary: true,
163
+ isMilestone: false,
164
+ durationMinutes: 0,
165
+ predecessors: [],
166
+ nowlineId: group.name,
167
+ ownerRefs: [],
168
+ });
169
+ for (const grandchild of group.content) {
170
+ walkGroupChild(grandchild, outline + 1, ctx, drops, tasks, startDate);
171
+ }
172
+ }
173
+ else if (child.$type === 'ParallelBlock') {
174
+ const parallel = child;
175
+ for (const grandchild of parallel.content) {
176
+ if (grandchild.$type === 'ItemDeclaration') {
177
+ emitTaskRow(grandchild, outline, ctx, drops, tasks);
178
+ }
179
+ else if (grandchild.$type === 'GroupBlock') {
180
+ walkSwimlaneChild(grandchild, outline, ctx, drops, tasks, startDate);
181
+ }
182
+ }
183
+ }
184
+ else if (child.$type === 'DescriptionDirective') {
185
+ drops.description += 1;
186
+ }
187
+ }
188
+ function walkGroupChild(child, outline, ctx, drops, tasks, startDate) {
189
+ if (child.$type === 'ItemDeclaration') {
190
+ emitTaskRow(child, outline, ctx, drops, tasks);
191
+ }
192
+ else if (child.$type === 'GroupBlock') {
193
+ const group = child;
194
+ tasks.push({
195
+ uid: ctx.uid++,
196
+ id: ctx.id++,
197
+ name: displayLabel(group),
198
+ outlineLevel: outline,
199
+ isSummary: true,
200
+ isMilestone: false,
201
+ durationMinutes: 0,
202
+ predecessors: [],
203
+ nowlineId: group.name,
204
+ ownerRefs: [],
205
+ });
206
+ for (const grandchild of group.content) {
207
+ walkGroupChild(grandchild, outline + 1, ctx, drops, tasks, startDate);
208
+ }
209
+ }
210
+ else if (child.$type === 'ParallelBlock') {
211
+ const parallel = child;
212
+ for (const grandchild of parallel.content) {
213
+ if (grandchild.$type === 'ItemDeclaration') {
214
+ emitTaskRow(grandchild, outline, ctx, drops, tasks);
215
+ }
216
+ }
217
+ }
218
+ else if (child.$type === 'DescriptionDirective') {
219
+ drops.description += 1;
220
+ }
221
+ }
222
+ function emitTaskRow(item, outline, ctx, drops, tasks) {
223
+ countDrops(item, drops);
224
+ tasks.push({
225
+ uid: ctx.uid++,
226
+ id: ctx.id++,
227
+ name: displayLabel(item),
228
+ outlineLevel: outline,
229
+ isSummary: false,
230
+ isMilestone: false,
231
+ durationMinutes: durationToMsProjMinutes(getProp(item, 'duration') ?? getProp(item, 'size')),
232
+ predecessors: getProps(item, 'after'),
233
+ nowlineId: item.name,
234
+ ownerRefs: getProps(item, 'owner'),
235
+ });
236
+ }
237
+ function countDrops(item, drops) {
238
+ if (getProps(item, 'labels').length > 0)
239
+ drops.labels += 1;
240
+ if (hasProp(item, 'style'))
241
+ drops.style += 1;
242
+ if (hasProp(item, 'remaining'))
243
+ drops.progress += 1;
244
+ if (getProps(item, 'before').length > 0)
245
+ drops.before += 1;
246
+ if (item.description)
247
+ drops.description += 1;
248
+ }
249
+ function emitTask(t, idToUid, lines) {
250
+ lines.push(' <Task>');
251
+ lines.push(` ${tag('UID', t.uid)}`);
252
+ lines.push(` ${tag('ID', t.id)}`);
253
+ lines.push(` ${tag('Name', t.name)}`);
254
+ if (t.isSummary)
255
+ lines.push(` <Summary>1</Summary>`);
256
+ if (t.isMilestone) {
257
+ lines.push(' <Milestone>1</Milestone>');
258
+ lines.push(' <Duration>PT0H0M0S</Duration>');
259
+ }
260
+ else {
261
+ lines.push(` <Duration>${minutesToMsProjDuration(t.durationMinutes)}</Duration>`);
262
+ }
263
+ lines.push(` <OutlineLevel>${t.outlineLevel}</OutlineLevel>`);
264
+ if (t.startsAt) {
265
+ lines.push(` <Start>${t.startsAt}T08:00:00</Start>`);
266
+ }
267
+ for (const pred of t.predecessors) {
268
+ const uid = idToUid.get(pred);
269
+ if (uid !== undefined) {
270
+ lines.push(' <PredecessorLink>');
271
+ lines.push(` ${tag('PredecessorUID', uid)}`);
272
+ lines.push(' <Type>1</Type>'); // FS
273
+ lines.push(' </PredecessorLink>');
274
+ }
275
+ }
276
+ lines.push(' </Task>');
277
+ }
278
+ // ---------- Resources ----------
279
+ function collectResources(ast) {
280
+ const out = [];
281
+ const ctx = { uid: 1, id: 1 };
282
+ for (const entry of ast.roadmapEntries) {
283
+ if (entry.$type === 'PersonDeclaration') {
284
+ const p = entry;
285
+ out.push({
286
+ uid: ctx.uid++,
287
+ id: ctx.id++,
288
+ name: displayLabel(p),
289
+ nowlineId: p.name,
290
+ });
291
+ }
292
+ else if (entry.$type === 'TeamDeclaration') {
293
+ collectTeam(entry, out, ctx);
294
+ }
295
+ }
296
+ return out;
297
+ }
298
+ function collectTeam(team, out, ctx) {
299
+ out.push({
300
+ uid: ctx.uid++,
301
+ id: ctx.id++,
302
+ name: displayLabel(team),
303
+ nowlineId: team.name,
304
+ });
305
+ for (const child of team.content) {
306
+ if (child.$type === 'TeamDeclaration') {
307
+ collectTeam(child, out, ctx);
308
+ }
309
+ }
310
+ }
311
+ function emitResource(r, lines) {
312
+ lines.push(' <Resource>');
313
+ lines.push(` ${tag('UID', r.uid)}`);
314
+ lines.push(` ${tag('ID', r.id)}`);
315
+ lines.push(` ${tag('Name', r.name)}`);
316
+ lines.push(` <CalendarUID>${STANDARD_RESOURCE_CALENDAR_UID}</CalendarUID>`);
317
+ lines.push(' </Resource>');
318
+ }
319
+ function collectAssignments(tasks, idToUid) {
320
+ const out = [];
321
+ const assignmentUid = 1;
322
+ void assignmentUid;
323
+ for (const t of tasks) {
324
+ for (const owner of t.ownerRefs) {
325
+ const uid = idToUid.get(owner);
326
+ if (uid !== undefined) {
327
+ out.push({ taskUid: t.uid, resourceUid: uid });
328
+ }
329
+ }
330
+ }
331
+ return out;
332
+ }
333
+ function emitAssignment(a, lines) {
334
+ lines.push(' <Assignment>');
335
+ lines.push(` ${tag('TaskUID', a.taskUid)}`);
336
+ lines.push(` ${tag('ResourceUID', a.resourceUid)}`);
337
+ lines.push(' <Units>1</Units>');
338
+ lines.push(' </Assignment>');
339
+ }
340
+ // ---------- Lossy summary ----------
341
+ function formatDrops(drops) {
342
+ const order = [
343
+ 'labels',
344
+ 'footnote',
345
+ 'bracket',
346
+ 'style',
347
+ 'progress',
348
+ 'before',
349
+ 'description',
350
+ ];
351
+ const parts = order.filter((k) => drops[k] > 0).map((k) => `${k} (${drops[k]})`);
352
+ if (parts.length === 0)
353
+ return null;
354
+ return `nowline: msproj export dropped ${parts.length} feature kinds: ${parts.join(', ')}`;
355
+ }
356
+ // ---------- helpers ----------
357
+ function resolveStartDate(option, today) {
358
+ if (option) {
359
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(option)) {
360
+ throw new Error(`invalid msproj startDate "${option}": expected YYYY-MM-DD`);
361
+ }
362
+ return option;
363
+ }
364
+ if (today) {
365
+ return today.toISOString().slice(0, 10);
366
+ }
367
+ return '2026-01-05'; // a deterministic Monday for tests
368
+ }
369
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/xml.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export declare function escapeXml(value: string): string;
2
+ export declare function tag(name: string, value: string | number | boolean): string;
3
+ export declare function selfTag(name: string): string;
4
+ //# sourceMappingURL=xml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../src/xml.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAE1E;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C"}
package/dist/xml.js ADDED
@@ -0,0 +1,18 @@
1
+ // XML escape utilities. We don't use a generic XML library because the
2
+ // output structure is fixed and small; manual emission keeps the package
3
+ // dependency-free per Resolution 1.
4
+ export function escapeXml(value) {
5
+ return value
6
+ .replace(/&/g, '&amp;')
7
+ .replace(/</g, '&lt;')
8
+ .replace(/>/g, '&gt;')
9
+ .replace(/"/g, '&quot;')
10
+ .replace(/'/g, '&apos;');
11
+ }
12
+ export function tag(name, value) {
13
+ return `<${name}>${escapeXml(String(value))}</${name}>`;
14
+ }
15
+ export function selfTag(name) {
16
+ return `<${name}/>`;
17
+ }
18
+ //# sourceMappingURL=xml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml.js","sourceRoot":"","sources":["../src/xml.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yEAAyE;AACzE,oCAAoC;AAEpC,MAAM,UAAU,SAAS,CAAC,KAAa;IACnC,OAAO,KAAK;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,KAAgC;IAC9D,OAAO,IAAI,IAAI,IAAI,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAChC,OAAO,IAAI,IAAI,IAAI,CAAC;AACxB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@nowline/export-msproj",
3
+ "version": "0.2.0",
4
+ "description": "Nowline MS Project XML exporter — lossy export for PM tool import",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "src/"
18
+ ],
19
+ "dependencies": {
20
+ "@nowline/core": "0.2.0",
21
+ "@nowline/export-core": "0.2.0",
22
+ "@nowline/layout": "0.2.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.0.0",
26
+ "langium": "~4.2.2",
27
+ "typescript": "~5.7.0",
28
+ "vitest": "^3.1.0"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -b tsconfig.json",
32
+ "watch": "tsc -b tsconfig.json --watch",
33
+ "test": "vitest run",
34
+ "test:watch": "vitest"
35
+ }
36
+ }
@@ -0,0 +1,63 @@
1
+ // Minimal Standard base calendar emitted on every MS Project XML export.
2
+ //
3
+ // Spec: specs/handoffs/m2c.md § 8 + Resolution 6.
4
+ // - One base calendar (UID=1, Name=Standard) — Mon–Fri working
5
+ // 08:00–12:00 / 13:00–17:00, Sat/Sun non-working. Matches Microsoft's
6
+ // default project template; reliably accepted across MSProject versions.
7
+ // - One resource calendar (UID=2, Name=Standard, BaseCalendarUID=1).
8
+ //
9
+ // Calendar UIDs are fixed (1 / 2). No timestamps in the calendar block →
10
+ // deterministic across runs.
11
+
12
+ const DAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
13
+
14
+ function dayBlock(dayIndex: number): string {
15
+ const isWeekend = dayIndex === 0 || dayIndex === 6;
16
+ if (isWeekend) {
17
+ return ` <WeekDay>
18
+ <DayType>${dayIndex + 1}</DayType>
19
+ <DayWorking>0</DayWorking>
20
+ </WeekDay>`;
21
+ }
22
+ return ` <WeekDay>
23
+ <DayType>${dayIndex + 1}</DayType>
24
+ <DayWorking>1</DayWorking>
25
+ <WorkingTimes>
26
+ <WorkingTime>
27
+ <FromTime>08:00:00</FromTime>
28
+ <ToTime>12:00:00</ToTime>
29
+ </WorkingTime>
30
+ <WorkingTime>
31
+ <FromTime>13:00:00</FromTime>
32
+ <ToTime>17:00:00</ToTime>
33
+ </WorkingTime>
34
+ </WorkingTimes>
35
+ </WeekDay>`;
36
+ }
37
+
38
+ export function buildCalendarsBlock(): string {
39
+ const weekDays = [0, 1, 2, 3, 4, 5, 6].map(dayBlock).join('\n');
40
+ return ` <Calendars>
41
+ <Calendar>
42
+ <UID>1</UID>
43
+ <Name>Standard</Name>
44
+ <IsBaseCalendar>1</IsBaseCalendar>
45
+ <BaseCalendarUID>-1</BaseCalendarUID>
46
+ <WeekDays>
47
+ ${weekDays}
48
+ </WeekDays>
49
+ </Calendar>
50
+ <Calendar>
51
+ <UID>2</UID>
52
+ <Name>Standard</Name>
53
+ <IsBaseCalendar>0</IsBaseCalendar>
54
+ <BaseCalendarUID>1</BaseCalendarUID>
55
+ </Calendar>
56
+ </Calendars>`;
57
+ }
58
+
59
+ /** Resource and base calendar UIDs surfaced for cross-references. */
60
+ export const STANDARD_BASE_CALENDAR_UID = 1;
61
+ export const STANDARD_RESOURCE_CALENDAR_UID = 2;
62
+
63
+ export { DAY_NAMES };
@@ -0,0 +1,48 @@
1
+ // Convert Nowline duration tokens into MS Project's `PT...H...M...S` form.
2
+ //
3
+ // MS Project uses ISO 8601 duration "PT<minutes>M0S" (e.g. `PT480M0S` = 8h)
4
+ // for Standard-calendar working time. We resolve every duration to working
5
+ // minutes under the calendar emitted by `calendar.ts` (Mon–Fri, 8h/day).
6
+ //
7
+ // Size buckets resolve the same way as in @nowline/export-mermaid.
8
+
9
+ const SIZE_BUCKET_DAYS: Readonly<Record<string, number>> = {
10
+ xs: 1,
11
+ sm: 3,
12
+ s: 3,
13
+ md: 5,
14
+ m: 5,
15
+ lg: 10,
16
+ l: 10,
17
+ xl: 15,
18
+ };
19
+
20
+ const NUMERIC_RE = /^(\d+(?:\.\d+)?)\s*(d|w|m|y)?$/i;
21
+
22
+ const MINUTES_PER_WORKING_DAY = 8 * 60; // Standard calendar
23
+ const WORKING_DAYS_PER_WEEK = 5;
24
+ const WORKING_DAYS_PER_MONTH = 22;
25
+ const WORKING_DAYS_PER_YEAR = 252;
26
+
27
+ export function durationToMsProjMinutes(literal: string | undefined): number {
28
+ if (!literal) return MINUTES_PER_WORKING_DAY; // 1d default
29
+ const trimmed = literal.trim().toLowerCase();
30
+ if (!trimmed) return MINUTES_PER_WORKING_DAY;
31
+ if (trimmed in SIZE_BUCKET_DAYS) {
32
+ return SIZE_BUCKET_DAYS[trimmed] * MINUTES_PER_WORKING_DAY;
33
+ }
34
+ const match = NUMERIC_RE.exec(trimmed);
35
+ if (!match) return MINUTES_PER_WORKING_DAY;
36
+ const value = Number(match[1]);
37
+ const unit = (match[2] ?? 'd').toLowerCase();
38
+ if (!Number.isFinite(value) || value <= 0) return MINUTES_PER_WORKING_DAY;
39
+ let days = value;
40
+ if (unit === 'w') days = value * WORKING_DAYS_PER_WEEK;
41
+ else if (unit === 'm') days = value * WORKING_DAYS_PER_MONTH;
42
+ else if (unit === 'y') days = value * WORKING_DAYS_PER_YEAR;
43
+ return Math.round(days * MINUTES_PER_WORKING_DAY);
44
+ }
45
+
46
+ export function minutesToMsProjDuration(minutes: number): string {
47
+ return `PT${Math.max(0, Math.round(minutes))}M0S`;
48
+ }