@stacksjs/defaults 0.72.57 → 0.72.61
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/ai/skills/stacks-events/SKILL.md +53 -7
- package/app/Actions/AI/AskAction.ts +1 -1
- package/app/Actions/AI/SummaryAction.ts +1 -1
- package/app/Actions/Auth/DisableTwoFactorAction.ts +1 -1
- package/app/Actions/Auth/EnableTwoFactorAction.ts +1 -1
- package/app/Actions/Auth/LoginAction.ts +2 -2
- package/app/Actions/Auth/MagicLinkConsumeAction.ts +1 -1
- package/app/Actions/Auth/MagicLinkSendAction.ts +1 -1
- package/app/Actions/Auth/RefreshTokenAction.ts +1 -1
- package/app/Actions/Auth/RegisterAction.ts +3 -3
- package/app/Actions/Auth/VerifyTwoFactorLoginAction.ts +2 -2
- package/app/Actions/Cms/CategorizableStoreAction.ts +3 -3
- package/app/Actions/Cms/CategorizableUpdateAction.ts +8 -2
- package/app/Actions/Cms/CommentStoreAction.ts +2 -2
- package/app/Actions/Cms/CommentUpdateAction.ts +8 -2
- package/app/Actions/Cms/PostViewsUpdateAction.ts +7 -1
- package/app/Actions/Cms/TaggableStoreAction.ts +2 -2
- package/app/Actions/Cms/TaggableUpdateAction.ts +8 -2
- package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +1 -1
- package/app/Actions/Dashboard/Kanban/UsersListAction.ts +1 -1
- package/app/Actions/LogAction.ts +2 -2
- package/app/Actions/UploadTestAction.ts +0 -1
- package/app/Middleware/Auth.ts +0 -1
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/routes/dashboard.ts +1 -1
|
@@ -122,12 +122,57 @@ The `Record<EventType, unknown>` intersection allows arbitrary event names beyon
|
|
|
122
122
|
|
|
123
123
|
## Model Events
|
|
124
124
|
|
|
125
|
-
Every model with `observe: true` trait
|
|
126
|
-
- `'{model}:created'` -- after insert
|
|
127
|
-
- `'{model}:updated'` -- after update
|
|
128
|
-
- `'{model}:deleted'` -- after delete
|
|
125
|
+
Every model with the `observe: true` trait emits **eight** events:
|
|
129
126
|
|
|
130
|
-
|
|
127
|
+
| Event | When | Payload |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `{model}:saving` | before any write | the model object |
|
|
130
|
+
| `{model}:creating` / `:updating` / `:deleting` | before that write | the model object |
|
|
131
|
+
| `{model}:created` / `:updated` / `:deleted` | after that write | the row |
|
|
132
|
+
| `{model}:saved` | after insert OR update | the row |
|
|
133
|
+
|
|
134
|
+
Model name is lowercased: `'user:created'`, `'post:updated'`, `'teammember:saved'`.
|
|
135
|
+
|
|
136
|
+
A **before** listener can cancel the write by returning `false`:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
listen('user:deleting', (model) => {
|
|
140
|
+
if (model.attributes.email.endsWith('@example.com'))
|
|
141
|
+
return false // the delete does not happen
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Before-events carry the model object (`.attributes` holds the row); after-events
|
|
146
|
+
carry the row itself.
|
|
147
|
+
|
|
148
|
+
### The payloads are typed, and nothing generates them
|
|
149
|
+
|
|
150
|
+
`listen('user:created', user => user.emial)` is a compile error - the payload is
|
|
151
|
+
the User row, with the columns your model declares.
|
|
152
|
+
|
|
153
|
+
`storage/framework/types/model-events.d.ts` derives the whole map from the models
|
|
154
|
+
barrel with a mapped type:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
type ModelAfterEvents = {
|
|
158
|
+
[K in keyof Models & string as `${Lowercase<K>}:${AfterEvent}`]: ModelRow<Models[K]>
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
So a model existing IS its events existing - there is no generated list to keep in
|
|
163
|
+
agreement, and nothing to re-run after adding a model. (It replaced an 817-line
|
|
164
|
+
generated file, and before that a hand-maintained one that listed three events per
|
|
165
|
+
model and typed every payload `Record<string, any>`.)
|
|
166
|
+
|
|
167
|
+
Declare your own events by augmenting `AppEvents`:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
declare module '@stacksjs/events' {
|
|
171
|
+
interface AppEvents {
|
|
172
|
+
'invoice:overdue': { id: number, daysLate: number }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
131
176
|
|
|
132
177
|
Events are dispatched via lazy `import('@stacksjs/events').then(({ dispatch }) => dispatch(...))` to avoid circular dependencies. If the import fails (e.g., browser context), errors are silently caught.
|
|
133
178
|
|
|
@@ -136,8 +181,9 @@ The `observe` trait can be:
|
|
|
136
181
|
- `['create', 'update']` -- emits only specified events
|
|
137
182
|
- `false` / undefined -- no events
|
|
138
183
|
|
|
139
|
-
|
|
140
|
-
|
|
184
|
+
There is no model list to keep here. Every model in `storage/framework/auto-imports/models.ts`
|
|
185
|
+
has its eight events, and that barrel is generated from disk for the runtime, so the
|
|
186
|
+
answer to "which models emit events" is "the ones that exist".
|
|
141
187
|
|
|
142
188
|
## Event-to-Listener Mapping (app/Events.ts)
|
|
143
189
|
|
|
@@ -12,7 +12,7 @@ export default new Action({
|
|
|
12
12
|
|
|
13
13
|
validations: {
|
|
14
14
|
email: {
|
|
15
|
-
rule: schema.string().email(),
|
|
15
|
+
rule: schema.string().email().required(),
|
|
16
16
|
message: 'Email must be a valid email address.',
|
|
17
17
|
},
|
|
18
18
|
// Presence only, NOT the creation policy. Enforcing a minimum length on
|
|
@@ -21,7 +21,7 @@ export default new Action({
|
|
|
21
21
|
// credentials are ever checked. The policy belongs on the paths that SET a
|
|
22
22
|
// password (#2226).
|
|
23
23
|
password: {
|
|
24
|
-
rule: schema.string().min(1).max(PASSWORD_MAX_LENGTH),
|
|
24
|
+
rule: schema.string().min(1).max(PASSWORD_MAX_LENGTH).required(),
|
|
25
25
|
message: PASSWORD_PRESENCE_MESSAGE,
|
|
26
26
|
},
|
|
27
27
|
},
|
|
@@ -12,15 +12,15 @@ export default new Action({
|
|
|
12
12
|
|
|
13
13
|
validations: {
|
|
14
14
|
email: {
|
|
15
|
-
rule: schema.string().email(),
|
|
15
|
+
rule: schema.string().email().required(),
|
|
16
16
|
message: 'Email must be a valid email address.',
|
|
17
17
|
},
|
|
18
18
|
password: {
|
|
19
|
-
rule: schema.string().min(PASSWORD_MIN_LENGTH).max(PASSWORD_MAX_LENGTH),
|
|
19
|
+
rule: schema.string().min(PASSWORD_MIN_LENGTH).max(PASSWORD_MAX_LENGTH).required(),
|
|
20
20
|
message: PASSWORD_POLICY_MESSAGE,
|
|
21
21
|
},
|
|
22
22
|
name: {
|
|
23
|
-
rule: schema.string().min(2).max(255),
|
|
23
|
+
rule: schema.string().min(2).max(255).required(),
|
|
24
24
|
message: 'Name must be between 2 and 255 characters.',
|
|
25
25
|
},
|
|
26
26
|
},
|
|
@@ -10,11 +10,11 @@ export default new Action({
|
|
|
10
10
|
|
|
11
11
|
validations: {
|
|
12
12
|
challenge_token: {
|
|
13
|
-
rule: schema.string().min(1),
|
|
13
|
+
rule: schema.string().min(1).required(),
|
|
14
14
|
message: 'A challenge token is required.',
|
|
15
15
|
},
|
|
16
16
|
code: {
|
|
17
|
-
rule: schema.string().min(6).max(6),
|
|
17
|
+
rule: schema.string().min(6).max(6).required(),
|
|
18
18
|
message: 'Code must be a 6-digit TOTP code.',
|
|
19
19
|
},
|
|
20
20
|
},
|
|
@@ -10,20 +10,20 @@ export default new Action({
|
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
11
|
await request.validate({
|
|
12
12
|
name: {
|
|
13
|
-
rule: schema.string(),
|
|
13
|
+
rule: schema.string().required(),
|
|
14
14
|
message: {
|
|
15
15
|
name: 'Name is required',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
description: {
|
|
19
|
-
rule: schema.string(),
|
|
19
|
+
rule: schema.string().required(),
|
|
20
20
|
message: {
|
|
21
21
|
description: 'Description is required',
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
|
|
25
25
|
categorizable_type: {
|
|
26
|
-
rule: schema.string(),
|
|
26
|
+
rule: schema.string().required(),
|
|
27
27
|
message: {
|
|
28
28
|
categorizable_type: 'Categorizable type is required',
|
|
29
29
|
},
|
|
@@ -8,17 +8,23 @@ export default new Action({
|
|
|
8
8
|
description: 'Category Update ORM Action',
|
|
9
9
|
method: 'PATCH',
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
|
+
/*
|
|
12
|
+
* PATCH, so these are NOT required: a partial update sends the fields it
|
|
13
|
+
* means to change. The messages used to say "X is required", which fires on
|
|
14
|
+
* a type failure and never on absence - a message describing a rule the
|
|
15
|
+
* block does not have.
|
|
16
|
+
*/
|
|
11
17
|
await request.validate({
|
|
12
18
|
name: {
|
|
13
19
|
rule: schema.string(),
|
|
14
20
|
message: {
|
|
15
|
-
name: 'Name
|
|
21
|
+
name: 'Name must be a string.',
|
|
16
22
|
},
|
|
17
23
|
},
|
|
18
24
|
description: {
|
|
19
25
|
rule: schema.string(),
|
|
20
26
|
message: {
|
|
21
|
-
description: 'Description
|
|
27
|
+
description: 'Description must be a string.',
|
|
22
28
|
},
|
|
23
29
|
},
|
|
24
30
|
})
|
|
@@ -10,13 +10,13 @@ export default new Action({
|
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
11
|
await request.validate({
|
|
12
12
|
title: {
|
|
13
|
-
rule: schema.string(),
|
|
13
|
+
rule: schema.string().required(),
|
|
14
14
|
message: {
|
|
15
15
|
title: 'Title is required',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
body: {
|
|
19
|
-
rule: schema.string(),
|
|
19
|
+
rule: schema.string().required(),
|
|
20
20
|
message: {
|
|
21
21
|
body: 'Body is required',
|
|
22
22
|
},
|
|
@@ -8,17 +8,23 @@ export default new Action({
|
|
|
8
8
|
description: 'Comment Update ORM Action',
|
|
9
9
|
method: 'PATCH',
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
|
+
/*
|
|
12
|
+
* PATCH, so these are NOT required: a partial update sends the fields it
|
|
13
|
+
* means to change. The messages used to say "X is required", which fires on
|
|
14
|
+
* a type failure and never on absence - a message describing a rule the
|
|
15
|
+
* block does not have.
|
|
16
|
+
*/
|
|
11
17
|
await request.validate({
|
|
12
18
|
title: {
|
|
13
19
|
rule: schema.string(),
|
|
14
20
|
message: {
|
|
15
|
-
title: 'Title
|
|
21
|
+
title: 'Title must be a string.',
|
|
16
22
|
},
|
|
17
23
|
},
|
|
18
24
|
body: {
|
|
19
25
|
rule: schema.string(),
|
|
20
26
|
message: {
|
|
21
|
-
body: 'Body
|
|
27
|
+
body: 'Body must be a string.',
|
|
22
28
|
},
|
|
23
29
|
},
|
|
24
30
|
})
|
|
@@ -8,11 +8,17 @@ export default new Action({
|
|
|
8
8
|
description: 'Updates the view count for a post',
|
|
9
9
|
method: 'PATCH',
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
|
+
/*
|
|
12
|
+
* PATCH, so these are NOT required: a partial update sends the fields it
|
|
13
|
+
* means to change. The messages used to say "X is required", which fires on
|
|
14
|
+
* a type failure and never on absence - a message describing a rule the
|
|
15
|
+
* block does not have.
|
|
16
|
+
*/
|
|
11
17
|
await request.validate({
|
|
12
18
|
views: {
|
|
13
19
|
rule: schema.number(),
|
|
14
20
|
message: {
|
|
15
|
-
views: 'Views
|
|
21
|
+
views: 'Views must be a number.',
|
|
16
22
|
},
|
|
17
23
|
},
|
|
18
24
|
})
|
|
@@ -10,13 +10,13 @@ export default new Action({
|
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
11
|
await request.validate({
|
|
12
12
|
name: {
|
|
13
|
-
rule: schema.string(),
|
|
13
|
+
rule: schema.string().required(),
|
|
14
14
|
message: {
|
|
15
15
|
name: 'Name is required',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
description: {
|
|
19
|
-
rule: schema.string(),
|
|
19
|
+
rule: schema.string().required(),
|
|
20
20
|
message: {
|
|
21
21
|
description: 'Description is required',
|
|
22
22
|
},
|
|
@@ -8,17 +8,23 @@ export default new Action({
|
|
|
8
8
|
description: 'Tag Update ORM Action',
|
|
9
9
|
method: 'PATCH',
|
|
10
10
|
async handle(request: RequestInstance) {
|
|
11
|
+
/*
|
|
12
|
+
* PATCH, so these are NOT required: a partial update sends the fields it
|
|
13
|
+
* means to change. The messages used to say "X is required", which fires on
|
|
14
|
+
* a type failure and never on absence - a message describing a rule the
|
|
15
|
+
* block does not have.
|
|
16
|
+
*/
|
|
11
17
|
await request.validate({
|
|
12
18
|
name: {
|
|
13
19
|
rule: schema.string(),
|
|
14
20
|
message: {
|
|
15
|
-
name: 'Name
|
|
21
|
+
name: 'Name must be a string.',
|
|
16
22
|
},
|
|
17
23
|
},
|
|
18
24
|
description: {
|
|
19
25
|
rule: schema.string(),
|
|
20
26
|
message: {
|
|
21
|
-
description: 'Description
|
|
27
|
+
description: 'Description must be a string.',
|
|
22
28
|
},
|
|
23
29
|
},
|
|
24
30
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
3
|
import { modelBoolean } from './kanban-model'
|
|
4
|
-
import { kanbanActionError
|
|
4
|
+
import { kanbanActionError } from './kanban-response'
|
|
5
5
|
|
|
6
6
|
interface BoardRow {
|
|
7
7
|
id: number
|
package/app/Actions/LogAction.ts
CHANGED
|
@@ -15,7 +15,7 @@ export default new Action({
|
|
|
15
15
|
// the request object is optional, but if it is provided, it will be used for validation
|
|
16
16
|
validations: {
|
|
17
17
|
message: {
|
|
18
|
-
rule: schema.string().min(3).max(255),
|
|
18
|
+
rule: schema.string().min(3).max(255).required(),
|
|
19
19
|
message: 'The message must be between 3 and 255 characters long.',
|
|
20
20
|
},
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ export default new Action({
|
|
|
25
25
|
// is not a function" at module evaluation. Use `schema.enum([...])`
|
|
26
26
|
// — that's the working enum primitive used throughout the framework
|
|
27
27
|
// defaults and in the typical project's models.
|
|
28
|
-
rule: schema.enum(['info', 'warn', 'error']),
|
|
28
|
+
rule: schema.enum(['info', 'warn', 'error']).required(),
|
|
29
29
|
message: 'The log level must be one of "info", "warn", or "error".',
|
|
30
30
|
},
|
|
31
31
|
},
|
package/app/Middleware/Auth.ts
CHANGED
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.72.
|
|
5
|
+
"version": "0.72.61",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@iconify-json/f7": "^1.2.2",
|
|
53
53
|
"@iconify-json/hugeicons": "^1.2.27",
|
|
54
|
-
"@stacksjs/mobile": "^0.72.
|
|
54
|
+
"@stacksjs/mobile": "^0.72.61",
|
|
55
55
|
"@stacksjs/sanitizer": "^0.2.113"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
package/routes/dashboard.ts
CHANGED