@stacksjs/orm 0.72.16 → 0.72.17

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.
Files changed (101) hide show
  1. package/dist/index.d.ts +97 -97
  2. package/dist/models/Activity.ts +95 -0
  3. package/dist/models/AnalyticsEvent.ts +70 -0
  4. package/dist/models/Automation.ts +23 -0
  5. package/dist/models/AutomationRun.ts +26 -0
  6. package/dist/models/Board.ts +104 -0
  7. package/dist/models/BoardColumn.ts +88 -0
  8. package/dist/models/Campaign.ts +321 -0
  9. package/dist/models/CampaignSend.ts +191 -0
  10. package/dist/models/CampaignVariant.ts +25 -0
  11. package/dist/models/Card.ts +118 -0
  12. package/dist/models/CardComment.ts +68 -0
  13. package/dist/models/Comment.ts +109 -0
  14. package/dist/models/CommunicationSuppression.ts +22 -0
  15. package/dist/models/ConsentEvent.ts +30 -0
  16. package/dist/models/Content/Author.ts +109 -0
  17. package/dist/models/Content/Menu.ts +56 -0
  18. package/dist/models/Content/MenuItem.ts +91 -0
  19. package/dist/models/Content/Page.ts +205 -0
  20. package/dist/models/Content/PageRevision.ts +86 -0
  21. package/dist/models/Content/Post.ts +230 -0
  22. package/dist/models/Content/Redirect.ts +80 -0
  23. package/dist/models/Deployment.ts +117 -0
  24. package/dist/models/EmailIdempotency.ts +64 -0
  25. package/dist/models/EmailList.ts +134 -0
  26. package/dist/models/EmailListSubscriber.ts +91 -0
  27. package/dist/models/EmailSuppression.ts +52 -0
  28. package/dist/models/EmailWebhookEvent.ts +51 -0
  29. package/dist/models/Error.ts +77 -0
  30. package/dist/models/FailedJob.ts +84 -0
  31. package/dist/models/Forms/Form.ts +91 -0
  32. package/dist/models/Forms/FormField.ts +127 -0
  33. package/dist/models/Forms/FormSubmission.ts +114 -0
  34. package/dist/models/Job.ts +69 -0
  35. package/dist/models/Label.ts +67 -0
  36. package/dist/models/Log.ts +134 -0
  37. package/dist/models/MagicLinkToken.ts +97 -0
  38. package/dist/models/MailPreference.ts +213 -0
  39. package/dist/models/Notification.ts +68 -0
  40. package/dist/models/NotificationDelivery.ts +102 -0
  41. package/dist/models/PaymentMethod.ts +98 -0
  42. package/dist/models/PaymentProduct.ts +91 -0
  43. package/dist/models/PaymentTransaction.ts +78 -0
  44. package/dist/models/QueryLog.ts +182 -0
  45. package/dist/models/README.md +31 -0
  46. package/dist/models/Release.ts +90 -0
  47. package/dist/models/Request.ts +133 -0
  48. package/dist/models/SenderDomain.ts +22 -0
  49. package/dist/models/Site.ts +112 -0
  50. package/dist/models/SiteDomain.ts +74 -0
  51. package/dist/models/SmsOptOut.ts +65 -0
  52. package/dist/models/SocialAccount.ts +70 -0
  53. package/dist/models/SocialPost.ts +144 -0
  54. package/dist/models/Subscriber.ts +89 -0
  55. package/dist/models/SubscriberEmail.ts +55 -0
  56. package/dist/models/Subscription.ts +153 -0
  57. package/dist/models/Tag.ts +105 -0
  58. package/dist/models/Team.ts +75 -0
  59. package/dist/models/TeamInvitation.ts +159 -0
  60. package/dist/models/TeamMember.ts +88 -0
  61. package/dist/models/UsageEvent.ts +24 -0
  62. package/dist/models/User.ts +142 -0
  63. package/dist/models/commerce/Auction.ts +173 -0
  64. package/dist/models/commerce/AuctionItem.ts +204 -0
  65. package/dist/models/commerce/Bid.ts +129 -0
  66. package/dist/models/commerce/Cart.ts +140 -0
  67. package/dist/models/commerce/CartItem.ts +138 -0
  68. package/dist/models/commerce/Category.ts +125 -0
  69. package/dist/models/commerce/Coupon.ts +186 -0
  70. package/dist/models/commerce/Customer.ts +132 -0
  71. package/dist/models/commerce/DeliveryRoute.ts +127 -0
  72. package/dist/models/commerce/DeliveryStop.ts +166 -0
  73. package/dist/models/commerce/DigitalDelivery.ts +106 -0
  74. package/dist/models/commerce/Driver.ts +168 -0
  75. package/dist/models/commerce/DriverPing.ts +103 -0
  76. package/dist/models/commerce/GiftCard.ts +195 -0
  77. package/dist/models/commerce/LicenseKey.ts +89 -0
  78. package/dist/models/commerce/LoyaltyPoint.ts +108 -0
  79. package/dist/models/commerce/LoyaltyReward.ts +129 -0
  80. package/dist/models/commerce/Manufacturer.ts +90 -0
  81. package/dist/models/commerce/Order.ts +198 -0
  82. package/dist/models/commerce/OrderIdempotency.ts +34 -0
  83. package/dist/models/commerce/OrderItem.ts +51 -0
  84. package/dist/models/commerce/Payment.ts +182 -0
  85. package/dist/models/commerce/Pledge.ts +112 -0
  86. package/dist/models/commerce/PrintDevice.ts +120 -0
  87. package/dist/models/commerce/Product.ts +153 -0
  88. package/dist/models/commerce/ProductUnit.ts +113 -0
  89. package/dist/models/commerce/ProductVariant.ts +111 -0
  90. package/dist/models/commerce/Receipt.ts +144 -0
  91. package/dist/models/commerce/Review.ts +171 -0
  92. package/dist/models/commerce/ShippingMethod.ts +102 -0
  93. package/dist/models/commerce/ShippingRate.ts +79 -0
  94. package/dist/models/commerce/ShippingZone.ts +116 -0
  95. package/dist/models/commerce/TaxRate.ts +167 -0
  96. package/dist/models/commerce/Transaction.ts +127 -0
  97. package/dist/models/commerce/WaitlistProduct.ts +151 -0
  98. package/dist/models/commerce/WaitlistRestaurant.ts +189 -0
  99. package/dist/models/realtime/Websocket.ts +78 -0
  100. package/dist/password-policy.ts +46 -0
  101. package/package.json +4 -4
@@ -0,0 +1,205 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * A real CMS page: a block document served publicly at `path` on its site.
6
+ *
7
+ * `blocks` is the content - an ordered JSON array validated against the block
8
+ * registry (`@stacksjs/cms` `validateBlocks`). One JSON column rather than
9
+ * Section/Block rows so a page edit is one atomic write and a revision is a
10
+ * row copy, not a snapshot-of-a-join.
11
+ *
12
+ * The `useApi` surface is the ADMIN surface (auth'd on both sides - the table
13
+ * now carries draft content, and a public read route is how drafts leak).
14
+ * Public visitors get pages through the stx servers' CMS fallback, which only
15
+ * ever serves `status = 'published'` rows for the request's site.
16
+ */
17
+ export default defineModel({
18
+ name: 'Page',
19
+ table: 'pages',
20
+ primaryKey: 'id',
21
+ autoIncrement: true,
22
+
23
+ indexes: [
24
+ {
25
+ name: 'pages_site_path_unique',
26
+ columns: ['site_id', 'path'],
27
+ unique: true,
28
+ },
29
+ ],
30
+
31
+ traits: {
32
+ useUuid: true,
33
+ useTimestamps: true,
34
+ useSearch: {
35
+ displayable: ['id', 'title', 'slug', 'path', 'template', 'status', 'views'],
36
+ searchable: ['title', 'slug', 'path'],
37
+ sortable: ['views', 'conversions', 'publishedAt'],
38
+ filterable: ['template', 'status'],
39
+ },
40
+ useApi: {
41
+ middleware: ['auth'],
42
+ uri: 'pages',
43
+ routes: ['index', 'store', 'show', 'update', 'destroy'],
44
+ },
45
+
46
+ useSeeder: {
47
+ count: 10,
48
+ },
49
+ },
50
+
51
+ belongsTo: ['Author', 'Site'],
52
+
53
+ attributes: {
54
+ title: {
55
+ required: true,
56
+ order: 1,
57
+ fillable: true,
58
+ validation: {
59
+ rule: schema.string().min(3).max(255),
60
+ message: {
61
+ min: 'Title must have a minimum of 3 characters',
62
+ max: 'Title must have a maximum of 255 characters',
63
+ },
64
+ },
65
+ factory: faker => faker.lorem.sentence(),
66
+ },
67
+
68
+ /** Last path segment, unique among siblings. */
69
+ slug: {
70
+ required: false,
71
+ order: 2,
72
+ fillable: true,
73
+ validation: {
74
+ rule: schema.string().max(255),
75
+ },
76
+ factory: faker => faker.lorem.slug(),
77
+ },
78
+
79
+ /**
80
+ * Materialized full path (`/admissions/visit`), unique per site. Derived
81
+ * from the parent chain + slug on save; a slug change rewrites descendant
82
+ * paths and leaves Redirect rows behind.
83
+ */
84
+ path: {
85
+ required: false,
86
+ order: 3,
87
+ fillable: true,
88
+ validation: {
89
+ rule: schema.string().max(2048),
90
+ },
91
+ factory: faker => `/${faker.lorem.slug()}`,
92
+ },
93
+
94
+ /** Page-tree parent. Null for top-level pages. */
95
+ parentId: {
96
+ required: false,
97
+ order: 4,
98
+ fillable: true,
99
+ validation: {
100
+ rule: schema.number().min(1),
101
+ },
102
+ factory: () => null,
103
+ },
104
+
105
+ template: {
106
+ required: true,
107
+ order: 5,
108
+ fillable: true,
109
+ validation: {
110
+ rule: schema.string().min(3),
111
+ message: {
112
+ min: 'Template must have a minimum of 3 characters',
113
+ },
114
+ },
115
+ factory: faker => faker.helpers.arrayElement(['default', 'landing', 'blog', 'contact']),
116
+ },
117
+
118
+ /** The block document. Ordered array of { id, type, props }. */
119
+ blocks: {
120
+ required: false,
121
+ order: 6,
122
+ fillable: true,
123
+ validation: {
124
+ rule: schema.json(),
125
+ },
126
+ factory: () => JSON.stringify([]),
127
+ },
128
+
129
+ metaDescription: {
130
+ required: false,
131
+ order: 7,
132
+ fillable: true,
133
+ validation: {
134
+ rule: schema.string().max(320),
135
+ },
136
+ factory: faker => faker.lorem.sentence(),
137
+ },
138
+
139
+ status: {
140
+ required: false,
141
+ order: 8,
142
+ fillable: true,
143
+ default: 'draft',
144
+ validation: {
145
+ rule: schema.enum(['draft', 'published', 'scheduled', 'archived'] as const),
146
+ },
147
+ factory: faker => faker.helpers.arrayElement(['draft', 'published']),
148
+ },
149
+
150
+ /** When a `scheduled` page goes live - enforced by the publish job. */
151
+ scheduledAt: {
152
+ required: false,
153
+ order: 9,
154
+ fillable: true,
155
+ validation: {
156
+ rule: schema.timestamp(),
157
+ },
158
+ factory: () => null,
159
+ },
160
+
161
+ views: {
162
+ required: false,
163
+ order: 10,
164
+ fillable: true,
165
+ default: 0,
166
+ validation: {
167
+ rule: schema.number().min(0),
168
+ message: {
169
+ min: 'Views count cannot be negative',
170
+ },
171
+ },
172
+ factory: faker => faker.number.int({ min: 0, max: 1000 }),
173
+ },
174
+
175
+ publishedAt: {
176
+ required: false,
177
+ order: 11,
178
+ fillable: true,
179
+ validation: {
180
+ rule: schema.timestamp(),
181
+ message: {
182
+ timestamp: 'Published timestamp must be a valid timestamp',
183
+ },
184
+ },
185
+ factory: (faker) => {
186
+ const date = faker.date.past()
187
+ return date.toISOString().slice(0, 19).replace('T', ' ')
188
+ },
189
+ },
190
+
191
+ conversions: {
192
+ required: false,
193
+ order: 12,
194
+ fillable: true,
195
+ default: 0,
196
+ validation: {
197
+ rule: schema.number().min(0),
198
+ message: {
199
+ min: 'Conversions count cannot be negative',
200
+ },
201
+ },
202
+ factory: faker => faker.number.int({ min: 0, max: 100 }),
203
+ },
204
+ },
205
+ } as const)
@@ -0,0 +1,86 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * A snapshot of a Page's document taken just before an overwrite - restoring
6
+ * is copying the snapshot back and recording a new revision of what it
7
+ * replaced. One row per save (the previous state), pruned to the newest N per
8
+ * page (`config.cms.revisions.keep`).
9
+ *
10
+ * No `useApi`: revisions are read and restored through the CMS actions, never
11
+ * CRUD'd directly.
12
+ */
13
+ export default defineModel({
14
+ name: 'PageRevision',
15
+ table: 'page_revisions',
16
+ primaryKey: 'id',
17
+ autoIncrement: true,
18
+
19
+ indexes: [
20
+ {
21
+ name: 'page_revisions_page_revision_unique',
22
+ columns: ['page_id', 'revision'],
23
+ unique: true,
24
+ },
25
+ ],
26
+
27
+ traits: {
28
+ useTimestamps: true,
29
+ },
30
+
31
+ belongsTo: ['Page', 'Author'],
32
+
33
+ attributes: {
34
+ /** Monotonic per page, starting at 1. */
35
+ revision: {
36
+ required: true,
37
+ order: 1,
38
+ fillable: true,
39
+ validation: {
40
+ rule: schema.number().min(1),
41
+ },
42
+ factory: faker => faker.number.int({ min: 1, max: 20 }),
43
+ },
44
+
45
+ title: {
46
+ required: true,
47
+ order: 2,
48
+ fillable: true,
49
+ validation: {
50
+ rule: schema.string().max(255),
51
+ },
52
+ factory: faker => faker.lorem.sentence(),
53
+ },
54
+
55
+ blocks: {
56
+ required: false,
57
+ order: 3,
58
+ fillable: true,
59
+ validation: {
60
+ rule: schema.json(),
61
+ },
62
+ factory: () => JSON.stringify([]),
63
+ },
64
+
65
+ metaDescription: {
66
+ required: false,
67
+ order: 4,
68
+ fillable: true,
69
+ validation: {
70
+ rule: schema.string().max(320),
71
+ },
72
+ factory: () => null,
73
+ },
74
+
75
+ /** Editor-supplied note ("reworded hero for spring"), optional. */
76
+ note: {
77
+ required: false,
78
+ order: 5,
79
+ fillable: true,
80
+ validation: {
81
+ rule: schema.string().max(500),
82
+ },
83
+ factory: () => null,
84
+ },
85
+ },
86
+ } as const)
@@ -0,0 +1,230 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'Post',
6
+ table: 'posts',
7
+ primaryKey: 'id',
8
+ autoIncrement: true,
9
+
10
+ traits: {
11
+ useUuid: true,
12
+ useTimestamps: true,
13
+ useSearch: {
14
+ displayable: ['id', 'title', 'slug', 'author', 'views', 'status', 'poster', 'focusKeyword', 'metaDescription', 'canonicalUrl'],
15
+ // `content`, not `body` - the column is `content`, and the old spelling
16
+ // silently indexed nothing. `comments` likewise was never a column.
17
+ searchable: ['title', 'slug', 'author', 'content', 'excerpt', 'focusKeyword', 'metaDescription'],
18
+ sortable: ['published_at', 'views'],
19
+ filterable: ['status'],
20
+ },
21
+
22
+ // No faker seeding. The public blog is markdown-based (content/blog/*.md)
23
+ // rendered by BunPress; this model backs the CMS dashboard only.
24
+ // `commentable`, not `commentables`: define-model checks the singular key,
25
+ // so the plural spelling left the trait inert. Now that the commentable
26
+ // trait targets the real `commentables` table, activating it is correct.
27
+ commentable: true,
28
+ useApi: {
29
+ // Admin surface now: the table carries drafts, and a public read route
30
+ // is how drafts leak. Public visitors get published posts through the
31
+ // site's own routes/pages, which filter by status themselves.
32
+ middleware: ['auth'],
33
+ uri: 'posts',
34
+ routes: ['index', 'store', 'show', 'update', 'destroy'],
35
+ },
36
+ },
37
+
38
+ belongsTo: ['Author', 'Site'],
39
+ belongsToMany: {
40
+ categories: {
41
+ model: 'Category',
42
+ table: 'categorizable_models',
43
+ foreignKey: 'categorizable_id',
44
+ relatedKey: 'category_id',
45
+ pivot: {
46
+ columns: {
47
+ categorizable_type: { default: 'posts' },
48
+ },
49
+ timestamps: true,
50
+ uniques: [['category_id', 'categorizable_id', 'categorizable_type']],
51
+ },
52
+ },
53
+ tags: {
54
+ model: 'Tag',
55
+ table: 'taggable_models',
56
+ foreignKey: 'taggable_id',
57
+ relatedKey: 'tag_id',
58
+ pivot: {
59
+ columns: {
60
+ taggable_type: { default: 'posts' },
61
+ },
62
+ timestamps: true,
63
+ uniques: [['tag_id', 'taggable_id', 'taggable_type']],
64
+ },
65
+ },
66
+ },
67
+
68
+ attributes: {
69
+ title: {
70
+ required: true,
71
+ order: 1,
72
+ fillable: true,
73
+ validation: {
74
+ rule: schema.string().min(3).max(255),
75
+ message: {
76
+ min: 'Title must have a minimum of 3 characters',
77
+ max: 'Title must have a maximum of 255 characters',
78
+ },
79
+ },
80
+ factory: faker => faker.lorem.sentence(),
81
+ },
82
+
83
+ /**
84
+ * URL identity: `/news/{slug}` beats `/blog/{id}` for a public site.
85
+ * Nullable for pre-slug rows; the RSS/sitemap actions fall back to id.
86
+ */
87
+ slug: {
88
+ required: false,
89
+ order: 2,
90
+ fillable: true,
91
+ validation: {
92
+ rule: schema.string().max(255),
93
+ },
94
+ factory: faker => faker.lorem.slug(),
95
+ },
96
+ poster: {
97
+ required: false,
98
+ order: 4,
99
+ fillable: true,
100
+ validation: {
101
+ rule: schema.string().url(),
102
+ message: {
103
+ url: 'Poster must be a valid URL',
104
+ },
105
+ },
106
+ factory: faker => faker.image.url().substring(0, 255),
107
+ },
108
+
109
+ content: {
110
+ required: true,
111
+ order: 5,
112
+ fillable: true,
113
+ validation: {
114
+ rule: schema.string().min(10).max(100000),
115
+ message: {
116
+ min: 'Post body must have a minimum of 10 characters',
117
+ max: 'Post body must have a maximum of 100000 characters',
118
+ },
119
+ },
120
+ factory: faker => faker.lorem.paragraphs(1),
121
+ },
122
+
123
+ excerpt: {
124
+ required: false,
125
+ order: 6,
126
+ fillable: true,
127
+ validation: {
128
+ rule: schema.string().min(10).max(500),
129
+ message: {
130
+ min: 'Excerpt must have a minimum of 10 characters',
131
+ max: 'Excerpt must have a maximum of 500 characters',
132
+ },
133
+ },
134
+ factory: faker => faker.lorem.paragraph(),
135
+ },
136
+
137
+ focusKeyword: {
138
+ required: false,
139
+ order: 7,
140
+ fillable: true,
141
+ validation: {
142
+ rule: schema.string().max(100),
143
+ message: {
144
+ max: 'Focus keyword must have a maximum of 100 characters',
145
+ },
146
+ },
147
+ },
148
+
149
+ metaDescription: {
150
+ required: false,
151
+ order: 8,
152
+ fillable: true,
153
+ validation: {
154
+ rule: schema.string().max(160),
155
+ message: {
156
+ max: 'Meta description must have a maximum of 160 characters',
157
+ },
158
+ },
159
+ },
160
+
161
+ canonicalUrl: {
162
+ required: false,
163
+ order: 9,
164
+ fillable: true,
165
+ validation: {
166
+ rule: schema.string().url(),
167
+ message: {
168
+ url: 'Canonical URL must be a valid URL',
169
+ },
170
+ },
171
+ },
172
+
173
+ views: {
174
+ required: false,
175
+ order: 10,
176
+ fillable: true,
177
+ default: 0,
178
+ validation: {
179
+ rule: schema.number().min(0),
180
+ message: {
181
+ min: 'Views count cannot be negative',
182
+ },
183
+ },
184
+ factory: faker => faker.number.int({ min: 0, max: 1000 }),
185
+ },
186
+
187
+ publishedAt: {
188
+ required: false,
189
+ order: 11,
190
+ fillable: true,
191
+ validation: {
192
+ rule: schema.timestamp(),
193
+ message: {
194
+ timestamp: 'Published timestamp must be a valid timestamp',
195
+ },
196
+ },
197
+ factory: (faker) => {
198
+ const date = faker.date.past()
199
+ return date.toISOString().slice(0, 19).replace('T', ' ')
200
+ },
201
+ },
202
+
203
+ status: {
204
+ required: true,
205
+ order: 12,
206
+ fillable: true,
207
+ default: 'draft',
208
+ validation: {
209
+ rule: schema.enum(['published', 'draft', 'archived']),
210
+ message: {
211
+ oneOf: 'Status must be either published, draft, or archived',
212
+ },
213
+ },
214
+ factory: faker => faker.helpers.arrayElement(['published', 'draft', 'archived']),
215
+ },
216
+
217
+ isFeatured: {
218
+ required: false,
219
+ order: 13,
220
+ fillable: true,
221
+ validation: {
222
+ rule: schema.number(),
223
+ message: {
224
+ number: 'Featured must be a number value',
225
+ },
226
+ },
227
+ factory: faker => faker.number.int({ min: 0, max: 1 }),
228
+ },
229
+ },
230
+ } as const)
@@ -0,0 +1,80 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * A path-level redirect on a site. Written automatically when a page's slug
6
+ * or ancestry changes (`source: 'slug-change'`) so old links keep working,
7
+ * and manually from the dashboard (`source: 'manual'`). Resolved by the CMS
8
+ * fallback after page lookup misses.
9
+ */
10
+ export default defineModel({
11
+ name: 'Redirect',
12
+ table: 'redirects',
13
+ primaryKey: 'id',
14
+ autoIncrement: true,
15
+
16
+ indexes: [
17
+ {
18
+ name: 'redirects_site_from_unique',
19
+ columns: ['site_id', 'from_path'],
20
+ unique: true,
21
+ },
22
+ ],
23
+
24
+ traits: {
25
+ useTimestamps: true,
26
+ useApi: {
27
+ middleware: ['auth'],
28
+ uri: 'redirects',
29
+ routes: ['index', 'store', 'show', 'update', 'destroy'],
30
+ },
31
+ },
32
+
33
+ belongsTo: ['Site'],
34
+
35
+ attributes: {
36
+ fromPath: {
37
+ required: true,
38
+ order: 1,
39
+ fillable: true,
40
+ validation: {
41
+ rule: schema.string().max(2048),
42
+ },
43
+ factory: faker => `/${faker.lorem.slug()}`,
44
+ },
45
+
46
+ toPath: {
47
+ required: true,
48
+ order: 2,
49
+ fillable: true,
50
+ validation: {
51
+ rule: schema.string().max(2048),
52
+ },
53
+ factory: faker => `/${faker.lorem.slug()}`,
54
+ },
55
+
56
+ statusCode: {
57
+ required: false,
58
+ order: 3,
59
+ fillable: true,
60
+ default: 301,
61
+ validation: {
62
+ // schema.enum is string-typed; the numeric range pins 301/302 (the
63
+ // read path coerces anything else to 301 anyway).
64
+ rule: schema.number().min(301).max(302),
65
+ },
66
+ factory: () => 301,
67
+ },
68
+
69
+ source: {
70
+ required: false,
71
+ order: 4,
72
+ fillable: true,
73
+ default: 'manual',
74
+ validation: {
75
+ rule: schema.enum(['slug-change', 'manual'] as const),
76
+ },
77
+ factory: () => 'manual',
78
+ },
79
+ },
80
+ } as const)
@@ -0,0 +1,117 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'Deployment',
6
+ table: 'deployments',
7
+ primaryKey: 'id',
8
+ autoIncrement: true,
9
+
10
+ traits: {
11
+ useUuid: true,
12
+ useTimestamps: true,
13
+ useSearch: {
14
+ displayable: ['id', 'commitHash', 'branch', 'status', 'duration', 'createdAt'],
15
+ searchable: ['commitHash', 'branch', 'status', 'author'],
16
+ sortable: ['createdAt', 'updatedAt', 'duration'],
17
+ filterable: ['status', 'branch', 'environment'],
18
+ },
19
+
20
+ useSeeder: {
21
+ count: 10,
22
+ },
23
+
24
+ useApi: {
25
+ uri: 'deployments',
26
+ routes: ['index', 'show'],
27
+ middleware: ['auth'],
28
+ },
29
+ },
30
+
31
+ attributes: {
32
+ commitHash: {
33
+ order: 1,
34
+ fillable: true,
35
+ validation: {
36
+ rule: schema.string().required().min(7).max(40),
37
+ },
38
+ factory: faker => faker.git.commitSha().substring(0, 7),
39
+ },
40
+
41
+ commitMessage: {
42
+ order: 2,
43
+ fillable: true,
44
+ validation: {
45
+ rule: schema.string().max(500),
46
+ },
47
+ factory: faker => faker.git.commitMessage(),
48
+ },
49
+
50
+ branch: {
51
+ order: 3,
52
+ fillable: true,
53
+ validation: {
54
+ rule: schema.string().required().max(255),
55
+ },
56
+ factory: faker => faker.helpers.arrayElement(['main', 'develop', 'staging', 'feature/auth', 'feature/api', 'fix/hotfix']),
57
+ },
58
+
59
+ status: {
60
+ order: 4,
61
+ fillable: true,
62
+ validation: {
63
+ rule: schema.string().required(),
64
+ },
65
+ factory: faker => faker.helpers.arrayElement(['success', 'failed', 'pending', 'running', 'cancelled']),
66
+ },
67
+
68
+ environment: {
69
+ order: 5,
70
+ fillable: true,
71
+ validation: {
72
+ rule: schema.string().required(),
73
+ },
74
+ factory: faker => faker.helpers.arrayElement(['production', 'staging', 'development']),
75
+ },
76
+
77
+ duration: {
78
+ order: 6,
79
+ fillable: true,
80
+ validation: {
81
+ rule: schema.number().min(0),
82
+ },
83
+ factory: faker => faker.number.int({ min: 15, max: 120 }),
84
+ },
85
+
86
+ author: {
87
+ order: 7,
88
+ fillable: true,
89
+ validation: {
90
+ rule: schema.string().required().max(255),
91
+ },
92
+ factory: faker => faker.person.fullName(),
93
+ },
94
+
95
+ url: {
96
+ order: 8,
97
+ fillable: true,
98
+ validation: {
99
+ rule: schema.string().max(500),
100
+ },
101
+ factory: faker => faker.internet.url(),
102
+ },
103
+
104
+ errorLog: {
105
+ order: 9,
106
+ fillable: true,
107
+ validation: {
108
+ rule: schema.string(),
109
+ },
110
+ factory: () => undefined,
111
+ },
112
+ },
113
+
114
+ dashboard: {
115
+ highlight: true,
116
+ },
117
+ } as const)