@parsrun/entity 0.1.35
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 +210 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/pg.d.ts +15 -0
- package/dist/pg.js +137 -0
- package/dist/pg.js.map +1 -0
- package/dist/sqlite.d.ts +15 -0
- package/dist/sqlite.js +132 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/types-CmS0cBdC.d.ts +149 -0
- package/package.json +63 -0
- package/src/define.ts +313 -0
- package/src/index.ts +61 -0
- package/src/pg.ts +224 -0
- package/src/sqlite.ts +216 -0
- package/src/types.ts +174 -0
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# @parsrun/entity
|
|
2
|
+
|
|
3
|
+
Single-source entity definitions for the Pars framework. Define once, generate ArkType validation schemas and Drizzle ORM tables.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @parsrun/entity
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @parsrun/entity
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { defineEntity, ref, enumField } from '@parsrun/entity'
|
|
17
|
+
import { toPgTable } from '@parsrun/entity/pg'
|
|
18
|
+
// or for SQLite/D1:
|
|
19
|
+
// import { toSqliteTable } from '@parsrun/entity/sqlite'
|
|
20
|
+
|
|
21
|
+
// Define your entity once
|
|
22
|
+
const Product = defineEntity({
|
|
23
|
+
name: 'products',
|
|
24
|
+
tenant: true, // Adds tenantId field
|
|
25
|
+
timestamps: true, // Adds insertedAt, updatedAt
|
|
26
|
+
softDelete: true, // Adds deletedAt
|
|
27
|
+
|
|
28
|
+
fields: {
|
|
29
|
+
name: 'string >= 1',
|
|
30
|
+
slug: 'string',
|
|
31
|
+
description: { type: 'string', optional: true },
|
|
32
|
+
price: { type: 'number', min: 0 },
|
|
33
|
+
stock: { type: 'number.integer', min: 0, default: 0 },
|
|
34
|
+
status: enumField(['draft', 'active', 'archived'], { default: 'draft' }),
|
|
35
|
+
categoryId: ref('categories', { onDelete: 'set null', optional: true }),
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
indexes: [
|
|
39
|
+
{ fields: ['tenantId', 'slug'], unique: true },
|
|
40
|
+
{ fields: ['tenantId', 'status'] },
|
|
41
|
+
],
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Generated schemas available:
|
|
45
|
+
Product.schema // Full entity schema
|
|
46
|
+
Product.createSchema // For create operations (no id, timestamps)
|
|
47
|
+
Product.updateSchema // For updates (all optional)
|
|
48
|
+
Product.querySchema // For filtering (includes pagination)
|
|
49
|
+
|
|
50
|
+
// Generate Drizzle table
|
|
51
|
+
const productsTable = toPgTable(Product)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Validation
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { type } from 'arktype'
|
|
60
|
+
|
|
61
|
+
// Validate create input
|
|
62
|
+
const input = Product.createSchema(requestBody)
|
|
63
|
+
if (input instanceof type.errors) {
|
|
64
|
+
return { error: 'Validation failed', details: input }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// input is now typed and validated
|
|
68
|
+
await db.insert(productsTable).values({
|
|
69
|
+
tenantId: ctx.tenantId,
|
|
70
|
+
...input,
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Database Queries
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { eq, and } from 'drizzle-orm'
|
|
78
|
+
import { toPgTable } from '@parsrun/entity/pg'
|
|
79
|
+
|
|
80
|
+
const productsTable = toPgTable(Product)
|
|
81
|
+
|
|
82
|
+
// Select
|
|
83
|
+
const products = await db
|
|
84
|
+
.select()
|
|
85
|
+
.from(productsTable)
|
|
86
|
+
.where(and(
|
|
87
|
+
eq(productsTable.tenantId, tenantId),
|
|
88
|
+
eq(productsTable.status, 'active')
|
|
89
|
+
))
|
|
90
|
+
|
|
91
|
+
// Insert
|
|
92
|
+
const [product] = await db
|
|
93
|
+
.insert(productsTable)
|
|
94
|
+
.values({ tenantId, name: 'Widget', price: 9.99 })
|
|
95
|
+
.returning()
|
|
96
|
+
|
|
97
|
+
// Update
|
|
98
|
+
await db
|
|
99
|
+
.update(productsTable)
|
|
100
|
+
.set({ price: 14.99 })
|
|
101
|
+
.where(eq(productsTable.id, productId))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Multiple Entities with References
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { createPgSchema } from '@parsrun/entity/pg'
|
|
108
|
+
|
|
109
|
+
const Category = defineEntity({
|
|
110
|
+
name: 'categories',
|
|
111
|
+
tenant: true,
|
|
112
|
+
timestamps: true,
|
|
113
|
+
fields: {
|
|
114
|
+
name: 'string >= 1',
|
|
115
|
+
slug: 'string',
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const Product = defineEntity({
|
|
120
|
+
name: 'products',
|
|
121
|
+
tenant: true,
|
|
122
|
+
timestamps: true,
|
|
123
|
+
fields: {
|
|
124
|
+
name: 'string >= 1',
|
|
125
|
+
categoryId: ref(Category, { onDelete: 'cascade' }),
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Creates tables with proper foreign key references
|
|
130
|
+
const schema = createPgSchema({ Category, Product })
|
|
131
|
+
// schema.Category, schema.Product
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Field Types
|
|
135
|
+
|
|
136
|
+
| Type | Description | Example |
|
|
137
|
+
|------|-------------|---------|
|
|
138
|
+
| `'string'` | Text field | `name: 'string'` |
|
|
139
|
+
| `'string >= N'` | Min length | `name: 'string >= 1'` |
|
|
140
|
+
| `'string.uuid'` | UUID field | `id: 'string.uuid'` |
|
|
141
|
+
| `'string.email'` | Email field | `email: 'string.email'` |
|
|
142
|
+
| `'string.url'` | URL field | `website: 'string.url'` |
|
|
143
|
+
| `'number'` | Numeric field | `price: 'number'` |
|
|
144
|
+
| `'number >= N'` | Min value | `price: 'number >= 0'` |
|
|
145
|
+
| `'number.integer'` | Integer field | `stock: 'number.integer'` |
|
|
146
|
+
| `'boolean'` | Boolean field | `isActive: 'boolean'` |
|
|
147
|
+
| `'Date'` | Timestamp | `expiresAt: 'Date'` |
|
|
148
|
+
| `'json'` | JSON field | `metadata: 'json'` |
|
|
149
|
+
| `"'a' \| 'b'"` | Union/Enum | `status: "'draft' \| 'active'"` |
|
|
150
|
+
|
|
151
|
+
## Helper Functions
|
|
152
|
+
|
|
153
|
+
### `enumField(values, options?)`
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
status: enumField(['draft', 'active', 'archived'], {
|
|
157
|
+
default: 'draft',
|
|
158
|
+
optional: false
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### `ref(entity, options?)`
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
categoryId: ref('categories', {
|
|
166
|
+
field: 'id', // default: 'id'
|
|
167
|
+
onDelete: 'cascade', // 'cascade' | 'set null' | 'restrict'
|
|
168
|
+
optional: true
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// Or with entity reference
|
|
172
|
+
categoryId: ref(Category, { onDelete: 'cascade' })
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `decimal(precision, scale, options?)`
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
price: decimal(10, 2, { min: 0 }) // DECIMAL(10,2)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `jsonField(options?)`
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
metadata: jsonField({ optional: true, default: {} })
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## SQLite / Cloudflare D1
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { toSqliteTable, createSqliteSchema } from '@parsrun/entity/sqlite'
|
|
191
|
+
|
|
192
|
+
const productsTable = toSqliteTable(Product)
|
|
193
|
+
|
|
194
|
+
// Or multiple tables
|
|
195
|
+
const schema = createSqliteSchema({ Category, Product })
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## TypeScript Types
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import type { InferEntity, InferCreateInput, InferUpdateInput } from '@parsrun/entity'
|
|
202
|
+
|
|
203
|
+
type Product = InferEntity<typeof Product>
|
|
204
|
+
type CreateProductInput = InferCreateInput<typeof Product>
|
|
205
|
+
type UpdateProductInput = InferUpdateInput<typeof Product>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { F as Field, E as EntityDefinition, a as Entity, b as FieldDefinition } from './types-CmS0cBdC.js';
|
|
2
|
+
export { D as DrizzleOptions, d as EntitySchemas, c as FieldType, I as IndexDefinition, f as InferCreateInput, e as InferEntity, g as InferUpdateInput, S as SimpleFieldDefinition } from './types-CmS0cBdC.js';
|
|
3
|
+
export { type } from 'arktype';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Define an entity with single-source schema generation
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const Product = defineEntity({
|
|
11
|
+
* name: 'products',
|
|
12
|
+
* tenant: true,
|
|
13
|
+
* timestamps: true,
|
|
14
|
+
* softDelete: true,
|
|
15
|
+
* fields: {
|
|
16
|
+
* name: 'string >= 1',
|
|
17
|
+
* price: { type: 'number', min: 0 },
|
|
18
|
+
* status: "'draft' | 'active' | 'archived'",
|
|
19
|
+
* },
|
|
20
|
+
* indexes: [
|
|
21
|
+
* { fields: ['tenantId', 'status'] },
|
|
22
|
+
* ],
|
|
23
|
+
* })
|
|
24
|
+
*
|
|
25
|
+
* // Use schemas
|
|
26
|
+
* const validated = Product.createSchema(input)
|
|
27
|
+
* const products = await db.select().from(Product.table)
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function defineEntity<TName extends string, TFields extends Record<string, Field>>(definition: EntityDefinition<TFields> & {
|
|
31
|
+
name: TName;
|
|
32
|
+
}): Entity<TName, TFields, Record<string, unknown>>;
|
|
33
|
+
/**
|
|
34
|
+
* Create a reference field to another entity
|
|
35
|
+
*/
|
|
36
|
+
declare function ref(entity: string | {
|
|
37
|
+
name: string;
|
|
38
|
+
}, options?: {
|
|
39
|
+
field?: string;
|
|
40
|
+
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
41
|
+
optional?: boolean;
|
|
42
|
+
}): FieldDefinition;
|
|
43
|
+
/**
|
|
44
|
+
* Create an enum field from a list of values
|
|
45
|
+
*/
|
|
46
|
+
declare function enumField<T extends string>(values: readonly T[], options?: {
|
|
47
|
+
default?: T;
|
|
48
|
+
optional?: boolean;
|
|
49
|
+
}): FieldDefinition;
|
|
50
|
+
/**
|
|
51
|
+
* Create a JSON field
|
|
52
|
+
*/
|
|
53
|
+
declare function jsonField<T = Record<string, unknown>>(options?: {
|
|
54
|
+
optional?: boolean;
|
|
55
|
+
default?: T;
|
|
56
|
+
}): FieldDefinition;
|
|
57
|
+
/**
|
|
58
|
+
* Create a decimal field with precision
|
|
59
|
+
*/
|
|
60
|
+
declare function decimal(precision: number, scale: number, options?: {
|
|
61
|
+
min?: number;
|
|
62
|
+
max?: number;
|
|
63
|
+
optional?: boolean;
|
|
64
|
+
}): FieldDefinition;
|
|
65
|
+
|
|
66
|
+
export { Entity, EntityDefinition, Field, FieldDefinition, decimal, defineEntity, enumField, jsonField, ref };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { type } from 'arktype';
|
|
2
|
+
export { type } from 'arktype';
|
|
3
|
+
|
|
4
|
+
// src/define.ts
|
|
5
|
+
function fieldToArkType(field) {
|
|
6
|
+
const def = typeof field === "string" ? { type: field } : field;
|
|
7
|
+
let typeStr = def.type;
|
|
8
|
+
if (def.min !== void 0 || def.max !== void 0) {
|
|
9
|
+
if (typeStr === "string" || typeStr.startsWith("string")) {
|
|
10
|
+
if (def.min !== void 0 && def.max !== void 0) {
|
|
11
|
+
typeStr = `string >= ${def.min} <= ${def.max}`;
|
|
12
|
+
} else if (def.min !== void 0) {
|
|
13
|
+
typeStr = `string >= ${def.min}`;
|
|
14
|
+
} else if (def.max !== void 0) {
|
|
15
|
+
typeStr = `string <= ${def.max}`;
|
|
16
|
+
}
|
|
17
|
+
} else if (typeStr === "number" || typeStr.startsWith("number")) {
|
|
18
|
+
const base = typeStr.includes(".integer") ? "number.integer" : "number";
|
|
19
|
+
if (def.min !== void 0 && def.max !== void 0) {
|
|
20
|
+
typeStr = `${base} >= ${def.min} <= ${def.max}`;
|
|
21
|
+
} else if (def.min !== void 0) {
|
|
22
|
+
typeStr = `${base} >= ${def.min}`;
|
|
23
|
+
} else if (def.max !== void 0) {
|
|
24
|
+
typeStr = `${base} <= ${def.max}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (def.optional) {
|
|
29
|
+
typeStr = `${typeStr} | undefined`;
|
|
30
|
+
}
|
|
31
|
+
return typeStr;
|
|
32
|
+
}
|
|
33
|
+
function buildSchemaObject(definition, mode) {
|
|
34
|
+
const schema = {};
|
|
35
|
+
if (mode === "full") {
|
|
36
|
+
schema["id"] = "string.uuid";
|
|
37
|
+
}
|
|
38
|
+
if (definition.tenant) {
|
|
39
|
+
if (mode === "full" || mode === "create") {
|
|
40
|
+
schema["tenantId"] = "string.uuid";
|
|
41
|
+
}
|
|
42
|
+
if (mode === "query") {
|
|
43
|
+
schema["tenantId?"] = "string.uuid";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const [name, field] of Object.entries(definition.fields)) {
|
|
47
|
+
const def = typeof field === "string" ? { } : field;
|
|
48
|
+
if (mode === "update" || mode === "query") {
|
|
49
|
+
schema[`${name}?`] = fieldToArkType(field);
|
|
50
|
+
} else if (mode === "create") {
|
|
51
|
+
if (def.default !== void 0 || def.optional) {
|
|
52
|
+
schema[`${name}?`] = fieldToArkType(field);
|
|
53
|
+
} else {
|
|
54
|
+
schema[name] = fieldToArkType(field);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
if (def.optional) {
|
|
58
|
+
schema[`${name}?`] = fieldToArkType(field);
|
|
59
|
+
} else {
|
|
60
|
+
schema[name] = fieldToArkType(field);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (definition.timestamps) {
|
|
65
|
+
if (mode === "full") {
|
|
66
|
+
schema["insertedAt"] = "Date";
|
|
67
|
+
schema["updatedAt"] = "Date";
|
|
68
|
+
}
|
|
69
|
+
if (mode === "query") {
|
|
70
|
+
schema["insertedAt?"] = "Date";
|
|
71
|
+
schema["updatedAt?"] = "Date";
|
|
72
|
+
schema["insertedAfter?"] = "Date";
|
|
73
|
+
schema["insertedBefore?"] = "Date";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (definition.softDelete) {
|
|
77
|
+
if (mode === "full") {
|
|
78
|
+
schema["deletedAt?"] = "Date";
|
|
79
|
+
}
|
|
80
|
+
if (mode === "query") {
|
|
81
|
+
schema["includeDeleted?"] = "boolean";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (mode === "query") {
|
|
85
|
+
schema["limit?"] = "number.integer > 0";
|
|
86
|
+
schema["offset?"] = "number.integer >= 0";
|
|
87
|
+
schema["cursor?"] = "string";
|
|
88
|
+
schema["orderBy?"] = "string";
|
|
89
|
+
schema["orderDirection?"] = "'asc' | 'desc'";
|
|
90
|
+
schema["search?"] = "string";
|
|
91
|
+
}
|
|
92
|
+
return schema;
|
|
93
|
+
}
|
|
94
|
+
function defineEntity(definition) {
|
|
95
|
+
const fullSchemaObj = buildSchemaObject(definition, "full");
|
|
96
|
+
const createSchemaObj = buildSchemaObject(definition, "create");
|
|
97
|
+
const updateSchemaObj = buildSchemaObject(definition, "update");
|
|
98
|
+
const querySchemaObj = buildSchemaObject(definition, "query");
|
|
99
|
+
const schema = type(fullSchemaObj);
|
|
100
|
+
const createSchema = type(createSchemaObj);
|
|
101
|
+
const updateSchema = type(updateSchemaObj);
|
|
102
|
+
const querySchema = type(querySchemaObj);
|
|
103
|
+
const autoFields = ["id"];
|
|
104
|
+
if (definition.timestamps) {
|
|
105
|
+
autoFields.push("insertedAt", "updatedAt");
|
|
106
|
+
}
|
|
107
|
+
if (definition.softDelete) {
|
|
108
|
+
autoFields.push("deletedAt");
|
|
109
|
+
}
|
|
110
|
+
const requiredFields = [];
|
|
111
|
+
const optionalFields = [];
|
|
112
|
+
if (definition.tenant) {
|
|
113
|
+
requiredFields.push("tenantId");
|
|
114
|
+
}
|
|
115
|
+
for (const [name, field] of Object.entries(definition.fields)) {
|
|
116
|
+
const def = typeof field === "string" ? { } : field;
|
|
117
|
+
if (def.optional || def.default !== void 0) {
|
|
118
|
+
optionalFields.push(name);
|
|
119
|
+
} else {
|
|
120
|
+
requiredFields.push(name);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
name: definition.name,
|
|
125
|
+
definition,
|
|
126
|
+
schema,
|
|
127
|
+
createSchema,
|
|
128
|
+
updateSchema,
|
|
129
|
+
querySchema,
|
|
130
|
+
infer: {},
|
|
131
|
+
autoFields,
|
|
132
|
+
requiredFields,
|
|
133
|
+
optionalFields
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function ref(entity, options) {
|
|
137
|
+
const entityName = typeof entity === "string" ? entity : entity.name;
|
|
138
|
+
const result = {
|
|
139
|
+
type: "string.uuid",
|
|
140
|
+
db: {
|
|
141
|
+
references: {
|
|
142
|
+
entity: entityName,
|
|
143
|
+
field: options?.field ?? "id",
|
|
144
|
+
onDelete: options?.onDelete ?? "restrict"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
if (options?.optional !== void 0) {
|
|
149
|
+
result.optional = options.optional;
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
function enumField(values, options) {
|
|
154
|
+
const typeStr = values.map((v) => `'${v}'`).join(" | ");
|
|
155
|
+
const result = {
|
|
156
|
+
type: typeStr
|
|
157
|
+
};
|
|
158
|
+
if (options?.default !== void 0) {
|
|
159
|
+
result.default = options.default;
|
|
160
|
+
}
|
|
161
|
+
if (options?.optional !== void 0) {
|
|
162
|
+
result.optional = options.optional;
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
function jsonField(options) {
|
|
167
|
+
const result = {
|
|
168
|
+
type: "json"
|
|
169
|
+
};
|
|
170
|
+
if (options?.optional !== void 0) {
|
|
171
|
+
result.optional = options.optional;
|
|
172
|
+
}
|
|
173
|
+
if (options?.default !== void 0) {
|
|
174
|
+
result.default = options.default;
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
function decimal(precision, scale, options) {
|
|
179
|
+
const result = {
|
|
180
|
+
type: "number",
|
|
181
|
+
db: {
|
|
182
|
+
precision,
|
|
183
|
+
scale
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
if (options?.min !== void 0) {
|
|
187
|
+
result.min = options.min;
|
|
188
|
+
}
|
|
189
|
+
if (options?.max !== void 0) {
|
|
190
|
+
result.max = options.max;
|
|
191
|
+
}
|
|
192
|
+
if (options?.optional !== void 0) {
|
|
193
|
+
result.optional = options.optional;
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { decimal, defineEntity, enumField, jsonField, ref };
|
|
199
|
+
//# sourceMappingURL=index.js.map
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/define.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { type, type Type } from 'arktype'\nimport type {\n EntityDefinition,\n Field,\n FieldDefinition,\n Entity,\n} from './types.js'\n\n/**\n * Convert a field definition to an ArkType type string\n */\nfunction fieldToArkType(field: Field): string {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n let typeStr = def.type\n\n // Handle min/max constraints\n if (def.min !== undefined || def.max !== undefined) {\n if (typeStr === 'string' || typeStr.startsWith('string')) {\n if (def.min !== undefined && def.max !== undefined) {\n typeStr = `string >= ${def.min} <= ${def.max}`\n } else if (def.min !== undefined) {\n typeStr = `string >= ${def.min}`\n } else if (def.max !== undefined) {\n typeStr = `string <= ${def.max}`\n }\n } else if (typeStr === 'number' || typeStr.startsWith('number')) {\n const base = typeStr.includes('.integer') ? 'number.integer' : 'number'\n if (def.min !== undefined && def.max !== undefined) {\n typeStr = `${base} >= ${def.min} <= ${def.max}`\n } else if (def.min !== undefined) {\n typeStr = `${base} >= ${def.min}`\n } else if (def.max !== undefined) {\n typeStr = `${base} <= ${def.max}`\n }\n }\n }\n\n // Handle optional fields\n if (def.optional) {\n // For optional fields, allow undefined or the type\n typeStr = `${typeStr} | undefined`\n }\n\n return typeStr\n}\n\n/**\n * Build the full schema object for ArkType\n */\nfunction buildSchemaObject(\n definition: EntityDefinition<Record<string, Field>>,\n mode: 'full' | 'create' | 'update' | 'query'\n): Record<string, string> {\n const schema: Record<string, string> = {}\n\n // Add id field for full schema\n if (mode === 'full') {\n schema['id'] = 'string.uuid'\n }\n\n // Add tenantId if tenant-scoped\n if (definition.tenant) {\n if (mode === 'full' || mode === 'create') {\n schema['tenantId'] = 'string.uuid'\n }\n if (mode === 'query') {\n schema['tenantId?'] = 'string.uuid'\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(definition.fields)) {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n if (mode === 'update' || mode === 'query') {\n // All fields optional for update/query\n schema[`${name}?`] = fieldToArkType(field)\n } else if (mode === 'create') {\n // Skip fields with defaults or optional fields\n if (def.default !== undefined || def.optional) {\n schema[`${name}?`] = fieldToArkType(field)\n } else {\n schema[name] = fieldToArkType(field)\n }\n } else {\n // Full schema\n if (def.optional) {\n schema[`${name}?`] = fieldToArkType(field)\n } else {\n schema[name] = fieldToArkType(field)\n }\n }\n }\n\n // Add timestamp fields\n if (definition.timestamps) {\n if (mode === 'full') {\n schema['insertedAt'] = 'Date'\n schema['updatedAt'] = 'Date'\n }\n if (mode === 'query') {\n schema['insertedAt?'] = 'Date'\n schema['updatedAt?'] = 'Date'\n schema['insertedAfter?'] = 'Date'\n schema['insertedBefore?'] = 'Date'\n }\n }\n\n // Add soft delete field\n if (definition.softDelete) {\n if (mode === 'full') {\n schema['deletedAt?'] = 'Date'\n }\n if (mode === 'query') {\n schema['includeDeleted?'] = 'boolean'\n }\n }\n\n // Add pagination for query\n if (mode === 'query') {\n schema['limit?'] = 'number.integer > 0'\n schema['offset?'] = 'number.integer >= 0'\n schema['cursor?'] = 'string'\n schema['orderBy?'] = 'string'\n schema['orderDirection?'] = \"'asc' | 'desc'\"\n schema['search?'] = 'string'\n }\n\n return schema\n}\n\n/**\n * Define an entity with single-source schema generation\n *\n * @example\n * ```typescript\n * const Product = defineEntity({\n * name: 'products',\n * tenant: true,\n * timestamps: true,\n * softDelete: true,\n * fields: {\n * name: 'string >= 1',\n * price: { type: 'number', min: 0 },\n * status: \"'draft' | 'active' | 'archived'\",\n * },\n * indexes: [\n * { fields: ['tenantId', 'status'] },\n * ],\n * })\n *\n * // Use schemas\n * const validated = Product.createSchema(input)\n * const products = await db.select().from(Product.table)\n * ```\n */\nexport function defineEntity<\n TName extends string,\n TFields extends Record<string, Field>,\n>(\n definition: EntityDefinition<TFields> & { name: TName }\n): Entity<TName, TFields, Record<string, unknown>> {\n // Build schema objects\n const fullSchemaObj = buildSchemaObject(definition, 'full')\n const createSchemaObj = buildSchemaObject(definition, 'create')\n const updateSchemaObj = buildSchemaObject(definition, 'update')\n const querySchemaObj = buildSchemaObject(definition, 'query')\n\n // Create ArkType schemas\n const schema = type(fullSchemaObj as Record<string, string>)\n const createSchema = type(createSchemaObj as Record<string, string>)\n const updateSchema = type(updateSchemaObj as Record<string, string>)\n const querySchema = type(querySchemaObj as Record<string, string>)\n\n // Determine auto and required fields\n const autoFields: string[] = ['id']\n if (definition.timestamps) {\n autoFields.push('insertedAt', 'updatedAt')\n }\n if (definition.softDelete) {\n autoFields.push('deletedAt')\n }\n\n const requiredFields: string[] = []\n const optionalFields: string[] = []\n\n if (definition.tenant) {\n requiredFields.push('tenantId')\n }\n\n for (const [name, field] of Object.entries(definition.fields)) {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n if (def.optional || def.default !== undefined) {\n optionalFields.push(name)\n } else {\n requiredFields.push(name)\n }\n }\n\n return {\n name: definition.name as TName,\n definition,\n schema: schema as Type<Record<string, unknown>>,\n createSchema: createSchema as Type<Record<string, unknown>>,\n updateSchema: updateSchema as Type<Record<string, unknown>>,\n querySchema: querySchema as Type<Record<string, unknown>>,\n infer: {} as Record<string, unknown>,\n autoFields,\n requiredFields,\n optionalFields,\n }\n}\n\n/**\n * Create a reference field to another entity\n */\nexport function ref(\n entity: string | { name: string },\n options?: {\n field?: string\n onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action'\n optional?: boolean\n }\n): FieldDefinition {\n const entityName = typeof entity === 'string' ? entity : entity.name\n const result: FieldDefinition = {\n type: 'string.uuid',\n db: {\n references: {\n entity: entityName,\n field: options?.field ?? 'id',\n onDelete: options?.onDelete ?? 'restrict',\n },\n },\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n\n/**\n * Create an enum field from a list of values\n */\nexport function enumField<T extends string>(\n values: readonly T[],\n options?: { default?: T; optional?: boolean }\n): FieldDefinition {\n const typeStr = values.map(v => `'${v}'`).join(' | ')\n const result: FieldDefinition = {\n type: typeStr as `'${string}'`,\n }\n if (options?.default !== undefined) {\n result.default = options.default\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n\n/**\n * Create a JSON field\n */\nexport function jsonField<T = Record<string, unknown>>(\n options?: { optional?: boolean; default?: T }\n): FieldDefinition {\n const result: FieldDefinition = {\n type: 'json',\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n if (options?.default !== undefined) {\n result.default = options.default\n }\n return result\n}\n\n/**\n * Create a decimal field with precision\n */\nexport function decimal(\n precision: number,\n scale: number,\n options?: { min?: number; max?: number; optional?: boolean }\n): FieldDefinition {\n const result: FieldDefinition = {\n type: 'number',\n db: {\n precision,\n scale,\n },\n }\n if (options?.min !== undefined) {\n result.min = options.min\n }\n if (options?.max !== undefined) {\n result.max = options.max\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n"]}
|
package/dist/pg.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { a as Entity, F as Field } from './types-CmS0cBdC.js';
|
|
2
|
+
import 'arktype';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate a Drizzle pgTable from an entity definition
|
|
6
|
+
*/
|
|
7
|
+
declare function toPgTable<E extends Entity<string, Record<string, Field>, unknown>>(entity: E, tableRefs?: Record<string, any>): any;
|
|
8
|
+
/**
|
|
9
|
+
* Create multiple tables with resolved references
|
|
10
|
+
*/
|
|
11
|
+
declare function createPgSchema<T extends Record<string, Entity<string, Record<string, Field>, unknown>>>(entities: T): {
|
|
12
|
+
[K in keyof T]: any;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { createPgSchema, toPgTable, toPgTable as toTable };
|
package/dist/pg.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { uuid, timestamp, pgTable, uniqueIndex, index, text, json, boolean, numeric, integer } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
|
|
4
|
+
// src/pg.ts
|
|
5
|
+
function fieldToColumn(name, field) {
|
|
6
|
+
const def = typeof field === "string" ? { type: field } : field;
|
|
7
|
+
const columnName = def.db?.column ?? toSnakeCase(name);
|
|
8
|
+
let column;
|
|
9
|
+
const fieldType = def.type.split(" ")[0] ?? "string";
|
|
10
|
+
switch (fieldType) {
|
|
11
|
+
case "string.uuid":
|
|
12
|
+
column = uuid(columnName);
|
|
13
|
+
break;
|
|
14
|
+
case "string.email":
|
|
15
|
+
case "string.url":
|
|
16
|
+
case "string":
|
|
17
|
+
column = text(columnName);
|
|
18
|
+
break;
|
|
19
|
+
case "number.integer":
|
|
20
|
+
column = integer(columnName);
|
|
21
|
+
break;
|
|
22
|
+
case "number":
|
|
23
|
+
if (def.db?.precision) {
|
|
24
|
+
column = numeric(columnName, {
|
|
25
|
+
precision: def.db.precision,
|
|
26
|
+
scale: def.db.scale ?? 2
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
column = numeric(columnName);
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
case "boolean":
|
|
33
|
+
column = boolean(columnName);
|
|
34
|
+
break;
|
|
35
|
+
case "Date":
|
|
36
|
+
column = timestamp(columnName, { withTimezone: true });
|
|
37
|
+
break;
|
|
38
|
+
case "json":
|
|
39
|
+
column = json(columnName);
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
column = text(columnName);
|
|
43
|
+
}
|
|
44
|
+
if (!def.optional && def.default === void 0) {
|
|
45
|
+
column = column.notNull();
|
|
46
|
+
}
|
|
47
|
+
if (def.default !== void 0) {
|
|
48
|
+
column = column.default(def.default);
|
|
49
|
+
}
|
|
50
|
+
return column;
|
|
51
|
+
}
|
|
52
|
+
function toSnakeCase(str) {
|
|
53
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
54
|
+
}
|
|
55
|
+
function toPgTable(entity, tableRefs) {
|
|
56
|
+
const def = entity.definition;
|
|
57
|
+
const columns = {};
|
|
58
|
+
columns["id"] = uuid("id").primaryKey().defaultRandom();
|
|
59
|
+
if (def.tenant) {
|
|
60
|
+
const tenantsTable = tableRefs?.["tenants"];
|
|
61
|
+
if (tenantsTable) {
|
|
62
|
+
columns["tenantId"] = uuid("tenant_id").notNull().references(() => tenantsTable.id, { onDelete: "cascade" });
|
|
63
|
+
} else {
|
|
64
|
+
columns["tenantId"] = uuid("tenant_id").notNull();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const [name, field] of Object.entries(def.fields)) {
|
|
68
|
+
const fieldDef = typeof field === "string" ? { } : field;
|
|
69
|
+
if (fieldDef.db?.references && tableRefs) {
|
|
70
|
+
const refTable = tableRefs[fieldDef.db.references.entity];
|
|
71
|
+
if (refTable) {
|
|
72
|
+
const refField = fieldDef.db.references.field ?? "id";
|
|
73
|
+
const onDelete = fieldDef.db.references.onDelete ?? "restrict";
|
|
74
|
+
let col = uuid(toSnakeCase(name)).references(() => refTable[refField], { onDelete });
|
|
75
|
+
if (!fieldDef.optional) {
|
|
76
|
+
col = col.notNull();
|
|
77
|
+
}
|
|
78
|
+
columns[name] = col;
|
|
79
|
+
} else {
|
|
80
|
+
columns[name] = fieldToColumn(name, field);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
columns[name] = fieldToColumn(name, field);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (def.timestamps) {
|
|
87
|
+
columns["insertedAt"] = timestamp("inserted_at", { withTimezone: true }).notNull().defaultNow();
|
|
88
|
+
columns["updatedAt"] = timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => /* @__PURE__ */ new Date());
|
|
89
|
+
}
|
|
90
|
+
if (def.softDelete) {
|
|
91
|
+
columns["deletedAt"] = timestamp("deleted_at", { withTimezone: true });
|
|
92
|
+
}
|
|
93
|
+
return pgTable(def.name, columns, (t) => {
|
|
94
|
+
const indexes = {};
|
|
95
|
+
if (def.indexes) {
|
|
96
|
+
for (const idx of def.indexes) {
|
|
97
|
+
const idxName = idx.name ?? `${def.name}_${idx.fields.join("_")}_idx`;
|
|
98
|
+
const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean);
|
|
99
|
+
if (idxColumns.length === 0) continue;
|
|
100
|
+
if (idx.unique) {
|
|
101
|
+
if (idx.where) {
|
|
102
|
+
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
103
|
+
} else {
|
|
104
|
+
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
if (idx.where) {
|
|
108
|
+
indexes[idxName] = index(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
109
|
+
} else {
|
|
110
|
+
indexes[idxName] = index(idxName).on(...idxColumns);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (def.tenant && t.tenantId) {
|
|
116
|
+
const tenantIdxName = `${def.name}_tenant_id_idx`;
|
|
117
|
+
if (!indexes[tenantIdxName]) {
|
|
118
|
+
indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return indexes;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function createPgSchema(entities) {
|
|
125
|
+
const tables = {};
|
|
126
|
+
for (const [key, entity] of Object.entries(entities)) {
|
|
127
|
+
tables[key] = toPgTable(entity);
|
|
128
|
+
}
|
|
129
|
+
for (const [key, entity] of Object.entries(entities)) {
|
|
130
|
+
tables[key] = toPgTable(entity, tables);
|
|
131
|
+
}
|
|
132
|
+
return tables;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { createPgSchema, toPgTable, toPgTable as toTable };
|
|
136
|
+
//# sourceMappingURL=pg.js.map
|
|
137
|
+
//# sourceMappingURL=pg.js.map
|