@stacksjs/defaults 0.75.243 → 0.75.244

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.
@@ -65,7 +65,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
65
65
  | `@stacksjs/newsletter` | [`stacks-newsletter`](../stacks-newsletter/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/newsletter/src/index.ts) | 7 |
66
66
  | `@stacksjs/notifications` | [`stacks-notifications`](../stacks-notifications/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts) | 16 |
67
67
  | `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) | 4 |
68
- | `@stacksjs/orm` | [`stacks-orm`](../stacks-orm/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts) | 63 |
68
+ | `@stacksjs/orm` | [`stacks-orm`](../stacks-orm/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts) | 64 |
69
69
  | `@stacksjs/pagination` | [`stacks-pagination`](../stacks-pagination/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/pagination/src/index.ts) | 0 |
70
70
  | `@stacksjs/path` | [`stacks-path`](../stacks-path/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/path/src/index.ts) | 11 |
71
71
  | `@stacksjs/payments` | [`stacks-payments`](../stacks-payments/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/index.ts) | 28 |
@@ -633,7 +633,7 @@ Declared runtime exports: `Activity`, `AnalyticsEvent`, `Auction`, `AuctionItem`
633
633
 
634
634
  Local source modules: [src/auto-crud.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/auto-crud.ts), [src/batch-loader.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/batch-loader.ts), [src/db.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/db.ts), [src/define-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/define-model.ts), [src/extend-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/extend-model.ts), [src/gdpr/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/gdpr/index.ts), [src/generate-database-schema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/generate-database-schema.ts), [src/locked-row.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/locked-row.ts), [src/messaging.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/messaging.ts), [src/model-types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/model-types.ts), [src/ownership.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/ownership.ts), [src/paginator-request.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/paginator-request.ts), [src/row-token.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/row-token.ts), [src/subquery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/subquery.ts), [src/traits/audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/audit.ts), [src/traits/billable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/billable.ts), [src/traits/categorizable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/categorizable.ts), [src/traits/commentable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/commentable.ts), [src/traits/likeable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/likeable.ts), [src/traits/taggable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/taggable.ts), [src/transaction.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/transaction.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/types.ts), [src/utils/encrypted.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/encrypted.ts), [src/utils/prunable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/prunable.ts).
635
635
 
636
- Evidence directory: [63 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
636
+ Evidence directory: [64 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
637
637
 
638
638
  ### @stacksjs/pagination
639
639
 
@@ -193,6 +193,12 @@ export default new Action({
193
193
  })
194
194
  }
195
195
 
196
- return { stats, httpMetrics, services, activities, issues }
196
+ // Which feature-backed sections exist, so the home page does not offer a
197
+ // card for a section the sidebar has hidden and whose pages cannot load.
198
+ // `feature()` is server-side only, and this component runs on the client.
199
+ // See stacksjs/stacks#2928.
200
+ const sections = { commerce, content: feature('cms'), marketing: feature('marketing') }
201
+
202
+ return { stats, httpMetrics, services, activities, issues, sections }
197
203
  },
198
204
  })
@@ -142,38 +142,48 @@ export default defineModel({
142
142
  rule: schema.string().max(255),
143
143
  },
144
144
  },
145
+ // `text`, all of these: an EXPLAIN plan, the tables a query touched and the
146
+ // suggestions made about it run past the varchar(255) Postgres enforces, and
147
+ // a log row that does not fit is dropped ("value too long for type character
148
+ // varying(255)") - losing exactly the slow queries this table exists for.
145
149
  tags: {
146
150
  fillable: true,
151
+ type: 'text',
147
152
  validation: {
148
153
  rule: schema.string(),
149
154
  },
150
155
  },
151
156
  affected_tables: {
152
157
  fillable: true,
158
+ type: 'text',
153
159
  validation: {
154
160
  rule: schema.string(),
155
161
  },
156
162
  },
157
163
  indexes_used: {
158
164
  fillable: true,
165
+ type: 'text',
159
166
  validation: {
160
167
  rule: schema.string(),
161
168
  },
162
169
  },
163
170
  missing_indexes: {
164
171
  fillable: true,
172
+ type: 'text',
165
173
  validation: {
166
174
  rule: schema.string(),
167
175
  },
168
176
  },
169
177
  explain_plan: {
170
178
  fillable: true,
179
+ type: 'text',
171
180
  validation: {
172
181
  rule: schema.string(),
173
182
  },
174
183
  },
175
184
  optimization_suggestions: {
176
185
  fillable: true,
186
+ type: 'text',
177
187
  validation: {
178
188
  rule: schema.string(),
179
189
  },
@@ -39,6 +39,11 @@ export interface DashboardIssue {
39
39
  }
40
40
 
41
41
  interface DashboardHomeResponse {
42
+ /**
43
+ * Which feature-backed sections the server has available, so the home page
44
+ * only offers cards for sections that exist. See stacksjs/stacks#2928.
45
+ */
46
+ sections?: Record<string, boolean>
42
47
  stats?: Array<{ label: string, value: string }>
43
48
  httpMetrics?: DashboardHttpMetric[]
44
49
  services?: SystemHealthItem[]
@@ -98,6 +103,7 @@ export async function fetchDashboardHome(): Promise<DashboardHomeResponse> {
98
103
  }
99
104
 
100
105
  export function useDashboard() {
106
+ const sections = ref<Record<string, boolean>>({})
101
107
  const stats = ref<DashboardStats[]>([])
102
108
  const httpMetrics = ref<DashboardHttpMetric[]>([])
103
109
  const recentActivity = ref<ActivityItem[]>([])
@@ -109,6 +115,7 @@ export function useDashboard() {
109
115
  async function fetchDashboardStats(timeRange = '7d') {
110
116
  void timeRange
111
117
  const data = await fetchDashboardHome()
118
+ sections.value = data.sections && typeof data.sections === 'object' ? data.sections : {}
112
119
  stats.value = normalizeStats(data.stats)
113
120
  issues.value = Array.isArray(data.issues) ? data.issues : []
114
121
  }
@@ -132,7 +139,8 @@ export function useDashboard() {
132
139
 
133
140
  try {
134
141
  const data = await fetchDashboardHome()
135
- stats.value = normalizeStats(data.stats)
142
+ sections.value = data.sections && typeof data.sections === 'object' ? data.sections : {}
143
+ stats.value = normalizeStats(data.stats)
136
144
  httpMetrics.value = Array.isArray(data.httpMetrics) ? data.httpMetrics : []
137
145
  recentActivity.value = normalizeActivities(data.activities)
138
146
  systemHealth.value = Array.isArray(data.services) ? data.services : []
@@ -157,6 +165,7 @@ export function useDashboard() {
157
165
  }
158
166
 
159
167
  return {
168
+ sections,
160
169
  stats,
161
170
  httpMetrics,
162
171
  recentActivity,
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.243",
5
+ "version": "0.75.244",
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.243",
5
+ "version": "0.75.244",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@iconify-json/f7": "^1.2.2",
46
46
  "@iconify-json/hugeicons": "^1.2.35",
47
- "@stacksjs/mobile": "0.75.243",
47
+ "@stacksjs/mobile": "0.75.244",
48
48
  "@stacksjs/sanitizer": "^0.2.534",
49
49
  "@xterm/addon-fit": "^0.11.0",
50
50
  "@xterm/xterm": "^6.0.0",
@@ -4,13 +4,20 @@ import { useDashboard } from '../../../../functions/dashboard'
4
4
  const dashboard = useDashboard()
5
5
  const loadingCards = [1, 2, 3, 4]
6
6
 
7
- const quickLinks = [
7
+ /**
8
+ * `section` names the feature-backed dashboard section a card belongs to.
9
+ * A card with one is only offered when the server says that section exists -
10
+ * otherwise the home page links to pages the sidebar has hidden and that
11
+ * cannot load. See stacksjs/stacks#2928.
12
+ */
13
+ const allQuickLinks = [
8
14
  {
9
15
  title: 'Blog',
10
16
  description: 'Manage persisted posts',
11
17
  href: '/content/dashboard',
12
18
  icon: 'i-hugeicons-book-open-01',
13
19
  tone: 'blue',
20
+ section: 'content',
14
21
  },
15
22
  {
16
23
  title: 'Commerce',
@@ -18,6 +25,7 @@ const quickLinks = [
18
25
  href: '/commerce/dashboard',
19
26
  icon: 'i-hugeicons-store-01',
20
27
  tone: 'green',
28
+ section: 'commerce',
21
29
  },
22
30
  {
23
31
  title: 'Cloud',
@@ -35,6 +43,16 @@ const quickLinks = [
35
43
  },
36
44
  ]
37
45
 
46
+ /**
47
+ * Cards without a `section` are always offered. A card with one waits for the
48
+ * server to say that section exists, so the home page never links to a
49
+ * section the sidebar has hidden and whose pages cannot load.
50
+ */
51
+ function quickLinks() {
52
+ const available = dashboard.sections.value
53
+ return allQuickLinks.filter(link => !link.section || available[link.section] !== false)
54
+ }
55
+
38
56
  const resourceLinks = [
39
57
  {
40
58
  title: 'Documentation',
@@ -153,7 +171,7 @@ onMount(() => {
153
171
  </div>
154
172
  </section>
155
173
 
156
- <QuickLinks class="mb-8" :links="quickLinks" />
174
+ <QuickLinks class="mb-8" :links="quickLinks()" />
157
175
 
158
176
  <div class="grid gap-6 grid-cols-1 lg:grid-cols-2 mt-8">
159
177
  <ServiceHealth :services="services()" :loading="dashboard.isLoading.value" />
@@ -45,12 +45,46 @@ export interface ResolvedDashboardToggles {
45
45
  data: DashboardDataRowToggles
46
46
  }
47
47
 
48
- export function defaultDashboardToggles(): ResolvedDashboardToggles {
48
+ /**
49
+ * Dashboard sections that mirror an installable feature.
50
+ *
51
+ * A section whose feature is off used to stay in the sidebar anyway, and every
52
+ * page under it failed: the models those pages query are never loaded, so the
53
+ * first call came back `Manufacturer.orderByDesc is not a function` and the
54
+ * endpoint 503'd. Switching commerce off took two edits in two files -
55
+ * `config/commerce.ts` and `config/dashboard.ts` - and missing the second left
56
+ * a Commerce section that could not load. See stacksjs/stacks#2928.
57
+ *
58
+ * `library`, `analytics`, `management` and `utilities` are deliberately absent:
59
+ * they have no feature of their own, so they stay on.
60
+ */
61
+ const FEATURE_BACKED_SECTIONS = {
62
+ commerce: 'commerce',
63
+ content: 'cms',
64
+ marketing: 'marketing',
65
+ } as const
66
+
67
+ /**
68
+ * Reads whether a feature is on. Supplied by the caller rather than imported,
69
+ * because this module is loaded on the dashboard's synchronous path and stays
70
+ * free of `@stacksjs/*` imports - its sibling `sidebar.ts` says why.
71
+ *
72
+ * Omitted, every section defaults on exactly as it did before.
73
+ */
74
+ export type FeatureResolver = (name: string) => boolean
75
+
76
+ function sectionFallback(section: keyof typeof FEATURE_BACKED_SECTIONS, isFeatureEnabled?: FeatureResolver): boolean {
77
+ if (!isFeatureEnabled)
78
+ return true
79
+ return isFeatureEnabled(FEATURE_BACKED_SECTIONS[section])
80
+ }
81
+
82
+ export function defaultDashboardToggles(isFeatureEnabled?: FeatureResolver): ResolvedDashboardToggles {
49
83
  return {
50
84
  library: true,
51
- content: true,
52
- commerce: true,
53
- marketing: true,
85
+ content: sectionFallback('content', isFeatureEnabled),
86
+ commerce: sectionFallback('commerce', isFeatureEnabled),
87
+ marketing: sectionFallback('marketing', isFeatureEnabled),
54
88
  analytics: true,
55
89
  management: true,
56
90
  utilities: true,
@@ -66,7 +100,7 @@ export function defaultDashboardToggles(): ResolvedDashboardToggles {
66
100
  }
67
101
  }
68
102
 
69
- export function resolveDashboardToggles(value: unknown): ResolvedDashboardToggles {
103
+ export function resolveDashboardToggles(value: unknown, isFeatureEnabled?: FeatureResolver): ResolvedDashboardToggles {
70
104
  const config = objectValue(value, 'dashboard config')
71
105
  const sections = config.sections === undefined
72
106
  ? {}
@@ -77,9 +111,11 @@ export function resolveDashboardToggles(value: unknown): ResolvedDashboardToggle
77
111
 
78
112
  return {
79
113
  library: sectionEnabled(sections.library, 'library', true),
80
- content: sectionEnabled(sections.content, 'content', true),
81
- commerce: sectionEnabled(sections.commerce, 'commerce', true),
82
- marketing: sectionEnabled(sections.marketing, 'marketing', true),
114
+ // An explicit `enabled` in config/dashboard.ts still wins, both ways: an
115
+ // app can show a section whose feature is off, or hide one that is on.
116
+ content: sectionEnabled(sections.content, 'content', sectionFallback('content', isFeatureEnabled)),
117
+ commerce: sectionEnabled(sections.commerce, 'commerce', sectionFallback('commerce', isFeatureEnabled)),
118
+ marketing: sectionEnabled(sections.marketing, 'marketing', sectionFallback('marketing', isFeatureEnabled)),
83
119
  analytics: sectionEnabled(sections.analytics, 'analytics', true),
84
120
  management: sectionEnabled(sections.management, 'management', true),
85
121
  utilities: sectionEnabled(sections.utilities, 'utilities', true),
@@ -157,16 +193,16 @@ export interface LoadedDashboardConfig {
157
193
  * One import rather than two: the config module is cache-busted on mtime, so a
158
194
  * second load of the same file would be a second evaluation of user code.
159
195
  */
160
- export async function loadDashboardConfig(configPath: string): Promise<LoadedDashboardConfig> {
196
+ export async function loadDashboardConfig(configPath: string, isFeatureEnabled?: FeatureResolver): Promise<LoadedDashboardConfig> {
161
197
  if (!existsSync(configPath))
162
- return { toggles: defaultDashboardToggles(), nav: [] }
198
+ return { toggles: defaultDashboardToggles(isFeatureEnabled), nav: [] }
163
199
 
164
200
  try {
165
201
  const moduleUrl = pathToFileURL(configPath)
166
202
  moduleUrl.searchParams.set('dashboard-config-mtime', String(statSync(configPath).mtimeMs))
167
203
  const configModule = await import(moduleUrl.href) as { default?: unknown }
168
204
  return {
169
- toggles: resolveDashboardToggles(configModule.default),
205
+ toggles: resolveDashboardToggles(configModule.default, isFeatureEnabled),
170
206
  nav: resolveDashboardNav(configModule.default),
171
207
  }
172
208
  }