rspace-online/modules/rswag/schemas.ts

101 lines
2.7 KiB
TypeScript

/**
* rSwag Automerge document schemas.
*
* Stores shared design metadata for collaborative artifact management.
* Actual image files remain server-side; this doc syncs titles, product
* types, status, and artifact references across space members.
*
* DocId format: {space}:swag:designs
*/
import type { DocSchema } from '../../shared/local-first/document';
// ── Document types ──
export interface SwagDesignProduct {
type: string;
provider: 'printful' | 'prodigi' | 'cosmolocal';
sku: string;
variants: string[];
retailPrice: number;
}
export interface SwagDesign {
id: string;
title: string;
productType: 'sticker' | 'poster' | 'tee' | 'hoodie';
/** Server artifact ID (if generated) */
artifactId: string | null;
/** How this design was created */
source: 'upload' | 'ai-generated' | 'artifact';
/** Design lifecycle status */
status: 'draft' | 'active' | 'paused' | 'removed';
/** URL to the design image (server-side path) */
imageUrl: string | null;
/** POD product configurations for this design */
products: SwagDesignProduct[];
/** Design slug (URL-friendly identifier) */
slug: string | null;
/** Description / concept text */
description: string | null;
/** Tags for categorization */
tags: string[];
createdBy: string | null;
createdAt: number;
updatedAt: number;
}
export interface SwagDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
designs: Record<string, SwagDesign>;
/** Currently active design ID for the space */
activeDesignId: string | null;
}
// ── Schema registration ──
export const swagSchema: DocSchema<SwagDoc> = {
module: 'swag',
collection: 'designs',
version: 2,
init: (): SwagDoc => ({
meta: {
module: 'swag',
collection: 'designs',
version: 2,
spaceSlug: '',
createdAt: Date.now(),
},
designs: {},
activeDesignId: null,
}),
migrate: (doc: any, _fromVersion: number) => {
if (!doc.designs) doc.designs = {};
if (!('activeDesignId' in doc)) doc.activeDesignId = null;
// v2 migration: add new fields to existing designs
for (const design of Object.values(doc.designs) as any[]) {
if (!('source' in design)) design.source = 'artifact';
if (!('status' in design)) design.status = design.artifactId ? 'active' : 'draft';
if (!('imageUrl' in design)) design.imageUrl = null;
if (!('products' in design)) design.products = [];
if (!('slug' in design)) design.slug = null;
if (!('description' in design)) design.description = null;
if (!('tags' in design)) design.tags = [];
}
doc.meta.version = 2;
return doc;
},
};
// ── Helpers ──
export function swagDocId(space: string) {
return `${space}:swag:designs` as const;
}