@deessejs/collections 0.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/package.json +60 -0
- package/src/adapter.ts +38 -0
- package/src/collection.ts +138 -0
- package/src/config.ts +134 -0
- package/src/field-type.ts +40 -0
- package/src/field.ts +46 -0
- package/src/fields/f.ts +192 -0
- package/src/fields/index.ts +1 -0
- package/src/index.ts +27 -0
- package/src/migrations.ts +49 -0
- package/src/operations/collection-operations.ts +808 -0
- package/src/operations/index.ts +2 -0
- package/src/operations/types.ts +141 -0
- package/src/schema.ts +62 -0
- package/tests/adapter.test.ts +35 -0
- package/tests/collection.test.ts +205 -0
- package/tests/config.test.ts +58 -0
- package/tests/field-type.test.ts +181 -0
- package/tests/field.test.ts +201 -0
- package/tests/fixtures.ts +44 -0
- package/tests/hooks.test.ts +1076 -0
- package/tests/integration/hooks.test.ts +329 -0
- package/tests/metadata.test.ts +200 -0
- package/tests/schema.test.ts +58 -0
- package/tests/type-inference.test.ts +108 -0
- package/tsconfig.json +32 -0
- package/tsup.config.ts +11 -0
- package/vitest.config.ts +29 -0
- package/vitest.integration.config.ts +9 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { fieldType } from '../src/field-type'
|
|
3
|
+
import { field } from '../src/field'
|
|
4
|
+
import { f } from '../src'
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
describe('fieldType', () => {
|
|
8
|
+
describe('creating field types', () => {
|
|
9
|
+
it('creates a basic field type', () => {
|
|
10
|
+
const textField = fieldType({
|
|
11
|
+
schema: z.string(),
|
|
12
|
+
database: { type: 'text' }
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const instance = textField()
|
|
16
|
+
|
|
17
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
18
|
+
expect(instance.database).toEqual({ type: 'text' })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('creates field type without database config', () => {
|
|
22
|
+
const textField = fieldType({
|
|
23
|
+
schema: z.string()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const instance = textField()
|
|
27
|
+
|
|
28
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
29
|
+
expect(instance.database).toEqual({})
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('field', () => {
|
|
35
|
+
describe('creating fields', () => {
|
|
36
|
+
it('creates a basic field with default options', () => {
|
|
37
|
+
const myField = field({ fieldType: f.text() })
|
|
38
|
+
|
|
39
|
+
expect(myField.required).toBe(false)
|
|
40
|
+
expect(myField.unique).toBe(false)
|
|
41
|
+
expect(myField.indexed).toBe(false)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('creates a field with all options', () => {
|
|
45
|
+
const myField = field({
|
|
46
|
+
fieldType: f.text(),
|
|
47
|
+
required: true,
|
|
48
|
+
unique: true,
|
|
49
|
+
indexed: true,
|
|
50
|
+
default: 'default value',
|
|
51
|
+
label: 'Name',
|
|
52
|
+
description: 'User name'
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
expect(myField.required).toBe(true)
|
|
56
|
+
expect(myField.unique).toBe(true)
|
|
57
|
+
expect(myField.indexed).toBe(true)
|
|
58
|
+
expect(myField.default).toBe('default value')
|
|
59
|
+
expect(myField.label).toBe('Name')
|
|
60
|
+
expect(myField.description).toBe('User name')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('works with different field types', () => {
|
|
64
|
+
const textField = field({ fieldType: f.text() })
|
|
65
|
+
const numberField = field({ fieldType: f.number() })
|
|
66
|
+
const booleanField = field({ fieldType: f.boolean() })
|
|
67
|
+
const dateField = field({ fieldType: f.date() })
|
|
68
|
+
const timestampField = field({ fieldType: f.timestamp() })
|
|
69
|
+
|
|
70
|
+
expect(textField.fieldType.schema).toBeInstanceOf(z.ZodString)
|
|
71
|
+
expect(numberField.fieldType.schema).toBeInstanceOf(z.ZodNumber)
|
|
72
|
+
expect(booleanField.fieldType.schema).toBeInstanceOf(z.ZodBoolean)
|
|
73
|
+
expect(dateField.fieldType.schema).toBeInstanceOf(z.ZodDate)
|
|
74
|
+
expect(timestampField.fieldType.schema).toBeInstanceOf(z.ZodDate)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('f (built-in field types)', () => {
|
|
80
|
+
describe('string types', () => {
|
|
81
|
+
it('text field type', () => {
|
|
82
|
+
const instance = f.text()
|
|
83
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
84
|
+
expect(instance.database).toEqual({ type: 'text' })
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('email field type', () => {
|
|
88
|
+
const emailField = fieldType({
|
|
89
|
+
schema: z.string().email(),
|
|
90
|
+
database: { type: 'text' }
|
|
91
|
+
})
|
|
92
|
+
const instance = emailField()
|
|
93
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('f.email() creates email field type', () => {
|
|
97
|
+
const instance = f.email()
|
|
98
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
99
|
+
expect(instance.database).toEqual({ type: 'text' })
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('f.url() creates url field type', () => {
|
|
103
|
+
const instance = f.url()
|
|
104
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
105
|
+
expect(instance.database).toEqual({ type: 'text' })
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('numeric types', () => {
|
|
110
|
+
it('number field type', () => {
|
|
111
|
+
const instance = f.number()
|
|
112
|
+
expect(instance.schema).toBeInstanceOf(z.ZodNumber)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('boolean type', () => {
|
|
117
|
+
it('boolean field type', () => {
|
|
118
|
+
const instance = f.boolean()
|
|
119
|
+
expect(instance.schema).toBeInstanceOf(z.ZodBoolean)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('date types', () => {
|
|
124
|
+
it('date field type', () => {
|
|
125
|
+
const instance = f.date()
|
|
126
|
+
expect(instance.schema).toBeInstanceOf(z.ZodDate)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('timestamp field type', () => {
|
|
130
|
+
const instance = f.timestamp()
|
|
131
|
+
expect(instance.schema).toBeInstanceOf(z.ZodDate)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
describe('enum type', () => {
|
|
136
|
+
it('enum field type', () => {
|
|
137
|
+
const status = f.select(['draft', 'published', 'archived'])
|
|
138
|
+
expect(status.schema).toBeInstanceOf(z.ZodEnum)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('json type', () => {
|
|
143
|
+
it('json field type', () => {
|
|
144
|
+
const instance = f.json()
|
|
145
|
+
expect(instance.schema).toBeInstanceOf(z.ZodAny)
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
describe('array type', () => {
|
|
150
|
+
it('array field type', () => {
|
|
151
|
+
const instance = f.array(z.string())
|
|
152
|
+
expect(instance.schema).toBeInstanceOf(z.ZodArray)
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
describe('relation types', () => {
|
|
157
|
+
it('relation field type - one-to-many', () => {
|
|
158
|
+
const instance = f.relation({ collection: 'users' })
|
|
159
|
+
expect(instance.schema).toBeInstanceOf(z.ZodString)
|
|
160
|
+
expect(instance.database).toEqual({
|
|
161
|
+
type: 'integer',
|
|
162
|
+
references: 'users',
|
|
163
|
+
through: undefined,
|
|
164
|
+
many: false,
|
|
165
|
+
singular: false
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('relation field type - one-to-one', () => {
|
|
170
|
+
const instance = f.relation({ collection: 'profiles', singular: true })
|
|
171
|
+
expect(instance.database.singular).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('relation field type - many-to-many', () => {
|
|
175
|
+
const instance = f.relation({ collection: 'tags', many: true })
|
|
176
|
+
expect(instance.schema).toBeInstanceOf(z.ZodArray)
|
|
177
|
+
expect(instance.database.many).toBe(true)
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe('field with built-in types', () => {
|
|
183
|
+
it('creates a field with text type', () => {
|
|
184
|
+
const myField = field({ fieldType: f.text() })
|
|
185
|
+
expect(myField.fieldType.schema).toBeInstanceOf(z.ZodString)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('creates a field with number type', () => {
|
|
189
|
+
const myField = field({ fieldType: f.number() })
|
|
190
|
+
expect(myField.fieldType.schema).toBeInstanceOf(z.ZodNumber)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('creates a field with relation type', () => {
|
|
194
|
+
const myField = field({
|
|
195
|
+
fieldType: f.relation({ collection: 'users' }),
|
|
196
|
+
required: true
|
|
197
|
+
})
|
|
198
|
+
expect(myField.required).toBe(true)
|
|
199
|
+
expect(myField.fieldType.database.references).toBe('users')
|
|
200
|
+
})
|
|
201
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { beforeEach } from 'vitest'
|
|
2
|
+
import { pgAdapter } from '../src'
|
|
3
|
+
import { collection } from '../src/collection'
|
|
4
|
+
import { field } from '../src/field'
|
|
5
|
+
import { f } from '../src'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared test fixtures to avoid DRY violations
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const testAdapter = pgAdapter({
|
|
12
|
+
url: 'postgres://localhost:5432/db'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const testCollections = {
|
|
16
|
+
users: collection({
|
|
17
|
+
slug: 'users',
|
|
18
|
+
name: 'Users',
|
|
19
|
+
fields: {
|
|
20
|
+
name: field({ fieldType: f.text() }),
|
|
21
|
+
email: field({ fieldType: f.text() })
|
|
22
|
+
}
|
|
23
|
+
}),
|
|
24
|
+
posts: collection({
|
|
25
|
+
slug: 'posts',
|
|
26
|
+
fields: {
|
|
27
|
+
title: field({ fieldType: f.text() }),
|
|
28
|
+
content: field({ fieldType: f.text() })
|
|
29
|
+
}
|
|
30
|
+
}),
|
|
31
|
+
comments: collection({
|
|
32
|
+
slug: 'comments',
|
|
33
|
+
fields: {
|
|
34
|
+
body: field({ fieldType: f.text() })
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Vitest setup - runs before each test file
|
|
41
|
+
*/
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
// Reset or cleanup if needed
|
|
44
|
+
})
|