@plandesk/api 0.17.0 → 0.19.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.
@@ -28,7 +28,10 @@ export type ScaffoldDocumentInput = {
28
28
  linkTo?: string;
29
29
  };
30
30
  export type ScaffoldPlanInput = {
31
- name: string;
31
+ /** Target an existing project; when set, the plan is added to it. Omit to create a new project. */
32
+ projectId?: string;
33
+ /** Name for a new project. Required when projectId is omitted; ignored when it is set. */
34
+ name?: string;
32
35
  description?: string | null;
33
36
  tasks: ScaffoldTaskInput[];
34
37
  edges?: ScaffoldEdgeInput[];
@@ -112,17 +112,37 @@ export function createProjectService(deps) {
112
112
  const keyToId = new Map();
113
113
  let projectId = '';
114
114
  db.transaction((tx) => {
115
- const project = dbCreateProject(tx, {
116
- name: input.name,
117
- description: input.description,
118
- });
119
- projectId = project.id;
120
- const defaultGoal = getOrCreateDefaultGoal(tx, project.id);
115
+ // Row offset so tasks scaffolded INTO a non-empty project are laid out
116
+ // below its existing nodes instead of stacking on top of them.
117
+ let startRow = 0;
118
+ if (input.projectId !== undefined) {
119
+ const existing = dbGetProject(tx, input.projectId);
120
+ if (!existing) {
121
+ throw new InvalidScaffoldError(`project not found: ${input.projectId}`);
122
+ }
123
+ projectId = existing.id;
124
+ const existingTasks = listTasks(tx, existing.id);
125
+ if (existingTasks.length > 0) {
126
+ const maxY = existingTasks.reduce((m, t) => Math.max(m, t.y ?? 0), 0);
127
+ startRow = Math.floor(maxY / 160) + 1;
128
+ }
129
+ }
130
+ else {
131
+ if (input.name === undefined || input.name.trim() === '') {
132
+ throw new InvalidScaffoldError('name is required to create a new project (or pass projectId to add to an existing one)');
133
+ }
134
+ const project = dbCreateProject(tx, {
135
+ name: input.name,
136
+ description: input.description,
137
+ });
138
+ projectId = project.id;
139
+ }
140
+ const defaultGoal = getOrCreateDefaultGoal(tx, projectId);
121
141
  input.tasks.forEach((taskInput, i) => {
122
142
  const x = taskInput.x ?? (i % 4) * 240;
123
- const y = taskInput.y ?? Math.floor(i / 4) * 160;
143
+ const y = taskInput.y ?? (startRow + Math.floor(i / 4)) * 160;
124
144
  const task = createTask(tx, {
125
- projectId: project.id,
145
+ projectId,
126
146
  goalId: defaultGoal.id,
127
147
  label: taskInput.label,
128
148
  status: taskInput.status,
@@ -140,7 +160,7 @@ export function createProjectService(deps) {
140
160
  throw new InvalidScaffoldError('edge references unknown task key');
141
161
  }
142
162
  const edge = createEdge(tx, {
143
- projectId: project.id,
163
+ projectId,
144
164
  fromTaskId,
145
165
  toTaskId,
146
166
  label: edgeInput.label ?? null,
@@ -151,7 +171,7 @@ export function createProjectService(deps) {
151
171
  for (const docInput of input.documents ?? []) {
152
172
  const linkedTaskId = docInput.linkTo !== undefined ? keyToId.get(docInput.linkTo) : undefined;
153
173
  const document = createDocument(tx, {
154
- projectId: project.id,
174
+ projectId,
155
175
  title: docInput.title,
156
176
  body: docInput.body,
157
177
  statusLine: docInput.statusLine,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plandesk/api",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -18,8 +18,8 @@
18
18
  "devDependencies": {
19
19
  "typescript": "^6.0.3",
20
20
  "vitest": "^3.2.6",
21
- "@plandesk/db": "0.17.0",
22
- "@plandesk/sync-server": "0.17.0"
21
+ "@plandesk/db": "0.19.0",
22
+ "@plandesk/sync-server": "0.19.0"
23
23
  },
24
24
  "dependencies": {
25
25
  "@hono/node-server": "^2.0.4",