mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-10-08 20:57:51 +00:00
feat(settings): Replace alert() calls with toast notifications (US-1158)
- Add duration support to toast system - Replace all 5 alert() calls in SettingsDialog with toasts - Success toasts auto-dismiss after 3s - Error/warning toasts persist until manually dismissed - Consistent with ManageTab toast patterns
This commit is contained in:
parent
0025dc5f47
commit
0f43e4e750
7 changed files with 1102 additions and 5 deletions
|
|
@ -511,3 +511,25 @@
|
|||
{"type":"task.created","taskId":"task_20260128_isj7oa","status":"todo","id":"evt_udJP0CW5DOjq","timestamp":"2026-01-28T09:08:43.805Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_mY2lH4","status":"todo","id":"evt_NK1qNmhq8BVa","timestamp":"2026-01-28T09:08:43.810Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_mY2lH4","status":"todo","id":"evt_xt1yJ4fnooxC","timestamp":"2026-01-28T09:08:43.811Z"}
|
||||
{"type":"task.status_changed","taskId":"task_20260128_CnogQO","project":"veritas-kanban","status":"done","previousStatus":"in-progress","id":"evt_cU8ml7qypcWS","timestamp":"2026-01-28T09:09:54.302Z"}
|
||||
{"type":"task.status_changed","taskId":"task_20260128_-KNOy3","project":"veritas-kanban","status":"done","previousStatus":"in-progress","id":"evt_yTJBMwcwMG55","timestamp":"2026-01-28T09:10:29.315Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_-WSaw3","project":"veritas-kanban","status":"todo","id":"evt_0MD7mJBvyDcK","timestamp":"2026-01-28T09:19:20.495Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_iFFyez","project":"veritas-kanban","status":"todo","id":"evt_v02WIexjF-c0","timestamp":"2026-01-28T09:19:33.479Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_hQpqum","project":"veritas-kanban","status":"todo","id":"evt_dJryjPl8BCS8","timestamp":"2026-01-28T09:19:48.668Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_62_v6g","project":"veritas-kanban","status":"todo","id":"evt_GhPlxwdVXEEp","timestamp":"2026-01-28T09:20:01.252Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_0hOHmd","project":"veritas-kanban","status":"todo","id":"evt_EgigZKRmCB8S","timestamp":"2026-01-28T09:20:12.261Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_KTgZMx","project":"veritas-kanban","status":"todo","id":"evt_2AwTp6w4FEQe","timestamp":"2026-01-28T09:20:22.111Z"}
|
||||
{"type":"task.created","taskId":"task_20260128_g86sZV","project":"veritas-kanban","status":"todo","id":"evt_-IY5pwmG3Xt3","timestamp":"2026-01-28T09:20:37.043Z"}
|
||||
{"type":"task.status_changed","taskId":"task_20260128_8KRThU","project":"veritas-kanban","status":"in-progress","previousStatus":"todo","id":"evt_ZcxGbZLizDuq","timestamp":"2026-01-28T09:20:48.829Z"}
|
||||
{"type":"task.status_changed","taskId":"task_20260128_8KRThU","project":"veritas-kanban","status":"done","previousStatus":"in-progress","id":"evt_bZ1gewAss99B","timestamp":"2026-01-28T09:27:59.574Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_njhuR8","project":"veritas-kanban","status":"done","id":"evt_7yObxOxuAU2i","timestamp":"2026-01-28T09:28:59.898Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_Kbjd3t","project":"veritas-kanban","status":"done","id":"evt_FNSSanv-Jq7S","timestamp":"2026-01-28T09:28:59.903Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_itHuxN","project":"veritas-kanban","status":"done","id":"evt__ydcHe_FrYEI","timestamp":"2026-01-28T09:28:59.907Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_ndyIRl","project":"veritas-kanban","status":"done","id":"evt_3ACasAI6HT7l","timestamp":"2026-01-28T09:28:59.909Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_HaNM-g","project":"veritas-kanban","status":"done","id":"evt_hi2G5vTTvi_U","timestamp":"2026-01-28T09:28:59.910Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_vYzfwx","project":"veritas-kanban","status":"done","id":"evt_L92AzZEwRG7o","timestamp":"2026-01-28T09:28:59.912Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_DUyAbh","project":"veritas-kanban","status":"done","id":"evt_IAJ3b2hdJPtA","timestamp":"2026-01-28T09:28:59.913Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_KbDn0E","project":"veritas-kanban","status":"done","id":"evt_pcQrDaRO5oJo","timestamp":"2026-01-28T09:28:59.927Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_gUHeIV","project":"veritas-kanban","status":"done","id":"evt_pVbJN-iGAoy9","timestamp":"2026-01-28T09:28:59.929Z"}
|
||||
{"type":"task.archived","taskId":"task_20260128_X6nifI","project":"veritas-kanban","status":"done","id":"evt_K4Iqlt-Dw14T","timestamp":"2026-01-28T09:28:59.931Z"}
|
||||
{"type":"task.status_changed","taskId":"task_20260128_8HU2XD","project":"veritas-kanban","status":"in-progress","previousStatus":"todo","id":"evt_4rB1Mba649ZE","timestamp":"2026-01-28T10:03:19.726Z"}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,200 @@
|
|||
[
|
||||
{
|
||||
"id": "activity_1769594599726_5lp9h98h5",
|
||||
"type": "status_changed",
|
||||
"taskId": "task_20260128_8HU2XD",
|
||||
"taskTitle": "US-1158: Replace alert() calls with toast notifications",
|
||||
"details": {
|
||||
"from": "todo",
|
||||
"status": "in-progress"
|
||||
},
|
||||
"timestamp": "2026-01-28T10:03:19.726Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592539931_twcrqcpt5",
|
||||
"type": "sprint_archived",
|
||||
"taskId": "US-1100",
|
||||
"taskTitle": "US-1100",
|
||||
"details": {
|
||||
"taskCount": 10
|
||||
},
|
||||
"timestamp": "2026-01-28T09:28:59.931Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592485058_6vdret7hw",
|
||||
"type": "task_updated",
|
||||
"taskId": "task_20260128_8KRThU",
|
||||
"taskTitle": "FEATURE REQUEST: Change Review Column to Blocked",
|
||||
"timestamp": "2026-01-28T09:28:05.058Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592479575_p0swcv4xx",
|
||||
"type": "status_changed",
|
||||
"taskId": "task_20260128_8KRThU",
|
||||
"taskTitle": "FEATURE REQUEST: Change Review Column to Blocked",
|
||||
"details": {
|
||||
"from": "in-progress",
|
||||
"status": "done"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:27:59.575Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592048834_lnlgvfmr9",
|
||||
"type": "status_changed",
|
||||
"taskId": "task_20260128_8KRThU",
|
||||
"taskTitle": "FEATURE REQUEST: Change Review Column to Blocked",
|
||||
"details": {
|
||||
"from": "todo",
|
||||
"status": "in-progress"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:20:48.834Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592048818_rv4n02og5",
|
||||
"type": "comment_added",
|
||||
"taskId": "task_20260128_8KRThU",
|
||||
"taskTitle": "FEATURE REQUEST: Change Review Column to Blocked",
|
||||
"details": {
|
||||
"author": "Veritas",
|
||||
"preview": "Sprint US-1500 created with 7 stories (US-1501–US-..."
|
||||
},
|
||||
"timestamp": "2026-01-28T09:20:48.818Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592037043_5b23m5plu",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_g86sZV",
|
||||
"taskTitle": "US-1507: Add blocked reason tracking",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "medium",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:20:37.043Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592022111_s9qvt78ma",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_KTgZMx",
|
||||
"taskTitle": "US-1506: Update test suite — review → blocked references",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "medium",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:20:22.111Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592012261_e2g8dvk3m",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_0hOHmd",
|
||||
"taskTitle": "US-1505: Data migration — convert existing review tasks to blocked",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "high",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:20:12.261Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769592001252_wkek4tpfs",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_62_v6g",
|
||||
"taskTitle": "US-1504: Propagate review → blocked across all backend routes and services",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "high",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:20:01.252Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591988668_y93p97rtf",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_hQpqum",
|
||||
"taskTitle": "US-1503: Propagate review → blocked across all frontend components",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "high",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:19:48.668Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591973480_ed1rh84tf",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_iFFyez",
|
||||
"taskTitle": "US-1502: Update Kanban column display — Blocked title + red color",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "high",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:19:33.480Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591960496_c9xm2ql3x",
|
||||
"type": "task_created",
|
||||
"taskId": "task_20260128_-WSaw3",
|
||||
"taskTitle": "US-1501: Rename TaskStatus review → blocked in shared types",
|
||||
"details": {
|
||||
"type": "code",
|
||||
"priority": "high",
|
||||
"project": "veritas-kanban"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:19:20.496Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591477776_dv9jz0ymr",
|
||||
"type": "task_updated",
|
||||
"taskId": "task_20260128_d47DtQ",
|
||||
"taskTitle": "FEATURE REQUEST: Host Kimi K2.5 Model In Azure",
|
||||
"timestamp": "2026-01-28T09:11:17.776Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591429316_9kwem5ftu",
|
||||
"type": "status_changed",
|
||||
"taskId": "task_20260128_-KNOy3",
|
||||
"taskTitle": "US-1155: Settings performance — memoization & lazy loading",
|
||||
"details": {
|
||||
"from": "in-progress",
|
||||
"status": "done"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:10:29.316Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591394306_t4h0ljnmv",
|
||||
"type": "status_changed",
|
||||
"taskId": "task_20260128_CnogQO",
|
||||
"taskTitle": "US-1157: Sanitize settings import against prototype pollution",
|
||||
"details": {
|
||||
"from": "in-progress",
|
||||
"status": "done"
|
||||
},
|
||||
"timestamp": "2026-01-28T09:09:54.306Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591389898_ue1r7w1pp",
|
||||
"type": "comment_added",
|
||||
"taskId": "task_20260128_CnogQO",
|
||||
"taskTitle": "US-1157: Sanitize settings import against prototype pollution",
|
||||
"details": {
|
||||
"author": "Veritas",
|
||||
"preview": "Already implemented in commit 3402051. Added Zod s..."
|
||||
},
|
||||
"timestamp": "2026-01-28T09:09:49.898Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591349039_t0t1gp5dq",
|
||||
"type": "comment_added",
|
||||
"taskId": "task_20260128_-KNOy3",
|
||||
"taskTitle": "US-1155: Settings performance — memoization & lazy loading",
|
||||
"details": {
|
||||
"author": "Veritas",
|
||||
"preview": "Added React.memo to shared row components, lazy-lo..."
|
||||
},
|
||||
"timestamp": "2026-01-28T09:09:09.039Z"
|
||||
},
|
||||
{
|
||||
"id": "activity_1769591243934_a59soco89",
|
||||
"type": "status_changed",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import {
|
|||
useFeatureSettings,
|
||||
useDebouncedFeatureUpdate,
|
||||
} from '@/hooks/useFeatureSettings';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import {
|
||||
Settings2, Layout, ListTodo, Cpu, Database, Bell, Archive,
|
||||
Download, Upload, RotateCcw,
|
||||
|
|
@ -95,6 +96,7 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
|||
const { settings: currentSettings } = useFeatureSettings();
|
||||
const { debouncedUpdate } = useDebouncedFeatureUpdate();
|
||||
const settingsFileInputRef = useRef<HTMLInputElement>(null);
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleExportSettings = () => {
|
||||
const blob = new Blob([JSON.stringify(currentSettings, null, 2)], { type: 'application/json' });
|
||||
|
|
@ -116,7 +118,11 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
|||
const text = await file.text();
|
||||
const imported = JSON.parse(text);
|
||||
if (!imported || typeof imported !== 'object') {
|
||||
alert('Invalid settings file: must be a JSON object');
|
||||
toast({
|
||||
title: 'Import failed',
|
||||
description: 'Invalid settings file: must be a JSON object',
|
||||
duration: Infinity,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Validate expected top-level keys
|
||||
|
|
@ -124,7 +130,11 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
|||
const importedKeys = Object.keys(imported);
|
||||
const unknownKeys = importedKeys.filter(k => !validSections.includes(k));
|
||||
if (unknownKeys.length > 0) {
|
||||
alert(`Warning: Unknown sections will be ignored: ${unknownKeys.join(', ')}`);
|
||||
toast({
|
||||
title: 'Warning',
|
||||
description: `Unknown sections will be ignored: ${unknownKeys.join(', ')}`,
|
||||
duration: Infinity,
|
||||
});
|
||||
}
|
||||
const validPatch: Record<string, any> = {};
|
||||
for (const key of importedKeys) {
|
||||
|
|
@ -133,15 +143,27 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
|||
}
|
||||
}
|
||||
if (Object.keys(validPatch).length === 0) {
|
||||
alert('No valid settings found in file');
|
||||
toast({
|
||||
title: 'Import failed',
|
||||
description: 'No valid settings found in file',
|
||||
duration: Infinity,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (confirm(`Import ${Object.keys(validPatch).length} setting sections: ${Object.keys(validPatch).join(', ')}?\n\nThis will overwrite current values.`)) {
|
||||
debouncedUpdate(validPatch);
|
||||
alert('Settings imported successfully!');
|
||||
toast({
|
||||
title: 'Import complete',
|
||||
description: 'Settings imported successfully!',
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
alert(`Import failed: ${err instanceof Error ? err.message : 'Invalid JSON'}`);
|
||||
toast({
|
||||
title: 'Import failed',
|
||||
description: err instanceof Error ? err.message : 'Invalid JSON',
|
||||
duration: Infinity,
|
||||
});
|
||||
} finally {
|
||||
if (settingsFileInputRef.current) settingsFileInputRef.current.value = '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ type ToastProps = {
|
|||
action?: ToastActionElement;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
duration?: number;
|
||||
};
|
||||
|
||||
const TOAST_LIMIT = 3;
|
||||
|
|
|
|||
741
web/src/lib/template-schema.d.ts
vendored
Normal file
741
web/src/lib/template-schema.d.ts
vendored
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
/**
|
||||
* Zod validation schema for task templates
|
||||
* Enforces strict validation, size limits, and security checks
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
/**
|
||||
* Task template schema with strict validation
|
||||
*/
|
||||
export declare const TaskTemplateSchema: z.ZodEffects<z.ZodObject<{
|
||||
id: z.ZodOptional<z.ZodString>;
|
||||
name: z.ZodEffects<z.ZodString, string, string>;
|
||||
description: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
||||
category: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
||||
version: z.ZodOptional<z.ZodNumber>;
|
||||
taskDefaults: z.ZodObject<{
|
||||
type: z.ZodOptional<z.ZodString>;
|
||||
priority: z.ZodOptional<z.ZodString>;
|
||||
project: z.ZodOptional<z.ZodString>;
|
||||
descriptionTemplate: z.ZodOptional<z.ZodString>;
|
||||
agent: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}>;
|
||||
subtaskTemplates: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
order: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}>, "many">>;
|
||||
blueprint: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
refId: z.ZodEffects<z.ZodString, string, string>;
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
taskDefaults: z.ZodOptional<z.ZodObject<{
|
||||
type: z.ZodOptional<z.ZodString>;
|
||||
priority: z.ZodOptional<z.ZodString>;
|
||||
project: z.ZodOptional<z.ZodString>;
|
||||
descriptionTemplate: z.ZodOptional<z.ZodString>;
|
||||
agent: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}>>;
|
||||
subtaskTemplates: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
order: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}>, "many">>;
|
||||
blockedByRefs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}>, "many">>;
|
||||
created: z.ZodOptional<z.ZodString>;
|
||||
updated: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}>, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}>;
|
||||
/**
|
||||
* Schema for importing templates (single or array)
|
||||
*/
|
||||
export declare const TemplateImportSchema: z.ZodUnion<[z.ZodEffects<z.ZodObject<{
|
||||
id: z.ZodOptional<z.ZodString>;
|
||||
name: z.ZodEffects<z.ZodString, string, string>;
|
||||
description: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
||||
category: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
||||
version: z.ZodOptional<z.ZodNumber>;
|
||||
taskDefaults: z.ZodObject<{
|
||||
type: z.ZodOptional<z.ZodString>;
|
||||
priority: z.ZodOptional<z.ZodString>;
|
||||
project: z.ZodOptional<z.ZodString>;
|
||||
descriptionTemplate: z.ZodOptional<z.ZodString>;
|
||||
agent: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}>;
|
||||
subtaskTemplates: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
order: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}>, "many">>;
|
||||
blueprint: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
refId: z.ZodEffects<z.ZodString, string, string>;
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
taskDefaults: z.ZodOptional<z.ZodObject<{
|
||||
type: z.ZodOptional<z.ZodString>;
|
||||
priority: z.ZodOptional<z.ZodString>;
|
||||
project: z.ZodOptional<z.ZodString>;
|
||||
descriptionTemplate: z.ZodOptional<z.ZodString>;
|
||||
agent: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}>>;
|
||||
subtaskTemplates: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
order: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}>, "many">>;
|
||||
blockedByRefs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}>, "many">>;
|
||||
created: z.ZodOptional<z.ZodString>;
|
||||
updated: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}>, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}>, z.ZodArray<z.ZodEffects<z.ZodObject<{
|
||||
id: z.ZodOptional<z.ZodString>;
|
||||
name: z.ZodEffects<z.ZodString, string, string>;
|
||||
description: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
||||
category: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
||||
version: z.ZodOptional<z.ZodNumber>;
|
||||
taskDefaults: z.ZodObject<{
|
||||
type: z.ZodOptional<z.ZodString>;
|
||||
priority: z.ZodOptional<z.ZodString>;
|
||||
project: z.ZodOptional<z.ZodString>;
|
||||
descriptionTemplate: z.ZodOptional<z.ZodString>;
|
||||
agent: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}>;
|
||||
subtaskTemplates: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
order: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}>, "many">>;
|
||||
blueprint: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
refId: z.ZodEffects<z.ZodString, string, string>;
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
taskDefaults: z.ZodOptional<z.ZodObject<{
|
||||
type: z.ZodOptional<z.ZodString>;
|
||||
priority: z.ZodOptional<z.ZodString>;
|
||||
project: z.ZodOptional<z.ZodString>;
|
||||
descriptionTemplate: z.ZodOptional<z.ZodString>;
|
||||
agent: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}, {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
}>>;
|
||||
subtaskTemplates: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||
title: z.ZodEffects<z.ZodString, string, string>;
|
||||
order: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}>, "many">>;
|
||||
blockedByRefs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}, {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}>, "many">>;
|
||||
created: z.ZodOptional<z.ZodString>;
|
||||
updated: z.ZodOptional<z.ZodString>;
|
||||
}, "strict", z.ZodTypeAny, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}>, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}, {
|
||||
name: string;
|
||||
taskDefaults: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
};
|
||||
id?: string | undefined;
|
||||
description?: string | undefined;
|
||||
created?: string | undefined;
|
||||
updated?: string | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
category?: string | undefined;
|
||||
blueprint?: {
|
||||
title: string;
|
||||
refId: string;
|
||||
taskDefaults?: {
|
||||
type?: string | undefined;
|
||||
project?: string | undefined;
|
||||
priority?: string | undefined;
|
||||
agent?: string | undefined;
|
||||
descriptionTemplate?: string | undefined;
|
||||
} | undefined;
|
||||
subtaskTemplates?: {
|
||||
title: string;
|
||||
order?: number | undefined;
|
||||
}[] | undefined;
|
||||
blockedByRefs?: string[] | undefined;
|
||||
}[] | undefined;
|
||||
version?: number | undefined;
|
||||
}>, "many">]>;
|
||||
/**
|
||||
* Type exports
|
||||
*/
|
||||
export type ValidatedTemplate = z.infer<typeof TaskTemplateSchema>;
|
||||
export type TemplateImport = z.infer<typeof TemplateImportSchema>;
|
||||
114
web/src/lib/template-schema.js
Normal file
114
web/src/lib/template-schema.js
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* Zod validation schema for task templates
|
||||
* Enforces strict validation, size limits, and security checks
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
// Dangerous keys that could lead to prototype pollution
|
||||
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
|
||||
/**
|
||||
* Check if a string contains dangerous keys
|
||||
*/
|
||||
function hasDangerousKeys(str) {
|
||||
return DANGEROUS_KEYS.some(key => str.includes(key));
|
||||
}
|
||||
/**
|
||||
* Subtask template schema
|
||||
*/
|
||||
const SubtaskTemplateSchema = z.object({
|
||||
title: z.string()
|
||||
.min(1, 'Subtask title cannot be empty')
|
||||
.refine(val => !hasDangerousKeys(val), {
|
||||
message: 'Subtask title contains forbidden keys',
|
||||
}),
|
||||
order: z.number().int().min(0).optional(),
|
||||
}).strict();
|
||||
/**
|
||||
* Blueprint task schema
|
||||
*/
|
||||
const BlueprintTaskSchema = z.object({
|
||||
refId: z.string()
|
||||
.min(1, 'Blueprint task refId cannot be empty')
|
||||
.refine(val => !hasDangerousKeys(val), {
|
||||
message: 'Blueprint refId contains forbidden keys',
|
||||
}),
|
||||
title: z.string()
|
||||
.min(1, 'Blueprint task title cannot be empty')
|
||||
.refine(val => !hasDangerousKeys(val), {
|
||||
message: 'Blueprint task title contains forbidden keys',
|
||||
}),
|
||||
taskDefaults: z.object({
|
||||
type: z.string().optional(),
|
||||
priority: z.string().optional(),
|
||||
project: z.string().optional(),
|
||||
descriptionTemplate: z.string().optional(),
|
||||
agent: z.string().optional(),
|
||||
}).strict().optional(),
|
||||
subtaskTemplates: z.array(SubtaskTemplateSchema).optional(),
|
||||
blockedByRefs: z.array(z.string()).optional(),
|
||||
}).strict();
|
||||
/**
|
||||
* Task template schema with strict validation
|
||||
*/
|
||||
export const TaskTemplateSchema = z.object({
|
||||
id: z.string().optional(), // Optional for imports
|
||||
name: z.string()
|
||||
.min(1, 'Template name is required')
|
||||
.max(100, 'Template name must be 100 characters or less')
|
||||
.refine(val => !hasDangerousKeys(val), {
|
||||
message: 'Template name contains forbidden keys',
|
||||
}),
|
||||
description: z.string()
|
||||
.max(500, 'Template description must be 500 characters or less')
|
||||
.optional()
|
||||
.refine(val => !val || !hasDangerousKeys(val), {
|
||||
message: 'Template description contains forbidden keys',
|
||||
}),
|
||||
category: z.string()
|
||||
.optional()
|
||||
.refine(val => !val || !hasDangerousKeys(val), {
|
||||
message: 'Template category contains forbidden keys',
|
||||
}),
|
||||
version: z.number().int().min(0).optional(),
|
||||
taskDefaults: z.object({
|
||||
type: z.string().optional(),
|
||||
priority: z.string().optional(),
|
||||
project: z.string().optional(),
|
||||
descriptionTemplate: z.string().optional(),
|
||||
agent: z.string().optional(),
|
||||
}).strict(),
|
||||
subtaskTemplates: z.array(SubtaskTemplateSchema)
|
||||
.max(50, 'Maximum 50 subtask templates allowed')
|
||||
.optional(),
|
||||
blueprint: z.array(BlueprintTaskSchema)
|
||||
.max(20, 'Maximum 20 blueprint tasks allowed')
|
||||
.optional(),
|
||||
created: z.string().optional(),
|
||||
updated: z.string().optional(),
|
||||
}).strict()
|
||||
.refine((data) => {
|
||||
// Validate blueprint dependency references
|
||||
if (!data.blueprint || data.blueprint.length === 0) {
|
||||
return true;
|
||||
}
|
||||
const refIds = new Set(data.blueprint.map(task => task.refId));
|
||||
for (const task of data.blueprint) {
|
||||
if (task.blockedByRefs) {
|
||||
for (const ref of task.blockedByRefs) {
|
||||
if (!refIds.has(ref)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
message: 'Blueprint dependency references must point to valid refIds within the same blueprint',
|
||||
});
|
||||
/**
|
||||
* Schema for importing templates (single or array)
|
||||
*/
|
||||
export const TemplateImportSchema = z.union([
|
||||
TaskTemplateSchema,
|
||||
z.array(TaskTemplateSchema),
|
||||
]);
|
||||
//# sourceMappingURL=template-schema.js.map
|
||||
1
web/src/lib/template-schema.js.map
Normal file
1
web/src/lib/template-schema.js.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"template-schema.js","sourceRoot":"","sources":["template-schema.ts"],"names":[],"mappings":"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"}
|
||||
Loading…
Add table
Reference in a new issue