@parsrun/entity 0.1.37 → 0.1.39
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/dist/index.d.ts +4 -2
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/pg.d.ts +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/{types-CmS0cBdC.d.ts → types-keJMe1IE.d.ts} +2 -0
- package/package.json +2 -2
- package/src/define.test.ts +112 -0
- package/src/define.ts +7 -4
- package/src/types.ts +2 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as Field, E as EntityDefinition, a as Entity, b as FieldDefinition } from './types-
|
|
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-
|
|
1
|
+
import { F as Field, E as EntityDefinition, a as Entity, b as FieldDefinition } from './types-keJMe1IE.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-keJMe1IE.js';
|
|
3
3
|
export { type } from 'arktype';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -39,6 +39,8 @@ declare function ref(entity: string | {
|
|
|
39
39
|
field?: string;
|
|
40
40
|
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
41
41
|
optional?: boolean;
|
|
42
|
+
/** ID type for this reference (default: 'string.uuid') */
|
|
43
|
+
idType?: string;
|
|
42
44
|
}): FieldDefinition;
|
|
43
45
|
/**
|
|
44
46
|
* Create an enum field from a list of values
|
package/dist/index.js
CHANGED
|
@@ -35,15 +35,16 @@ function fieldToArkType(field) {
|
|
|
35
35
|
}
|
|
36
36
|
function buildSchemaObject(definition, mode) {
|
|
37
37
|
const schema = {};
|
|
38
|
+
const idType = definition.idType ?? "string.uuid";
|
|
38
39
|
if (mode === "full") {
|
|
39
|
-
schema["id"] =
|
|
40
|
+
schema["id"] = idType;
|
|
40
41
|
}
|
|
41
42
|
if (definition.tenant) {
|
|
42
43
|
if (mode === "full" || mode === "create") {
|
|
43
|
-
schema["tenantId"] =
|
|
44
|
+
schema["tenantId"] = idType;
|
|
44
45
|
}
|
|
45
46
|
if (mode === "query") {
|
|
46
|
-
schema["tenantId?"] =
|
|
47
|
+
schema["tenantId?"] = idType;
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
for (const [name, field] of Object.entries(definition.fields)) {
|
|
@@ -139,7 +140,7 @@ function defineEntity(definition) {
|
|
|
139
140
|
function ref(entity, options) {
|
|
140
141
|
const entityName = typeof entity === "string" ? entity : entity.name;
|
|
141
142
|
const result = {
|
|
142
|
-
type: "string.uuid",
|
|
143
|
+
type: options?.idType ?? "string.uuid",
|
|
143
144
|
db: {
|
|
144
145
|
references: {
|
|
145
146
|
entity: entityName,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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 json type - ArkType doesn't have 'json', use 'object' instead\n if (typeStr === 'json') {\n typeStr = 'object'\n }\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"]}
|
|
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 json type - ArkType doesn't have 'json', use 'object' instead\n if (typeStr === 'json') {\n typeStr = 'object'\n }\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 const idType = definition.idType ?? 'string.uuid'\n\n // Add id field for full schema\n if (mode === 'full') {\n schema['id'] = idType\n }\n\n // Add tenantId if tenant-scoped\n if (definition.tenant) {\n if (mode === 'full' || mode === 'create') {\n schema['tenantId'] = idType\n }\n if (mode === 'query') {\n schema['tenantId?'] = idType\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 /** ID type for this reference (default: 'string.uuid') */\n idType?: string\n }\n): FieldDefinition {\n const entityName = typeof entity === 'string' ? entity : entity.name\n const result: FieldDefinition = {\n type: (options?.idType ?? 'string.uuid') as FieldDefinition['type'],\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
CHANGED
package/dist/sqlite.d.ts
CHANGED
|
@@ -71,6 +71,8 @@ interface EntityDefinition<TFields extends Record<string, Field>> {
|
|
|
71
71
|
description?: string;
|
|
72
72
|
/** Whether this entity is tenant-scoped (adds tenantId field) */
|
|
73
73
|
tenant?: boolean;
|
|
74
|
+
/** ID type for primary key and references (default: 'string.uuid') */
|
|
75
|
+
idType?: FieldType;
|
|
74
76
|
/** Field definitions */
|
|
75
77
|
fields: TFields;
|
|
76
78
|
/** Index definitions */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parsrun/entity",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "Single-source entity definitions for Pars - generates ArkType schemas and Drizzle tables",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"arktype": "^2.0.0",
|
|
29
|
-
"@parsrun/core": "0.1.
|
|
29
|
+
"@parsrun/core": "0.1.39"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"drizzle-orm": "^0.44.0",
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { defineEntity, ref, enumField, jsonField, decimal } from './define.js'
|
|
3
|
+
|
|
4
|
+
describe('defineEntity', () => {
|
|
5
|
+
it('should create entity with basic fields', () => {
|
|
6
|
+
const Product = defineEntity({
|
|
7
|
+
name: 'products',
|
|
8
|
+
fields: {
|
|
9
|
+
name: 'string',
|
|
10
|
+
price: 'number',
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(Product.name).toBe('products')
|
|
15
|
+
expect(Product.requiredFields).toContain('name')
|
|
16
|
+
expect(Product.requiredFields).toContain('price')
|
|
17
|
+
expect(Product.autoFields).toContain('id')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should handle tenant-scoped entities', () => {
|
|
21
|
+
const Order = defineEntity({
|
|
22
|
+
name: 'orders',
|
|
23
|
+
tenant: true,
|
|
24
|
+
fields: {
|
|
25
|
+
total: 'number',
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
expect(Order.requiredFields).toContain('tenantId')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should handle timestamps', () => {
|
|
33
|
+
const Post = defineEntity({
|
|
34
|
+
name: 'posts',
|
|
35
|
+
timestamps: true,
|
|
36
|
+
fields: {
|
|
37
|
+
title: 'string',
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
expect(Post.autoFields).toContain('insertedAt')
|
|
42
|
+
expect(Post.autoFields).toContain('updatedAt')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should handle soft delete', () => {
|
|
46
|
+
const User = defineEntity({
|
|
47
|
+
name: 'users',
|
|
48
|
+
softDelete: true,
|
|
49
|
+
fields: {
|
|
50
|
+
email: 'string.email',
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
expect(User.autoFields).toContain('deletedAt')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should handle optional fields', () => {
|
|
58
|
+
const Profile = defineEntity({
|
|
59
|
+
name: 'profiles',
|
|
60
|
+
fields: {
|
|
61
|
+
bio: { type: 'string', optional: true },
|
|
62
|
+
age: 'number',
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
expect(Profile.optionalFields).toContain('bio')
|
|
67
|
+
expect(Profile.requiredFields).toContain('age')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should handle fields with defaults', () => {
|
|
71
|
+
const Settings = defineEntity({
|
|
72
|
+
name: 'settings',
|
|
73
|
+
fields: {
|
|
74
|
+
theme: { type: 'string', default: 'light' },
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expect(Settings.optionalFields).toContain('theme')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('helper functions', () => {
|
|
83
|
+
it('ref should create a reference field', () => {
|
|
84
|
+
const field = ref('users', { onDelete: 'cascade' })
|
|
85
|
+
|
|
86
|
+
expect(field.type).toBe('string.uuid')
|
|
87
|
+
expect(field.db?.references?.entity).toBe('users')
|
|
88
|
+
expect(field.db?.references?.onDelete).toBe('cascade')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('enumField should create union type', () => {
|
|
92
|
+
const field = enumField(['draft', 'published', 'archived'] as const)
|
|
93
|
+
|
|
94
|
+
expect(field.type).toBe("'draft' | 'published' | 'archived'")
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('jsonField should create json field', () => {
|
|
98
|
+
const field = jsonField({ optional: true })
|
|
99
|
+
|
|
100
|
+
expect(field.type).toBe('json')
|
|
101
|
+
expect(field.optional).toBe(true)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('decimal should create decimal field', () => {
|
|
105
|
+
const field = decimal(10, 2, { min: 0 })
|
|
106
|
+
|
|
107
|
+
expect(field.type).toBe('number')
|
|
108
|
+
expect(field.db?.precision).toBe(10)
|
|
109
|
+
expect(field.db?.scale).toBe(2)
|
|
110
|
+
expect(field.min).toBe(0)
|
|
111
|
+
})
|
|
112
|
+
})
|
package/src/define.ts
CHANGED
|
@@ -60,19 +60,20 @@ function buildSchemaObject(
|
|
|
60
60
|
mode: 'full' | 'create' | 'update' | 'query'
|
|
61
61
|
): Record<string, string> {
|
|
62
62
|
const schema: Record<string, string> = {}
|
|
63
|
+
const idType = definition.idType ?? 'string.uuid'
|
|
63
64
|
|
|
64
65
|
// Add id field for full schema
|
|
65
66
|
if (mode === 'full') {
|
|
66
|
-
schema['id'] =
|
|
67
|
+
schema['id'] = idType
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
// Add tenantId if tenant-scoped
|
|
70
71
|
if (definition.tenant) {
|
|
71
72
|
if (mode === 'full' || mode === 'create') {
|
|
72
|
-
schema['tenantId'] =
|
|
73
|
+
schema['tenantId'] = idType
|
|
73
74
|
}
|
|
74
75
|
if (mode === 'query') {
|
|
75
|
-
schema['tenantId?'] =
|
|
76
|
+
schema['tenantId?'] = idType
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -233,11 +234,13 @@ export function ref(
|
|
|
233
234
|
field?: string
|
|
234
235
|
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action'
|
|
235
236
|
optional?: boolean
|
|
237
|
+
/** ID type for this reference (default: 'string.uuid') */
|
|
238
|
+
idType?: string
|
|
236
239
|
}
|
|
237
240
|
): FieldDefinition {
|
|
238
241
|
const entityName = typeof entity === 'string' ? entity : entity.name
|
|
239
242
|
const result: FieldDefinition = {
|
|
240
|
-
type: 'string.uuid',
|
|
243
|
+
type: (options?.idType ?? 'string.uuid') as FieldDefinition['type'],
|
|
241
244
|
db: {
|
|
242
245
|
references: {
|
|
243
246
|
entity: entityName,
|
package/src/types.ts
CHANGED
|
@@ -88,6 +88,8 @@ export interface EntityDefinition<TFields extends Record<string, Field>> {
|
|
|
88
88
|
description?: string
|
|
89
89
|
/** Whether this entity is tenant-scoped (adds tenantId field) */
|
|
90
90
|
tenant?: boolean
|
|
91
|
+
/** ID type for primary key and references (default: 'string.uuid') */
|
|
92
|
+
idType?: FieldType
|
|
91
93
|
/** Field definitions */
|
|
92
94
|
fields: TFields
|
|
93
95
|
/** Index definitions */
|