@inflector/optima 1.0.0 → 1.0.1

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/src/schema.ts DELETED
@@ -1,446 +0,0 @@
1
- import * as z from "zod";
2
-
3
- type DefaultConfig = { [K in keyof ColumnConfig]: false };
4
- // Helper: Sets Key K to true in Config C
5
- type SetFlag<C extends ColumnConfig, K extends keyof ColumnConfig> = Omit<
6
- C,
7
- K
8
- > & { [P in K]: true };
9
-
10
- export type Prettify<T> = {
11
- [K in keyof T]: T[K];
12
- } & {};
13
-
14
- export type Restrict<K extends keyof ColumnConfig> = {
15
- [P in keyof ColumnConfig]: P extends K ? true : false;
16
- };
17
-
18
- // Phantom type brand symbol
19
- declare const __parentBrand: unique symbol;
20
- type ParentBrand<Name extends string> = { [__parentBrand]: Name };
21
-
22
- // Reference wrapper types for one-to-one and one-to-many relationships
23
- // Using string literal type discrimination instead of symbols for cross-module compatibility
24
- export type One<T> = T & { readonly __refKind: "one" };
25
- export type Many<T> = T & { readonly __refKind: "many" };
26
-
27
- // Helper functions for reference syntax
28
- export const refHelpers = {
29
- one: <T>(ref: T): One<T> => ref as One<T>,
30
- many: <T>(ref: T): Many<T> => ref as Many<T>,
31
- };
32
-
33
- export type InferColumnType<T> = T extends ColumnBuilder<infer U, any, any>
34
- ? U
35
- : never;
36
-
37
- export type Infer<T extends Record<string, any>> = Prettify<
38
- // 1. Handle Required Keys (notnull: true)
39
- {
40
- [K in keyof T as K extends `__${string}`
41
- ? never
42
- : T[K] extends ColumnBuilder<any, infer C, any>
43
- ? C["notnull"] extends true
44
- ? K
45
- : never
46
- : never]: T[K] extends ColumnBuilder<infer U, any, any> ? U : never;
47
- } & // 2. Handle Optional Keys (notnull: false)
48
- {
49
- [K in keyof T as K extends `__${string}`
50
- ? never
51
- : T[K] extends ColumnBuilder<any, infer C, any>
52
- ? C["notnull"] extends true
53
- ? never
54
- : K
55
- : never]: T[K] extends ColumnBuilder<infer U, any, any>
56
- ? U | null
57
- : never;
58
- }
59
- >;
60
- export type InferAdd<T extends Record<string, any>> = Prettify<
61
- // 1. Handle Required Keys (notnull: true)
62
- {
63
- [K in keyof T as K extends `__${string}`
64
- ? never
65
- : T[K] extends ColumnBuilder<any, infer C, any>
66
- ? C["notnull"] extends true
67
- ? K
68
- : never
69
- : never]: T[K] extends ColumnBuilder<infer U, any, any> ? U : never;
70
- } & // 2. Handle Optional Keys (notnull: false)
71
- {
72
- [K in keyof T as K extends `__${string}`
73
- ? never
74
- : T[K] extends ColumnBuilder<any, infer C, any>
75
- ? C["notnull"] extends true
76
- ? never
77
- : K
78
- : never]?: T[K] extends ColumnBuilder<infer U, any, any>
79
- ? U | null
80
- : never;
81
- }
82
- >;
83
-
84
- export type ColumnConfig = {
85
- SQlType: boolean;
86
- primaryKey: boolean;
87
- notnull: boolean;
88
- unique: boolean;
89
- default: boolean;
90
- defaultNow: boolean;
91
- reference: boolean;
92
- validate: boolean;
93
- transform: boolean;
94
- deprecated: boolean;
95
- STRUCTType: boolean;
96
- };
97
-
98
- export type ColumnBuilder<
99
- Type,
100
- Config extends ColumnConfig,
101
- RefSchema = never
102
- > = {
103
- [K in keyof ColumnConfig as K extends "STRUCTType"
104
- ? never
105
- : Config[K] extends true
106
- ? never
107
- : K]: K extends "SQlType"
108
- ? (
109
- val: string
110
- ) => ColumnBuilder<Type, SetFlag<Config, "SQlType">, RefSchema>
111
- : K extends "primaryKey"
112
- ? () => ColumnBuilder<Type, SetFlag<Config, "primaryKey">, RefSchema>
113
- : K extends "notnull"
114
- ? () => ColumnBuilder<Type, SetFlag<Config, "notnull">, RefSchema>
115
- : K extends "unique"
116
- ? () => ColumnBuilder<Type, SetFlag<Config, "unique">, RefSchema>
117
- : K extends "default"
118
- ? (
119
- val: Type | (() => Type)
120
- ) => ColumnBuilder<Type, SetFlag<Config, "default">, RefSchema>
121
- : K extends "defaultNow"
122
- ? () => ColumnBuilder<Type, SetFlag<Config, "defaultNow">, RefSchema>
123
- : K extends "reference"
124
- ? <TRef>(
125
- ref: (helpers: typeof refHelpers) => TRef
126
- ) => ColumnBuilder<Type, SetFlag<Config, "reference">, TRef>
127
- : K extends "validate"
128
- ? (
129
- fn: (val: Type) => boolean
130
- ) => ColumnBuilder<Type, SetFlag<Config, "validate">, RefSchema>
131
- : K extends "transform"
132
- ? (
133
- fn: (val: Type) => Type
134
- ) => ColumnBuilder<Type, SetFlag<Config, "transform">, RefSchema>
135
- : K extends "deprecated"
136
- ? () => ColumnBuilder<Type, SetFlag<Config, "deprecated">, RefSchema>
137
- : never;
138
- };
139
-
140
- class ColumnImpl<Type, RefSchema = never> {
141
- public config: Partial<Record<keyof ColumnConfig, any>> = {};
142
-
143
- constructor(config: Partial<Record<keyof ColumnConfig, any>> = {}) {
144
- this.config = config;
145
- }
146
-
147
- private next(key: keyof ColumnConfig, value: any = true): any {
148
- return new ColumnImpl({ ...this.config, [key]: value });
149
- }
150
-
151
- SQlType(val: string) {
152
- return this.next("SQlType", val);
153
- }
154
- primaryKey() {
155
- return this.next("primaryKey");
156
- }
157
- notnull() {
158
- return this.next("notnull");
159
- }
160
- unique() {
161
- return this.next("unique");
162
- }
163
- default(val: Type | (() => Type)) {
164
- return this.next("default", val);
165
- }
166
- defaultNow() {
167
- return this.next("defaultNow");
168
- }
169
- reference<TRef>(c: (helpers: typeof refHelpers) => TRef) {
170
- // Execute function with helpers to get the wrapped reference
171
- const result = c(refHelpers) as any;
172
-
173
- // Detect if it's a Many wrapper by checking the symbol
174
- const isManyRef =
175
- result?.[Symbol.for("__refType")] === "many" ||
176
- (typeof result === "object" && result !== null && "__refType" in result);
177
-
178
- // Unwrap the actual column reference
179
- const actualRef = result;
180
-
181
- let refName = "";
182
-
183
- if (actualRef?.__parent && actualRef?.__fieldName) {
184
- refName = `${actualRef.__parent}.${actualRef.__fieldName}`;
185
- } else {
186
- // Fallback: parse function string
187
- const code = c.toString();
188
- // Extract the reference from patterns like: one(Users.ID) or many(Users.ID)
189
- const match = code.match(/(?:one|many)\s*\(\s*([^)]+)\s*\)/);
190
- if (match && match[1]) {
191
- const refPart = match[1].trim();
192
- if (refPart.includes(".")) {
193
- refName = refPart;
194
- }
195
- }
196
- }
197
-
198
- // Check if the TRef type is Many<...>
199
- // At runtime we need a different check - look at the function body
200
- const codeStr = c.toString();
201
- const isMany = codeStr.includes("many(");
202
-
203
- const nextConfig = { ...this.config, reference: { ref: refName, isMany } };
204
- return new ColumnImpl(nextConfig);
205
- }
206
-
207
- validate(fn: (val: Type) => boolean) {
208
- return this.next("validate", fn);
209
- }
210
- transform(fn: (val: any) => Type) {
211
- return this.next("transform", fn);
212
- }
213
- deprecated() {
214
- return this.next("deprecated");
215
- }
216
- STRUCTType(val: string) {
217
- return this.next("STRUCTType", val);
218
- }
219
- }
220
-
221
- const Column = <
222
- Type,
223
- Config extends ColumnConfig = DefaultConfig,
224
- RefSchema = never
225
- >(
226
- conf?: Partial<Record<keyof ColumnConfig, any>>
227
- ): ColumnBuilder<Type, Config, RefSchema> => {
228
- return new ColumnImpl<Type, RefSchema>(conf) as any;
229
- };
230
-
231
- export const Int = () =>
232
- Column<number, Restrict<"defaultNow">>().SQlType("INTEGER");
233
- export const BigInt = () =>
234
- Column<bigint, Restrict<"defaultNow">>().SQlType("BIGINT");
235
- export const Float = () =>
236
- Column<number, Restrict<"defaultNow">>().SQlType("FLOAT");
237
- export const Boolean = () =>
238
- Column<boolean, Restrict<"defaultNow" | "primaryKey" | "unique">>().SQlType(
239
- "BOOLEAN"
240
- );
241
- export const Text = () =>
242
- Column<string, Restrict<"defaultNow">>().SQlType("VARCHAR");
243
- export const Uuid = () =>
244
- Column<string, Restrict<"defaultNow">>().SQlType("VARCHAR");
245
- export const DateType = () =>
246
- Column<Date, Restrict<"default">>().SQlType("DATE");
247
- export const Timestamp = () =>
248
- Column<Date, Restrict<"default">>().SQlType("TIMESTAMP");
249
- export const Enum = <T extends string | number>(vals: readonly T[]) => {
250
- const isString = typeof vals[0] === "string";
251
- return Column<T, Restrict<"defaultNow">>()
252
- .validate((v) => vals.includes(v))
253
- .SQlType(isString ? "VARCHAR" : "INTEGER");
254
- };
255
-
256
- // --- RECURSIVE ZOD PARSER START ---
257
- export * from "zod";
258
-
259
- const zodToDuckDBType = (zodType: z.ZodTypeAny): string => {
260
- const def: any = (zodType as any)?._def;
261
-
262
- if (
263
- def &&
264
- (def.typeName === "ZodOptional" || def.typeName === "ZodNullable")
265
- ) {
266
- return zodToDuckDBType(def.innerType);
267
- }
268
-
269
- if (def && def.typeName === "ZodDefault") {
270
- return zodToDuckDBType(def.innerType);
271
- }
272
-
273
- if (zodType instanceof z.ZodString) return "VARCHAR";
274
- if (zodType instanceof z.ZodNumber) return "INTEGER";
275
- if (zodType instanceof z.ZodBoolean) return "BOOLEAN";
276
- if (zodType instanceof z.ZodDate) return "TIMESTAMP";
277
- if (zodType instanceof z.ZodBigInt) return "BIGINT";
278
-
279
- if ("element" in (def ?? {})) {
280
- const innerType = zodToDuckDBType(def.element);
281
- return `${innerType}[]`;
282
- }
283
-
284
- if ("shape" in (def ?? {})) {
285
- const shape: Record<string, z.ZodTypeAny> =
286
- typeof def.shape === "function" ? def.shape() : def.shape;
287
- const structParts = Object.entries(shape).map(([key, value]) => {
288
- const fieldType = zodToDuckDBType(value as z.ZodTypeAny);
289
- return `${key} ${fieldType}`;
290
- });
291
- return `STRUCT(${structParts.join(", ")})`;
292
- }
293
-
294
- return "VARCHAR";
295
- };
296
-
297
- export const Json = <T extends z.ZodRawShape>(obj: T) => {
298
- const zodObj = z.object(obj);
299
- const duckDBStructString = zodToDuckDBType(zodObj);
300
-
301
- const col = Column<
302
- z.infer<z.ZodObject<T>>,
303
- Restrict<"defaultNow" | "primaryKey" | "unique">
304
- >().SQlType("STRUCT");
305
-
306
- return (col as any).STRUCTType(duckDBStructString) as typeof col;
307
- };
308
-
309
- export const Array = <T extends z.ZodTypeAny>(schema: T) => {
310
- const innerType = zodToDuckDBType(schema);
311
-
312
- const col = Column<
313
- z.infer<T>[],
314
- Restrict<"defaultNow" | "primaryKey" | "unique">
315
- >().SQlType("LIST");
316
-
317
- return (col as any).STRUCTType(`${innerType}[]`) as typeof col;
318
- };
319
-
320
- // ADD-ONS
321
- export const Email = () =>
322
- Column<string, Restrict<"defaultNow">>()
323
- .SQlType("VARCHAR")
324
- .validate((v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v));
325
- export const Slug = () =>
326
- Column<string, Restrict<"defaultNow">>()
327
- .SQlType("VARCHAR")
328
- .validate((v) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(v));
329
- export const Color = () =>
330
- Column<string, Restrict<"defaultNow">>()
331
- .SQlType("VARCHAR")
332
- .validate((v) => /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(v));
333
- export const Bytes = () =>
334
- Column<Uint8Array, Restrict<"defaultNow" | "primaryKey">>().SQlType("BLOB");
335
- export const Password = () =>
336
- Column<string, Restrict<"defaultNow" | "primaryKey" | "unique">>().SQlType(
337
- "VARCHAR"
338
- );
339
- export const GeoPoint = () =>
340
- Column<
341
- { latitude: number; longitude: number },
342
- Restrict<"defaultNow" | "primaryKey">
343
- >().SQlType("STRUCT");
344
- export const GeoArea = () =>
345
- Column<
346
- {
347
- latitude: number;
348
- longitude: number;
349
- radius: number;
350
- },
351
- Restrict<"defaultNow" | "primaryKey">
352
- >().SQlType("STRUCT");
353
-
354
- // ---------------------------------------------------------
355
- // Column & Table Definitions
356
- // ---------------------------------------------------------
357
-
358
- export const Table = <
359
- Name extends string,
360
- T extends Record<string, ColumnBuilder<any, any>>
361
- >(
362
- name: Name,
363
- fields: T
364
- ): T & { __tableName: Name } => {
365
- // Runtime Metadata Injection
366
- for (const [key, value] of Object.entries(fields)) {
367
- Object.assign(value, { __parent: name, __fieldName: key });
368
- }
369
-
370
- return { ...fields, __tableName: name } as any;
371
- };
372
-
373
- const GetFieldConfig = (
374
- f: ColumnBuilder<any, any>
375
- ): {
376
- SQlType: string;
377
- primaryKey: boolean;
378
- notnull: boolean;
379
- unique: boolean;
380
- default: boolean;
381
- defaultNow: boolean;
382
- reference: boolean;
383
- STRUCTType?: string;
384
- } => {
385
- return (f as any).config;
386
- };
387
-
388
- export class SQLBuilder {
389
- static BuildField(name: string, f: ColumnBuilder<any, any>) {
390
- const FieldConfig = GetFieldConfig(f);
391
- let sql = `${name} ${FieldConfig.SQlType || "TEXT"}`;
392
-
393
- if (
394
- (FieldConfig.SQlType == "STRUCT" || FieldConfig.SQlType == "LIST") &&
395
- FieldConfig.STRUCTType
396
- ) {
397
- sql = `${name} ${FieldConfig.STRUCTType}`;
398
- }
399
-
400
- if (FieldConfig.primaryKey) sql += " PRIMARY KEY";
401
- if (FieldConfig.notnull) sql += " NOT NULL";
402
- if (FieldConfig.unique) sql += " UNIQUE";
403
- if (FieldConfig.defaultNow) sql += ` DEFAULT CURRENT_TIMESTAMP`;
404
-
405
- if (
406
- FieldConfig.SQlType !== "STRUCT" &&
407
- FieldConfig.SQlType !== "LIST" &&
408
- FieldConfig.default !== false &&
409
- FieldConfig.default !== undefined
410
- ) {
411
- if (typeof FieldConfig.default !== "function") {
412
- sql += ` DEFAULT ${
413
- typeof FieldConfig.default === "string"
414
- ? `'${FieldConfig.default}'`
415
- : JSON.stringify(FieldConfig.default)
416
- }`;
417
- }
418
- }
419
-
420
- if (
421
- (FieldConfig.SQlType == "STRUCT" || FieldConfig.SQlType == "LIST") &&
422
- FieldConfig.default !== false &&
423
- FieldConfig.default !== undefined
424
- ) {
425
- // Use split/join for compatibility with older JS/TS targets (replaceAll not supported)
426
- sql += ` DEFAULT ${JSON.stringify(FieldConfig.default).split('"').join("'")}`;
427
- }
428
-
429
- return {
430
- sql: sql.trim(),
431
- // @ts-ignore
432
- ref: FieldConfig.reference,
433
- };
434
- }
435
- static BuildTable(name: string, t: Record<string, any>) {
436
- const fieldDefs = Object.entries(t)
437
- .filter(([key]) => key !== "__tableName")
438
- .map(([fieldName, builder]) => {
439
- const Result = this.BuildField(fieldName, builder);
440
- return Result.sql;
441
- });
442
- return `CREATE TABLE IF NOT EXISTS ${name} (\n ${fieldDefs.join(
443
- ",\n "
444
- )}\n);`;
445
- }
446
- }