History

refactor: extract shared normalizeProjectScripts into @t3tools/contracts

Move the duplicated script normalization logic (dedup-by-id + single runOnWorktreeCreate enforcement) from apps/web/src/store.ts and apps/server/src/projectRegistry.ts into packages/contracts/src/project.ts so both consumers share one canonical implementation. Applied via @cursor push command

Cursor Agent committed 8 months ago

Commit b236dcc1 parent ac05ddc3 files · +41 −66Browse files

Showing the changes to apps/server/src (1 of 3 files). Show all

Unified · Split

apps/server/src/projectRegistry.ts+3−34
@@ -11,6 +11,7 @@ import { · 10 unmodified lines
11 type ProjectScript,11 type ProjectScript,
12 type ProjectUpdateScriptsInput,12 type ProjectUpdateScriptsInput,
13 type ProjectUpdateScriptsResult,13 type ProjectUpdateScriptsResult,
14 normalizeProjectScripts,
14 projectAddInputSchema,15 projectAddInputSchema,
15 projectRecordSchema,16 projectRecordSchema,
16 projectRemoveInputSchema,17 projectRemoveInputSchema,
@@ -130,7 +131,7 @@ export class ProjectRegistry { · 113 unmodified lines
130 throw new Error(`Project not found: ${input.id}`);131 throw new Error(`Project not found: ${input.id}`);
131 }132 }
132 133
133 const nextScripts = dedupeScriptsById(projectScriptsSchema.parse(input.scripts));134 const nextScripts = normalizeProjectScripts(projectScriptsSchema.parse(input.scripts));
134 const nextUpdatedAt = new Date().toISOString();135 const nextUpdatedAt = new Date().toISOString();
135 const nextProject: ProjectRecord = {136 const nextProject: ProjectRecord = {
136 ...this.projects[index]!,137 ...this.projects[index]!,
@@ -167,7 +168,7 @@ export class ProjectRegistry { · 30 unmodified lines
167 cwd: normalizedCwd,168 cwd: normalizedCwd,
168 name:169 name:
169 project.name.trim().length > 0 ? project.name.trim() : inferProjectName(normalizedCwd),170 project.name.trim().length > 0 ? project.name.trim() : inferProjectName(normalizedCwd),
170 scripts: dedupeScriptsById(project.scripts),171 scripts: normalizeProjectScripts(project.scripts),
171 };172 };
172 deduped.set(normalizedCwd, normalizedProject);173 deduped.set(normalizedCwd, normalizedProject);
173 normalizedProjects.push(normalizedProject);174 normalizedProjects.push(normalizedProject);
@@ -232,35 +233,3 @@ export class ProjectRegistry { · 58 unmodified lines
232 fs.renameSync(tempFile, this.filePath);233 fs.renameSync(tempFile, this.filePath);
233 }234 }
234}235}
235
236function dedupeScriptsById(scripts: readonly ProjectScript[]): ProjectScript[] {
237 const seenIds = new Set<string>();
238 const deduped: ProjectScript[] = [];
239 for (const script of scripts) {
240 if (seenIds.has(script.id)) continue;
241 seenIds.add(script.id);
242 deduped.push({
243 id: script.id,
244 name: script.name,
245 command: script.command,
246 icon: script.icon,
247 runOnWorktreeCreate: script.runOnWorktreeCreate,
248 });
249 }
250
251 let setupAssigned = false;
252 const normalized: ProjectScript[] = [];
253 for (const script of deduped) {
254 if (!script.runOnWorktreeCreate) {
255 normalized.push(script);
256 continue;
257 }
258 if (!setupAssigned) {
259 setupAssigned = true;
260 normalized.push(script);
261 continue;
262 }
263 normalized.push({ ...script, runOnWorktreeCreate: false });
264 }
265 return normalized;
266}