ENG-1975 Add schema file contract and shared foundation for Obsidian export/import#1180
ENG-1975 Add schema file contract and shared foundation for Obsidian export/import#1180trangdoan982 wants to merge 1 commit into
Conversation
…export/import. Adds DiscourseSchemaFile and DiscourseSchemaTemplate type definitions, plus getDgSchemaFileName and DG_SCHEMA_EXPORT_VERSION — the minimal shared primitives needed by both the schema export (ENG-1976) and import (ENG-1977) features. Co-authored-by: Cursor <cursoragent@cursor.com>
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
| id: z.string(), | ||
| label: z.string(), | ||
| complement: z.string(), | ||
| color: z.string(), |
There was a problem hiding this comment.
🟡 Schema validation accepts any color string but downstream types require one of 13 specific color names
The relation-type color is validated as any string (z.string() at apps/obsidian/src/utils/specValidation.ts:29), then force-cast to a type that only allows 13 specific color names, so an imported file with a bogus color like "rainbow" passes validation and silently violates the type contract.
Impact: Imported schema files with invalid color names pass validation, potentially causing unexpected rendering in the canvas UI.
Type mismatch between Zod schema and DiscourseRelationType.color
The DiscourseRelationType type at apps/obsidian/src/types.ts:27 defines color: TldrawColorName, which is a union of 13 specific string literals defined at apps/obsidian/src/utils/tldrawColors.ts:5-19 (e.g. "black", "blue", "red", etc.).
However, the Zod schema at line 29 uses z.string() which accepts any string. The parseDgSchemaFile function at line 82 then casts the result as DiscourseSchemaFile, hiding this mismatch from the type system.
Downstream code like COLOR_PALETTE[relationType.color] at apps/obsidian/src/components/canvas/utils/relationTypeUtils.ts:135 or direct prop assignment at apps/obsidian/src/components/canvas/overlays/DragHandleOverlay.tsx:371 relies on this being a valid TldrawColorName. While some call sites have fallbacks (e.g. toTldrawColor()), the validation layer should catch this at parse time rather than allowing invalid data through.
The fix would be to use z.enum(TLDRAW_COLOR_NAMES) instead of z.string() for the color field, matching the actual type constraint.
| color: z.string(), | |
| color: z.enum(TLDRAW_COLOR_NAMES), |
Was this helpful? React with 👍 or 👎 to provide feedback.
| export const dgSchemaFileSchema = z.object({ | ||
| version: z.literal(DG_SCHEMA_EXPORT_VERSION), | ||
| exportedAt: z.string(), | ||
| pluginVersion: z.string(), | ||
| vaultName: z.string(), | ||
| nodeTypes: z.array(discourseNodeSchema), | ||
| relationTypes: z.array(discourseRelationTypeSchema), | ||
| discourseRelations: z.array(discourseRelationSchema), | ||
| templates: z.array(templateExportSchema), | ||
| }); |
There was a problem hiding this comment.
🚩 Zod default behavior silently strips extra properties from imported schema files
The Zod schemas use z.object({...}) which by default strips unknown properties. This means if a future version of the schema adds new fields (e.g., a description on DiscourseRelationType), importing a file from that newer version will silently drop those fields without any warning. This is a design choice worth being aware of — if round-trip fidelity matters (export → share → import), consider using .passthrough() on the top-level schema, or at least documenting that extra fields are intentionally dropped.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Minimal shared foundation needed by both the export (ENG-1976) and import (ENG-1977) schema features:
types.ts— addsDiscourseSchemaFileandDiscourseSchemaTemplatetypes that define the JSON schema file shapespecValidation.ts— addsgetDgSchemaFileName(vaultName?)(producesdg-schema-<vault>.json) andDG_SCHEMA_EXPORT_VERSION = 1Explicitly out of scope: export UI, import UI, file dialog utilities, Zod validation (each lives with its single consumer).
Test plan
pnpm --filter @discourse-graphs/obsidian check-typesgetDgSchemaFileNamereturns correct kebab-cased filenameStack
PR 1 of 3. ENG-1976 (export) stacks on this branch.
Linear: ENG-1975
Made with Cursor