@stacksjs/defaults 0.75.467 → 0.75.468
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-actions/SKILL.md +1 -1
- package/ai/skills/stacks-mobile/SKILL.md +4 -2
- package/app/Actions/Dashboard/Content/FileDestroyAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileDirectoryStoreAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileDuplicateAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileFavoriteAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileIndexAction.ts +3 -5
- package/app/Actions/Dashboard/Content/FileRenameAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileReprocessAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileTagsAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileUploadAction.ts +3 -4
- package/app/Actions/Dashboard/Content/FileVisibilityAction.ts +3 -4
- package/app/Actions/Dashboard/Content/file-response.test.ts +61 -0
- package/app/Actions/Dashboard/Content/file-response.ts +18 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-actions
|
|
3
|
-
description: Use when working with Stacks server actions - creating actions in app/Actions/, auto-generated API actions from the useApi model trait, the
|
|
3
|
+
description: Use when working with Stacks server actions - creating actions in app/Actions/, auto-generated API actions from the useApi model trait, the 676 default framework actions (auth, dashboard, commerce, content, deployment, jobs), action request/response handling, or action registration. Covers @stacksjs/actions and storage/framework/defaults/app/Actions/.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -177,8 +177,10 @@ browser with an explicit return path.
|
|
|
177
177
|
|
|
178
178
|
A named route parameter may need to exclude website paths. Configure
|
|
179
179
|
`nativeScreenConstraints` on both platforms using the exact route key, for
|
|
180
|
-
example `{ '/:username': { username: { pattern: '[a-z]+', exclude: ['terms'],
|
|
181
|
-
|
|
180
|
+
example `{ '/:username': { username: { pattern: '[a-z]+', exclude: ['terms'], normalize: 'lowercase' } } }`.
|
|
181
|
+
Normalization lowercases decoded parameters and excluded values independently
|
|
182
|
+
of device locale; the match returns the normalized parameter. Patterns then
|
|
183
|
+
match the complete value case-sensitively; use syntax shared by JavaScript,
|
|
182
184
|
Foundation and Kotlin. Constraints apply before query items and preserve static
|
|
183
185
|
route priority. The build requires Craft's `nativeRouteConstraints` capability
|
|
184
186
|
and stops if an installed host would ignore the configuration. For application
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { deleteDashboardFile } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileDestroyAction',
|
|
@@ -16,9 +17,7 @@ export default new Action({
|
|
|
16
17
|
return response.json(deleted)
|
|
17
18
|
}
|
|
18
19
|
catch (error) {
|
|
19
|
-
|
|
20
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
21
|
-
throw error
|
|
20
|
+
return dashboardFileFailure(error, 'The file could not be deleted.', 'FileDestroyAction')
|
|
22
21
|
}
|
|
23
22
|
},
|
|
24
23
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import { createDashboardDirectory
|
|
4
|
+
import { createDashboardDirectory } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileDirectoryStoreAction',
|
|
@@ -17,9 +18,7 @@ export default new Action({
|
|
|
17
18
|
return response.json(directory, 201)
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
|
|
21
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
22
|
-
throw error
|
|
21
|
+
return dashboardFileFailure(error, 'The folder could not be created.', 'FileDirectoryStoreAction')
|
|
23
22
|
}
|
|
24
23
|
},
|
|
25
24
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { duplicateDashboardFile } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileDuplicateAction',
|
|
@@ -17,9 +18,7 @@ export default new Action({
|
|
|
17
18
|
return response.json(duplicated)
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
|
|
21
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
22
|
-
throw error
|
|
21
|
+
return dashboardFileFailure(error, 'The file could not be duplicated.', 'FileDuplicateAction')
|
|
23
22
|
}
|
|
24
23
|
},
|
|
25
24
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { setDashboardFileFavorite } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileFavoriteAction',
|
|
@@ -17,9 +18,7 @@ export default new Action({
|
|
|
17
18
|
return response.json(result)
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
|
|
21
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
22
|
-
throw error
|
|
21
|
+
return dashboardFileFailure(error, 'The favorite could not be saved.', 'FileFavoriteAction')
|
|
23
22
|
}
|
|
24
23
|
},
|
|
25
24
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { getDashboardFileSnapshot, normalizeDashboardFileLimit } from './file-manager'
|
|
4
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
5
|
|
|
6
6
|
export default new Action({
|
|
7
7
|
name: 'FileIndexAction',
|
|
@@ -16,9 +16,7 @@ export default new Action({
|
|
|
16
16
|
})
|
|
17
17
|
}
|
|
18
18
|
catch (error) {
|
|
19
|
-
|
|
20
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
21
|
-
throw error
|
|
19
|
+
return dashboardFileFailure(error, 'Files could not be loaded.', 'FileIndexAction')
|
|
22
20
|
}
|
|
23
21
|
},
|
|
24
22
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { renameDashboardFile } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileRenameAction',
|
|
@@ -17,9 +18,7 @@ export default new Action({
|
|
|
17
18
|
return response.json(renamed)
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
|
|
21
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
22
|
-
throw error
|
|
21
|
+
return dashboardFileFailure(error, 'The file could not be renamed.', 'FileRenameAction')
|
|
23
22
|
}
|
|
24
23
|
},
|
|
25
24
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { reprocessDashboardFile } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileReprocessAction',
|
|
@@ -23,9 +24,7 @@ export default new Action({
|
|
|
23
24
|
return response.json(result)
|
|
24
25
|
}
|
|
25
26
|
catch (error) {
|
|
26
|
-
|
|
27
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
28
|
-
throw error
|
|
27
|
+
return dashboardFileFailure(error, 'The file could not be reprocessed.', 'FileReprocessAction')
|
|
29
28
|
}
|
|
30
29
|
},
|
|
31
30
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { setDashboardFileTags } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileTagsAction',
|
|
@@ -19,9 +20,7 @@ export default new Action({
|
|
|
19
20
|
return response.json(result)
|
|
20
21
|
}
|
|
21
22
|
catch (error) {
|
|
22
|
-
|
|
23
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
24
|
-
throw error
|
|
23
|
+
return dashboardFileFailure(error, 'The tags could not be saved.', 'FileTagsAction')
|
|
25
24
|
}
|
|
26
25
|
},
|
|
27
26
|
})
|
|
@@ -2,7 +2,8 @@ import type { RequestInstance } from '@stacksjs/types'
|
|
|
2
2
|
import type { UploadedFileLike } from '@stacksjs/storage'
|
|
3
3
|
import { Action } from '@stacksjs/actions/runtime'
|
|
4
4
|
import { response } from '@stacksjs/router'
|
|
5
|
-
import {
|
|
5
|
+
import { uploadDashboardFiles } from './file-manager'
|
|
6
|
+
import { dashboardFileFailure } from './file-response'
|
|
6
7
|
|
|
7
8
|
const MAX_FILES = 20
|
|
8
9
|
const MAX_FILE_BYTES = 10 * 1024 * 1024
|
|
@@ -34,9 +35,7 @@ export default new Action({
|
|
|
34
35
|
return response.json({ uploaded }, 201)
|
|
35
36
|
}
|
|
36
37
|
catch (error) {
|
|
37
|
-
|
|
38
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
39
|
-
throw error
|
|
38
|
+
return dashboardFileFailure(error, 'The upload could not be completed.', 'FileUploadAction')
|
|
40
39
|
}
|
|
41
40
|
},
|
|
42
41
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
3
3
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
4
|
+
import { setDashboardFileVisibility } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'FileVisibilityAction',
|
|
@@ -17,9 +18,7 @@ export default new Action({
|
|
|
17
18
|
return response.json(updated)
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
|
|
21
|
-
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
22
|
-
throw error
|
|
21
|
+
return dashboardFileFailure(error, 'The visibility could not be changed.', 'FileVisibilityAction')
|
|
23
22
|
}
|
|
24
23
|
},
|
|
25
24
|
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
2
|
+
import { afterEach, describe, expect, spyOn, test } from 'bun:test'
|
|
3
|
+
import FileIndexAction from './FileIndexAction'
|
|
4
|
+
import { DashboardFileError } from './file-manager'
|
|
5
|
+
import { dashboardFileFailure } from './file-response'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* File actions answer a failure they did not write with a safe 503
|
|
9
|
+
* (stacksjs/stacks#3174). They rethrew it, and the router put the driver's own
|
|
10
|
+
* message - `relation "storage_items" does not exist` - in the response body
|
|
11
|
+
* in every environment, which the file manager then showed in its banner.
|
|
12
|
+
*/
|
|
13
|
+
const driverMessage = 'relation "storage_items" does not exist'
|
|
14
|
+
|
|
15
|
+
let quiet: ReturnType<typeof spyOn> | null = null
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
quiet?.mockRestore()
|
|
18
|
+
quiet = null
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('dashboardFileFailure', () => {
|
|
22
|
+
test('keeps the status, message and fields of an error written to be shown', async () => {
|
|
23
|
+
const reply = dashboardFileFailure(
|
|
24
|
+
new DashboardFileError('Choose a limit between 1 and 5000.', 422, { limit: 'Too high.' }),
|
|
25
|
+
'Files could not be loaded.',
|
|
26
|
+
'FileIndexAction',
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
expect(reply.status).toBe(422)
|
|
30
|
+
expect(await reply.json()).toEqual({ message: 'Choose a limit between 1 and 5000.', fields: { limit: 'Too high.' } })
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('answers any other failure 503 with the safe message and logs the real one', async () => {
|
|
34
|
+
quiet = spyOn(console, 'error').mockImplementation(() => {})
|
|
35
|
+
const failure = new Error(driverMessage)
|
|
36
|
+
|
|
37
|
+
const reply = dashboardFileFailure(failure, 'Files could not be loaded.', 'FileIndexAction')
|
|
38
|
+
|
|
39
|
+
expect(reply.status).toBe(503)
|
|
40
|
+
expect(await reply.json()).toEqual({ message: 'Files could not be loaded.' })
|
|
41
|
+
expect(quiet).toHaveBeenCalledWith('[dashboard/api] FileIndexAction failed:', failure)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('FileIndexAction', () => {
|
|
46
|
+
test('does not send a driver error to the browser', async () => {
|
|
47
|
+
quiet = spyOn(console, 'error').mockImplementation(() => {})
|
|
48
|
+
const request = {
|
|
49
|
+
get: () => {
|
|
50
|
+
throw new Error(driverMessage)
|
|
51
|
+
},
|
|
52
|
+
} as unknown as RequestInstance
|
|
53
|
+
|
|
54
|
+
const reply = await FileIndexAction.handle(request) as Response
|
|
55
|
+
|
|
56
|
+
expect(reply.status).toBe(503)
|
|
57
|
+
const body = await reply.text()
|
|
58
|
+
expect(body).not.toContain('storage_items')
|
|
59
|
+
expect(JSON.parse(body)).toEqual({ message: 'Files could not be loaded.' })
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { response } from '@stacksjs/router'
|
|
2
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
3
|
+
import { DashboardFileError } from './file-manager'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The response for a file action that threw.
|
|
7
|
+
*
|
|
8
|
+
* A `DashboardFileError` is written to be shown, so it keeps its status,
|
|
9
|
+
* message and fields. Anything else - the database behind `storage_items`
|
|
10
|
+
* being down or not migrated, a driver failing - is logged and answered 503
|
|
11
|
+
* with `message`, because rethrowing it let the router send the raw driver
|
|
12
|
+
* message to the browser in every environment (stacksjs/stacks#3174).
|
|
13
|
+
*/
|
|
14
|
+
export function dashboardFileFailure(error: unknown, message: string, action: string): Response {
|
|
15
|
+
if (error instanceof DashboardFileError)
|
|
16
|
+
return response.json({ message: error.message, fields: error.fields }, error.status)
|
|
17
|
+
return dashboardOperationalError(error, message, action)
|
|
18
|
+
}
|
package/ide/vscode/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"publisher": "Stacks",
|
|
3
3
|
"name": "vscode-stacks",
|
|
4
4
|
"displayName": "Stacks",
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.468",
|
|
6
6
|
"description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.468",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@iconify-json/f7": "^1.2.2",
|
|
47
47
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
48
|
-
"@stacksjs/mobile": "0.75.
|
|
48
|
+
"@stacksjs/mobile": "0.75.468",
|
|
49
49
|
"@stacksjs/sanitizer": "0.2.798",
|
|
50
50
|
"@stacksjs/stx": "0.2.798",
|
|
51
51
|
"@xterm/addon-fit": "^0.11.0",
|