@stacksjs/defaults 0.75.255 → 0.75.257
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-buddy/SKILL.md +1 -0
- package/ai/skills/stacks-env/SKILL.md +24 -1
- package/ai/skills/stacks-native/CATALOG.md +25 -25
- package/functions/api.ts +54 -5
- package/ide/vscode/package.json +1 -1
- package/package.json +5 -4
- package/resources/components/Dashboard/App/SourceInventory.stx +39 -13
- package/resources/components/Dashboard/Content/BlogPostEditorDialog.stx +9 -5
- package/resources/components/Dashboard/Content/PostEditorDialog.stx +8 -4
- package/resources/functions/dashboard/data.ts +92 -105
- package/resources/functions/dashboard/sidebar.ts +31 -10
- package/resources/plugins/preloader.ts +4 -4
- package/scaffold/README.md +36 -0
- package/scaffold/config/analytics.ts +16 -0
- package/scaffold/config/app.ts +49 -0
- package/scaffold/config/blog.ts +79 -0
- package/scaffold/config/buddy-bot.ts +50 -0
- package/scaffold/config/cloud.ts +103 -0
- package/scaffold/config/dns.ts +16 -0
- package/scaffold/config/docs.ts +57 -0
- package/scaffold/config/email.ts +27 -0
- package/scaffold/config/library.ts +64 -0
- package/scaffold/config/lint.ts +42 -0
- package/scaffold/config/mobile.ts +27 -0
- package/scaffold/config/team.ts +6 -0
- package/scaffold/resources/views/index.stx +65 -0
- package/views/dashboard/.discovered-models.json +0 -893
|
@@ -28,7 +28,11 @@ interface PostDraft {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const open = useReactiveProp('open', false)
|
|
31
|
-
|
|
31
|
+
// Named `editing`, not `post`, while still reading the `post` prop.
|
|
32
|
+
// A bare `post` in a `{{ … }}` interpolation is resolved server-side
|
|
33
|
+
// against module and global scope, where it finds the dashboard API
|
|
34
|
+
// client's own `post()` and calls it mid-render (stacksjs/stacks#2944).
|
|
35
|
+
const editing = useReactiveProp('post', null as PostRecord | null)
|
|
32
36
|
const categories = useReactiveProp('categories', [] as OptionRecord[])
|
|
33
37
|
const tags = useReactiveProp('tags', [] as OptionRecord[])
|
|
34
38
|
const authors = useReactiveProp('authors', [] as AuthorRecord[])
|
|
@@ -49,7 +53,7 @@ let wasOpen = false
|
|
|
49
53
|
effect(() => {
|
|
50
54
|
const isOpen = open()
|
|
51
55
|
if (isOpen && !wasOpen) {
|
|
52
|
-
const record =
|
|
56
|
+
const record = editing()
|
|
53
57
|
title.set(record?.title || '')
|
|
54
58
|
excerpt.set(record?.excerpt || '')
|
|
55
59
|
content.set(record?.content || '')
|
|
@@ -93,7 +97,7 @@ function submit(): void {
|
|
|
93
97
|
|
|
94
98
|
<Modal
|
|
95
99
|
:isOpen="open()"
|
|
96
|
-
:title="
|
|
100
|
+
:title="editing() ? 'Edit post' : 'New post'"
|
|
97
101
|
description="Content, ownership, and taxonomies are persisted together."
|
|
98
102
|
size="xl"
|
|
99
103
|
:closable="!busy()"
|
|
@@ -169,7 +173,7 @@ function submit(): void {
|
|
|
169
173
|
<div class="flex gap-2 justify-end w-full">
|
|
170
174
|
<Button :disabled="busy()" variant="secondary" @click="close">Cancel</Button>
|
|
171
175
|
<Button type="submit" form="post-editor-form" :disabled="!valid()" :loading="busy()">
|
|
172
|
-
{{
|
|
176
|
+
{{ editing() ? 'Save changes' : 'Create post' }}
|
|
173
177
|
</Button>
|
|
174
178
|
</div>
|
|
175
179
|
</template>
|
|
@@ -17,123 +17,110 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { existsSync } from 'node:fs'
|
|
20
|
-
import { basename,
|
|
21
|
-
|
|
22
|
-
let projectRootCache: string | null = null
|
|
23
|
-
|
|
24
|
-
const VENDORED_DEFAULTS_PREFIX = 'storage/framework/defaults/'
|
|
25
|
-
let defaultsRootCache: string | null = null
|
|
20
|
+
import { basename, resolve } from 'node:path'
|
|
21
|
+
import { defaultsPath, projectPath } from '@stacksjs/path'
|
|
26
22
|
|
|
27
23
|
/**
|
|
28
|
-
* The
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
24
|
+
* The project root, and where the defaults tree is, both from
|
|
25
|
+
* `@stacksjs/path`.
|
|
26
|
+
*
|
|
27
|
+
* This file used to hand-roll the walk, explicitly to avoid importing the
|
|
28
|
+
* path package, which was recorded as having "its own evaluation graph" that
|
|
29
|
+
* "would slow page renders". Measured rather than inherited: importing
|
|
30
|
+
* `@stacksjs/path` in this checkout costs 1.5-2.7ms, once per process because
|
|
31
|
+
* the module cache holds it, not once per render. `preloader.ts` next door
|
|
32
|
+
* already imports it.
|
|
33
|
+
*
|
|
34
|
+
* The walk also had to go, independently of cost. It anchored on a directory
|
|
35
|
+
* containing `storage/framework/defaults`, which is the one directory
|
|
36
|
+
* stacksjs/stacks#2941 is removing: in a project resolving its defaults from
|
|
37
|
+
* `node_modules`, the walk would find no anchor and fall through to
|
|
38
|
+
* `process.cwd()`, which is right only by luck.
|
|
32
39
|
*/
|
|
33
|
-
function defaultsRoot(): string {
|
|
34
|
-
if (defaultsRootCache) return defaultsRootCache
|
|
35
|
-
const own = resolve(dirname(new URL(import.meta.url).pathname), '../../..')
|
|
36
|
-
const candidates = [
|
|
37
|
-
own,
|
|
38
|
-
resolve(projectRoot(), 'storage/framework/defaults'),
|
|
39
|
-
resolve(projectRoot(), 'node_modules/@stacksjs/defaults'),
|
|
40
|
-
]
|
|
41
|
-
defaultsRootCache = candidates.find(candidate => existsSync(resolve(candidate, 'app/Models'))) ?? candidates[1]!
|
|
42
|
-
return defaultsRootCache
|
|
43
|
-
}
|
|
44
|
-
|
|
45
40
|
function projectRoot(): string {
|
|
46
|
-
|
|
47
|
-
// Walk up from this file location to find the project root (the dir
|
|
48
|
-
// that contains `storage/framework`). This mirrors what
|
|
49
|
-
// `@stacksjs/path` does, but without pulling in the path package
|
|
50
|
-
// (which has its own evaluation graph and would slow page renders).
|
|
51
|
-
let dir = dirname(new URL(import.meta.url).pathname)
|
|
52
|
-
for (let i = 0; i < 12; i++) {
|
|
53
|
-
if (existsSync(resolve(dir, 'storage/framework'))) {
|
|
54
|
-
projectRootCache = dir
|
|
55
|
-
return dir
|
|
56
|
-
}
|
|
57
|
-
const parent = dirname(dir)
|
|
58
|
-
if (parent === dir) break
|
|
59
|
-
dir = parent
|
|
60
|
-
}
|
|
61
|
-
// Fall back to cwd — STX serves run from the project root.
|
|
62
|
-
projectRootCache = process.cwd()
|
|
63
|
-
return projectRootCache
|
|
41
|
+
return projectPath()
|
|
64
42
|
}
|
|
65
43
|
|
|
66
44
|
/**
|
|
67
|
-
* Fast-path map of common model names to
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
45
|
+
* Fast-path map of common model names to the two places they can live:
|
|
46
|
+
* `[path under the project, path under the defaults tree]`.
|
|
47
|
+
*
|
|
48
|
+
* The first existing file wins, and user overrides always take priority over
|
|
49
|
+
* framework defaults. A cached filesystem index below covers newly-added and
|
|
50
|
+
* nested models, so this map is an optimization rather than a correctness
|
|
51
|
+
* requirement.
|
|
52
|
+
*
|
|
53
|
+
* The second element is relative to the defaults tree rather than to the
|
|
54
|
+
* project, because that tree is not always inside the project: it is
|
|
55
|
+
* `storage/framework/defaults` when the project vendors a current copy and the
|
|
56
|
+
* installed `@stacksjs/defaults` otherwise (stacksjs/stacks#2941). Fifty-five
|
|
57
|
+
* rows used to spell `storage/framework/defaults/` out by hand.
|
|
71
58
|
*/
|
|
72
|
-
const MODEL_PATHS: Record<string, string
|
|
59
|
+
const MODEL_PATHS: Record<string, [string, string]> = {
|
|
73
60
|
// Auth / core
|
|
74
|
-
User: ['app/Models/User.ts', '
|
|
75
|
-
Team: ['app/Models/Team.ts', '
|
|
76
|
-
Subscriber: ['app/Models/Subscriber.ts', '
|
|
77
|
-
SubscriberEmail: ['app/Models/SubscriberEmail.ts', '
|
|
78
|
-
Subscription: ['app/Models/Subscription.ts', '
|
|
61
|
+
User: ['app/Models/User.ts', 'app/Models/User.ts'],
|
|
62
|
+
Team: ['app/Models/Team.ts', 'app/Models/Team.ts'],
|
|
63
|
+
Subscriber: ['app/Models/Subscriber.ts', 'app/Models/Subscriber.ts'],
|
|
64
|
+
SubscriberEmail: ['app/Models/SubscriberEmail.ts', 'app/Models/SubscriberEmail.ts'],
|
|
65
|
+
Subscription: ['app/Models/Subscription.ts', 'app/Models/Subscription.ts'],
|
|
79
66
|
|
|
80
67
|
// Content
|
|
81
|
-
Post: ['app/Models/Post.ts', '
|
|
82
|
-
Page: ['app/Models/Page.ts', '
|
|
83
|
-
Author: ['app/Models/Author.ts', '
|
|
84
|
-
Comment: ['app/Models/Comment.ts', '
|
|
85
|
-
Tag: ['app/Models/Tag.ts', '
|
|
86
|
-
Category: ['app/Models/Category.ts', '
|
|
68
|
+
Post: ['app/Models/Post.ts', 'app/Models/Content/Post.ts'],
|
|
69
|
+
Page: ['app/Models/Page.ts', 'app/Models/Content/Page.ts'],
|
|
70
|
+
Author: ['app/Models/Author.ts', 'app/Models/Content/Author.ts'],
|
|
71
|
+
Comment: ['app/Models/Comment.ts', 'app/Models/Comment.ts'],
|
|
72
|
+
Tag: ['app/Models/Tag.ts', 'app/Models/Tag.ts'],
|
|
73
|
+
Category: ['app/Models/Category.ts', 'app/Models/commerce/Category.ts'],
|
|
87
74
|
|
|
88
75
|
// App / operations
|
|
89
|
-
Activity: ['app/Models/Activity.ts', '
|
|
90
|
-
Deployment: ['app/Models/Deployment.ts', '
|
|
91
|
-
Release: ['app/Models/Release.ts', '
|
|
92
|
-
Notification: ['app/Models/Notification.ts', '
|
|
93
|
-
Job: ['app/Models/Job.ts', '
|
|
94
|
-
FailedJob: ['app/Models/FailedJob.ts', '
|
|
95
|
-
Log: ['app/Models/Log.ts', '
|
|
96
|
-
Request: ['app/Models/Request.ts', '
|
|
97
|
-
Error: ['app/Models/Error.ts', '
|
|
98
|
-
Websocket: ['app/Models/Websocket.ts', '
|
|
76
|
+
Activity: ['app/Models/Activity.ts', 'app/Models/Activity.ts'],
|
|
77
|
+
Deployment: ['app/Models/Deployment.ts', 'app/Models/Deployment.ts'],
|
|
78
|
+
Release: ['app/Models/Release.ts', 'app/Models/Release.ts'],
|
|
79
|
+
Notification: ['app/Models/Notification.ts', 'app/Models/Notification.ts'],
|
|
80
|
+
Job: ['app/Models/Job.ts', 'app/Models/Job.ts'],
|
|
81
|
+
FailedJob: ['app/Models/FailedJob.ts', 'app/Models/FailedJob.ts'],
|
|
82
|
+
Log: ['app/Models/Log.ts', 'app/Models/Log.ts'],
|
|
83
|
+
Request: ['app/Models/Request.ts', 'app/Models/Request.ts'],
|
|
84
|
+
Error: ['app/Models/Error.ts', 'app/Models/Error.ts'],
|
|
85
|
+
Websocket: ['app/Models/Websocket.ts', 'app/Models/realtime/Websocket.ts'],
|
|
99
86
|
|
|
100
87
|
// Marketing
|
|
101
|
-
Campaign: ['app/Models/Campaign.ts', '
|
|
102
|
-
EmailList: ['app/Models/EmailList.ts', '
|
|
103
|
-
SocialPost: ['app/Models/SocialPost.ts', '
|
|
88
|
+
Campaign: ['app/Models/Campaign.ts', 'app/Models/Campaign.ts'],
|
|
89
|
+
EmailList: ['app/Models/EmailList.ts', 'app/Models/EmailList.ts'],
|
|
90
|
+
SocialPost: ['app/Models/SocialPost.ts', 'app/Models/SocialPost.ts'],
|
|
104
91
|
|
|
105
92
|
// Commerce
|
|
106
|
-
Order: ['app/Models/Order.ts', '
|
|
107
|
-
OrderItem: ['app/Models/OrderItem.ts', '
|
|
108
|
-
Cart: ['app/Models/Cart.ts', '
|
|
109
|
-
CartItem: ['app/Models/CartItem.ts', '
|
|
110
|
-
Customer: ['app/Models/Customer.ts', '
|
|
111
|
-
Product: ['app/Models/Product.ts', '
|
|
112
|
-
ProductVariant: ['app/Models/ProductVariant.ts', '
|
|
113
|
-
ProductUnit: ['app/Models/ProductUnit.ts', '
|
|
114
|
-
Manufacturer: ['app/Models/Manufacturer.ts', '
|
|
115
|
-
Coupon: ['app/Models/Coupon.ts', '
|
|
116
|
-
GiftCard: ['app/Models/GiftCard.ts', '
|
|
117
|
-
LicenseKey: ['app/Models/LicenseKey.ts', '
|
|
118
|
-
Review: ['app/Models/Review.ts', '
|
|
119
|
-
Receipt: ['app/Models/Receipt.ts', '
|
|
120
|
-
PrintDevice: ['app/Models/PrintDevice.ts', '
|
|
121
|
-
ShippingMethod: ['app/Models/ShippingMethod.ts', '
|
|
122
|
-
ShippingRate: ['app/Models/ShippingRate.ts', '
|
|
123
|
-
ShippingZone: ['app/Models/ShippingZone.ts', '
|
|
124
|
-
DeliveryRoute: ['app/Models/DeliveryRoute.ts', '
|
|
125
|
-
DigitalDelivery: ['app/Models/DigitalDelivery.ts', '
|
|
126
|
-
Courier: ['app/Models/Courier.ts', '
|
|
127
|
-
TaxRate: ['app/Models/TaxRate.ts', '
|
|
128
|
-
WaitlistProduct: ['app/Models/WaitlistProduct.ts', '
|
|
129
|
-
WaitlistRestaurant: ['app/Models/WaitlistRestaurant.ts', '
|
|
130
|
-
LoyaltyPoint: ['app/Models/LoyaltyPoint.ts', '
|
|
131
|
-
LoyaltyReward: ['app/Models/LoyaltyReward.ts', '
|
|
132
|
-
Payment: ['app/Models/Payment.ts', '
|
|
133
|
-
PaymentMethod: ['app/Models/PaymentMethod.ts', '
|
|
134
|
-
PaymentProduct: ['app/Models/PaymentProduct.ts', '
|
|
135
|
-
PaymentTransaction: ['app/Models/PaymentTransaction.ts', '
|
|
136
|
-
Transaction: ['app/Models/Transaction.ts', '
|
|
93
|
+
Order: ['app/Models/Order.ts', 'app/Models/commerce/Order.ts'],
|
|
94
|
+
OrderItem: ['app/Models/OrderItem.ts', 'app/Models/commerce/OrderItem.ts'],
|
|
95
|
+
Cart: ['app/Models/Cart.ts', 'app/Models/commerce/Cart.ts'],
|
|
96
|
+
CartItem: ['app/Models/CartItem.ts', 'app/Models/commerce/CartItem.ts'],
|
|
97
|
+
Customer: ['app/Models/Customer.ts', 'app/Models/commerce/Customer.ts'],
|
|
98
|
+
Product: ['app/Models/Product.ts', 'app/Models/commerce/Product.ts'],
|
|
99
|
+
ProductVariant: ['app/Models/ProductVariant.ts', 'app/Models/commerce/ProductVariant.ts'],
|
|
100
|
+
ProductUnit: ['app/Models/ProductUnit.ts', 'app/Models/commerce/ProductUnit.ts'],
|
|
101
|
+
Manufacturer: ['app/Models/Manufacturer.ts', 'app/Models/commerce/Manufacturer.ts'],
|
|
102
|
+
Coupon: ['app/Models/Coupon.ts', 'app/Models/commerce/Coupon.ts'],
|
|
103
|
+
GiftCard: ['app/Models/GiftCard.ts', 'app/Models/commerce/GiftCard.ts'],
|
|
104
|
+
LicenseKey: ['app/Models/LicenseKey.ts', 'app/Models/commerce/LicenseKey.ts'],
|
|
105
|
+
Review: ['app/Models/Review.ts', 'app/Models/commerce/Review.ts'],
|
|
106
|
+
Receipt: ['app/Models/Receipt.ts', 'app/Models/commerce/Receipt.ts'],
|
|
107
|
+
PrintDevice: ['app/Models/PrintDevice.ts', 'app/Models/commerce/PrintDevice.ts'],
|
|
108
|
+
ShippingMethod: ['app/Models/ShippingMethod.ts', 'app/Models/commerce/ShippingMethod.ts'],
|
|
109
|
+
ShippingRate: ['app/Models/ShippingRate.ts', 'app/Models/commerce/ShippingRate.ts'],
|
|
110
|
+
ShippingZone: ['app/Models/ShippingZone.ts', 'app/Models/commerce/ShippingZone.ts'],
|
|
111
|
+
DeliveryRoute: ['app/Models/DeliveryRoute.ts', 'app/Models/commerce/DeliveryRoute.ts'],
|
|
112
|
+
DigitalDelivery: ['app/Models/DigitalDelivery.ts', 'app/Models/commerce/DigitalDelivery.ts'],
|
|
113
|
+
Courier: ['app/Models/Courier.ts', 'app/Models/commerce/Courier.ts'],
|
|
114
|
+
TaxRate: ['app/Models/TaxRate.ts', 'app/Models/commerce/TaxRate.ts'],
|
|
115
|
+
WaitlistProduct: ['app/Models/WaitlistProduct.ts', 'app/Models/commerce/WaitlistProduct.ts'],
|
|
116
|
+
WaitlistRestaurant: ['app/Models/WaitlistRestaurant.ts', 'app/Models/commerce/WaitlistRestaurant.ts'],
|
|
117
|
+
LoyaltyPoint: ['app/Models/LoyaltyPoint.ts', 'app/Models/commerce/LoyaltyPoint.ts'],
|
|
118
|
+
LoyaltyReward: ['app/Models/LoyaltyReward.ts', 'app/Models/commerce/LoyaltyReward.ts'],
|
|
119
|
+
Payment: ['app/Models/Payment.ts', 'app/Models/commerce/Payment.ts'],
|
|
120
|
+
PaymentMethod: ['app/Models/PaymentMethod.ts', 'app/Models/PaymentMethod.ts'],
|
|
121
|
+
PaymentProduct: ['app/Models/PaymentProduct.ts', 'app/Models/PaymentProduct.ts'],
|
|
122
|
+
PaymentTransaction: ['app/Models/PaymentTransaction.ts', 'app/Models/PaymentTransaction.ts'],
|
|
123
|
+
Transaction: ['app/Models/Transaction.ts', 'app/Models/commerce/Transaction.ts'],
|
|
137
124
|
}
|
|
138
125
|
|
|
139
126
|
const modelCache = new Map<string, any>()
|
|
@@ -153,7 +140,7 @@ async function discoverModelPaths(): Promise<Map<string, string[]>> {
|
|
|
153
140
|
const paths = new Map<string, string[]>()
|
|
154
141
|
const modelRoots = [
|
|
155
142
|
resolve(root, 'app/Models'),
|
|
156
|
-
|
|
143
|
+
defaultsPath('app/Models'),
|
|
157
144
|
]
|
|
158
145
|
|
|
159
146
|
for (const modelsRoot of modelRoots) {
|
|
@@ -178,11 +165,11 @@ async function discoverModelPaths(): Promise<Map<string, string[]>> {
|
|
|
178
165
|
}
|
|
179
166
|
|
|
180
167
|
async function modelCandidatePaths(name: string): Promise<string[]> {
|
|
181
|
-
const root = projectRoot()
|
|
182
168
|
const discovered = (await discoverModelPaths()).get(name) ?? []
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
169
|
+
const entry = MODEL_PATHS[name]
|
|
170
|
+
const explicit = entry
|
|
171
|
+
? [resolve(projectRoot(), entry[0]), defaultsPath(entry[1])]
|
|
172
|
+
: []
|
|
186
173
|
return [...new Set([...discovered, ...explicit])]
|
|
187
174
|
}
|
|
188
175
|
|
|
@@ -111,15 +111,41 @@ interface NavItem {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
114
|
+
* Where `buddy dev --dashboard` writes the discovered-models manifest, and
|
|
115
|
+
* where this reads it: under `storage/framework/runtime/`, which `.gitignore`
|
|
116
|
+
* covers.
|
|
117
|
+
*/
|
|
118
|
+
export const DISCOVERED_MANIFEST_PATH = 'storage/framework/runtime/dashboard/.discovered-models.json'
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Where it used to be written, inside the vendored framework tree.
|
|
122
|
+
*
|
|
123
|
+
* Read as a fallback rather than dropped, because an app that upgrades does
|
|
124
|
+
* not get a new manifest until its next `buddy dev --dashboard` - and without
|
|
125
|
+
* this it would render an empty sidebar until then. New writes only ever go
|
|
126
|
+
* to the path above.
|
|
127
|
+
*/
|
|
128
|
+
export const LEGACY_DISCOVERED_MANIFEST_PATH = 'storage/framework/defaults/views/dashboard/.discovered-models.json'
|
|
129
|
+
|
|
130
|
+
/** The first of the two manifest paths that exists, or null when neither does. */
|
|
131
|
+
function discoveredManifestPath(cwd: string): string | null {
|
|
132
|
+
for (const rel of [DISCOVERED_MANIFEST_PATH, LEGACY_DISCOVERED_MANIFEST_PATH]) {
|
|
133
|
+
const candidate = resolve(cwd, rel)
|
|
134
|
+
if (existsSync(candidate))
|
|
135
|
+
return candidate
|
|
136
|
+
}
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Pull the discovered-models manifest written by the dashboard dev server.
|
|
142
|
+
* Returns the default empty payload when neither path has one (fresh project
|
|
117
143
|
* before the first dashboard run regenerates it).
|
|
118
144
|
*/
|
|
119
145
|
export function loadDiscoveredManifest(
|
|
120
|
-
manifestPath =
|
|
146
|
+
manifestPath = discoveredManifestPath(process.cwd()),
|
|
121
147
|
): DiscoveredManifest {
|
|
122
|
-
if (!existsSync(manifestPath))
|
|
148
|
+
if (manifestPath === null || !existsSync(manifestPath))
|
|
123
149
|
return { models: [], sections: defaultToggles(), nav: [] }
|
|
124
150
|
|
|
125
151
|
try {
|
|
@@ -682,10 +708,5 @@ export function buildWebSidebarSections(): WebSidebarSection[] {
|
|
|
682
708
|
...(item.roles && item.roles.length > 0 ? { roles: item.roles } : {}),
|
|
683
709
|
})),
|
|
684
710
|
})),
|
|
685
|
-
{
|
|
686
|
-
id: 'system',
|
|
687
|
-
label: '',
|
|
688
|
-
items: [{ id: 'settings', label: 'Settings', icon: NAV_ICON_CLASSES.settings, iconColor: 'blue', href: '/settings' }],
|
|
689
|
-
},
|
|
690
711
|
]
|
|
691
712
|
}
|
|
@@ -171,8 +171,6 @@ if (!isRepl && !isPostinstall) {
|
|
|
171
171
|
|
|
172
172
|
if (typeof applyRuntimeDirectoryEnv === 'function')
|
|
173
173
|
applyRuntimeDirectoryEnv()
|
|
174
|
-
else
|
|
175
|
-
console.warn('[stacks] installed @stacksjs/path has no applyRuntimeDirectoryEnv; stx and ts-cloud will use their default state directories. Run `buddy upgrade` to refresh the framework packages.')
|
|
176
174
|
|
|
177
175
|
// An app that does not vendor the framework defaults gets a gitignored
|
|
178
176
|
// `storage/framework/defaults` link to the installed package. A fresh clone
|
|
@@ -181,6 +179,8 @@ if (!isRepl && !isPostinstall) {
|
|
|
181
179
|
// published-version reason as the helper above.
|
|
182
180
|
if (typeof linkPackagedDefaults === 'function')
|
|
183
181
|
linkPackagedDefaults()
|
|
182
|
+
else
|
|
183
|
+
console.warn('[stacks] installed @stacksjs/path has no applyRuntimeDirectoryEnv; stx and ts-cloud will use their default state directories. Run `buddy upgrade` to refresh the framework packages.')
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// stx template engine plugin
|
|
@@ -359,7 +359,7 @@ export async function loadAutoImports() {
|
|
|
359
359
|
// Priority: user models (app/Models) override framework defaults
|
|
360
360
|
const modelDirs = [
|
|
361
361
|
path.userModelsPath(),
|
|
362
|
-
path.
|
|
362
|
+
path.defaultsPath('app/Models'),
|
|
363
363
|
]
|
|
364
364
|
|
|
365
365
|
const loadedModels = new Set<string>()
|
|
@@ -430,7 +430,7 @@ export async function loadAutoImports() {
|
|
|
430
430
|
// Priority: user controllers > default controllers
|
|
431
431
|
const controllerDirs = [
|
|
432
432
|
path.userControllersPath(),
|
|
433
|
-
path.
|
|
433
|
+
path.defaultsPath('app/Controllers'),
|
|
434
434
|
]
|
|
435
435
|
const loadedControllers = new Set<string>()
|
|
436
436
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# __APP_NAME__
|
|
2
|
+
|
|
3
|
+
A [Stacks](https://stacksjs.com) application.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
./buddy dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`./buddy doctor` checks your setup if anything does not start.
|
|
12
|
+
|
|
13
|
+
## Everyday commands
|
|
14
|
+
|
|
15
|
+
| Command | What it does |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `./buddy dev` | Start the development servers |
|
|
18
|
+
| `./buddy migrate` | Apply pending database migrations |
|
|
19
|
+
| `./buddy test` | Run the test suite |
|
|
20
|
+
| `./buddy typecheck` | Type check `app/`, `config/`, `resources/` and `routes/` |
|
|
21
|
+
| `./buddy lint` | Lint, and `./buddy lint:fix` to fix what can be fixed |
|
|
22
|
+
| `./buddy upgrade` | Move to the latest Stacks release |
|
|
23
|
+
| `./buddy list` | Every other command |
|
|
24
|
+
|
|
25
|
+
## Where things live
|
|
26
|
+
|
|
27
|
+
- `app/`, `resources/`, `routes/`, `database/`: your application.
|
|
28
|
+
- `config/`: every setting, one typed file per subsystem. System dependencies
|
|
29
|
+
are declared in `config/deps.ts`, and `deps.yaml` is generated from it.
|
|
30
|
+
- `storage/framework/defaults`: the framework's default models, actions, views
|
|
31
|
+
and routes. It is a gitignored link to `node_modules/@stacksjs/defaults`, so
|
|
32
|
+
it upgrades with the `stacks` dependency. Override any default by creating the
|
|
33
|
+
same path under `app/` or `resources/`, or run `./buddy publish:defaults` to
|
|
34
|
+
commit a copy of the whole tree instead.
|
|
35
|
+
|
|
36
|
+
Read more at [stacksjs.com/docs](https://stacksjs.com/docs).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AnalyticsConfig } from '@stacksjs/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AnalyticsHQ is Stacks' first-party analytics driver. Paste the site id from
|
|
5
|
+
* your AnalyticsHQ app to enable it. An empty site id emits no tracking tags.
|
|
6
|
+
*/
|
|
7
|
+
export default {
|
|
8
|
+
driver: 'analyticshq',
|
|
9
|
+
environments: ['production', 'staging'],
|
|
10
|
+
|
|
11
|
+
drivers: {
|
|
12
|
+
analyticshq: {
|
|
13
|
+
siteId: '',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
} satisfies AnalyticsConfig
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AppConfig } from '@stacksjs/types'
|
|
2
|
+
import { env } from '@stacksjs/env'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* **Application Configuration**
|
|
6
|
+
*
|
|
7
|
+
* This configuration defines all of your application options. Because Stacks is fully-typed,
|
|
8
|
+
* you may hover any of the options below and the definitions will be provided. In case
|
|
9
|
+
* you have any questions, feel free to reach out via Discord or GitHub Discussions.
|
|
10
|
+
*/
|
|
11
|
+
export default {
|
|
12
|
+
name: env.APP_NAME ?? '__APP_NAME__',
|
|
13
|
+
// Shown on the starter page, the coming-soon page, and any page that sets
|
|
14
|
+
// no description of its own.
|
|
15
|
+
description: '',
|
|
16
|
+
env: env.APP_ENV ?? 'local',
|
|
17
|
+
url: env.APP_URL ?? 'stacks.localhost',
|
|
18
|
+
redirectUrls: [],
|
|
19
|
+
debug: env.DEBUG ?? false,
|
|
20
|
+
key: env.APP_KEY,
|
|
21
|
+
|
|
22
|
+
maintenanceMode: env.APP_MAINTENANCE ?? false,
|
|
23
|
+
comingSoonMode: env.APP_COMING_SOON ?? false,
|
|
24
|
+
comingSoonSecret: env.APP_COMING_SOON_SECRET ?? '',
|
|
25
|
+
// docMode: true, // instead of example.com/docs, deploys example.com as main entry point for docs
|
|
26
|
+
docMode: false,
|
|
27
|
+
|
|
28
|
+
timezone: 'America/Los_Angeles',
|
|
29
|
+
locale: 'en',
|
|
30
|
+
fallbackLocale: 'en',
|
|
31
|
+
cipher: 'aes-256-cbc',
|
|
32
|
+
|
|
33
|
+
// /sitemap.xml and /robots.txt are generated from resources/views, in dev
|
|
34
|
+
// and production alike. A page that says `noindex` (a robots meta tag, or
|
|
35
|
+
// `const noindex = true` in its server script) is left out, as are dynamic
|
|
36
|
+
// [param] routes, error pages and anything robots.txt disallows. A file you
|
|
37
|
+
// put in public/ wins over the generated one.
|
|
38
|
+
seo: {
|
|
39
|
+
sitemap: {
|
|
40
|
+
// Concrete URLs for dynamic routes, e.g. '/products/blue-mug'.
|
|
41
|
+
include: [],
|
|
42
|
+
exclude: [],
|
|
43
|
+
},
|
|
44
|
+
robots: {
|
|
45
|
+
// Added to the default '/api/'.
|
|
46
|
+
disallow: [],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
} satisfies AppConfig
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { env } from '@stacksjs/env'
|
|
2
|
+
|
|
3
|
+
export interface BlogConfig {
|
|
4
|
+
subdomain: string
|
|
5
|
+
title: string
|
|
6
|
+
description: string
|
|
7
|
+
postsPerPage: number
|
|
8
|
+
/**
|
|
9
|
+
* NOT YET HONOURED by the static blog builder. Nothing reads this: the
|
|
10
|
+
* generated pages carry no comment section either way. The CMS has
|
|
11
|
+
* commenting (the `commentable` trait and `comments` in `@stacksjs/cms`),
|
|
12
|
+
* but the static build does not render it (stacksjs/stacks#365-adjacent;
|
|
13
|
+
* found auditing config keys nothing reads).
|
|
14
|
+
*/
|
|
15
|
+
enableComments: boolean
|
|
16
|
+
/** Honoured: generates `feed.xml` and the RSS link in the layout. */
|
|
17
|
+
enableRss: boolean
|
|
18
|
+
/** Honoured: generates `sitemap.xml`. */
|
|
19
|
+
enableSitemap: boolean
|
|
20
|
+
/**
|
|
21
|
+
* NOT YET HONOURED by the static blog builder. Nothing reads this and no
|
|
22
|
+
* search index is generated.
|
|
23
|
+
*/
|
|
24
|
+
enableSearch: boolean
|
|
25
|
+
/** Short title used in the blog layout nav; defaults to `title`. */
|
|
26
|
+
siteTitle?: string
|
|
27
|
+
/** Fallback post author when frontmatter has none. */
|
|
28
|
+
author?: string
|
|
29
|
+
/** Canonical site origin for feed/sitemap URLs when no request origin exists. */
|
|
30
|
+
url?: string
|
|
31
|
+
nav?: { text: string, link: string }[]
|
|
32
|
+
/** Which modes the blog theme toggle offers. */
|
|
33
|
+
themes?: ('colored' | 'light' | 'dark')[]
|
|
34
|
+
defaultTheme?: 'colored' | 'light' | 'dark'
|
|
35
|
+
/** Raw HTML for the blog footer colophon line. */
|
|
36
|
+
colophon?: string
|
|
37
|
+
social: {
|
|
38
|
+
twitter?: string
|
|
39
|
+
github?: string
|
|
40
|
+
}
|
|
41
|
+
theme: {
|
|
42
|
+
primaryColor: string
|
|
43
|
+
logo?: string
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The site origin feeds and the sitemap are written against. APP_URL is often
|
|
48
|
+
// a bare host, which is not a URL on its own.
|
|
49
|
+
const appUrl = String(env.APP_URL || '').replace(/\/+$/, '')
|
|
50
|
+
const siteUrl = appUrl && !/^https?:\/\//.test(appUrl) ? `https://${appUrl}` : appUrl
|
|
51
|
+
|
|
52
|
+
const config: BlogConfig = {
|
|
53
|
+
subdomain: 'blog',
|
|
54
|
+
title: `${env.APP_NAME || '__APP_NAME__'} Blog`,
|
|
55
|
+
description: '',
|
|
56
|
+
postsPerPage: 10,
|
|
57
|
+
// `enableComments` and `enableSearch` are declared but not yet implemented
|
|
58
|
+
// by the static blog builder - see the interface above. Left `true` so the
|
|
59
|
+
// value does not have to change when they are.
|
|
60
|
+
enableComments: true,
|
|
61
|
+
enableRss: true,
|
|
62
|
+
enableSitemap: true,
|
|
63
|
+
enableSearch: true,
|
|
64
|
+
author: env.APP_NAME || '__APP_NAME__',
|
|
65
|
+
...(siteUrl && { url: siteUrl }),
|
|
66
|
+
nav: [
|
|
67
|
+
{ text: 'Home', link: '/' },
|
|
68
|
+
{ text: 'Blog', link: '/blog' },
|
|
69
|
+
],
|
|
70
|
+
themes: ['colored', 'light', 'dark'],
|
|
71
|
+
defaultTheme: 'light',
|
|
72
|
+
colophon: '<a href="/blog/feed.xml">RSS</a>',
|
|
73
|
+
social: {},
|
|
74
|
+
theme: {
|
|
75
|
+
primaryColor: '#171717',
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default config
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { BuddyConfig } from '@buddysh/buddy'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* buddy-bot is the one dependency bot a Stacks app runs.
|
|
5
|
+
*
|
|
6
|
+
* `.github/workflows/buddy-bot.yml` is what drives it: updates every two
|
|
7
|
+
* hours, a dashboard issue 15 minutes later, and a daily pass that closes pull
|
|
8
|
+
* requests whose bump already landed. Nothing else should be opening
|
|
9
|
+
* dependency pull requests here - two bots proposing the same bump is
|
|
10
|
+
* stacksjs/stacks#2574, and it costs a full CI matrix per duplicate.
|
|
11
|
+
*/
|
|
12
|
+
export default {
|
|
13
|
+
repository: {
|
|
14
|
+
provider: 'github',
|
|
15
|
+
// In Actions this is already the account or organization the repository
|
|
16
|
+
// lives under, which is the only place the workflow above runs. Replace the
|
|
17
|
+
// fallback if you also run buddy-bot locally.
|
|
18
|
+
owner: process.env.GITHUB_REPOSITORY_OWNER || 'your-github-owner',
|
|
19
|
+
name: '__APP_SLUG__',
|
|
20
|
+
// token: process.env.BUDDY_BOT_TOKEN,
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
dashboard: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
title: 'Dependency Dashboard',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
workflows: {
|
|
29
|
+
enabled: true,
|
|
30
|
+
outputDir: '.github/workflows',
|
|
31
|
+
templates: {
|
|
32
|
+
daily: true,
|
|
33
|
+
weekly: true,
|
|
34
|
+
monthly: true,
|
|
35
|
+
},
|
|
36
|
+
custom: [],
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
packages: {
|
|
40
|
+
strategy: 'all',
|
|
41
|
+
ignore: [
|
|
42
|
+
// Packages to hold back, e.g. '@types/node'
|
|
43
|
+
],
|
|
44
|
+
ignorePaths: [
|
|
45
|
+
// Glob patterns to skip, e.g. 'apps/legacy/**'
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
verbose: false,
|
|
50
|
+
} satisfies BuddyConfig
|