@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,127 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * One field of a Form. `name` is the machine key submissions store values
6
+ * under; `conditions` is a show/hide rule set evaluated client-side for UX
7
+ * and re-evaluated server-side for correctness (a hidden required field is
8
+ * not required, and values for hidden fields are discarded).
9
+ */
10
+ export default defineModel({
11
+ name: 'FormField',
12
+ table: 'form_fields',
13
+ primaryKey: 'id',
14
+ autoIncrement: true,
15
+
16
+ indexes: [
17
+ {
18
+ name: 'form_fields_form_name_unique',
19
+ columns: ['form_id', 'name'],
20
+ unique: true,
21
+ },
22
+ ],
23
+
24
+ traits: {
25
+ useTimestamps: true,
26
+ useApi: {
27
+ middleware: ['auth'],
28
+ uri: 'form-fields',
29
+ routes: ['index', 'store', 'show', 'update', 'destroy'],
30
+ },
31
+ },
32
+
33
+ belongsTo: ['Form'],
34
+
35
+ attributes: {
36
+ name: {
37
+ required: true,
38
+ order: 1,
39
+ fillable: true,
40
+ validation: {
41
+ rule: schema.string().min(1).max(64).matches(/^[a-z0-9_]+$/),
42
+ },
43
+ factory: faker => faker.lorem.word(),
44
+ },
45
+
46
+ label: {
47
+ required: true,
48
+ order: 2,
49
+ fillable: true,
50
+ validation: {
51
+ rule: schema.string().min(1).max(255),
52
+ },
53
+ factory: faker => faker.lorem.words(2),
54
+ },
55
+
56
+ type: {
57
+ required: true,
58
+ order: 3,
59
+ fillable: true,
60
+ validation: {
61
+ rule: schema.enum(['text', 'textarea', 'email', 'phone', 'select', 'checkbox', 'radio', 'date', 'file', 'currency', 'section_break'] as const),
62
+ },
63
+ factory: faker => faker.helpers.arrayElement(['text', 'email', 'select']),
64
+ },
65
+
66
+ required: {
67
+ required: false,
68
+ order: 4,
69
+ fillable: true,
70
+ default: false,
71
+ validation: {
72
+ rule: schema.boolean(),
73
+ },
74
+ factory: faker => faker.datatype.boolean(),
75
+ },
76
+
77
+ position: {
78
+ required: false,
79
+ order: 5,
80
+ fillable: true,
81
+ default: 0,
82
+ validation: {
83
+ rule: schema.number().min(0),
84
+ },
85
+ factory: faker => faker.number.int({ min: 0, max: 20 }),
86
+ },
87
+
88
+ width: {
89
+ required: false,
90
+ order: 6,
91
+ fillable: true,
92
+ default: 'full',
93
+ validation: {
94
+ rule: schema.enum(['full', 'half'] as const),
95
+ },
96
+ factory: () => 'full',
97
+ },
98
+
99
+ /**
100
+ * Per-type options JSON: { placeholder?, choices?: {label,value}[],
101
+ * min?, max?, accept?: string[], maxSizeMb?, amountCents? }
102
+ */
103
+ options: {
104
+ required: false,
105
+ order: 7,
106
+ fillable: true,
107
+ validation: {
108
+ rule: schema.json(),
109
+ },
110
+ factory: () => JSON.stringify({}),
111
+ },
112
+
113
+ /**
114
+ * Visibility rules JSON: { action: 'show'|'hide', logic: 'all'|'any',
115
+ * rules: [{ field, op, value? }] }
116
+ */
117
+ conditions: {
118
+ required: false,
119
+ order: 8,
120
+ fillable: true,
121
+ validation: {
122
+ rule: schema.json(),
123
+ },
124
+ factory: () => null,
125
+ },
126
+ },
127
+ } as const)
@@ -0,0 +1,114 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * One submission of a Form. `values` is the validated field map; `email` and
6
+ * `name` are extracted into typed columns at submit time (from the fields the
7
+ * form's settings name, defaulting to the first email field) so lookups,
8
+ * dedupe and confirmation addressing never need JSON extraction SQL.
9
+ *
10
+ * No `useApi`: public writes and admin reads both go through the
11
+ * `@stacksjs/forms` routes, which own spam guards, payment states and
12
+ * site scoping.
13
+ */
14
+ export default defineModel({
15
+ name: 'FormSubmission',
16
+ table: 'form_submissions',
17
+ primaryKey: 'id',
18
+ autoIncrement: true,
19
+
20
+ traits: {
21
+ useUuid: true,
22
+ useTimestamps: true,
23
+ },
24
+
25
+ belongsTo: ['Form', 'Site'],
26
+
27
+ attributes: {
28
+ /** Validated { [fieldName]: value } JSON. Named data, not values - values is a SQL keyword. */
29
+ data: {
30
+ required: true,
31
+ order: 1,
32
+ fillable: true,
33
+ validation: {
34
+ rule: schema.json(),
35
+ },
36
+ factory: () => JSON.stringify({}),
37
+ },
38
+
39
+ email: {
40
+ required: false,
41
+ order: 2,
42
+ fillable: true,
43
+ validation: {
44
+ rule: schema.string().email().max(255),
45
+ },
46
+ factory: faker => faker.internet.email(),
47
+ },
48
+
49
+ name: {
50
+ required: false,
51
+ order: 3,
52
+ fillable: true,
53
+ validation: {
54
+ rule: schema.string().max(255),
55
+ },
56
+ factory: faker => faker.person.fullName(),
57
+ },
58
+
59
+ status: {
60
+ required: false,
61
+ order: 4,
62
+ fillable: true,
63
+ default: 'complete',
64
+ validation: {
65
+ rule: schema.enum(['pending_payment', 'complete', 'spam'] as const),
66
+ },
67
+ factory: () => 'complete',
68
+ },
69
+
70
+ /** Server-computed. Never trusted from the client. */
71
+ amountCents: {
72
+ required: false,
73
+ order: 5,
74
+ fillable: true,
75
+ validation: {
76
+ rule: schema.number().min(0),
77
+ },
78
+ factory: () => null,
79
+ },
80
+
81
+ paymentIntentId: {
82
+ required: false,
83
+ order: 6,
84
+ fillable: true,
85
+ validation: {
86
+ rule: schema.string().max(255),
87
+ },
88
+ factory: () => null,
89
+ },
90
+
91
+ ip: {
92
+ required: false,
93
+ order: 7,
94
+ fillable: true,
95
+ validation: {
96
+ rule: schema.string().max(64),
97
+ },
98
+ factory: () => null,
99
+ },
100
+
101
+ submittedAt: {
102
+ required: false,
103
+ order: 8,
104
+ fillable: true,
105
+ validation: {
106
+ rule: schema.timestamp(),
107
+ },
108
+ factory: (faker) => {
109
+ const date = faker.date.recent()
110
+ return date.toISOString().slice(0, 19).replace('T', ' ')
111
+ },
112
+ },
113
+ },
114
+ } as const)
@@ -0,0 +1,69 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'Job',
6
+ table: 'jobs',
7
+ primaryKey: 'id',
8
+ autoIncrement: true,
9
+
10
+ traits: {
11
+ useTimestamps: true,
12
+ useSeeder: {
13
+ count: 15,
14
+ },
15
+ },
16
+
17
+ attributes: {
18
+ queue: {
19
+ fillable: true,
20
+ validation: {
21
+ rule: schema.string().required().max(255),
22
+ message: {
23
+ max: 'Queue must have a maximum of 255 characters',
24
+ },
25
+ },
26
+ factory: () => 'default',
27
+ },
28
+
29
+ payload: {
30
+ fillable: true,
31
+ validation: {
32
+ rule: schema.string().required(),
33
+ },
34
+ factory: () => JSON.stringify({
35
+ jobName: 'ExampleJob',
36
+ payload: {},
37
+ options: { tries: 3 },
38
+ envelopeVersion: 1,
39
+ dispatchedAt: new Date().toISOString(),
40
+ }),
41
+ },
42
+
43
+ attempts: {
44
+ fillable: true,
45
+ validation: {
46
+ rule: schema.number(),
47
+ message: {
48
+ number: 'attempts must be a number',
49
+ },
50
+ },
51
+ factory: () => 0,
52
+ },
53
+
54
+ available_at: {
55
+ fillable: true,
56
+ validation: {
57
+ rule: schema.number(),
58
+ },
59
+ factory: () => Math.floor(Date.now() / 1000),
60
+ },
61
+ reserved_at: {
62
+ fillable: true,
63
+ validation: {
64
+ rule: schema.number(),
65
+ },
66
+ factory: () => null,
67
+ },
68
+ },
69
+ } as const)
@@ -0,0 +1,67 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * Card label / tag (stacksjs/stacks#1846).
6
+ *
7
+ * Labels are board-scoped: each board owns its own label palette
8
+ * (so "Bug" on board A is a different record from "Bug" on board B,
9
+ * with potentially different colours). Cards attach labels via the
10
+ * `card_labels` pivot — that wiring lands in Phase 3.
11
+ *
12
+ * Hidden from the sidebar as a standalone model row — labels are
13
+ * managed inside the board view, not as a top-level surface.
14
+ */
15
+ export default defineModel({
16
+ name: 'Label',
17
+ table: 'labels',
18
+ primaryKey: 'id',
19
+ autoIncrement: true,
20
+
21
+ traits: {
22
+ useUuid: true,
23
+ useTimestamps: true,
24
+ useSearch: {
25
+ displayable: ['id', 'name', 'color', 'boardId'],
26
+ searchable: ['name'],
27
+ sortable: ['name', 'createdAt'],
28
+ filterable: ['boardId'],
29
+ },
30
+
31
+ useSeeder: { count: 0 },
32
+
33
+ useApi: {
34
+ uri: 'labels',
35
+ routes: ['index', 'store', 'show', 'update', 'destroy'],
36
+ middleware: ['auth'],
37
+ },
38
+ },
39
+
40
+ belongsTo: ['Board'],
41
+
42
+ attributes: {
43
+ boardId: {
44
+ order: 1,
45
+ fillable: true,
46
+ validation: { rule: schema.number().required() },
47
+ factory: () => 1,
48
+ },
49
+
50
+ name: {
51
+ order: 2,
52
+ fillable: true,
53
+ validation: { rule: schema.string().required().min(1).max(60) },
54
+ factory: faker => faker.helpers.arrayElement(['Bug', 'Feature', 'Docs', 'Refactor', 'Urgent']),
55
+ },
56
+
57
+ color: {
58
+ order: 3,
59
+ fillable: true,
60
+ // Hue token resolved by the UI — same palette as Board.color.
61
+ validation: { rule: schema.string().max(40) },
62
+ factory: faker => faker.helpers.arrayElement(['red', 'amber', 'emerald', 'blue', 'violet', 'slate']),
63
+ },
64
+ },
65
+
66
+ dashboard: { enabled: false },
67
+ } as const)
@@ -0,0 +1,134 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'Log',
6
+ table: 'logs',
7
+ primaryKey: 'id',
8
+ autoIncrement: true,
9
+
10
+ indexes: [
11
+ {
12
+ name: 'logs_timestamp_index',
13
+ columns: ['timestamp'],
14
+ },
15
+ {
16
+ name: 'logs_type_timestamp_index',
17
+ columns: ['type', 'timestamp'],
18
+ },
19
+ {
20
+ name: 'logs_source_timestamp_index',
21
+ columns: ['source', 'timestamp'],
22
+ },
23
+ {
24
+ name: 'logs_project_timestamp_index',
25
+ columns: ['project', 'timestamp'],
26
+ },
27
+ ],
28
+
29
+ traits: {
30
+ useTimestamps: true,
31
+ useApi: {
32
+ uri: 'logs',
33
+ routes: ['index', 'show'],
34
+ middleware: ['auth'],
35
+ },
36
+ useSeeder: {
37
+ count: 50,
38
+ },
39
+ useSearch: {
40
+ displayable: ['id', 'type', 'source', 'message', 'project', 'timestamp'],
41
+ searchable: ['message', 'project', 'file'],
42
+ sortable: ['timestamp', 'type', 'source'],
43
+ filterable: ['type', 'source', 'project'],
44
+ },
45
+ },
46
+
47
+ attributes: {
48
+ timestamp: {
49
+ order: 1,
50
+ fillable: true,
51
+ validation: {
52
+ rule: schema.number().required(),
53
+ message: {
54
+ date: 'Timestamp must be a valid date',
55
+ },
56
+ },
57
+ factory: faker => faker.date.recent().getTime(),
58
+ },
59
+
60
+ type: {
61
+ order: 2,
62
+ fillable: true,
63
+ validation: {
64
+ rule: schema.enum(['warning', 'error', 'info', 'success']).required(),
65
+ message: {
66
+ enum: 'Type must be one of: warning, error, info, success',
67
+ },
68
+ },
69
+ factory: faker => faker.helpers.arrayElement(['warning', 'error', 'info', 'success']),
70
+ },
71
+
72
+ source: {
73
+ order: 3,
74
+ fillable: true,
75
+ validation: {
76
+ rule: schema.enum(['file', 'cli', 'system']).required(),
77
+ message: {
78
+ enum: 'Source must be one of: file, cli, system',
79
+ },
80
+ },
81
+ factory: faker => faker.helpers.arrayElement(['file', 'cli', 'system']),
82
+ },
83
+
84
+ message: {
85
+ order: 4,
86
+ fillable: true,
87
+ validation: {
88
+ rule: schema.string().required().min(1).max(1000),
89
+ message: {
90
+ min: 'Message must not be empty',
91
+ max: 'Message must not exceed 1000 characters',
92
+ },
93
+ },
94
+ factory: faker => faker.lorem.sentence(),
95
+ },
96
+
97
+ project: {
98
+ order: 5,
99
+ fillable: true,
100
+ validation: {
101
+ rule: schema.string().min(1).required().max(255),
102
+ message: {
103
+ min: 'Project name must not be empty',
104
+ max: 'Project name must not exceed 255 characters',
105
+ },
106
+ },
107
+ factory: faker => faker.company.name(),
108
+ },
109
+
110
+ stacktrace: {
111
+ order: 6,
112
+ fillable: true,
113
+ validation: {
114
+ rule: schema.string().required().max(5000),
115
+ message: {
116
+ max: 'Stacktrace must not exceed 5000 characters',
117
+ },
118
+ },
119
+ factory: faker => faker.lorem.paragraphs(2),
120
+ },
121
+
122
+ file: {
123
+ order: 7,
124
+ fillable: true,
125
+ validation: {
126
+ rule: schema.string().required().max(255),
127
+ message: {
128
+ max: 'File path must not exceed 255 characters',
129
+ },
130
+ },
131
+ factory: faker => faker.system.filePath().substring(0, 255),
132
+ },
133
+ },
134
+ } as const)
@@ -0,0 +1,97 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ /**
5
+ * A single-use passwordless sign-in token. `token` stores the SHA-256 of the
6
+ * raw value - deterministic, so consume is one indexed lookup, and with 256
7
+ * bits of entropy in the raw token an offline attack on a fast hash is
8
+ * irrelevant (deliberately NOT the bcrypt-and-scan of password resets).
9
+ *
10
+ * No `useApi`: these rows are written and consumed by the auth flow only.
11
+ */
12
+ export default defineModel({
13
+ name: 'MagicLinkToken',
14
+ table: 'magic_link_tokens',
15
+ primaryKey: 'id',
16
+ autoIncrement: true,
17
+
18
+ indexes: [
19
+ {
20
+ name: 'magic_link_tokens_token_unique',
21
+ columns: ['token'],
22
+ unique: true,
23
+ },
24
+ ],
25
+
26
+ traits: {
27
+ useTimestamps: true,
28
+ },
29
+
30
+ belongsTo: ['User'],
31
+
32
+ attributes: {
33
+ email: {
34
+ required: true,
35
+ order: 1,
36
+ fillable: true,
37
+ validation: {
38
+ rule: schema.string().email().max(255),
39
+ },
40
+ factory: faker => faker.internet.email(),
41
+ },
42
+
43
+ /** SHA-256 hex of the raw token. Never the raw value. */
44
+ token: {
45
+ required: true,
46
+ order: 2,
47
+ fillable: true,
48
+ unique: true,
49
+ validation: {
50
+ rule: schema.string().min(64).max(64),
51
+ },
52
+ factory: faker => faker.string.hexadecimal({ length: 64, prefix: '' }).toLowerCase(),
53
+ },
54
+
55
+ expiresAt: {
56
+ required: true,
57
+ order: 3,
58
+ fillable: true,
59
+ validation: {
60
+ rule: schema.timestamp(),
61
+ },
62
+ factory: () => new Date(Date.now() + 15 * 60_000).toISOString().slice(0, 19).replace('T', ' '),
63
+ },
64
+
65
+ consumedAt: {
66
+ required: false,
67
+ order: 4,
68
+ fillable: true,
69
+ validation: {
70
+ rule: schema.timestamp(),
71
+ },
72
+ factory: () => null,
73
+ },
74
+
75
+ /** Relative path the consumed link lands on. Validated relative-only. */
76
+ redirectTo: {
77
+ required: false,
78
+ order: 5,
79
+ fillable: true,
80
+ validation: {
81
+ rule: schema.string().max(2048),
82
+ },
83
+ factory: () => null,
84
+ },
85
+
86
+ /** The site whose primary host the link targets, on multi-site apps. */
87
+ siteId: {
88
+ required: false,
89
+ order: 6,
90
+ fillable: true,
91
+ validation: {
92
+ rule: schema.number().min(1),
93
+ },
94
+ factory: () => null,
95
+ },
96
+ },
97
+ } as const)