Skip to content

ENG-1975 Add schema file contract and shared foundation for Obsidian export/import#1180

Open
trangdoan982 wants to merge 1 commit into
mainfrom
eng-1975-schema-foundation
Open

ENG-1975 Add schema file contract and shared foundation for Obsidian export/import#1180
trangdoan982 wants to merge 1 commit into
mainfrom
eng-1975-schema-foundation

Conversation

@trangdoan982

@trangdoan982 trangdoan982 commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

Minimal shared foundation needed by both the export (ENG-1976) and import (ENG-1977) schema features:

  • types.ts — adds DiscourseSchemaFile and DiscourseSchemaTemplate types that define the JSON schema file shape
  • specValidation.ts — adds getDgSchemaFileName(vaultName?) (produces dg-schema-<vault>.json) and DG_SCHEMA_EXPORT_VERSION = 1

Explicitly 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-types
  • Verify getDgSchemaFileName returns correct kebab-cased filename

Stack

PR 1 of 3. ENG-1976 (export) stacks on this branch.

Linear: ENG-1975

Made with Cursor


Open in Devin Review

…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>
@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

ENG-1975

@supabase

supabase Bot commented Jun 30, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
discourse-graph Skipped Skipped Jun 30, 2026 10:26pm

Request Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

id: z.string(),
label: z.string(),
complement: z.string(),
color: z.string(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Suggested change
color: z.string(),
color: z.enum(TLDRAW_COLOR_NAMES),
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +54 to +63
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),
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant