@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,159 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * A single-use invitation to join a team.
6
+ *
7
+ * The raw bearer token is only returned by the dedicated invite action and
8
+ * sent to the recipient. This model stores its SHA-256 digest so generated
9
+ * API responses and database reads cannot leak a usable invitation.
10
+ */
11
+ export default defineModel({
12
+ name: 'TeamInvitation',
13
+ table: 'team_invitations',
14
+ primaryKey: 'id',
15
+ autoIncrement: true,
16
+
17
+ indexes: [
18
+ {
19
+ name: 'team_invitations_token_hash_unique',
20
+ columns: ['token_hash'],
21
+ unique: true,
22
+ },
23
+ {
24
+ name: 'team_invitations_pending_key_unique',
25
+ columns: ['pending_key'],
26
+ unique: true,
27
+ },
28
+ {
29
+ name: 'team_invitations_team_email_status_index',
30
+ columns: ['team_id', 'email', 'status'],
31
+ },
32
+ ],
33
+
34
+ belongsTo: ['Team'],
35
+
36
+ traits: {
37
+ useUuid: true,
38
+ useTimestamps: true,
39
+ useSeeder: { count: 0 },
40
+ useSearch: {
41
+ displayable: ['id', 'teamId', 'email', 'role', 'status', 'deliveryStatus', 'expiresAt', 'createdAt'],
42
+ searchable: ['email'],
43
+ sortable: ['email', 'role', 'status', 'expiresAt', 'createdAt', 'updatedAt'],
44
+ filterable: ['teamId', 'role', 'status', 'deliveryStatus'],
45
+ },
46
+ useApi: {
47
+ uri: 'team-invitations',
48
+ routes: ['index', 'show', 'destroy'],
49
+ middleware: ['auth', 'team'],
50
+ },
51
+ },
52
+
53
+ attributes: {
54
+ teamId: {
55
+ required: true,
56
+ fillable: true,
57
+ validation: {
58
+ rule: schema.number().required(),
59
+ },
60
+ },
61
+
62
+ email: {
63
+ required: true,
64
+ fillable: true,
65
+ validation: {
66
+ rule: schema.string().email().required().max(320),
67
+ },
68
+ },
69
+
70
+ role: {
71
+ required: true,
72
+ fillable: true,
73
+ default: 'member',
74
+ validation: {
75
+ rule: schema.enum(['admin', 'member', 'viewer']),
76
+ },
77
+ },
78
+
79
+ tokenHash: {
80
+ required: true,
81
+ hidden: true,
82
+ validation: {
83
+ rule: schema.string().required().max(64),
84
+ },
85
+ },
86
+
87
+ pendingKey: {
88
+ required: false,
89
+ hidden: true,
90
+ validation: {
91
+ rule: schema.string().max(384),
92
+ },
93
+ },
94
+
95
+ invitedByUserId: {
96
+ required: false,
97
+ validation: {
98
+ rule: schema.number(),
99
+ },
100
+ },
101
+
102
+ acceptedByUserId: {
103
+ required: false,
104
+ validation: {
105
+ rule: schema.number(),
106
+ },
107
+ },
108
+
109
+ status: {
110
+ required: true,
111
+ fillable: true,
112
+ default: 'pending',
113
+ validation: {
114
+ rule: schema.enum(['pending', 'accepted', 'revoked', 'expired']),
115
+ },
116
+ },
117
+
118
+ deliveryStatus: {
119
+ required: true,
120
+ default: 'pending',
121
+ validation: {
122
+ rule: schema.enum(['pending', 'sent', 'failed']),
123
+ },
124
+ },
125
+
126
+ deliveryError: {
127
+ required: false,
128
+ hidden: true,
129
+ validation: {
130
+ rule: schema.string().max(2000),
131
+ },
132
+ },
133
+
134
+ expiresAt: {
135
+ required: true,
136
+ validation: {
137
+ rule: schema.timestamp().required(),
138
+ },
139
+ },
140
+
141
+ deliveredAt: {
142
+ required: false,
143
+ validation: {
144
+ rule: schema.timestamp(),
145
+ },
146
+ },
147
+
148
+ acceptedAt: {
149
+ required: false,
150
+ validation: {
151
+ rule: schema.timestamp(),
152
+ },
153
+ },
154
+ },
155
+
156
+ dashboard: {
157
+ enabled: false,
158
+ },
159
+ } as const)
@@ -0,0 +1,88 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * A user's membership in one team.
6
+ *
7
+ * The auth package already resolves active-team context from this table.
8
+ * Keeping the pivot as a model gives applications a typed, model-driven
9
+ * schema and an authenticated REST surface instead of relying on an
10
+ * undocumented table that may not exist.
11
+ */
12
+ export default defineModel({
13
+ name: 'TeamMember',
14
+ table: 'team_members',
15
+ primaryKey: 'id',
16
+ autoIncrement: true,
17
+
18
+ indexes: [
19
+ {
20
+ name: 'team_members_team_user_unique',
21
+ columns: ['team_id', 'user_id'],
22
+ unique: true,
23
+ },
24
+ {
25
+ name: 'team_members_user_status_index',
26
+ columns: ['user_id', 'status'],
27
+ },
28
+ ],
29
+
30
+ belongsTo: ['Team', 'User'],
31
+
32
+ traits: {
33
+ useUuid: true,
34
+ useTimestamps: true,
35
+ useSeeder: { count: 0 },
36
+ useSearch: {
37
+ displayable: ['id', 'teamId', 'userId', 'role', 'status', 'createdAt'],
38
+ searchable: [],
39
+ sortable: ['role', 'status', 'createdAt', 'updatedAt'],
40
+ filterable: ['teamId', 'userId', 'role', 'status'],
41
+ },
42
+ useApi: {
43
+ uri: 'team-members',
44
+ routes: ['index', 'store', 'show', 'update', 'destroy'],
45
+ middleware: ['auth', 'team'],
46
+ },
47
+ },
48
+
49
+ attributes: {
50
+ teamId: {
51
+ required: true,
52
+ fillable: true,
53
+ validation: {
54
+ rule: schema.number().required(),
55
+ },
56
+ },
57
+
58
+ userId: {
59
+ required: true,
60
+ fillable: true,
61
+ validation: {
62
+ rule: schema.number().required(),
63
+ },
64
+ },
65
+
66
+ role: {
67
+ required: true,
68
+ fillable: true,
69
+ default: 'member',
70
+ validation: {
71
+ rule: schema.enum(['owner', 'admin', 'member', 'viewer']),
72
+ },
73
+ },
74
+
75
+ status: {
76
+ required: true,
77
+ fillable: true,
78
+ default: 'active',
79
+ validation: {
80
+ rule: schema.enum(['active', 'suspended']),
81
+ },
82
+ },
83
+ },
84
+
85
+ dashboard: {
86
+ enabled: false,
87
+ },
88
+ } as const)
@@ -0,0 +1,24 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'UsageEvent',
6
+ table: 'usage_events',
7
+ belongsTo: ['Team'],
8
+ traits: {
9
+ useUuid: true,
10
+ useTimestamps: true,
11
+ useApi: { uri: 'usage-events', routes: ['index', 'show'], middleware: ['auth', 'team'] },
12
+ },
13
+ indexes: [
14
+ { name: 'usage_events_idempotency_unique', columns: ['idempotency_key'], unique: true },
15
+ { name: 'usage_events_meter_period', columns: ['team_id', 'meter', 'occurred_at'] },
16
+ ],
17
+ attributes: {
18
+ meter: { required: true, fillable: true, validation: { rule: schema.enum(['contacts', 'email_sends', 'sms_segments', 'ai_generations', 'storage_bytes']) }, factory: () => 'email_sends' },
19
+ quantity: { required: true, fillable: true, default: 1, validation: { rule: schema.number().min(0) }, factory: () => 1 },
20
+ idempotencyKey: { required: true, fillable: true, validation: { rule: schema.string().max(255) }, factory: faker => faker.string.uuid() },
21
+ metadata: { required: false, fillable: true, validation: { rule: schema.json() }, factory: () => JSON.stringify({}) },
22
+ occurredAt: { required: true, fillable: true, validation: { rule: schema.timestamp() }, factory: () => new Date().toISOString() },
23
+ },
24
+ } as const)
@@ -0,0 +1,142 @@
1
+ import type { Attributes } from '@stacksjs/types'
2
+ import { defineModel } from '@stacksjs/orm'
3
+ import { makeHash } from '@stacksjs/security'
4
+ // soon, these will be auto-imported
5
+ import { schema } from '@stacksjs/validation'
6
+ import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH } from '../password-policy'
7
+
8
+ export default defineModel({
9
+ name: 'User', // defaults to the sanitized file name
10
+ table: 'users', // defaults to the lowercase, plural name of the model name (or the name of the model file)
11
+ primaryKey: 'id', // defaults to `id`
12
+ autoIncrement: true, // defaults to true
13
+
14
+ // Define composite indexes
15
+ indexes: [
16
+ {
17
+ name: 'users_email_name_index',
18
+ columns: ['email', 'name'],
19
+ },
20
+ ],
21
+
22
+ traits: {
23
+ useAuth: {
24
+ usePasskey: true,
25
+ },
26
+ // Not every app bills through the User model — leave `billable`
27
+ // off by default here. An app that wants checkout()/
28
+ // createStripeUser()/activeSubscription() etc. (createBillableMethods
29
+ // in orm/define-model.ts) should `buddy publish:model User` and
30
+ // enable the trait on its own app/Models/User.ts override, alongside
31
+ // the matching users.stripe_id guarantee-ALTER already provided by
32
+ // ensureUsersAuthColumns() regardless of this flag.
33
+ billable: false,
34
+ useUuid: true,
35
+ useTimestamps: true, // defaults to true, `timestampable` used as an alias
36
+ useSocials: ['github'],
37
+ useSearch: {
38
+ displayable: ['id', 'name', 'email'], // the fields to become d (defaults to all fields)
39
+ searchable: ['name', 'email'], // the fields to become searchable (defaults to all fields)
40
+ sortable: ['created_at', 'updated_at'], // the fields to become sortable (defaults to all fields)
41
+ filterable: [], // the fields to become filterable (defaults to all fields)
42
+ // options: {}, // you may pass options to the search engine
43
+ },
44
+
45
+ useSeeder: {
46
+ // defaults to a count of 10, `seedable` used as an alias
47
+ count: 10,
48
+ },
49
+
50
+ useApi: {
51
+ uri: 'users', // your-url.com/api/users
52
+
53
+ routes: ['index', 'store', 'show'],
54
+ // Registration is handled by the dedicated, rate-limited
55
+ // `/register` action. The generated User CRUD surface exposes PII
56
+ // and must only be available to authenticated operators.
57
+ middleware: ['auth'],
58
+ },
59
+ },
60
+
61
+ hasOne: ['Subscriber', 'Driver', 'Author'],
62
+
63
+ hasMany: [
64
+ 'PersonalAccessToken',
65
+ 'Customer',
66
+ 'TeamMember',
67
+ ],
68
+ attributes: {
69
+ name: {
70
+ order: 2,
71
+ fillable: true,
72
+ validation: {
73
+ rule: schema.string().required().min(5).max(100),
74
+ message: {
75
+ min: 'Name must have a minimum of 3 characters',
76
+ max: 'Name must have a maximum of 255 characters',
77
+ },
78
+ },
79
+
80
+ factory: faker => faker.person.fullName(),
81
+ },
82
+
83
+ email: {
84
+ unique: true,
85
+ order: 1,
86
+ fillable: true,
87
+ validation: {
88
+ rule: schema.string().email().required(),
89
+ message: {
90
+ required: 'Email is required',
91
+ email: 'Email must be a valid email address',
92
+ },
93
+ },
94
+
95
+ factory: faker => faker.internet.email(),
96
+ },
97
+ password: {
98
+ order: 3,
99
+ hidden: true,
100
+ fillable: true,
101
+ validation: {
102
+ rule: schema.string().required().min(PASSWORD_MIN_LENGTH).max(PASSWORD_MAX_LENGTH),
103
+ message: {
104
+ required: 'Password is required',
105
+ min: `Password must have a minimum of ${PASSWORD_MIN_LENGTH} characters`,
106
+ max: `Password must have a maximum of ${PASSWORD_MAX_LENGTH} characters`,
107
+ },
108
+ },
109
+
110
+ // Must satisfy the rule above, or seeded users fail their own model's
111
+ // validation. `123456` did once the minimum moved to 8 (#2226).
112
+ factory: () => 'password1234',
113
+ },
114
+
115
+ avatar: {
116
+ order: 4,
117
+ fillable: true,
118
+ validation: {
119
+ rule: schema.string().max(2048),
120
+ message: {
121
+ max: 'Avatar URL must be at most 2048 characters',
122
+ },
123
+ },
124
+
125
+ factory: faker => faker.image.avatar(),
126
+ },
127
+ },
128
+ get: {
129
+ salutationName: (attributes: Attributes) => {
130
+ return `Mr. ${attributes.name}`
131
+ },
132
+ },
133
+
134
+ set: {
135
+ password: async (attributes: Attributes) => {
136
+ return await makeHash(attributes.password, { algorithm: 'bcrypt' })
137
+ },
138
+ },
139
+ dashboard: {
140
+ highlight: true,
141
+ },
142
+ } as const)
@@ -0,0 +1,173 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * An auction: a catalogue of lots that opens, takes bids, and closes.
6
+ *
7
+ * `eventId` is a plain column rather than a `belongsTo`, because the thing an
8
+ * auction hangs off is the application's own event model - a gala, a fun run,
9
+ * a capital campaign evening - and the framework does not ship one. An app that
10
+ * has an Event model declares `hasOne: ['Auction']` on its side and the column
11
+ * below is what that resolves through; an app with no events leaves it null and
12
+ * runs the auction standalone.
13
+ */
14
+ export default defineModel({
15
+ name: 'Auction',
16
+ table: 'auctions',
17
+ primaryKey: 'id',
18
+ autoIncrement: true,
19
+
20
+ traits: {
21
+ useUuid: true,
22
+ useTimestamps: true,
23
+
24
+ useSearch: {
25
+ displayable: ['id', 'title', 'status', 'opensAt', 'closesAt', 'goalAmount'],
26
+ searchable: ['title', 'description'],
27
+ sortable: ['createdAt', 'opensAt', 'closesAt'],
28
+ filterable: ['status'],
29
+ },
30
+
31
+ useApi: {
32
+ uri: 'auctions',
33
+ middleware: { read: ['auth'], write: ['auth'] },
34
+ },
35
+
36
+ observe: true,
37
+ },
38
+
39
+ hasMany: ['AuctionItem', 'Bid', 'Pledge'],
40
+
41
+ indexes: [
42
+ { name: 'auctions_event_id_index', columns: ['event_id'] },
43
+ { name: 'auctions_status_index', columns: ['status'] },
44
+ ],
45
+
46
+ attributes: {
47
+ eventId: {
48
+ order: 1,
49
+ fillable: true,
50
+ validation: {
51
+ rule: schema.number(),
52
+ },
53
+ factory: faker => faker.number.int({ min: 1, max: 20 }),
54
+ },
55
+
56
+ title: {
57
+ order: 2,
58
+ fillable: true,
59
+ validation: {
60
+ rule: schema.string().required().max(160),
61
+ message: {
62
+ required: 'Title is required',
63
+ max: 'Title must have a maximum of 160 characters',
64
+ },
65
+ },
66
+ factory: () => 'Spring Benefit Auction',
67
+ },
68
+
69
+ description: {
70
+ order: 3,
71
+ fillable: true,
72
+ validation: {
73
+ rule: schema.string(),
74
+ },
75
+ factory: faker => faker.lorem.sentence(),
76
+ },
77
+
78
+ status: {
79
+ default: 'draft',
80
+ order: 4,
81
+ fillable: true,
82
+ validation: {
83
+ rule: schema.enum(['draft', 'preview', 'open', 'closed', 'settled']).required(),
84
+ message: {
85
+ required: 'Status is required',
86
+ },
87
+ },
88
+ factory: () => 'draft',
89
+ },
90
+
91
+ currency: {
92
+ default: 'USD',
93
+ order: 5,
94
+ fillable: true,
95
+ validation: {
96
+ rule: schema.string().max(3),
97
+ },
98
+ factory: () => 'USD',
99
+ },
100
+
101
+ /**
102
+ * The fundraising target, in cents. Null when the school would rather not
103
+ * put a number on the wall.
104
+ */
105
+ goalAmount: {
106
+ order: 6,
107
+ fillable: true,
108
+ validation: {
109
+ rule: schema.number().min(0),
110
+ },
111
+ factory: faker => faker.number.int({ min: 500000, max: 5000000 }),
112
+ },
113
+
114
+ opensAt: {
115
+ order: 7,
116
+ fillable: true,
117
+ validation: {
118
+ rule: schema.date().required(),
119
+ },
120
+ factory: faker => faker.date.soon().toISOString(),
121
+ },
122
+
123
+ closesAt: {
124
+ order: 8,
125
+ fillable: true,
126
+ validation: {
127
+ rule: schema.date().required(),
128
+ },
129
+ factory: faker => faker.date.future().toISOString(),
130
+ },
131
+
132
+ /** How far a lot's close is pushed out when a bid lands in the window. */
133
+ antiSnipeMinutes: {
134
+ default: 2,
135
+ order: 9,
136
+ fillable: true,
137
+ validation: {
138
+ rule: schema.number().min(0).max(60),
139
+ },
140
+ factory: () => 2,
141
+ },
142
+
143
+ /** How close to the end a bid has to land to trigger an extension. */
144
+ extendOnBidWindowMinutes: {
145
+ default: 2,
146
+ order: 10,
147
+ fillable: true,
148
+ validation: {
149
+ rule: schema.number().min(0).max(60),
150
+ },
151
+ factory: () => 2,
152
+ },
153
+
154
+ /**
155
+ * The hard stop. Without it two determined bidders can hold one lot open
156
+ * all night and the gala staff cannot go home.
157
+ */
158
+ maxExtensions: {
159
+ default: 20,
160
+ order: 11,
161
+ fillable: true,
162
+ validation: {
163
+ rule: schema.number().min(0),
164
+ },
165
+ factory: () => 20,
166
+ },
167
+ },
168
+
169
+ dashboard: {
170
+ icon: 'i-hugeicons-auction',
171
+ label: 'Auctions',
172
+ },
173
+ } as const)