cogsbox-shape 0.5.177 → 0.5.179
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -65
- package/dist/schema.d.ts +87 -66
- package/dist/schema.js +218 -171
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,16 +33,18 @@ Traditional approaches require defining these layers separately, leading to type
|
|
|
33
33
|
Define a field by chaining methods. Each step is optional — use only what you need.
|
|
34
34
|
|
|
35
35
|
```
|
|
36
|
-
s.sql() → .
|
|
36
|
+
s.sql() → .client() → .server() → .transform()
|
|
37
|
+
→ .derive() → .sqlOnly()
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
| Method | Purpose |
|
|
40
41
|
| -------------------------------------------- | -------------------------------------------------------------- |
|
|
41
42
|
| `s.sql({ type })` | Database column type. The starting point for every field. |
|
|
42
|
-
| `.
|
|
43
|
-
| `.client(fn)` | Client-side validation. Overrides the client type if needed. |
|
|
43
|
+
| `.client({ value, schema })` | Client-side validation and default value for new records. |
|
|
44
44
|
| `.server(fn)` | Server-side validation. Stricter rules before database writes. |
|
|
45
|
-
| `.transform({ toClient, toDb })`
|
|
45
|
+
| `.transform({ toClient, toDb })` | Converts between database and client representations. |
|
|
46
|
+
| `.derive({ field: (row) => computedValue })` | Adds computed fields based on other field values. |
|
|
47
|
+
| `.sqlOnly` | Marks a field as server-only (not sent to client). |
|
|
46
48
|
|
|
47
49
|
### 1. SQL — Define Your Database Schema
|
|
48
50
|
|
|
@@ -61,66 +63,34 @@ const userSchema = schema({
|
|
|
61
63
|
|
|
62
64
|
This generates a Zod schema matching your SQL types exactly.
|
|
63
65
|
|
|
64
|
-
### 2.
|
|
66
|
+
### 2. Client — Defaults and Client-Side Validation
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
`.client()` sets the default value and client-side validation type for new records.
|
|
67
69
|
|
|
68
70
|
```typescript
|
|
69
71
|
const userSchema = schema({
|
|
70
72
|
_tableName: "users",
|
|
71
73
|
// DB stores auto-increment integers, but new records need a temp string ID
|
|
72
|
-
id: s.sql({ type: "int", pk: true }).
|
|
74
|
+
id: s.sql({ type: "int", pk: true }).client({
|
|
73
75
|
value: () => crypto.randomUUID(),
|
|
74
76
|
schema: z.string(),
|
|
75
|
-
clientPk: true, // Explicitly marks this as a client PK, auto-creating a union type
|
|
76
77
|
}),
|
|
77
|
-
// Client type becomes: string | number (union of SQL +
|
|
78
|
+
// Client type becomes: string | number (union of SQL + client)
|
|
78
79
|
// Default value: a generated UUID string
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
If the type you pass to `.initialState()` matches the SQL type, no union is created:
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
count: s.sql({ type: "int" }).initialState({ value: 0 }),
|
|
86
|
-
// Client type: number (no union, same type)
|
|
87
|
-
// Default value: 0
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### 3. Client — Client-Side Validation
|
|
91
80
|
|
|
92
|
-
|
|
81
|
+
// Simple default without type override
|
|
82
|
+
name: s.sql({ type: "varchar" }).client({ value: "Anonymous" }),
|
|
83
|
+
// Client type: string (inherits from SQL)
|
|
84
|
+
// Default value: "Anonymous"
|
|
93
85
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
value: (
|
|
98
|
-
|
|
99
|
-
}),
|
|
100
|
-
// Client type: string | number
|
|
101
|
-
// (string from initialState + number from SQL)
|
|
102
|
-
|
|
103
|
-
// With .client() — add validation rules
|
|
104
|
-
name: s
|
|
105
|
-
.sql({ type: "varchar" })
|
|
106
|
-
.client(({ sql }) => sql.min(2).max(100)),
|
|
107
|
-
// Client type: string (with min/max validation)
|
|
108
|
-
|
|
109
|
-
// With .client() — declare a different type entirely
|
|
110
|
-
// Pair with .transform() to convert between them
|
|
111
|
-
isActive: s
|
|
112
|
-
.sql({ type: "int" })
|
|
113
|
-
.client(() => z.boolean())
|
|
114
|
-
.transform({
|
|
115
|
-
toClient: (dbValue) => dbValue === 1,
|
|
116
|
-
toDb: (clientValue) => (clientValue ? 1 : 0),
|
|
117
|
-
}),
|
|
118
|
-
// Client type: boolean (DB stores 0/1, client works with true/false)
|
|
86
|
+
// Type-only override (no default value change)
|
|
87
|
+
count: s.sql({ type: "int" }).client(() => z.number().min(0)),
|
|
88
|
+
// Client type: number (with min validation)
|
|
89
|
+
// Default value: inferred from type (0 for number)
|
|
90
|
+
});
|
|
119
91
|
```
|
|
120
92
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
### 4. Server — Server-Side Validation
|
|
93
|
+
### 3. Server — Server-Side Validation
|
|
124
94
|
|
|
125
95
|
`.server()` adds validation rules that run at the server boundary before database writes. It builds on the client schema, adding stricter constraints.
|
|
126
96
|
|
|
@@ -146,7 +116,7 @@ name: s
|
|
|
146
116
|
.server(({ client }) => client.min(2, "Too short")),
|
|
147
117
|
```
|
|
148
118
|
|
|
149
|
-
###
|
|
119
|
+
### 4. Transform — Convert Between Layers
|
|
150
120
|
|
|
151
121
|
`.transform()` defines bidirectional conversion functions. These run on the server when reading from or writing to the database.
|
|
152
122
|
|
|
@@ -162,6 +132,84 @@ status: s
|
|
|
162
132
|
|
|
163
133
|
Transforms are optional — only needed when the client type differs from the SQL type.
|
|
164
134
|
|
|
135
|
+
### 6. Derive — Computed Fields
|
|
136
|
+
|
|
137
|
+
`.derive()` adds computed fields that are calculated from other values in the row. These are:
|
|
138
|
+
- Available in client schema and defaults
|
|
139
|
+
- NOT stored in the database (computed at runtime)
|
|
140
|
+
- Useful for display-only fields, combinations, or formatted values
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const userSchema = schema({
|
|
144
|
+
_tableName: "users",
|
|
145
|
+
firstName: s.sql({ type: "varchar" }).client({ value: "John" }),
|
|
146
|
+
lastName: s.sql({ type: "varchar" }).client({ value: "Doe" }),
|
|
147
|
+
// Define placeholder for derived field
|
|
148
|
+
fullName: s.client(""), // Required: tells schema this field exists on client
|
|
149
|
+
}).derive({
|
|
150
|
+
fullName: (row) => `${row.firstName} ${row.lastName}`,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Now defaults and toClient include the computed value
|
|
154
|
+
const defaults = box.users.defaults;
|
|
155
|
+
// { firstName: "John", lastName: "Doe", fullName: "John Doe" }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The derived field's default value is computed from other defaults at initialization time.
|
|
159
|
+
|
|
160
|
+
### 7. sqlOnly — Server-Only Fields
|
|
161
|
+
|
|
162
|
+
`.sql({ sqlOnly: true })` marks a field as server-only:
|
|
163
|
+
- Included in SQL schema (stored in DB)
|
|
164
|
+
- Excluded from client schema (not sent to client)
|
|
165
|
+
- Useful for internal tokens, computed scores, or sensitive data
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const userSchema = schema({
|
|
169
|
+
_tableName: "users",
|
|
170
|
+
id: s.sql({ type: "int", pk: true }),
|
|
171
|
+
email: s.sql({ type: "varchar" }),
|
|
172
|
+
internalToken: s.sql({ type: "varchar", sqlOnly: true }),
|
|
173
|
+
trustScore: s.sql({ type: "int", sqlOnly: true }),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Client schema: { id, email } — internalToken and trustScore excluded
|
|
177
|
+
// SQL schema: { id, email, internalToken, trustScore } — all fields
|
|
178
|
+
// Defaults: { id: 0, email: "" } — sqlOnly fields excluded
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 8. Client-Only Fields
|
|
182
|
+
|
|
183
|
+
Use `s.client()` without `s.sql()` to define fields that exist only on the client:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const orderSchema = schema({
|
|
187
|
+
_tableName: "orders",
|
|
188
|
+
id: s.sql({ type: "int", pk: true }),
|
|
189
|
+
total: s.sql({ type: "int" }).client({ value: 0 }),
|
|
190
|
+
// Client-only: computed from other fields, not stored
|
|
191
|
+
formattedTotal: s.client(""),
|
|
192
|
+
}).derive({
|
|
193
|
+
formattedTotal: (row) => `$${(row.total / 100).toFixed(2)}`,
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Schema Object Structure
|
|
198
|
+
|
|
199
|
+
The returned schema object has a clear separation of concerns:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const schema = createSchema(mySchema);
|
|
203
|
+
|
|
204
|
+
schema.schemas; // { sqlSchema, clientSchema, serverSchema } — Zod schemas
|
|
205
|
+
schema.transforms; // { toClient, toDb, parseForDb, parseFromDb } — transformations
|
|
206
|
+
schema.defaults; // Default values for forms
|
|
207
|
+
schema.generateDefaults; // Function to generate fresh defaults (executes randomizers)
|
|
208
|
+
schema.pk; // Primary key field names
|
|
209
|
+
schema.clientPk; // Client-side primary key field names
|
|
210
|
+
schema.isClientRecord; // Function to check if a record is client-created
|
|
211
|
+
```
|
|
212
|
+
|
|
165
213
|
## Using Schemas
|
|
166
214
|
|
|
167
215
|
### Single Schema with `createSchema`
|
|
@@ -173,10 +221,9 @@ import { s, schema, createSchema } from "cogsbox-shape";
|
|
|
173
221
|
|
|
174
222
|
const contactSchema = schema({
|
|
175
223
|
_tableName: "contacts",
|
|
176
|
-
id: s.sql({ type: "int", pk: true }).
|
|
224
|
+
id: s.sql({ type: "int", pk: true }).client({
|
|
177
225
|
value: () => `new_${crypto.randomUUID().slice(0, 8)}`,
|
|
178
226
|
schema: z.string(),
|
|
179
|
-
clientPk: true,
|
|
180
227
|
}),
|
|
181
228
|
name: s.sql({ type: "varchar" }).server(({ sql }) => sql.min(2)),
|
|
182
229
|
email: s.sql({ type: "varchar" }).server(({ sql }) => sql.email()),
|
|
@@ -189,15 +236,14 @@ const contactSchema = schema({
|
|
|
189
236
|
}),
|
|
190
237
|
});
|
|
191
238
|
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
} = createSchema(contactSchema);
|
|
239
|
+
const schema = createSchema(contactSchema);
|
|
240
|
+
|
|
241
|
+
// Access schemas directly
|
|
242
|
+
const { clientSchema, serverSchema, sqlSchema } = schema;
|
|
243
|
+
const { defaultValues, generateDefaults } = schema;
|
|
244
|
+
|
|
245
|
+
// Transforms for converting between layers
|
|
246
|
+
const { toClient, toDb, parseForDb, parseFromDb } = schema.transforms;
|
|
201
247
|
|
|
202
248
|
// Use in a form
|
|
203
249
|
const [data, setData] = useState(generateDefaults());
|
|
@@ -252,11 +298,19 @@ const box = createSchemaBox({ users, posts }, (s) => ({
|
|
|
252
298
|
Base schemas **exclude relations** by default, preventing circular dependencies:
|
|
253
299
|
|
|
254
300
|
```typescript
|
|
255
|
-
const { schemas, defaults } = box.users;
|
|
301
|
+
const { schemas, defaults, transforms, pk, clientPk } = box.users;
|
|
256
302
|
|
|
257
303
|
type UserClient = z.infer<typeof schemas.client>;
|
|
258
304
|
// { id: number; name: string; }
|
|
259
305
|
// No 'posts' — relations are excluded from base schemas
|
|
306
|
+
|
|
307
|
+
// Convert data between layers
|
|
308
|
+
const dbRow = transforms.toDb(clientData);
|
|
309
|
+
const clientData = transforms.toClient(dbRow);
|
|
310
|
+
|
|
311
|
+
// Validate and convert in one step
|
|
312
|
+
const dbRow = transforms.parseForDb(appData);
|
|
313
|
+
const clientData = transforms.parseFromDb(dbRow);
|
|
260
314
|
```
|
|
261
315
|
|
|
262
316
|
### 4. Create Views to Include Relations
|
|
@@ -275,6 +329,8 @@ type UserWithPosts = z.infer<typeof userWithPosts.schemas.client>;
|
|
|
275
329
|
// posts: { id: number; title: string; authorId: number; }[]
|
|
276
330
|
// }
|
|
277
331
|
|
|
278
|
-
|
|
279
|
-
|
|
332
|
+
// Views also have transforms for the selected fields
|
|
333
|
+
const { defaults, transforms } = userWithPosts;
|
|
334
|
+
// transforms.apply() handles nested relations automatically
|
|
335
|
+
```
|
|
280
336
|
```
|
package/dist/schema.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ type BaseConfig = {
|
|
|
33
33
|
nullable?: boolean;
|
|
34
34
|
pk?: true;
|
|
35
35
|
field?: string;
|
|
36
|
+
sqlOnly?: true;
|
|
36
37
|
};
|
|
37
38
|
type SQLToZodType<T extends SQLType, TDefault extends boolean> = T["pk"] extends true ? TDefault extends true ? z.ZodString : z.ZodNumber : T["nullable"] extends true ? T["type"] extends "varchar" | "char" | "text" | "longtext" ? z.ZodNullable<z.ZodString> : T["type"] extends "int" ? z.ZodNullable<z.ZodNumber> : T["type"] extends "boolean" ? z.ZodNullable<z.ZodBoolean> : T["type"] extends "date" | "datetime" | "timestamp" ? T extends {
|
|
38
39
|
default: "CURRENT_TIMESTAMP";
|
|
@@ -41,45 +42,59 @@ type SQLToZodType<T extends SQLType, TDefault extends boolean> = T["pk"] extends
|
|
|
41
42
|
} ? TDefault extends true ? never : z.ZodDate : z.ZodDate : never;
|
|
42
43
|
type ZodTypeFromPrimitive<T> = T extends string ? z.ZodString : T extends number ? z.ZodNumber : T extends boolean ? z.ZodBoolean : T extends Date ? z.ZodDate : z.ZodAny;
|
|
43
44
|
type CollapsedUnion<A extends z.ZodTypeAny, B extends z.ZodTypeAny> = A extends B ? (B extends A ? A : z.ZodUnion<[A, B]>) : z.ZodUnion<[A, B]>;
|
|
44
|
-
export interface IBuilderMethods<T extends DbConfig, TSql extends z.ZodTypeAny,
|
|
45
|
-
|
|
45
|
+
export interface IBuilderMethods<T extends DbConfig, TSql extends z.ZodTypeAny, TInitialValue, TClient extends z.ZodTypeAny, TValidation extends z.ZodTypeAny> {
|
|
46
|
+
client<const TValue>(options: {
|
|
46
47
|
value: TValue | ((tools: {
|
|
47
48
|
uuid: () => string;
|
|
48
49
|
}) => TValue);
|
|
49
50
|
schema?: never;
|
|
50
51
|
clientPk?: boolean | ((val: any) => boolean);
|
|
51
|
-
}): Prettify<Builder<"
|
|
52
|
-
|
|
52
|
+
}): Prettify<Builder<"client", T, TSql, TValue extends () => infer R ? R : TValue, CollapsedUnion<TSql, ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>>, CollapsedUnion<TSql, ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>>>>;
|
|
53
|
+
client<const TSchema extends z.ZodTypeAny>(options: {
|
|
53
54
|
value?: never;
|
|
54
55
|
schema: TSchema;
|
|
55
56
|
clientPk?: boolean | ((val: any) => boolean);
|
|
56
|
-
}): Prettify<Builder<"
|
|
57
|
-
|
|
57
|
+
}): Prettify<Builder<"client", T, TSql, z.infer<TSchema>, CollapsedUnion<TSql, TSchema>, CollapsedUnion<TSql, TSchema>>>;
|
|
58
|
+
client<const TSchema extends z.ZodTypeAny>(options: {
|
|
59
|
+
value?: never;
|
|
60
|
+
schema: TSchema | ((tools: any) => TSchema);
|
|
61
|
+
clientPk?: boolean | ((val: any) => boolean);
|
|
62
|
+
}): Prettify<Builder<"client", T, TSql, z.infer<TSchema>, CollapsedUnion<TSql, TSchema>, CollapsedUnion<TSql, TSchema>>>;
|
|
63
|
+
client<const TValue>(options: {
|
|
64
|
+
value: TValue | ((tools: {
|
|
65
|
+
uuid: () => string;
|
|
66
|
+
}) => TValue);
|
|
67
|
+
schema?: never;
|
|
68
|
+
clientPk?: boolean | ((val: any) => boolean);
|
|
69
|
+
}): Prettify<Builder<"client", T, TSql, TValue extends () => infer R ? R : TValue, CollapsedUnion<TSql, ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>>, CollapsedUnion<TSql, ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>>>>;
|
|
70
|
+
client(options: {
|
|
71
|
+
value?: never;
|
|
72
|
+
schema: (tools: any) => z.ZodTypeAny;
|
|
73
|
+
}): Prettify<Builder<"client", T, TSql, unknown, z.ZodTypeAny, z.ZodTypeAny>>;
|
|
74
|
+
client<const TValue, const TSchema extends z.ZodTypeAny>(options: {
|
|
58
75
|
value: TValue | ((tools: {
|
|
59
76
|
uuid: () => string;
|
|
60
77
|
}) => TValue);
|
|
61
78
|
schema: TSchema | ((base: ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>) => TSchema);
|
|
62
79
|
clientPk?: boolean | ((val: any) => boolean);
|
|
63
|
-
}): Prettify<Builder<"
|
|
80
|
+
}): Prettify<Builder<"client", T, TSql, TValue extends () => infer R ? R : TValue, CollapsedUnion<TSql, TSchema>, CollapsedUnion<TSql, TSchema>>>;
|
|
81
|
+
client<TClientNext extends z.ZodTypeAny>(schema: ((tools: {
|
|
82
|
+
sql: TSql;
|
|
83
|
+
}) => TClientNext) | TClientNext): Prettify<Builder<"client", T, TSql, TInitialValue, TClientNext, TClientNext>>;
|
|
64
84
|
reference: <TRefSchema extends {
|
|
65
85
|
_tableName: string;
|
|
66
86
|
}>(fieldGetter: () => any) => Builder<"sql", T & {
|
|
67
87
|
references: typeof fieldGetter;
|
|
68
|
-
}, TSql,
|
|
69
|
-
client: <TClientNext extends z.ZodTypeAny>(schema: ((tools: {
|
|
70
|
-
sql: TSql;
|
|
71
|
-
initialState: TNew;
|
|
72
|
-
}) => TClientNext) | TClientNext) => Prettify<Builder<"client", T, TSql, TNew, TInitialValue, TClientNext, TClientNext>>;
|
|
88
|
+
}, TSql, TInitialValue, TClient, TValidation>;
|
|
73
89
|
server: <TValidationNext extends z.ZodTypeAny>(schema: ((tools: {
|
|
74
90
|
sql: TSql;
|
|
75
|
-
initialState: TNew;
|
|
76
91
|
client: TClient;
|
|
77
|
-
}) => TValidationNext) | TValidationNext) => Prettify<Builder<"server", T, TSql,
|
|
92
|
+
}) => TValidationNext) | TValidationNext) => Prettify<Builder<"server", T, TSql, TInitialValue, TClient, TValidationNext>>;
|
|
78
93
|
transform: (transforms: {
|
|
79
94
|
toClient: (dbValue: z.infer<TSql>) => z.infer<TClient>;
|
|
80
95
|
toDb: (clientValue: z.infer<TClient>) => z.infer<TSql>;
|
|
81
96
|
}) => {
|
|
82
|
-
config: Prettify<BuilderConfig<T, TSql,
|
|
97
|
+
config: Prettify<BuilderConfig<T, TSql, TInitialValue, TClient, TValidation>> & {
|
|
83
98
|
transforms: typeof transforms;
|
|
84
99
|
};
|
|
85
100
|
};
|
|
@@ -99,35 +114,32 @@ export type RelationConfig<T extends Schema<any>> = (BaseRelationConfig<T> & {
|
|
|
99
114
|
}) | (BaseRelationConfig<T> & {
|
|
100
115
|
type: "manyToMany";
|
|
101
116
|
});
|
|
102
|
-
type Stage = "sql" | "relation" | "
|
|
117
|
+
type Stage = "sql" | "relation" | "client" | "server" | "done";
|
|
103
118
|
type StageMethods = {
|
|
104
|
-
sql: "
|
|
105
|
-
relation: "server" | "transform";
|
|
106
|
-
new: "client" | "server" | "transform";
|
|
119
|
+
sql: "client" | "server" | "transform" | "reference";
|
|
120
|
+
relation: "client" | "server" | "transform";
|
|
107
121
|
client: "server" | "transform";
|
|
108
122
|
server: "transform";
|
|
109
123
|
done: never;
|
|
110
124
|
};
|
|
111
|
-
type BuilderConfig<T extends DbConfig, TSql extends z.ZodTypeAny,
|
|
125
|
+
type BuilderConfig<T extends DbConfig, TSql extends z.ZodTypeAny, TInitialValue, TClient extends z.ZodTypeAny, TValidation extends z.ZodTypeAny> = {
|
|
112
126
|
sql: T;
|
|
113
127
|
zodSqlSchema: TSql;
|
|
114
|
-
zodNewSchema: TNew;
|
|
115
128
|
initialValue: TInitialValue;
|
|
116
129
|
zodClientSchema: TClient;
|
|
117
130
|
zodValidationSchema: TValidation;
|
|
118
131
|
clientTransform?: (schema: z.ZodTypeAny) => z.ZodTypeAny;
|
|
119
132
|
validationTransform?: (schema: z.ZodTypeAny) => z.ZodTypeAny;
|
|
120
133
|
};
|
|
121
|
-
export type Builder<TStage extends Stage, T extends DbConfig, TSql extends z.ZodTypeAny,
|
|
134
|
+
export type Builder<TStage extends Stage, T extends DbConfig, TSql extends z.ZodTypeAny, TInitialValue, TClient extends z.ZodTypeAny, TValidation extends z.ZodTypeAny> = {
|
|
122
135
|
config: {
|
|
123
136
|
sql: T;
|
|
124
137
|
zodSqlSchema: TSql;
|
|
125
|
-
zodNewSchema: TNew;
|
|
126
138
|
initialValue: TInitialValue;
|
|
127
139
|
zodClientSchema: TClient;
|
|
128
140
|
zodValidationSchema: TValidation;
|
|
129
141
|
};
|
|
130
|
-
} & Pick<IBuilderMethods<T, TSql,
|
|
142
|
+
} & Pick<IBuilderMethods<T, TSql, TInitialValue, TClient, TValidation>, StageMethods[TStage]>;
|
|
131
143
|
type HasManyDefault = true | undefined | [] | {
|
|
132
144
|
count: number;
|
|
133
145
|
};
|
|
@@ -137,11 +149,11 @@ export type Reference<TGetter extends () => any> = {
|
|
|
137
149
|
getter: TGetter;
|
|
138
150
|
};
|
|
139
151
|
interface ShapeAPI {
|
|
140
|
-
|
|
152
|
+
client: <const TValue>(value: TValue | ((tools: {
|
|
141
153
|
uuid: () => string;
|
|
142
|
-
}) => TValue)) => Builder<"
|
|
143
|
-
|
|
144
|
-
sql: <const T extends SQLType>(sqlConfig: T) => Builder<"sql", T, SQLToZodType<T, false>,
|
|
154
|
+
}) => TValue)) => Builder<"client", null, z.ZodUndefined, // No SQL schema
|
|
155
|
+
TValue extends () => infer R ? R : TValue, ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>, ZodTypeFromPrimitive<TValue extends () => infer R ? R : TValue>>;
|
|
156
|
+
sql: <const T extends SQLType>(sqlConfig: T) => Builder<"sql", T, SQLToZodType<T, false>, z.infer<SQLToZodType<T, false>>, SQLToZodType<T, false>, SQLToZodType<T, false>>;
|
|
145
157
|
reference: <TGetter extends () => any>(getter: TGetter) => Reference<TGetter>;
|
|
146
158
|
hasMany: <T extends HasManyDefault>(config?: T) => PlaceholderRelation<"hasMany">;
|
|
147
159
|
hasOne: (config?: HasOneDefault) => PlaceholderRelation<"hasOne">;
|
|
@@ -173,7 +185,9 @@ type PickPrimaryKeys<T extends ShapeSchema> = {
|
|
|
173
185
|
};
|
|
174
186
|
type SchemaBuilder<T extends ShapeSchema> = Prettify<EnrichFields<T>> & {
|
|
175
187
|
__primaryKeySQL?: string;
|
|
188
|
+
__derives?: Record<string, (row: any) => any>;
|
|
176
189
|
primaryKeySQL: (definer: (pkFields: PickPrimaryKeys<T>) => string) => SchemaBuilder<T>;
|
|
190
|
+
derive: <D extends Partial<Record<keyof T, (row: Prettify<z.infer<z.ZodObject<Prettify<DeriveSchemaByKey<T, "zodClientSchema">>>>>) => any>>>(derivers: D) => SchemaBuilder<T>;
|
|
177
191
|
};
|
|
178
192
|
export declare function schema<T extends string, U extends ShapeSchema<T>>(schema: U): SchemaBuilder<U>;
|
|
179
193
|
export type RelationType = "hasMany" | "hasOne" | "manyToMany";
|
|
@@ -267,7 +281,7 @@ type ResolveField<Field, Resolution> = Field extends PlaceholderReference ? Reso
|
|
|
267
281
|
type: "belongsTo";
|
|
268
282
|
} : RelType extends "manyToMany" ? BaseRelationConfig<TargetSchema> & {
|
|
269
283
|
type: "manyToMany";
|
|
270
|
-
} : never, RelType extends "hasMany" | "manyToMany" ? z.ZodArray<z.ZodObject<any>> : z.ZodObject<any>, RelType extends "hasMany" | "manyToMany" ?
|
|
284
|
+
} : never, RelType extends "hasMany" | "manyToMany" ? z.ZodArray<z.ZodObject<any>> : z.ZodObject<any>, RelType extends "hasMany" | "manyToMany" ? any[] : any, RelType extends "hasMany" | "manyToMany" ? z.ZodArray<z.ZodObject<any>> : z.ZodObject<any>, RelType extends "hasMany" | "manyToMany" ? z.ZodArray<z.ZodObject<any>> : z.ZodObject<any>> : never : never : Field;
|
|
271
285
|
type ResolveSchema<Schema extends SchemaWithPlaceholders, Resolutions extends Record<string, any>> = {
|
|
272
286
|
[K in keyof Schema]: K extends keyof Resolutions ? ResolveField<Schema[K], Resolutions[K]> : Schema[K];
|
|
273
287
|
};
|
|
@@ -280,15 +294,17 @@ type ResolvedRegistryWithSchemas<S extends Record<string, SchemaWithPlaceholders
|
|
|
280
294
|
serverSchema: z.ZodObject<Prettify<DeriveSchemaByKey<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>, "zodValidationSchema">>>;
|
|
281
295
|
defaultValues: Prettify<DeriveDefaults<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>>>;
|
|
282
296
|
stateType: Prettify<DeriveStateType<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>>>;
|
|
297
|
+
};
|
|
298
|
+
transforms: {
|
|
283
299
|
toClient: (dbObject: any) => any;
|
|
284
300
|
toDb: (clientObject: any) => any;
|
|
285
301
|
parseForDb: (appData: any) => any;
|
|
286
302
|
parseFromDb: (dbData: any) => any;
|
|
287
|
-
pk: string[] | null;
|
|
288
|
-
clientPk: string[] | null;
|
|
289
|
-
isClientRecord: (record: any) => boolean;
|
|
290
|
-
generateDefaults: () => Prettify<DeriveDefaults<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>>>;
|
|
291
303
|
};
|
|
304
|
+
pk: string[] | null;
|
|
305
|
+
clientPk: string[] | null;
|
|
306
|
+
isClientRecord: (record: any) => boolean;
|
|
307
|
+
generateDefaults: () => Prettify<DeriveDefaults<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>>>;
|
|
292
308
|
};
|
|
293
309
|
};
|
|
294
310
|
type IsRelationField<Field> = Field extends {
|
|
@@ -313,7 +329,8 @@ type GetRelationRegistryKey<Field, TRegistry extends RegistryShape> = Field exte
|
|
|
313
329
|
type OmitRelationFields<Shape, RawSchema> = Omit<Shape, {
|
|
314
330
|
[K in keyof Shape]: K extends keyof RawSchema ? IsRelationField<RawSchema[K]> extends true ? K : never : never;
|
|
315
331
|
}[keyof Shape]>;
|
|
316
|
-
type _DeriveViewShape<TTableName extends keyof TRegistry, TSelection, TRegistry extends RegistryShape, TKey extends "clientSchema" | "serverSchema", Depth extends any[] = []> = Depth["length"] extends 10 ? any : TRegistry[TTableName]["zodSchemas"][
|
|
332
|
+
type _DeriveViewShape<TTableName extends keyof TRegistry, TSelection, TRegistry extends RegistryShape, TKey extends "clientSchema" | "serverSchema" | "sqlSchema", Depth extends any[] = []> = Depth["length"] extends 10 ? any : TKey extends "sqlSchema" ? TRegistry[TTableName]["zodSchemas"]["sqlSchema"] extends z.ZodObject<infer BaseShape> ? _DeriveViewShapeInner<BaseShape, TTableName, TSelection, TRegistry, TKey, Depth> : never : TRegistry[TTableName]["zodSchemas"][TKey] extends z.ZodObject<infer BaseShape> ? _DeriveViewShapeInner<BaseShape, TTableName, TSelection, TRegistry, TKey, Depth> : never;
|
|
333
|
+
type _DeriveViewShapeInner<BaseShape, TTableName extends keyof TRegistry, TSelection, TRegistry extends RegistryShape, TKey extends "clientSchema" | "serverSchema" | "sqlSchema", Depth extends any[] = []> = TSelection extends Record<string, any> ? Prettify<OmitRelationFields<BaseShape, TRegistry[TTableName]["rawSchema"]> & {
|
|
317
334
|
[K in keyof TSelection & keyof TRegistry[TTableName]["rawSchema"] as IsRelationField<TRegistry[TTableName]["rawSchema"][K]> extends true ? K : never]: GetRelationRegistryKey<TRegistry[TTableName]["rawSchema"][K], TRegistry> extends infer TargetKey ? TargetKey extends keyof TRegistry ? TRegistry[TTableName]["rawSchema"][K] extends {
|
|
318
335
|
config: {
|
|
319
336
|
sql: {
|
|
@@ -327,7 +344,7 @@ type _DeriveViewShape<TTableName extends keyof TRegistry, TSelection, TRegistry
|
|
|
327
344
|
...Depth,
|
|
328
345
|
1
|
|
329
346
|
]>>> : never : never : never;
|
|
330
|
-
}> : OmitRelationFields<BaseShape, TRegistry[TTableName]["rawSchema"]
|
|
347
|
+
}> : OmitRelationFields<BaseShape, TRegistry[TTableName]["rawSchema"]>;
|
|
331
348
|
type DeriveViewDefaults<TTableName extends keyof TRegistry, TSelection, TRegistry extends RegistryShape, Depth extends any[] = []> = Prettify<TRegistry[TTableName]["zodSchemas"]["defaultValues"] & (TSelection extends Record<string, any> ? {
|
|
332
349
|
[K in keyof TSelection & keyof TRegistry[TTableName]["rawSchema"] as IsRelationField<TRegistry[TTableName]["rawSchema"][K]> extends true ? K : never]: TRegistry[TTableName]["rawSchema"][K] extends {
|
|
333
350
|
config: {
|
|
@@ -353,11 +370,11 @@ export type DeriveViewResult<TTableName extends keyof TRegistry, TSelection, TRe
|
|
|
353
370
|
server: z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "serverSchema">>;
|
|
354
371
|
};
|
|
355
372
|
transforms: {
|
|
356
|
-
toClient: TRegistry[TTableName]["
|
|
357
|
-
toDb: TRegistry[TTableName]["
|
|
373
|
+
toClient: TRegistry[TTableName]["transforms"]["toClient"];
|
|
374
|
+
toDb: TRegistry[TTableName]["transforms"]["toDb"];
|
|
375
|
+
parseForDb: (appData: z.input<z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "serverSchema">>>) => z.infer<z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "sqlSchema">>>;
|
|
376
|
+
parseFromDb: (dbData: Partial<z.infer<z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "sqlSchema">>>>) => z.infer<z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "clientSchema">>>;
|
|
358
377
|
};
|
|
359
|
-
parseForDb: (appData: z.input<TRegistry[TTableName]["zodSchemas"]["serverSchema"]>) => z.infer<TRegistry[TTableName]["zodSchemas"]["sqlSchema"]>;
|
|
360
|
-
parseFromDb: (dbData: Partial<z.infer<TRegistry[TTableName]["zodSchemas"]["sqlSchema"]>>) => z.infer<TRegistry[TTableName]["zodSchemas"]["clientSchema"]>;
|
|
361
378
|
defaults: DeriveViewDefaults<TTableName, TSelection, TRegistry>;
|
|
362
379
|
pk: string[] | null;
|
|
363
380
|
clientPk: string[] | null;
|
|
@@ -397,15 +414,17 @@ type RegistryShape = Record<string, {
|
|
|
397
414
|
serverSchema: z.ZodObject<any>;
|
|
398
415
|
defaultValues: any;
|
|
399
416
|
stateType: any;
|
|
417
|
+
};
|
|
418
|
+
transforms: {
|
|
400
419
|
toClient: (dbObject: any) => any;
|
|
401
420
|
toDb: (clientObject: any) => any;
|
|
402
421
|
parseForDb: (appData: any) => any;
|
|
403
422
|
parseFromDb: (dbData: any) => any;
|
|
404
|
-
pk: string[] | null;
|
|
405
|
-
clientPk: string[] | null;
|
|
406
|
-
isClientRecord: (record: any) => boolean;
|
|
407
|
-
generateDefaults: () => any;
|
|
408
423
|
};
|
|
424
|
+
pk: string[] | null;
|
|
425
|
+
clientPk: string[] | null;
|
|
426
|
+
isClientRecord: (record: any) => boolean;
|
|
427
|
+
generateDefaults: () => any;
|
|
409
428
|
}>;
|
|
410
429
|
type CreateSchemaBoxReturn<S extends Record<string, SchemaWithPlaceholders>, R extends ResolutionMap<S>, Resolved extends RegistryShape = ResolvedRegistryWithSchemas<S, R> extends RegistryShape ? ResolvedRegistryWithSchemas<S, R> : RegistryShape> = {
|
|
411
430
|
[K in keyof Resolved]: {
|
|
@@ -419,9 +438,9 @@ type CreateSchemaBoxReturn<S extends Record<string, SchemaWithPlaceholders>, R e
|
|
|
419
438
|
transforms: {
|
|
420
439
|
toClient: (dbData: z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>) => z.infer<Resolved[K]["zodSchemas"]["clientSchema"]>;
|
|
421
440
|
toDb: (clientData: z.infer<Resolved[K]["zodSchemas"]["clientSchema"]>) => z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>;
|
|
441
|
+
parseForDb: (appData: z.input<Resolved[K]["zodSchemas"]["serverSchema"]>) => z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>;
|
|
442
|
+
parseFromDb: (dbData: Partial<z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>>) => z.infer<Resolved[K]["zodSchemas"]["clientSchema"]>;
|
|
422
443
|
};
|
|
423
|
-
parseForDb: (appData: z.input<Resolved[K]["zodSchemas"]["serverSchema"]>) => z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>;
|
|
424
|
-
parseFromDb: (dbData: Partial<z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>>) => z.infer<Resolved[K]["zodSchemas"]["clientSchema"]>;
|
|
425
444
|
defaults: Resolved[K]["zodSchemas"]["defaultValues"];
|
|
426
445
|
stateType: Resolved[K]["zodSchemas"]["stateType"];
|
|
427
446
|
generateDefaults: () => Resolved[K]["zodSchemas"]["defaultValues"];
|
|
@@ -434,20 +453,7 @@ type CreateSchemaBoxReturn<S extends Record<string, SchemaWithPlaceholders>, R e
|
|
|
434
453
|
__registry: Resolved;
|
|
435
454
|
};
|
|
436
455
|
};
|
|
437
|
-
export declare function createSchemaBox<S extends Record<string, SchemaWithPlaceholders>, R extends ResolutionMap<S>>(schemas: S,
|
|
438
|
-
type SchemaProxy<S extends Record<string, SchemaWithPlaceholders>> = {
|
|
439
|
-
[K in keyof S]: {
|
|
440
|
-
[F in keyof S[K] as F extends "_tableName" ? never : F]: S[K][F] extends {
|
|
441
|
-
config: infer Config;
|
|
442
|
-
} ? S[K][F] & {
|
|
443
|
-
__meta: {
|
|
444
|
-
_key: F;
|
|
445
|
-
_fieldType: S[K][F];
|
|
446
|
-
};
|
|
447
|
-
__parentTableType: S[K];
|
|
448
|
-
} : S[K][F];
|
|
449
|
-
};
|
|
450
|
-
};
|
|
456
|
+
export declare function createSchemaBox<S extends Record<string, SchemaWithPlaceholders>, R extends ResolutionMap<S>>(schemas: S, resolutions: R): CreateSchemaBoxReturn<S, R>;
|
|
451
457
|
type Prettify<T> = {
|
|
452
458
|
[K in keyof T]: T[K];
|
|
453
459
|
} & {};
|
|
@@ -465,7 +471,13 @@ type GetDbKey<K, Field> = Field extends Reference<infer TGetter> ? ReturnType<TG
|
|
|
465
471
|
};
|
|
466
472
|
} ? string extends F ? K : F : K;
|
|
467
473
|
type DeriveSchemaByKey<T, Key extends "zodSqlSchema" | "zodClientSchema" | "zodValidationSchema", Depth extends any[] = []> = Depth["length"] extends 10 ? any : {
|
|
468
|
-
[K in keyof T as K extends "_tableName" | typeof SchemaWrapperBrand | "__primaryKeySQL" | "primaryKeySQL" ? never : K extends keyof T ? T[K] extends
|
|
474
|
+
[K in keyof T as K extends "_tableName" | typeof SchemaWrapperBrand | "__primaryKeySQL" | "primaryKeySQL" | "derive" | "__derives" ? never : K extends keyof T ? T[K] extends {
|
|
475
|
+
config: {
|
|
476
|
+
sql: {
|
|
477
|
+
sqlOnly: true;
|
|
478
|
+
};
|
|
479
|
+
};
|
|
480
|
+
} ? Key extends "zodSqlSchema" ? GetDbKey<K, T[K]> : never : T[K] extends Reference<any> ? Key extends "zodSqlSchema" ? GetDbKey<K, T[K]> : K : T[K] extends {
|
|
469
481
|
config: {
|
|
470
482
|
sql: {
|
|
471
483
|
type: "hasMany" | "manyToMany" | "hasOne" | "belongsTo";
|
|
@@ -482,7 +494,13 @@ type DeriveSchemaByKey<T, Key extends "zodSqlSchema" | "zodClientSchema" | "zodV
|
|
|
482
494
|
} ? ZodSchema : never;
|
|
483
495
|
};
|
|
484
496
|
type DeriveDefaults<T, Depth extends any[] = []> = Prettify<Depth["length"] extends 10 ? any : {
|
|
485
|
-
[K in keyof T as K extends "_tableName" | typeof SchemaWrapperBrand | "__primaryKeySQL" | "primaryKeySQL" ? never : K extends keyof T ? T[K] extends
|
|
497
|
+
[K in keyof T as K extends "_tableName" | typeof SchemaWrapperBrand | "__primaryKeySQL" | "primaryKeySQL" | "derive" | "__derives" ? never : K extends keyof T ? T[K] extends {
|
|
498
|
+
config: {
|
|
499
|
+
sql: {
|
|
500
|
+
sqlOnly: true;
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
} ? never : T[K] extends Reference<any> ? K : T[K] extends {
|
|
486
504
|
config: {
|
|
487
505
|
sql: {
|
|
488
506
|
type: "hasMany" | "manyToMany" | "hasOne" | "belongsTo";
|
|
@@ -494,15 +512,18 @@ type DeriveDefaults<T, Depth extends any[] = []> = Prettify<Depth["length"] exte
|
|
|
494
512
|
};
|
|
495
513
|
} ? D extends () => infer R ? R : D : never : T[K] extends {
|
|
496
514
|
config: {
|
|
497
|
-
zodNewSchema: infer TNew;
|
|
498
|
-
zodSqlSchema: infer TSql;
|
|
499
515
|
zodClientSchema: infer TClient extends z.ZodTypeAny;
|
|
500
|
-
initialValue: infer D;
|
|
501
516
|
};
|
|
502
|
-
} ?
|
|
517
|
+
} ? z.infer<TClient> : never;
|
|
503
518
|
}>;
|
|
504
519
|
type DeriveStateType<T, Depth extends any[] = []> = Prettify<Depth["length"] extends 10 ? any : {
|
|
505
|
-
[K in keyof T as K extends "_tableName" | typeof SchemaWrapperBrand | "__primaryKeySQL" | "primaryKeySQL" ? never : K extends keyof T ? T[K] extends
|
|
520
|
+
[K in keyof T as K extends "_tableName" | typeof SchemaWrapperBrand | "__primaryKeySQL" | "primaryKeySQL" | "derive" | "__derives" ? never : K extends keyof T ? T[K] extends {
|
|
521
|
+
config: {
|
|
522
|
+
sql: {
|
|
523
|
+
sqlOnly: true;
|
|
524
|
+
};
|
|
525
|
+
};
|
|
526
|
+
} ? never : T[K] extends Reference<any> ? K : T[K] extends {
|
|
506
527
|
config: {
|
|
507
528
|
sql: {
|
|
508
529
|
type: "hasMany" | "manyToMany" | "hasOne" | "belongsTo";
|
package/dist/schema.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
export const isFunction = (fn) => typeof fn === "function";
|
|
4
|
-
// Function to create a properly typed current timestamp config
|
|
5
4
|
export function currentTimeStamp() {
|
|
6
5
|
return {
|
|
7
6
|
default: "CURRENT_TIMESTAMP",
|
|
@@ -9,9 +8,8 @@ export function currentTimeStamp() {
|
|
|
9
8
|
};
|
|
10
9
|
}
|
|
11
10
|
export const s = {
|
|
12
|
-
|
|
11
|
+
client: (value) => {
|
|
13
12
|
const actualValue = isFunction(value) ? value({ uuid }) : value;
|
|
14
|
-
// Infer the Zod type from the primitive value
|
|
15
13
|
let inferredZodType;
|
|
16
14
|
if (typeof actualValue === "string") {
|
|
17
15
|
inferredZodType = z.string();
|
|
@@ -32,10 +30,9 @@ export const s = {
|
|
|
32
30
|
inferredZodType = z.any();
|
|
33
31
|
}
|
|
34
32
|
return createBuilder({
|
|
35
|
-
stage: "
|
|
33
|
+
stage: "client",
|
|
36
34
|
sqlConfig: null,
|
|
37
35
|
sqlZod: z.undefined(),
|
|
38
|
-
newZod: inferredZodType,
|
|
39
36
|
initialValue: actualValue,
|
|
40
37
|
clientZod: inferredZodType,
|
|
41
38
|
validationZod: inferredZodType,
|
|
@@ -56,7 +53,7 @@ export const s = {
|
|
|
56
53
|
hasOne: (config) => ({
|
|
57
54
|
__type: "placeholder-relation",
|
|
58
55
|
relationType: "hasOne",
|
|
59
|
-
defaultConfig: config,
|
|
56
|
+
defaultConfig: config,
|
|
60
57
|
}),
|
|
61
58
|
manyToMany: (config) => ({
|
|
62
59
|
__type: "placeholder-relation",
|
|
@@ -96,7 +93,6 @@ export const s = {
|
|
|
96
93
|
stage: "sql",
|
|
97
94
|
sqlConfig: sqlConfig,
|
|
98
95
|
sqlZod: sqlZodType,
|
|
99
|
-
newZod: sqlZodType,
|
|
100
96
|
initialValue: inferDefaultFromZod(sqlZodType, sqlConfig),
|
|
101
97
|
clientZod: sqlZodType,
|
|
102
98
|
validationZod: sqlZodType,
|
|
@@ -109,7 +105,6 @@ function createBuilder(config) {
|
|
|
109
105
|
config: {
|
|
110
106
|
sql: config.sqlConfig,
|
|
111
107
|
zodSqlSchema: config.sqlZod,
|
|
112
|
-
zodNewSchema: config.newZod,
|
|
113
108
|
initialValue: config.initialValue ||
|
|
114
109
|
inferDefaultFromZod(config.clientZod, config.sqlConfig),
|
|
115
110
|
zodClientSchema: config.clientZod,
|
|
@@ -117,138 +112,163 @@ function createBuilder(config) {
|
|
|
117
112
|
clientTransform: config.clientTransform,
|
|
118
113
|
validationTransform: config.validationTransform,
|
|
119
114
|
},
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
115
|
+
reference: (fieldGetter) => {
|
|
116
|
+
return createBuilder({
|
|
117
|
+
...config,
|
|
118
|
+
sqlConfig: {
|
|
119
|
+
...config.sqlConfig,
|
|
120
|
+
reference: fieldGetter,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
client: (...args) => {
|
|
125
|
+
if (completedStages.has("client")) {
|
|
126
|
+
throw new Error("client() can only be called once in the chain");
|
|
127
|
+
}
|
|
128
|
+
if (completedStages.has("server")) {
|
|
129
|
+
throw new Error("client() must be called before server()");
|
|
130
|
+
}
|
|
131
|
+
const newCompletedStages = new Set(completedStages);
|
|
132
|
+
newCompletedStages.add("client");
|
|
133
|
+
let optionsOrSchema = args[0];
|
|
134
|
+
if (config.stage === "relation") {
|
|
135
|
+
const assert = typeof optionsOrSchema === "function" ||
|
|
136
|
+
optionsOrSchema instanceof z.ZodType
|
|
137
|
+
? optionsOrSchema
|
|
138
|
+
: optionsOrSchema?.schema;
|
|
139
|
+
return createBuilder({
|
|
140
|
+
...config,
|
|
141
|
+
stage: "client",
|
|
142
|
+
completedStages: newCompletedStages,
|
|
143
|
+
clientTransform: (baseSchema) => {
|
|
144
|
+
if (isFunction(assert)) {
|
|
145
|
+
return assert({ sql: baseSchema });
|
|
146
|
+
}
|
|
147
|
+
return assert;
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
let isDirectShortcut = false;
|
|
152
|
+
let isValueAndSchemaShortcut = false;
|
|
153
|
+
let options = {};
|
|
154
|
+
if (optionsOrSchema !== undefined &&
|
|
155
|
+
typeof optionsOrSchema === "function") {
|
|
156
|
+
if (args.length === 2 && isFunction(args[1])) {
|
|
157
|
+
isValueAndSchemaShortcut = true;
|
|
158
|
+
options = {
|
|
159
|
+
schema: optionsOrSchema,
|
|
160
|
+
value: args[1],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
options = { schema: optionsOrSchema };
|
|
165
|
+
isDirectShortcut = true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (optionsOrSchema !== undefined &&
|
|
169
|
+
typeof optionsOrSchema === "object" &&
|
|
170
|
+
!("_def" in optionsOrSchema) &&
|
|
171
|
+
!("parse" in optionsOrSchema) &&
|
|
172
|
+
(optionsOrSchema.value !== undefined ||
|
|
173
|
+
optionsOrSchema.schema !== undefined ||
|
|
174
|
+
optionsOrSchema.clientPk !== undefined)) {
|
|
175
|
+
options = optionsOrSchema;
|
|
176
|
+
}
|
|
177
|
+
else if (optionsOrSchema !== undefined) {
|
|
178
|
+
options = { schema: optionsOrSchema };
|
|
179
|
+
isDirectShortcut = true;
|
|
123
180
|
}
|
|
124
181
|
const { value, schema: schemaOrModifier, clientPk } = options;
|
|
125
|
-
let actualValue;
|
|
182
|
+
let actualValue = config.initialValue;
|
|
126
183
|
let finalSchema;
|
|
127
|
-
// 1. Determine the actual value
|
|
128
184
|
if (value !== undefined) {
|
|
129
185
|
actualValue = isFunction(value) ? value({ uuid }) : value;
|
|
130
186
|
}
|
|
131
187
|
else if (schemaOrModifier &&
|
|
132
188
|
typeof schemaOrModifier === "object" &&
|
|
133
189
|
"_def" in schemaOrModifier) {
|
|
134
|
-
|
|
135
|
-
|
|
190
|
+
if (config.sqlZod instanceof z.ZodUndefined ||
|
|
191
|
+
actualValue === undefined) {
|
|
192
|
+
actualValue = inferDefaultFromZod(schemaOrModifier, config.sqlConfig);
|
|
193
|
+
}
|
|
136
194
|
}
|
|
137
|
-
// 2. Determine the final schema
|
|
138
195
|
let baseSchema;
|
|
139
196
|
if (schemaOrModifier &&
|
|
140
197
|
typeof schemaOrModifier === "object" &&
|
|
141
198
|
"_def" in schemaOrModifier) {
|
|
142
|
-
// A raw Zod schema was passed
|
|
143
199
|
finalSchema = schemaOrModifier;
|
|
144
200
|
}
|
|
145
201
|
else {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
202
|
+
if (value !== undefined) {
|
|
203
|
+
if (typeof actualValue === "string")
|
|
204
|
+
baseSchema = z.string();
|
|
205
|
+
else if (typeof actualValue === "number")
|
|
206
|
+
baseSchema = z.number();
|
|
207
|
+
else if (typeof actualValue === "boolean")
|
|
208
|
+
baseSchema = z.boolean();
|
|
209
|
+
else if (actualValue instanceof Date)
|
|
210
|
+
baseSchema = z.date();
|
|
211
|
+
else if (actualValue === null)
|
|
212
|
+
baseSchema = z.null();
|
|
213
|
+
else
|
|
214
|
+
baseSchema = z.any();
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
baseSchema = config.clientZod;
|
|
218
|
+
}
|
|
159
219
|
if (isFunction(schemaOrModifier)) {
|
|
160
|
-
|
|
161
|
-
|
|
220
|
+
if (isDirectShortcut) {
|
|
221
|
+
finalSchema = schemaOrModifier({ sql: config.sqlZod });
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
finalSchema = schemaOrModifier(baseSchema);
|
|
225
|
+
}
|
|
162
226
|
}
|
|
163
227
|
else {
|
|
164
|
-
// No schema/modifier, use the inferred base schema
|
|
165
228
|
finalSchema = baseSchema;
|
|
166
229
|
}
|
|
167
230
|
}
|
|
168
|
-
const newCompletedStages = new Set(completedStages);
|
|
169
|
-
newCompletedStages.add("new");
|
|
170
231
|
const newConfig = { ...config.sqlConfig };
|
|
171
232
|
if (clientPk !== undefined) {
|
|
172
|
-
// Store the boolean OR the function directly into the config
|
|
173
233
|
newConfig.isClientPk = clientPk;
|
|
174
234
|
}
|
|
175
235
|
let clientAndServerSchema;
|
|
176
236
|
if (clientPk) {
|
|
177
|
-
// Always union for clientPk fields
|
|
178
237
|
clientAndServerSchema = z.union([config.sqlZod, finalSchema]);
|
|
179
238
|
}
|
|
180
|
-
else if (schemaOrModifier) {
|
|
181
|
-
|
|
182
|
-
|
|
239
|
+
else if (schemaOrModifier !== undefined) {
|
|
240
|
+
if (isDirectShortcut) {
|
|
241
|
+
clientAndServerSchema = finalSchema;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
clientAndServerSchema = finalSchema;
|
|
245
|
+
}
|
|
183
246
|
}
|
|
184
247
|
else {
|
|
185
|
-
|
|
186
|
-
|
|
248
|
+
if (config.sqlZod instanceof z.ZodUndefined) {
|
|
249
|
+
clientAndServerSchema = finalSchema;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
clientAndServerSchema = z.union([config.sqlZod, finalSchema]);
|
|
253
|
+
}
|
|
187
254
|
}
|
|
188
255
|
return createBuilder({
|
|
189
256
|
...config,
|
|
190
|
-
stage: "
|
|
257
|
+
stage: "client",
|
|
191
258
|
sqlConfig: newConfig,
|
|
192
|
-
newZod: finalSchema,
|
|
193
259
|
initialValue: actualValue,
|
|
194
260
|
clientZod: clientAndServerSchema,
|
|
195
261
|
validationZod: clientAndServerSchema,
|
|
196
262
|
completedStages: newCompletedStages,
|
|
197
263
|
});
|
|
198
264
|
},
|
|
199
|
-
reference: (fieldGetter) => {
|
|
200
|
-
return createBuilder({
|
|
201
|
-
...config,
|
|
202
|
-
sqlConfig: {
|
|
203
|
-
...config.sqlConfig,
|
|
204
|
-
reference: fieldGetter,
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
},
|
|
208
|
-
client: (assert) => {
|
|
209
|
-
if (completedStages.has("client")) {
|
|
210
|
-
throw new Error("client() can only be called once in the chain");
|
|
211
|
-
}
|
|
212
|
-
if (completedStages.has("server")) {
|
|
213
|
-
throw new Error("client() must be called before validation()");
|
|
214
|
-
}
|
|
215
|
-
const newCompletedStages = new Set(completedStages);
|
|
216
|
-
newCompletedStages.add("client");
|
|
217
|
-
if (config.stage === "relation") {
|
|
218
|
-
return createBuilder({
|
|
219
|
-
...config,
|
|
220
|
-
stage: "client",
|
|
221
|
-
completedStages: newCompletedStages,
|
|
222
|
-
clientTransform: (baseSchema) => {
|
|
223
|
-
if (isFunction(assert)) {
|
|
224
|
-
return assert({
|
|
225
|
-
sql: baseSchema,
|
|
226
|
-
initialState: config.newZod,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
return assert;
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
const clientSchema = isFunction(assert)
|
|
234
|
-
? assert({ sql: config.sqlZod, initialState: config.newZod })
|
|
235
|
-
: assert;
|
|
236
|
-
return createBuilder({
|
|
237
|
-
...config,
|
|
238
|
-
stage: "client",
|
|
239
|
-
clientZod: clientSchema,
|
|
240
|
-
validationZod: clientSchema,
|
|
241
|
-
completedStages: newCompletedStages,
|
|
242
|
-
});
|
|
243
|
-
},
|
|
244
265
|
server: (assert) => {
|
|
245
266
|
if (completedStages.has("server")) {
|
|
246
|
-
throw new Error("
|
|
267
|
+
throw new Error("server() can only be called once in the chain");
|
|
247
268
|
}
|
|
248
269
|
const serverSchema = isFunction(assert)
|
|
249
270
|
? assert({
|
|
250
271
|
sql: config.sqlZod,
|
|
251
|
-
initialState: config.newZod,
|
|
252
272
|
client: config.clientZod,
|
|
253
273
|
})
|
|
254
274
|
: assert;
|
|
@@ -263,7 +283,7 @@ function createBuilder(config) {
|
|
|
263
283
|
},
|
|
264
284
|
transform: (transforms) => {
|
|
265
285
|
if (!completedStages.has("server") && !completedStages.has("client")) {
|
|
266
|
-
throw new Error("transform() requires at least client() or
|
|
286
|
+
throw new Error("transform() requires at least client() or server() to be called first");
|
|
267
287
|
}
|
|
268
288
|
return {
|
|
269
289
|
config: {
|
|
@@ -280,7 +300,6 @@ function createBuilder(config) {
|
|
|
280
300
|
}
|
|
281
301
|
export const SchemaWrapperBrand = Symbol("SchemaWrapper");
|
|
282
302
|
export function schema(schema) {
|
|
283
|
-
// Create the enriched schema with all fields
|
|
284
303
|
const enrichedSchema = {};
|
|
285
304
|
for (const key in schema) {
|
|
286
305
|
if (Object.prototype.hasOwnProperty.call(schema, key)) {
|
|
@@ -298,6 +317,7 @@ export function schema(schema) {
|
|
|
298
317
|
}
|
|
299
318
|
enrichedSchema[SchemaWrapperBrand] = true;
|
|
300
319
|
enrichedSchema.__primaryKeySQL = undefined;
|
|
320
|
+
enrichedSchema.__derives = undefined;
|
|
301
321
|
enrichedSchema.primaryKeySQL = function (definer) {
|
|
302
322
|
const pkFieldsOnly = {};
|
|
303
323
|
for (const key in schema) {
|
|
@@ -311,6 +331,10 @@ export function schema(schema) {
|
|
|
311
331
|
enrichedSchema.__primaryKeySQL = definer(pkFieldsOnly);
|
|
312
332
|
return enrichedSchema;
|
|
313
333
|
};
|
|
334
|
+
enrichedSchema.derive = function (derivers) {
|
|
335
|
+
enrichedSchema.__derives = derivers;
|
|
336
|
+
return enrichedSchema;
|
|
337
|
+
};
|
|
314
338
|
return enrichedSchema;
|
|
315
339
|
}
|
|
316
340
|
function inferDefaultFromZod(zodType, sqlConfig) {
|
|
@@ -323,10 +347,6 @@ function inferDefaultFromZod(zodType, sqlConfig) {
|
|
|
323
347
|
if ("default" in sqlConfig && sqlConfig.default !== undefined) {
|
|
324
348
|
return sqlConfig.default;
|
|
325
349
|
}
|
|
326
|
-
if (typeof sqlConfig.type === "string" &&
|
|
327
|
-
["hasMany", "hasOne", "belongsTo", "manyToMany"].includes(sqlConfig.type)) {
|
|
328
|
-
// ...
|
|
329
|
-
}
|
|
330
350
|
const sqlTypeConfig = sqlConfig;
|
|
331
351
|
if (sqlTypeConfig.type && !sqlTypeConfig.nullable) {
|
|
332
352
|
switch (sqlTypeConfig.type) {
|
|
@@ -373,13 +393,16 @@ export function createSchema(schema, relations) {
|
|
|
373
393
|
const fieldTransforms = {};
|
|
374
394
|
const clientToDbKeys = {};
|
|
375
395
|
const dbToClientKeys = {};
|
|
396
|
+
const sqlOnlyDbKeys = new Set();
|
|
376
397
|
const fullSchema = { ...schema, ...(relations || {}) };
|
|
377
398
|
let pkKeys = [];
|
|
378
399
|
let clientPkKeys = [];
|
|
400
|
+
const derives = schema.__derives;
|
|
379
401
|
for (const key in fullSchema) {
|
|
380
402
|
const value = fullSchema[key];
|
|
381
403
|
if (key === "_tableName" ||
|
|
382
404
|
key.startsWith("__") ||
|
|
405
|
+
key === "derive" ||
|
|
383
406
|
key === String(SchemaWrapperBrand) ||
|
|
384
407
|
key === "primaryKeySQL" ||
|
|
385
408
|
typeof value === "function")
|
|
@@ -390,19 +413,29 @@ export function createSchema(schema, relations) {
|
|
|
390
413
|
if (targetField && targetField.config) {
|
|
391
414
|
const config = targetField.config;
|
|
392
415
|
const dbFieldName = config.sql?.field || key;
|
|
393
|
-
clientToDbKeys[key] = dbFieldName;
|
|
394
|
-
dbToClientKeys[dbFieldName] = key;
|
|
395
416
|
sqlFields[dbFieldName] = config.zodSqlSchema;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
417
|
+
if (config.sql?.sqlOnly) {
|
|
418
|
+
sqlOnlyDbKeys.add(dbFieldName);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
clientToDbKeys[key] = dbFieldName;
|
|
422
|
+
dbToClientKeys[dbFieldName] = key;
|
|
423
|
+
clientFields[key] = config.zodClientSchema;
|
|
424
|
+
serverFields[key] = config.zodValidationSchema;
|
|
425
|
+
const initialValueOrFn = config.initialValue;
|
|
426
|
+
defaultGenerators[key] = initialValueOrFn;
|
|
427
|
+
let rawDefault = isFunction(initialValueOrFn)
|
|
428
|
+
? initialValueOrFn({ uuid })
|
|
429
|
+
: initialValueOrFn;
|
|
430
|
+
if (config.transforms?.toClient && rawDefault !== undefined) {
|
|
431
|
+
defaultValues[key] = config.transforms.toClient(rawDefault);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
defaultValues[key] = rawDefault;
|
|
435
|
+
}
|
|
436
|
+
if (config.transforms) {
|
|
437
|
+
fieldTransforms[key] = config.transforms;
|
|
438
|
+
}
|
|
406
439
|
}
|
|
407
440
|
}
|
|
408
441
|
continue;
|
|
@@ -419,11 +452,34 @@ export function createSchema(schema, relations) {
|
|
|
419
452
|
["hasMany", "hasOne", "belongsTo", "manyToMany"].includes(sqlConfig.type)) {
|
|
420
453
|
continue;
|
|
421
454
|
}
|
|
422
|
-
else {
|
|
423
|
-
const dbFieldName = sqlConfig
|
|
424
|
-
clientToDbKeys[key] = dbFieldName;
|
|
425
|
-
dbToClientKeys[dbFieldName] = key;
|
|
455
|
+
else if (sqlConfig) {
|
|
456
|
+
const dbFieldName = sqlConfig.field || key;
|
|
426
457
|
sqlFields[dbFieldName] = config.zodSqlSchema;
|
|
458
|
+
if (sqlConfig.sqlOnly) {
|
|
459
|
+
sqlOnlyDbKeys.add(dbFieldName);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
clientToDbKeys[key] = dbFieldName;
|
|
463
|
+
dbToClientKeys[dbFieldName] = key;
|
|
464
|
+
clientFields[key] = config.zodClientSchema;
|
|
465
|
+
serverFields[key] = config.zodValidationSchema;
|
|
466
|
+
if (config.transforms) {
|
|
467
|
+
fieldTransforms[key] = config.transforms;
|
|
468
|
+
}
|
|
469
|
+
const initialValueOrFn = config.initialValue;
|
|
470
|
+
defaultGenerators[key] = initialValueOrFn;
|
|
471
|
+
let rawDefault = isFunction(initialValueOrFn)
|
|
472
|
+
? initialValueOrFn({ uuid })
|
|
473
|
+
: initialValueOrFn;
|
|
474
|
+
if (config.transforms?.toClient && rawDefault !== undefined) {
|
|
475
|
+
defaultValues[key] = config.transforms.toClient(rawDefault);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
defaultValues[key] = rawDefault;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
427
483
|
clientFields[key] = config.zodClientSchema;
|
|
428
484
|
serverFields[key] = config.zodValidationSchema;
|
|
429
485
|
if (config.transforms) {
|
|
@@ -443,7 +499,6 @@ export function createSchema(schema, relations) {
|
|
|
443
499
|
}
|
|
444
500
|
}
|
|
445
501
|
}
|
|
446
|
-
// --- NEW: SMART CHECKER BUILDER ---
|
|
447
502
|
let isClientRecord = () => false;
|
|
448
503
|
if (clientPkKeys.length > 0) {
|
|
449
504
|
const checkers = [];
|
|
@@ -453,21 +508,16 @@ export function createSchema(schema, relations) {
|
|
|
453
508
|
const dbKey = sqlConfig?.field || key;
|
|
454
509
|
const isClientPkVal = sqlConfig?.isClientPk;
|
|
455
510
|
if (typeof isClientPkVal === "function") {
|
|
456
|
-
// Explicit checker provided directly in the field!
|
|
457
511
|
checkers.push({ clientKey: key, dbKey, check: isClientPkVal });
|
|
458
512
|
}
|
|
459
513
|
else {
|
|
460
|
-
// Fallback auto-detection: If they just passed `true`
|
|
461
514
|
const initialValueOrFn = field?.config?.initialValue;
|
|
462
515
|
let sampleValue = initialValueOrFn;
|
|
463
|
-
// Safely execute the function once to figure out its return type!
|
|
464
516
|
if (isFunction(initialValueOrFn)) {
|
|
465
517
|
try {
|
|
466
518
|
sampleValue = initialValueOrFn({ uuid });
|
|
467
519
|
}
|
|
468
|
-
catch (e) {
|
|
469
|
-
// Ignore if the factory fails with a dummy payload
|
|
470
|
-
}
|
|
520
|
+
catch (e) { }
|
|
471
521
|
}
|
|
472
522
|
if (sqlConfig?.type === "int" && typeof sampleValue === "string") {
|
|
473
523
|
checkers.push({
|
|
@@ -483,7 +533,6 @@ export function createSchema(schema, relations) {
|
|
|
483
533
|
if (!record || typeof record !== "object")
|
|
484
534
|
return false;
|
|
485
535
|
return checkers.some(({ clientKey, dbKey, check }) => {
|
|
486
|
-
// Look at both the client shape key AND the db shape key safely
|
|
487
536
|
const val = record[clientKey] !== undefined ? record[clientKey] : record[dbKey];
|
|
488
537
|
return check(val);
|
|
489
538
|
});
|
|
@@ -501,6 +550,11 @@ export function createSchema(schema, relations) {
|
|
|
501
550
|
? fieldTransforms[key].toClient(rawValue)
|
|
502
551
|
: rawValue;
|
|
503
552
|
}
|
|
553
|
+
if (derives) {
|
|
554
|
+
for (const key in derives) {
|
|
555
|
+
freshDefaults[key] = derives[key](freshDefaults);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
504
558
|
return freshDefaults;
|
|
505
559
|
};
|
|
506
560
|
const toClient = (dbObject) => {
|
|
@@ -508,12 +562,19 @@ export function createSchema(schema, relations) {
|
|
|
508
562
|
for (const dbKey in dbObject) {
|
|
509
563
|
if (dbObject[dbKey] === undefined)
|
|
510
564
|
continue;
|
|
565
|
+
if (sqlOnlyDbKeys.has(dbKey))
|
|
566
|
+
continue;
|
|
511
567
|
const clientKey = dbToClientKeys[dbKey] || dbKey;
|
|
512
568
|
const transform = fieldTransforms[clientKey]?.toClient;
|
|
513
569
|
clientObject[clientKey] = transform
|
|
514
570
|
? transform(dbObject[dbKey])
|
|
515
571
|
: dbObject[dbKey];
|
|
516
572
|
}
|
|
573
|
+
if (derives) {
|
|
574
|
+
for (const key in derives) {
|
|
575
|
+
clientObject[key] = derives[key](clientObject);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
517
578
|
return clientObject;
|
|
518
579
|
};
|
|
519
580
|
const toDb = (clientObject) => {
|
|
@@ -615,31 +676,8 @@ function createViewObject(initialRegistryKey, selection, registry, tableNameToRe
|
|
|
615
676
|
supportsReconciliation: allTablesSupportsReconciliation,
|
|
616
677
|
};
|
|
617
678
|
}
|
|
618
|
-
export function createSchemaBox(schemas,
|
|
619
|
-
const
|
|
620
|
-
get(target, tableName) {
|
|
621
|
-
const schema = schemas[tableName];
|
|
622
|
-
if (!schema)
|
|
623
|
-
return undefined;
|
|
624
|
-
return new Proxy({}, {
|
|
625
|
-
get(target, fieldName) {
|
|
626
|
-
const field = schema[fieldName];
|
|
627
|
-
if (field && typeof field === "object") {
|
|
628
|
-
return {
|
|
629
|
-
...field,
|
|
630
|
-
__meta: {
|
|
631
|
-
_key: fieldName,
|
|
632
|
-
_fieldType: field,
|
|
633
|
-
},
|
|
634
|
-
__parentTableType: schema,
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
return field;
|
|
638
|
-
},
|
|
639
|
-
});
|
|
640
|
-
},
|
|
641
|
-
});
|
|
642
|
-
const resolutionConfig = resolver(schemaProxy);
|
|
679
|
+
export function createSchemaBox(schemas, resolutions) {
|
|
680
|
+
const resolutionConfig = resolutions;
|
|
643
681
|
const resolvedSchemas = schemas;
|
|
644
682
|
for (const tableName in schemas) {
|
|
645
683
|
for (const fieldName in schemas[tableName]) {
|
|
@@ -694,7 +732,6 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
694
732
|
defaultConfig: field.defaultConfig,
|
|
695
733
|
},
|
|
696
734
|
sqlZod: zodSchema,
|
|
697
|
-
newZod: zodSchema,
|
|
698
735
|
initialValue,
|
|
699
736
|
clientZod: zodSchema,
|
|
700
737
|
validationZod: zodSchema,
|
|
@@ -709,6 +746,16 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
709
746
|
finalRegistry[tableName] = {
|
|
710
747
|
rawSchema: resolvedSchemas[tableName],
|
|
711
748
|
zodSchemas: zodSchemas,
|
|
749
|
+
transforms: {
|
|
750
|
+
toClient: zodSchemas.toClient,
|
|
751
|
+
toDb: zodSchemas.toDb,
|
|
752
|
+
parseForDb: zodSchemas.parseForDb,
|
|
753
|
+
parseFromDb: zodSchemas.parseFromDb,
|
|
754
|
+
},
|
|
755
|
+
pk: zodSchemas.pk,
|
|
756
|
+
clientPk: zodSchemas.clientPk,
|
|
757
|
+
isClientRecord: zodSchemas.isClientRecord,
|
|
758
|
+
generateDefaults: zodSchemas.generateDefaults,
|
|
712
759
|
};
|
|
713
760
|
}
|
|
714
761
|
const createNavProxy = (currentTable, registry) => {
|
|
@@ -746,17 +793,17 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
746
793
|
server: entry.zodSchemas.serverSchema,
|
|
747
794
|
},
|
|
748
795
|
transforms: {
|
|
749
|
-
toClient: entry.
|
|
750
|
-
toDb: entry.
|
|
796
|
+
toClient: entry.transforms.toClient,
|
|
797
|
+
toDb: entry.transforms.toDb,
|
|
798
|
+
parseForDb: entry.transforms.parseForDb,
|
|
799
|
+
parseFromDb: entry.transforms.parseFromDb,
|
|
751
800
|
},
|
|
752
|
-
|
|
753
|
-
parseFromDb: entry.zodSchemas.parseFromDb,
|
|
754
|
-
defaults: entry.zodSchemas.defaultValues,
|
|
801
|
+
defaults: entry.generateDefaults(),
|
|
755
802
|
stateType: entry.zodSchemas.stateType,
|
|
756
|
-
generateDefaults: entry.
|
|
757
|
-
pk: entry.
|
|
758
|
-
clientPk: entry.
|
|
759
|
-
isClientRecord: entry.
|
|
803
|
+
generateDefaults: entry.generateDefaults,
|
|
804
|
+
pk: entry.pk,
|
|
805
|
+
clientPk: entry.clientPk,
|
|
806
|
+
isClientRecord: entry.isClientRecord,
|
|
760
807
|
nav: createNavProxy(tableName, finalRegistry),
|
|
761
808
|
createView: (selection) => {
|
|
762
809
|
const view = createViewObject(tableName, selection, finalRegistry, tableNameToRegistryKeyMap);
|
|
@@ -767,7 +814,7 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
767
814
|
if (Array.isArray(dbData))
|
|
768
815
|
return dbData.map((item) => deepToClient(item, currentSelection, currentKey));
|
|
769
816
|
const regEntry = finalRegistry[currentKey];
|
|
770
|
-
const baseMapped = regEntry.
|
|
817
|
+
const baseMapped = { ...regEntry.transforms.toClient(dbData) };
|
|
771
818
|
if (typeof currentSelection === "object") {
|
|
772
819
|
for (const relKey in currentSelection) {
|
|
773
820
|
if (currentSelection[relKey] &&
|
|
@@ -784,16 +831,20 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
784
831
|
}
|
|
785
832
|
}
|
|
786
833
|
}
|
|
834
|
+
if (regEntry.rawSchema.__derives) {
|
|
835
|
+
for (const key in regEntry.rawSchema.__derives) {
|
|
836
|
+
baseMapped[key] = regEntry.rawSchema.__derives[key](baseMapped);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
787
839
|
return baseMapped;
|
|
788
840
|
};
|
|
789
|
-
// --- NEW: Implement recursive toDb ---
|
|
790
841
|
const deepToDb = (clientData, currentSelection, currentKey) => {
|
|
791
842
|
if (!clientData)
|
|
792
843
|
return clientData;
|
|
793
844
|
if (Array.isArray(clientData))
|
|
794
845
|
return clientData.map((item) => deepToDb(item, currentSelection, currentKey));
|
|
795
846
|
const regEntry = finalRegistry[currentKey];
|
|
796
|
-
const baseMapped = regEntry.
|
|
847
|
+
const baseMapped = regEntry.transforms.toDb(clientData);
|
|
797
848
|
if (typeof currentSelection === "object") {
|
|
798
849
|
for (const relKey in currentSelection) {
|
|
799
850
|
if (currentSelection[relKey] &&
|
|
@@ -813,7 +864,6 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
813
864
|
return baseMapped;
|
|
814
865
|
};
|
|
815
866
|
const viewToClient = (dbData) => deepToClient(dbData, selection, tableName);
|
|
816
|
-
// --- NEW: View To Db ---
|
|
817
867
|
const viewToDb = (clientData) => deepToDb(clientData, selection, tableName);
|
|
818
868
|
return {
|
|
819
869
|
definition: entry.rawSchema,
|
|
@@ -825,16 +875,13 @@ export function createSchemaBox(schemas, resolver) {
|
|
|
825
875
|
},
|
|
826
876
|
transforms: {
|
|
827
877
|
toClient: viewToClient,
|
|
828
|
-
toDb: viewToDb,
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
parseFromDb: (dbData) => {
|
|
836
|
-
const mapped = viewToClient(dbData);
|
|
837
|
-
return view.client.parse(mapped);
|
|
878
|
+
toDb: viewToDb,
|
|
879
|
+
parseForDb: (appData) => {
|
|
880
|
+
return viewToDb(appData);
|
|
881
|
+
},
|
|
882
|
+
parseFromDb: (dbData) => {
|
|
883
|
+
return viewToClient(dbData);
|
|
884
|
+
},
|
|
838
885
|
},
|
|
839
886
|
defaults: defaults,
|
|
840
887
|
pk: entry.zodSchemas.pk,
|
|
@@ -863,7 +910,7 @@ function computeViewDefaults(currentRegistryKey, selection, registry, tableNameT
|
|
|
863
910
|
return {};
|
|
864
911
|
}
|
|
865
912
|
const rawSchema = entry.rawSchema;
|
|
866
|
-
const baseDefaults =
|
|
913
|
+
const baseDefaults = entry.generateDefaults();
|
|
867
914
|
if (selection === true || typeof selection !== "object") {
|
|
868
915
|
return baseDefaults;
|
|
869
916
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.179",
|
|
4
4
|
"description": "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 relationships and serialization.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|