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:
- No Type Safety: Any data can be stored, no compile-time checks
- Runtime Errors: Invalid data shapes cause crashes
- Poor IDE Support: No autocomplete or documentation
- 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 buildpasses -
pnpm nx testpasses
END OF PROMPT
This prompt resolves TD-016 from the technical debt register.