cogsbox-shape
Version:
A TypeScript library for creating type-safe database schemas with Zod validation, SQL type definitions, and automatic client/server transformations. Unifies client, server, and database types through a single schema definition, with built-in support for r
264 lines (263 loc) • 10.3 kB
TypeScript
import { z } from "zod";
/** Minimal shape of a createSchemaBox entry — matches journalSchemaBox.journalTechnical etc. */
export type ShapeRefineInfo = {
fieldToGroup: Record<string, number[]>;
groups: {
deps: string[] | null;
}[];
};
export type ShapeSchemaBoxEntry = {
/** Field-key → value map from DeriveStateType (not z.infer on a flattened client object). */
stateType: Record<string, unknown>;
generateDefaults: () => unknown;
schemas: {
client: z.ZodTypeAny;
};
validators?: {
client: z.ZodTypeAny;
};
transforms?: {
parseForDb?: (value: any) => any;
parsePatchForDb?: (value: any) => any;
parseFromDb?: (value: any) => any;
toClient?: (value: any) => any;
};
pk?: readonly string[] | null;
clientPk?: readonly string[] | null;
isClientRecord?: (value: any) => boolean;
refineInfo?: ShapeRefineInfo;
};
type ShapeViewEntry = Omit<ShapeSchemaBoxEntry, "generateDefaults" | "stateType" | "isClientRecord"> & {
defaults: () => any;
reconcile?: any;
isView: true;
viewSelection: unknown;
baseTable: string;
};
type ShapeSchemaBoxEntryWithViews = ShapeSchemaBoxEntry & {
RelationSelection?: unknown;
createView?: (selection: any) => ShapeViewEntry;
};
export type ShapeSchemaBox = Record<string, ShapeSchemaBoxEntryWithViews>;
type NormalizedShapeEntry<TState extends Record<string, unknown> = Record<string, unknown>> = ShapeSchemaBoxEntry & {
stateType: TState;
};
/** Per-box-key state: each entry's field keys stay typed via stateType. */
export type InferShapeBoxState<TBox extends ShapeSchemaBox> = {
[K in keyof TBox]: TBox[K]["stateType"];
};
type ShapeBoxKey<TBox extends ShapeSchemaBox> = keyof TBox & string;
type ViewStateShape<TBox extends ShapeSchemaBox, TFrom extends ShapeBoxKey<TBox>, TWith> = TBox[TFrom] extends {
createView: (selection: TWith) => infer TView;
} ? TView extends {
schemas: {
client: z.ZodTypeAny;
};
} ? z.infer<TView["schemas"]["client"]> : Record<string, unknown> : Record<string, unknown>;
type TransformStateParams = {
stateKey: string;
setOptions: (options: {
validation?: {
zodSchemaV4?: z.ZodTypeAny;
onBlur?: "error" | "warning";
};
}) => void;
};
type FormUpdateParams = {
stateKey: string;
path: string[];
event: {
activityType: string;
details?: Record<string, unknown>;
};
getState: () => unknown;
addZodErrors: (errors: Array<{
path: string[];
message: string;
code?: string;
}>) => void;
clearZodErrors?: (paths: string[][]) => void;
};
type UpdateParams = {
stateKey: string;
update: {
path: string[];
updateType: string;
oldValue: unknown;
newValue: unknown;
};
getState: () => unknown;
addZodErrors: (errors: Array<{
path: string[];
message: string;
code?: string;
}>) => void;
clearZodErrors?: (paths: string[][]) => void;
};
type ShapeKeyValidationParams = {
stateKey: string;
path: string[];
keys?: readonly string[];
getState?: () => unknown;
/** When true (default), writes filtered issues to shadow validation metadata. */
persist?: boolean;
/** When true, clears existing errors on sibling fields outside `keys`. */
clearOutsideKeys?: boolean;
};
export type ValidateGroupOptions = Pick<ShapeKeyValidationParams, "persist" | "clearOutsideKeys">;
type ShapeStatusValue = "idle" | "loading" | "success" | "error";
export type ShapeStatus = {
cacheKey: string;
isDirty: boolean;
dirtyPaths: string[];
isLoading: boolean;
isSaving: boolean;
loadStatus: ShapeStatusValue;
saveStatus: Exclude<ShapeStatusValue, "loading"> | "saving";
error?: unknown;
lastLoadedAt?: number;
lastSavedAt?: number;
};
type ShapePluginOptions = {
logs?: boolean;
};
type ShapePersistenceContext<TEntry extends ShapeSchemaBoxEntry> = {
stateKey: string;
cacheKey: string;
path: string[];
value: TEntry["stateType"];
data?: unknown;
entry: TEntry;
id?: Record<string, unknown>;
operation?: "insert" | "update";
options?: ShapePluginOptions;
status: ShapeStatus;
};
export type ShapePersistenceAdapter<TEntry extends ShapeSchemaBoxEntry = ShapeSchemaBoxEntry> = {
key?: (ctx: {
shape: TEntry["stateType"];
stateKey: string;
}) => string | number | boolean | null | undefined;
load?: (ctx: ShapePersistenceContext<TEntry>) => Promise<TEntry["stateType"] | unknown> | TEntry["stateType"] | unknown;
save?: (ctx: ShapePersistenceContext<TEntry> & {
data: unknown;
operation: "insert" | "update";
}) => Promise<TEntry["stateType"] | unknown> | TEntry["stateType"] | unknown;
insert?: (ctx: ShapePersistenceContext<TEntry> & {
data: unknown;
operation: "insert";
}) => Promise<TEntry["stateType"] | unknown> | TEntry["stateType"] | unknown;
update?: (ctx: ShapePersistenceContext<TEntry> & {
data: unknown;
operation: "update";
}) => Promise<TEntry["stateType"] | unknown> | TEntry["stateType"] | unknown;
};
type ShapeViewStateConfig<TBox extends ShapeSchemaBox, TFrom extends ShapeBoxKey<TBox>, TWith> = Omit<ShapePersistenceAdapter<NormalizedShapeEntry<ViewStateShape<TBox, TFrom, TWith>>>, "key"> & {
from: TFrom;
with: TWith;
key?: (ctx: {
shape: TBox[TFrom]["stateType"];
stateKey: string;
}) => string | number | boolean | null | undefined;
};
type ShapeViewStateConfigUnion<TBox extends ShapeSchemaBox> = {
[K in ShapeBoxKey<TBox>]: ShapeViewStateConfig<TBox, K, any>;
}[ShapeBoxKey<TBox>];
type ShapeStateConfigEntry<TBox extends ShapeSchemaBox, TKey extends PropertyKey> = TKey extends ShapeBoxKey<TBox> ? ShapePersistenceAdapter<TBox[TKey]> : ShapeViewStateConfigUnion<TBox>;
type ShapeStateConfig<TBox extends ShapeSchemaBox> = Partial<{
[K in ShapeBoxKey<TBox>]: ShapeStateConfigEntry<TBox, K>;
}>;
type ShapeStateConfigFor<TBox extends ShapeSchemaBox, TState extends Record<string, unknown>> = {
[K in keyof TState]: K extends ShapeBoxKey<TBox> ? ShapePersistenceAdapter<TBox[K]> : TState[K] extends {
from: infer TFrom;
with: infer TWith;
} ? TFrom extends ShapeBoxKey<TBox> ? ShapeViewStateConfig<TBox, TFrom, TWith> : never : ShapeViewStateConfigUnion<TBox>;
};
type StateEntryShape<TBox extends ShapeSchemaBox, TEntry, TFallbackKey extends PropertyKey> = TEntry extends {
from: infer TFrom;
with: infer TWith;
} ? TFrom extends ShapeBoxKey<TBox> ? ViewStateShape<TBox, TFrom, TWith> : never : TFallbackKey extends ShapeBoxKey<TBox> ? TBox[TFallbackKey]["stateType"] : never;
type InferConfiguredShapeState<TBox extends ShapeSchemaBox, TState extends Record<string, unknown>> = InferShapeBoxState<TBox> & {
[K in keyof TState]: StateEntryShape<TBox, NonNullable<TState[K]>, K>;
};
export type ShapePluginConfig<TBox extends ShapeSchemaBox, TState extends Record<string, unknown> = {}> = {
state?: ShapeStateConfig<TBox> & TState & ShapeStateConfigFor<TBox, TState>;
/** @deprecated use state */
server?: {
[K in keyof TBox & string]?: ShapePersistenceAdapter<TBox[K]>;
};
};
export declare function wireShapeValidationOptions(box: ShapeSchemaBox, params: TransformStateParams): void;
/** Cross-field refine errors only — field rules are handled by state via setOptions. */
export declare function validateShapeRefines(box: ShapeSchemaBox, params: FormUpdateParams): void;
export declare function validateShapeRefinesOnUpdate(box: ShapeSchemaBox, params: UpdateParams): void;
export declare function validateShapeKeys(box: ShapeSchemaBox, params: ShapeKeyValidationParams): {
success: boolean;
results: {
key: string;
path: string[];
success: boolean;
data: unknown;
}[];
};
export declare function createShapePlugin<const TBox extends ShapeSchemaBox, const TState extends Record<string, unknown> = {}>(box: TBox, config?: ShapePluginConfig<TBox, TState>): import("cogsbox-state").CogsPluginBuilder<"shape", {
logs: boolean | undefined;
}, {
cacheKey: string | undefined;
baseline: any;
dirtyPaths: string[] | undefined;
isDirty: boolean | undefined;
isLoading: boolean | undefined;
isSaving: boolean | undefined;
loadStatus: "error" | "idle" | "loading" | "success" | undefined;
saveStatus: "error" | "idle" | "success" | "saving" | undefined;
error: any;
lastLoadedAt: number | undefined;
lastSavedAt: number | undefined;
suppressDirtyOnce: boolean | undefined;
}, unknown, never, {
validateGroup: import("cogsbox-state").ChainMethodDefinition<(ctx: import("cogsbox-state").ChainMethodContext<any, any>, keys?: readonly string[] | undefined) => {
success: boolean;
results: {
key: string;
path: string[];
success: boolean;
data: unknown;
}[];
}>;
status: import("cogsbox-state").ChainMethodDefinition<(ctx: import("cogsbox-state").ChainMethodContext<any, any>) => ShapeStatus>;
load: import("cogsbox-state").ChainMethodDefinition<(ctx: import("cogsbox-state").ChainMethodContext<any, any>) => Promise<{
success: boolean;
data: any;
cacheKey: string;
error?: undefined;
} | {
success: boolean;
error: unknown;
cacheKey: string;
data?: undefined;
}>>;
save: import("cogsbox-state").ChainMethodDefinition<(ctx: import("cogsbox-state").ChainMethodContext<any, any>) => Promise<{
success: boolean;
data: any;
operation: string;
cacheKey: string;
error?: undefined;
} | {
success: boolean;
error: unknown;
operation: string;
cacheKey: string;
data?: undefined;
}>>;
revert: import("cogsbox-state").ChainMethodDefinition<(ctx: import("cogsbox-state").ChainMethodContext<any, any>) => {
success: boolean;
error: string;
data?: undefined;
} | {
success: boolean;
data: {} | null;
error?: undefined;
}>;
}, true, true, true, true, false, true, InferConfiguredShapeState<TBox, TState>>;
export {};