create-kyro 0.1.0

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.
@@ -0,0 +1,36 @@
1
+ export function validateProjectName(name: string): string | true {
2
+ if (!name) {
3
+ return 'Project name is required';
4
+ }
5
+
6
+ if (!/^[a-z0-9-]+$/.test(name)) {
7
+ return 'Use lowercase letters, numbers, and hyphens only';
8
+ }
9
+
10
+ if (name.length < 2) {
11
+ return 'Project name must be at least 2 characters';
12
+ }
13
+
14
+ if (name.length > 50) {
15
+ return 'Project name must be less than 50 characters';
16
+ }
17
+
18
+ if (/^[-0-9]/.test(name)) {
19
+ return 'Project name cannot start with a number or hyphen';
20
+ }
21
+
22
+ const reserved = ['node_modules', 'dist', 'build', 'public', 'src', 'test', 'tests'];
23
+ if (reserved.includes(name)) {
24
+ return `"${name}" is a reserved name`;
25
+ }
26
+
27
+ return true;
28
+ }
29
+
30
+ export function validateEmail(email: string): string | true {
31
+ if (!email) return true;
32
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
33
+ return 'Invalid email address';
34
+ }
35
+ return true;
36
+ }
@@ -0,0 +1,186 @@
1
+ import type { CollectionConfig } from '@kyro-cms/core';
2
+
3
+ export const blogCollections: Record<string, CollectionConfig> = {
4
+ posts: {
5
+ slug: 'posts',
6
+ label: 'Posts',
7
+ labelPlural: 'Posts',
8
+ singularLabel: 'Post',
9
+ admin: {
10
+ useAsTitle: 'title',
11
+ defaultColumns: ['title', 'category', 'status', 'createdAt'],
12
+ description: 'Blog posts and articles'
13
+ },
14
+ fields: [
15
+ {
16
+ name: 'title',
17
+ type: 'text',
18
+ required: true,
19
+ label: 'Title'
20
+ },
21
+ {
22
+ name: 'slug',
23
+ type: 'text',
24
+ required: true,
25
+ label: 'Slug'
26
+ },
27
+ {
28
+ name: 'excerpt',
29
+ type: 'textarea',
30
+ label: 'Excerpt',
31
+ admin: { description: 'Brief summary for listings' }
32
+ },
33
+ {
34
+ name: 'content',
35
+ type: 'richtext',
36
+ label: 'Content'
37
+ },
38
+ {
39
+ name: 'featuredImage',
40
+ type: 'upload',
41
+ label: 'Featured Image',
42
+ upload: {
43
+ mimeTypes: ['image/*']
44
+ }
45
+ },
46
+ {
47
+ name: 'category',
48
+ type: 'relationship',
49
+ label: 'Category',
50
+ relationTo: 'categories'
51
+ },
52
+ {
53
+ name: 'tags',
54
+ type: 'array',
55
+ label: 'Tags',
56
+ fields: [
57
+ { name: 'tag', type: 'text' }
58
+ ]
59
+ },
60
+ {
61
+ name: 'status',
62
+ type: 'select',
63
+ label: 'Status',
64
+ options: ['draft', 'published'],
65
+ defaultValue: 'draft'
66
+ },
67
+ {
68
+ name: 'publishedAt',
69
+ type: 'date',
70
+ label: 'Published At'
71
+ }
72
+ ],
73
+ timestamps: true
74
+ },
75
+
76
+ categories: {
77
+ slug: 'categories',
78
+ label: 'Categories',
79
+ labelPlural: 'Categories',
80
+ singularLabel: 'Category',
81
+ admin: {
82
+ useAsTitle: 'name',
83
+ defaultColumns: ['name', 'slug', 'postCount'],
84
+ description: 'Post categories'
85
+ },
86
+ fields: [
87
+ {
88
+ name: 'name',
89
+ type: 'text',
90
+ required: true,
91
+ label: 'Name'
92
+ },
93
+ {
94
+ name: 'slug',
95
+ type: 'text',
96
+ required: true,
97
+ label: 'Slug'
98
+ },
99
+ {
100
+ name: 'description',
101
+ type: 'textarea',
102
+ label: 'Description'
103
+ },
104
+ {
105
+ name: 'parent',
106
+ type: 'relationship',
107
+ label: 'Parent Category',
108
+ relationTo: 'categories'
109
+ }
110
+ ],
111
+ timestamps: true
112
+ },
113
+
114
+ media: {
115
+ slug: 'media',
116
+ label: 'Media',
117
+ labelPlural: 'Media',
118
+ singularLabel: 'Medium',
119
+ admin: {
120
+ useAsTitle: 'filename',
121
+ defaultColumns: ['filename', 'mimeType', 'size', 'uploadedAt'],
122
+ description: 'Uploaded files and images'
123
+ },
124
+ fields: [
125
+ {
126
+ name: 'filename',
127
+ type: 'text',
128
+ required: true,
129
+ label: 'Filename'
130
+ },
131
+ {
132
+ name: 'alt',
133
+ type: 'text',
134
+ label: 'Alt Text',
135
+ admin: { description: 'Alternative text for images' }
136
+ },
137
+ {
138
+ name: 'url',
139
+ type: 'text',
140
+ required: true,
141
+ label: 'URL'
142
+ },
143
+ {
144
+ name: 'mimeType',
145
+ type: 'text',
146
+ label: 'MIME Type'
147
+ },
148
+ {
149
+ name: 'size',
150
+ type: 'number',
151
+ label: 'Size (bytes)'
152
+ },
153
+ {
154
+ name: 'width',
155
+ type: 'number',
156
+ label: 'Width'
157
+ },
158
+ {
159
+ name: 'height',
160
+ type: 'number',
161
+ label: 'Height'
162
+ }
163
+ ],
164
+ timestamps: true
165
+ }
166
+ };
167
+
168
+ export const blogGlobals = {
169
+ siteSettings: {
170
+ name: 'siteSettings',
171
+ label: 'Site Settings',
172
+ fields: {
173
+ siteName: { type: 'text', defaultValue: 'My Blog' },
174
+ siteDescription: { type: 'textarea', label: 'Site Description' },
175
+ logo: { type: 'text', label: 'Logo URL' },
176
+ socialLinks: {
177
+ type: 'array',
178
+ label: 'Social Links',
179
+ fields: {
180
+ platform: { type: 'text', label: 'Platform' },
181
+ url: { type: 'text', label: 'URL' }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ };
@@ -0,0 +1,272 @@
1
+ import type { CollectionConfig, GlobalConfig } from '@kyro-cms/core';
2
+
3
+ export const ecommerceCollections: Record<string, CollectionConfig> = {
4
+ products: {
5
+ slug: 'products',
6
+ label: 'Products',
7
+ labelPlural: 'Products',
8
+ singularLabel: 'Product',
9
+ admin: {
10
+ useAsTitle: 'title',
11
+ defaultColumns: ['title', 'price', 'status', 'inventory'],
12
+ description: 'Product catalog'
13
+ },
14
+ fields: [
15
+ {
16
+ name: 'title',
17
+ type: 'text',
18
+ required: true,
19
+ label: 'Title'
20
+ },
21
+ {
22
+ name: 'slug',
23
+ type: 'text',
24
+ required: true,
25
+ label: 'Slug'
26
+ },
27
+ {
28
+ name: 'description',
29
+ type: 'richtext',
30
+ label: 'Description'
31
+ },
32
+ {
33
+ name: 'price',
34
+ type: 'number',
35
+ required: true,
36
+ label: 'Price'
37
+ },
38
+ {
39
+ name: 'compareAtPrice',
40
+ type: 'number',
41
+ label: 'Compare at Price',
42
+ admin: { description: 'Original price for sale display' }
43
+ },
44
+ {
45
+ name: 'costPrice',
46
+ type: 'number',
47
+ label: 'Cost Price',
48
+ admin: { description: 'For profit calculation' }
49
+ },
50
+ {
51
+ name: 'sku',
52
+ type: 'text',
53
+ required: true,
54
+ label: 'SKU'
55
+ },
56
+ {
57
+ name: 'barcode',
58
+ type: 'text',
59
+ label: 'Barcode'
60
+ },
61
+ {
62
+ name: 'status',
63
+ type: 'select',
64
+ label: 'Status',
65
+ options: ['draft', 'active', 'archived'],
66
+ defaultValue: 'draft'
67
+ },
68
+ {
69
+ name: 'images',
70
+ type: 'array',
71
+ label: 'Images',
72
+ fields: [
73
+ { name: 'url', type: 'text', label: 'URL' },
74
+ { name: 'alt', type: 'text', label: 'Alt Text' }
75
+ ]
76
+ },
77
+ {
78
+ name: 'category',
79
+ type: 'relationship',
80
+ label: 'Category',
81
+ relationTo: 'categories'
82
+ },
83
+ {
84
+ name: 'inventory',
85
+ type: 'number',
86
+ label: 'Inventory',
87
+ defaultValue: 0
88
+ }
89
+ ],
90
+ timestamps: true
91
+ },
92
+
93
+ categories: {
94
+ slug: 'categories',
95
+ label: 'Categories',
96
+ labelPlural: 'Categories',
97
+ singularLabel: 'Category',
98
+ admin: {
99
+ useAsTitle: 'name',
100
+ defaultColumns: ['name', 'slug', 'productCount'],
101
+ description: 'Product categories'
102
+ },
103
+ fields: [
104
+ { name: 'name', type: 'text', required: true, label: 'Name' },
105
+ { name: 'slug', type: 'text', required: true, label: 'Slug' },
106
+ { name: 'description', type: 'textarea', label: 'Description' },
107
+ { name: 'image', type: 'text', label: 'Image URL' },
108
+ {
109
+ name: 'parent',
110
+ type: 'relationship',
111
+ label: 'Parent Category',
112
+ relationTo: 'categories'
113
+ }
114
+ ],
115
+ timestamps: true
116
+ },
117
+
118
+ customers: {
119
+ slug: 'customers',
120
+ label: 'Customers',
121
+ labelPlural: 'Customers',
122
+ singularLabel: 'Customer',
123
+ admin: {
124
+ useAsTitle: 'email',
125
+ defaultColumns: ['email', 'firstName', 'lastName', 'orderCount', 'createdAt'],
126
+ description: 'Customer accounts'
127
+ },
128
+ fields: [
129
+ { name: 'email', type: 'email', required: true, label: 'Email' },
130
+ { name: 'firstName', type: 'text', label: 'First Name' },
131
+ { name: 'lastName', type: 'text', label: 'Last Name' },
132
+ { name: 'phone', type: 'text', label: 'Phone' },
133
+ {
134
+ name: 'addresses',
135
+ type: 'array',
136
+ label: 'Addresses',
137
+ fields: [
138
+ { name: 'type', type: 'text', label: 'Type' },
139
+ { name: 'line1', type: 'text', label: 'Address Line 1' },
140
+ { name: 'line2', type: 'text', label: 'Address Line 2' },
141
+ { name: 'city', type: 'text', label: 'City' },
142
+ { name: 'state', type: 'text', label: 'State' },
143
+ { name: 'postalCode', type: 'text', label: 'Postal Code' },
144
+ { name: 'country', type: 'text', label: 'Country' }
145
+ ]
146
+ },
147
+ {
148
+ name: 'status',
149
+ type: 'select',
150
+ label: 'Status',
151
+ options: ['active', 'inactive', 'banned'],
152
+ defaultValue: 'active'
153
+ }
154
+ ],
155
+ timestamps: true
156
+ },
157
+
158
+ orders: {
159
+ slug: 'orders',
160
+ label: 'Orders',
161
+ labelPlural: 'Orders',
162
+ singularLabel: 'Order',
163
+ admin: {
164
+ useAsTitle: 'orderNumber',
165
+ defaultColumns: ['orderNumber', 'customer', 'status', 'total', 'createdAt'],
166
+ description: 'Customer orders'
167
+ },
168
+ fields: [
169
+ {
170
+ name: 'orderNumber',
171
+ type: 'text',
172
+ required: true,
173
+ label: 'Order Number'
174
+ },
175
+ {
176
+ name: 'customer',
177
+ type: 'relationship',
178
+ required: true,
179
+ label: 'Customer',
180
+ relationTo: 'customers'
181
+ },
182
+ {
183
+ name: 'status',
184
+ type: 'select',
185
+ label: 'Status',
186
+ options: ['pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded'],
187
+ defaultValue: 'pending'
188
+ },
189
+ {
190
+ name: 'paymentStatus',
191
+ type: 'select',
192
+ label: 'Payment Status',
193
+ options: ['pending', 'paid', 'failed', 'refunded'],
194
+ defaultValue: 'pending'
195
+ },
196
+ {
197
+ name: 'items',
198
+ type: 'array',
199
+ label: 'Items',
200
+ fields: [
201
+ { name: 'product', type: 'text', label: 'Product' },
202
+ { name: 'quantity', type: 'number', label: 'Quantity' },
203
+ { name: 'unitPrice', type: 'number', label: 'Unit Price' },
204
+ { name: 'total', type: 'number', label: 'Total' }
205
+ ]
206
+ },
207
+ { name: 'subtotal', type: 'number', required: true, label: 'Subtotal' },
208
+ { name: 'tax', type: 'number', label: 'Tax' },
209
+ { name: 'shipping', type: 'number', label: 'Shipping' },
210
+ { name: 'discount', type: 'number', label: 'Discount' },
211
+ { name: 'total', type: 'number', required: true, label: 'Total' },
212
+ { name: 'notes', type: 'textarea', label: 'Notes' }
213
+ ],
214
+ timestamps: true
215
+ },
216
+
217
+ coupons: {
218
+ slug: 'coupons',
219
+ label: 'Coupons',
220
+ labelPlural: 'Coupons',
221
+ singularLabel: 'Coupon',
222
+ admin: {
223
+ useAsTitle: 'code',
224
+ defaultColumns: ['code', 'type', 'value', 'active', 'expiresAt'],
225
+ description: 'Discount codes and promotions'
226
+ },
227
+ fields: [
228
+ { name: 'code', type: 'text', required: true, label: 'Code' },
229
+ {
230
+ name: 'type',
231
+ type: 'select',
232
+ required: true,
233
+ label: 'Type',
234
+ options: ['percentage', 'fixed', 'freeShipping']
235
+ },
236
+ { name: 'value', type: 'number', label: 'Value' },
237
+ { name: 'minPurchase', type: 'number', label: 'Minimum Purchase' },
238
+ { name: 'maxDiscount', type: 'number', label: 'Max Discount' },
239
+ { name: 'usageLimit', type: 'number', label: 'Usage Limit' },
240
+ { name: 'usedCount', type: 'number', defaultValue: 0, label: 'Used Count' },
241
+ { name: 'startsAt', type: 'date', label: 'Starts At' },
242
+ { name: 'expiresAt', type: 'date', label: 'Expires At' },
243
+ { name: 'active', type: 'checkbox', defaultValue: true, label: 'Active' }
244
+ ],
245
+ timestamps: true
246
+ }
247
+ };
248
+
249
+ export const ecommerceGlobals: Record<string, GlobalConfig> = {
250
+ storeSettings: {
251
+ name: 'storeSettings',
252
+ label: 'Store Settings',
253
+ fields: {
254
+ storeName: { type: 'text', defaultValue: 'My Store' },
255
+ storeEmail: { type: 'email', label: 'Contact Email' },
256
+ storePhone: { type: 'text', label: 'Phone' },
257
+ address: {
258
+ type: 'group',
259
+ label: 'Address',
260
+ fields: {
261
+ line1: { type: 'text', label: 'Address Line 1' },
262
+ city: { type: 'text', label: 'City' },
263
+ state: { type: 'text', label: 'State' },
264
+ postalCode: { type: 'text', label: 'Postal Code' },
265
+ country: { type: 'text', label: 'Country' }
266
+ }
267
+ },
268
+ currency: { type: 'text', defaultValue: 'USD', label: 'Currency' },
269
+ taxRate: { type: 'number', defaultValue: 0, label: 'Tax Rate (%)' }
270
+ }
271
+ }
272
+ };
@@ -0,0 +1,53 @@
1
+ import type { CollectionConfig } from '@kyro-cms/core';
2
+
3
+ export const minimalCollections: Record<string, CollectionConfig> = {
4
+ posts: {
5
+ slug: 'posts',
6
+ label: 'Posts',
7
+ labelPlural: 'Posts',
8
+ singularLabel: 'Post',
9
+ admin: {
10
+ useAsTitle: 'title',
11
+ defaultColumns: ['title', 'status', 'createdAt'],
12
+ description: 'Blog posts and articles'
13
+ },
14
+ fields: [
15
+ {
16
+ name: 'title',
17
+ type: 'text',
18
+ required: true,
19
+ label: 'Title',
20
+ admin: { description: 'The post title' }
21
+ },
22
+ {
23
+ name: 'slug',
24
+ type: 'text',
25
+ required: true,
26
+ label: 'Slug',
27
+ admin: { description: 'URL-friendly identifier' }
28
+ },
29
+ {
30
+ name: 'content',
31
+ type: 'richtext',
32
+ label: 'Content'
33
+ },
34
+ {
35
+ name: 'status',
36
+ type: 'select',
37
+ label: 'Status',
38
+ options: ['draft', 'published'],
39
+ defaultValue: 'draft',
40
+ admin: {
41
+ description: 'Publication status'
42
+ }
43
+ },
44
+ {
45
+ name: 'publishedAt',
46
+ type: 'date',
47
+ label: 'Published At',
48
+ admin: { description: 'When to publish this post' }
49
+ }
50
+ ],
51
+ timestamps: true
52
+ }
53
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "./dist",
10
+ "declaration": true,
11
+ "lib": ["ES2022"]
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "dist"]
15
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm'],
6
+ dts: true,
7
+ splitting: false,
8
+ clean: true,
9
+ target: 'node18',
10
+ platform: 'node'
11
+ });