Skip to content

AI Prompt: Forma3D.Connect — Phase 5r: Metadata Type Definitions

Purpose: This prompt instructs an AI to replace generic Record<string, unknown> types with specific metadata interfaces
Estimated Effort: 4-6 hours
Prerequisites: Phase 5q completed (Shipment Tests)
Output: Typed metadata interfaces for all contexts
Status: 🟡 PENDING


🎯 Mission

You are implementing Phase 5r: Metadata Type Definitions — addressing TD-016 (Generic Record Types for Metadata) from the technical debt register.

Why This Matters:

Extensive use of Record<string, unknown> for metadata causes:

  1. No Type Safety: Any data can be stored, no compile-time checks
  2. Runtime Errors: Invalid data shapes cause crashes
  3. Poor IDE Support: No autocomplete or documentation
  4. Schema Drift: Different parts of code expect different shapes

Phase 5r delivers:

  • Specific metadata interfaces for each context
  • Zod schemas for runtime validation
  • Type guards for safe access

📋 Context: Technical Debt Item

TD-016: Generic Record Types for Metadata

Attribute Value
Type Code Debt
Priority Medium
Location Throughout codebase (41+ occurrences)
Interest Rate Low-Medium
Principal (Effort) 4-6 hours

🛠️ Implementation

Phase 1: Identify Metadata Usage (1 hour)

Search for all Record<string, unknown> usage:

rg "Record<string, unknown>" apps/ libs/ --type ts

Phase 2: Create Metadata Schemas (2 hours)

Create libs/domain/src/schemas/metadata/:

// order-metadata.schema.ts
import { z } from 'zod';

export const OrderMetadataSchema = z.object({
  shopifyNoteAttributes: z.array(z.object({
    name: z.string(),
    value: z.string(),
  })).optional(),
  customFields: z.record(z.string()).optional(),
  source: z.enum(['shopify', 'manual', 'api']).optional(),
}).passthrough();

export type OrderMetadata = z.infer<typeof OrderMetadataSchema>;

// shipment-metadata.schema.ts
export const ShipmentMetadataSchema = z.object({
  weight: z.number().optional(),
  dimensions: z.object({
    length: z.number(),
    width: z.number(),
    height: z.number(),
    unit: z.enum(['cm', 'in']),
  }).optional(),
  sendcloudParcelId: z.string().optional(),
}).passthrough();

export type ShipmentMetadata = z.infer<typeof ShipmentMetadataSchema>;

// print-job-metadata.schema.ts
export const PrintJobMetadataSchema = z.object({
  simplyPrintJobId: z.string().optional(),
  printerId: z.string().optional(),
  filamentUsed: z.number().optional(),
  printTimeMinutes: z.number().optional(),
}).passthrough();

export type PrintJobMetadata = z.infer<typeof PrintJobMetadataSchema>;

Phase 3: Update Entity Types (2 hours)

Replace Record<string, unknown> with specific types:

// Before
metadata: Record<string, unknown> | null;

// After
metadata: OrderMetadata | null;

Phase 4: Add Type Guards (1 hour)

export function isOrderMetadata(value: unknown): value is OrderMetadata {
  return OrderMetadataSchema.safeParse(value).success;
}

📁 Files to Create/Modify

New Files

libs/domain/src/schemas/metadata/order-metadata.schema.ts
libs/domain/src/schemas/metadata/shipment-metadata.schema.ts
libs/domain/src/schemas/metadata/print-job-metadata.schema.ts
libs/domain/src/schemas/metadata/index.ts

Modified Files

  • All files using Record<string, unknown> for metadata

✅ Validation Checklist

  • All metadata contexts identified
  • Zod schemas created for each context
  • Type exports added to domain library
  • Generic Record types replaced
  • pnpm nx build passes
  • pnpm nx test passes

END OF PROMPT


This prompt resolves TD-016 from the technical debt register.