@stacksjs/defaults 0.75.465 → 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 +12 -0
- 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
|
|
@@ -175,6 +175,18 @@ component rather than duplicate it. Compile and test the actual screen bundle
|
|
|
175
175
|
for both targets. External authorization or checkout routes may use the system
|
|
176
176
|
browser with an explicit return path.
|
|
177
177
|
|
|
178
|
+
A named route parameter may need to exclude website paths. Configure
|
|
179
|
+
`nativeScreenConstraints` on both platforms using the exact route key, for
|
|
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,
|
|
184
|
+
Foundation and Kotlin. Constraints apply before query items and preserve static
|
|
185
|
+
route priority. The build requires Craft's `nativeRouteConstraints` capability
|
|
186
|
+
and stops if an installed host would ignore the configuration. For application
|
|
187
|
+
resolution, use the published browser-safe `craft-native/mobile`
|
|
188
|
+
`matchNativePath(screens, path, constraints)` rather than copying the matcher.
|
|
189
|
+
|
|
178
190
|
For the web renderer, choose a primary content source:
|
|
179
191
|
|
|
180
192
|
- `url`: load the deployed Stacks application and keep server-rendered routes.
|
|
@@ -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",
|