@stacksjs/defaults 0.75.12 → 0.75.13

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.
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.12",
5
+ "version": "0.75.13",
6
6
  "description": "A modern Stacks development environment.",
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.12",
5
+ "version": "0.75.13",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@iconify-json/f7": "^1.2.2",
57
57
  "@iconify-json/hugeicons": "^1.2.27",
58
- "@stacksjs/mobile": "^0.75.12",
58
+ "@stacksjs/mobile": "^0.75.13",
59
59
  "@stacksjs/sanitizer": "^0.2.113",
60
60
  "ts-qr-codes": "^0.1.8"
61
61
  }
@@ -0,0 +1,41 @@
1
+ // The signed-in user's columns, derived rather than declared.
2
+ //
3
+ // `request.user()` resolves to `AuthenticatedUser` from `@stacksjs/types`. That
4
+ // package cannot import the ORM (it sits underneath it), so on its own it knows
5
+ // only `id` and `email`, and every other field is `unknown` until something
6
+ // says otherwise. This is what says otherwise: the columns of the User model
7
+ // the application actually runs, read off the same barrel
8
+ // `injectGlobalAutoImports` loads at runtime. Override `app/Models/User.ts` and
9
+ // the type follows with nothing to regenerate; add a column and `user.team_id`
10
+ // is typed the moment the model declares it.
11
+ //
12
+ // When that model declares `traits: { billable: true }`, the billable instance
13
+ // methods (`user.checkout(...)`, `user.newSubscription(...)`) come with it,
14
+ // because every record it hydrates carries them. With the trait off, they are
15
+ // absent, and `isBillable(user)` from `@stacksjs/orm` is how to ask.
16
+ //
17
+ // `id` stays as `@stacksjs/types` declares it, `number | string`, rather than
18
+ // the model's `number`: a BIGINT key comes back from Postgres as a string.
19
+
20
+ import type { BillableMethods, Def, ModelRow } from '@stacksjs/orm'
21
+
22
+ /** Every model, by the name the ORM exposes it under. */
23
+ type Models = typeof import('../auto-imports/models')
24
+
25
+ /** `true` for `any`, which is what an unresolvable barrel reads as. */
26
+ type IsAny<T> = 0 extends 1 & T ? true : false
27
+
28
+ /** The application's User model, or nothing when there is none to read. */
29
+ type AppUser = IsAny<Models> extends true ? never : Models extends { User: infer TUser } ? TUser : never
30
+
31
+ /** Its columns, minus the `id` the base interface declares more honestly. */
32
+ type AppUserColumns = [AppUser] extends [never] ? {} : Omit<ModelRow<AppUser>, 'id'>
33
+
34
+ /** Its billable methods, when it declares the trait. */
35
+ type AppUserBillable = [AppUser] extends [never]
36
+ ? {}
37
+ : Def<AppUser> extends { readonly traits: { readonly billable: true } } ? BillableMethods : {}
38
+
39
+ declare module '@stacksjs/types' {
40
+ interface AuthenticatedUser extends AppUserColumns, AppUserBillable {}
41
+ }
@@ -0,0 +1,60 @@
1
+ export interface TransactionHistory {
2
+ id?: number
3
+ uuid?: string
4
+ name: string
5
+ description?: string
6
+ amount: number
7
+ type: string
8
+ provider_id?: string
9
+ user_id?: number
10
+ paymentmethod_id?: number
11
+ created_at: string
12
+ updated_at?: string
13
+ }
14
+
15
+ export interface PaymentMethod {
16
+ id?: number
17
+ uuid?: string
18
+ type: string
19
+ last_four: number
20
+ brand: string
21
+ exp_month: number
22
+ exp_year: number
23
+ is_default?: boolean
24
+ provider_id?: string
25
+ user_id?: number
26
+ created_at: string
27
+ updated_at?: string
28
+ }
29
+
30
+ export interface Product {
31
+ id?: number
32
+ uuid?: string
33
+ name: string
34
+ description?: number
35
+ key: number
36
+ unit_price?: number
37
+ status?: string
38
+ image?: string
39
+ provider_id?: string
40
+ created_at: string
41
+ updated_at?: string
42
+ }
43
+
44
+ export interface Subscription {
45
+ id?: number
46
+ uuid?: string
47
+ type: string
48
+ provider_id: string
49
+ provider_status: string
50
+ unit_price?: number
51
+ provider_type: string
52
+ provider_price_id?: string
53
+ quantity?: number
54
+ trial_ends_at?: string
55
+ ends_at?: string
56
+ last_used_at?: string
57
+ user_id?: number
58
+ created_at: string
59
+ updated_at?: string
60
+ }
@@ -0,0 +1,34 @@
1
+ // The application's environment variables, derived rather than generated.
2
+ //
3
+ // `config/env.ts` declares them with `defineEnv`, and this teaches `env` about
4
+ // them. The application used to have to write that itself, at the bottom of the
5
+ // file it had just filled in:
6
+ //
7
+ // declare module '@stacksjs/env' {
8
+ // interface StacksEnv extends InferEnv<typeof envSchema> {}
9
+ // }
10
+ //
11
+ // which is boilerplate that only ever has one correct spelling, and silently
12
+ // leaves every variable untyped when it is missing. The framework can read the
13
+ // schema itself.
14
+ //
15
+ // `@stacksjs/env` cannot do this from inside the package: a published `.d.ts`
16
+ // naming `../../../config/env` resolves to nothing under `node_modules/`. So it
17
+ // lives here, in the project, alongside the other derivations.
18
+
19
+ import type { InferEnv } from '@stacksjs/env'
20
+
21
+ /** The schema `config/env.ts` default-exports. */
22
+ type EnvSchema = typeof import('../../../config/env')['default']
23
+
24
+ declare module '@stacksjs/env' {
25
+ /**
26
+ * Each variable typed by its validator - `schema.number()` is a `number`,
27
+ * `schema.enum([...])` the union of those literals - and optional, because
28
+ * the process may simply not have it set. `InferEnv` drops the keys the
29
+ * framework already declares, so restating one cannot conflict.
30
+ */
31
+ interface StacksEnv extends InferEnv<EnvSchema> {}
32
+ }
33
+
34
+ export {}
@@ -0,0 +1,58 @@
1
+ // Gates and policies, derived rather than listed.
2
+ //
3
+ // Both halves of `app/Gates.ts` name things that exist elsewhere - an ability
4
+ // the file itself defines, and a model the ORM exposes - and both used to be
5
+ // `string`. A mapping to a policy that is not there, or for a model that is not
6
+ // there, registered nothing and then denied every check against it, which is
7
+ // indistinguishable from a policy that means to say no.
8
+ //
9
+ // Nothing here is generated. The gates ARE the declaration, read back off the
10
+ // file, and the models come from the same barrel `injectGlobalAutoImports`
11
+ // loads at runtime - so neither can drift from what the application has.
12
+
13
+ /** Every model, by the name the ORM exposes it under. */
14
+ type Models = typeof import('../auto-imports/models')
15
+
16
+ /** The application's own `app/Gates.ts`, as it is written. */
17
+ type Authorization = typeof import('../../../app/Gates')['default']
18
+
19
+ /**
20
+ * The ability names its gates define, and the arguments each one takes.
21
+ *
22
+ * Bound with `infer` rather than looked up as `Authorization['gates']` behind
23
+ * an `extends GatesDefinition` check. That check passes, but inside its true
24
+ * branch the lookup reads back through the constraint's own index signature -
25
+ * `Readonly<Record<string, GateCallback>>` - so `keyof` came out `string`.
26
+ * `AppGates` then had a string index signature, `GateName` was `string`, and
27
+ * nothing was constrained: `Gate.allows('definitely-not-a-real-gate', user)`
28
+ * typechecked, which is exactly what this file exists to prevent.
29
+ *
30
+ * `A extends { gates: infer G }` binds the real object, so the names survive.
31
+ * It also still degrades to nothing rather than to an error when a project has
32
+ * replaced `Gates.ts` with something of another shape.
33
+ */
34
+ type GateNames = Authorization extends { gates: infer G }
35
+ ? Extract<keyof G, string>
36
+ : never
37
+
38
+ /**
39
+ * Each gate mapped to the arguments it takes AFTER the user.
40
+ *
41
+ * The registry used to map every name to `true`, recording that a gate existed
42
+ * and nothing about how to call it, so `Gate.allows('view-dashboard', user,
43
+ * 'junk', 42)` passed against a gate declared `(user) => boolean`.
44
+ */
45
+ type GateArgsOf = Authorization extends { gates: infer G }
46
+ ? {
47
+ [K in Extract<keyof G, string>]: G[K] extends (..._args: infer P) => unknown
48
+ ? (P extends [unknown, ...infer Rest] ? Rest : [])
49
+ : never
50
+ }
51
+ : Record<never, never>
52
+
53
+ declare module '@stacksjs/auth' {
54
+ interface AppGates extends GateArgsOf {}
55
+ interface PolicyModels extends Record<keyof Models & string, true> {}
56
+ }
57
+
58
+ export {}
@@ -0,0 +1,293 @@
1
+ export interface GitHubCommit {
2
+ sha: string
3
+ node_id: string
4
+ commit: {
5
+ author: {
6
+ name: string
7
+ email: string
8
+ date: string
9
+ }
10
+ committer: {
11
+ name: string
12
+ email: string
13
+ date: string
14
+ }
15
+ message: string
16
+ tree: {
17
+ sha: string
18
+ url: string
19
+ }
20
+ url: string
21
+ comment_count: number
22
+ verification: {
23
+ verified: boolean
24
+ reason: string
25
+ signature: string | null
26
+ payload: string | null
27
+ }
28
+ }
29
+ url: string
30
+ html_url: string
31
+ comments_url: string
32
+ author: {
33
+ login: string
34
+ id: number
35
+ node_id: string
36
+ avatar_url: string
37
+ gravatar_id: string
38
+ url: string
39
+ html_url: string
40
+ followers_url: string
41
+ following_url: string
42
+ gists_url: string
43
+ starred_url: string
44
+ subscriptions_url: string
45
+ organizations_url: string
46
+ repos_url: string
47
+ events_url: string
48
+ received_events_url: string
49
+ type: string
50
+ site_admin: boolean
51
+ }
52
+ committer: {
53
+ login: string
54
+ id: number
55
+ node_id: string
56
+ avatar_url: string
57
+ gravatar_id: string
58
+ url: string
59
+ html_url: string
60
+ followers_url: string
61
+ following_url: string
62
+ gists_url: string
63
+ starred_url: string
64
+ subscriptions_url: string
65
+ organizations_url: string
66
+ repos_url: string
67
+ events_url: string
68
+ received_events_url: string
69
+ type: string
70
+ site_admin: boolean
71
+ }
72
+ }
73
+
74
+ export interface WorkflowRun {
75
+ id: number
76
+ name: string
77
+ node_id: string
78
+ head_branch: string
79
+ head_sha: string
80
+ path: string
81
+ display_title: string
82
+ run_number: number
83
+ event: string
84
+ status: string
85
+ conclusion: string
86
+ workflow_id: number
87
+ check_suite_id: number
88
+ check_suite_node_id: string
89
+ url: string
90
+ html_url: string
91
+ pull_requests: any[] // You might want to create a more detailed interface for pull requests
92
+ created_at: string
93
+ updated_at: string
94
+ actor: {
95
+ login: string
96
+ id: number
97
+ node_id: string
98
+ avatar_url: string
99
+ gravatar_id: string
100
+ url: string
101
+ html_url: string
102
+ followers_url: string
103
+ following_url: string
104
+ gists_url: string
105
+ starred_url: string
106
+ subscriptions_url: string
107
+ organizations_url: string
108
+ repos_url: string
109
+ events_url: string
110
+ received_events_url: string
111
+ type: string
112
+ site_admin: boolean
113
+ }
114
+ run_attempt: number
115
+ referenced_workflows: any[] // You might want to create a more detailed interface for workflows
116
+ run_started_at: string
117
+ triggering_actor: {
118
+ login: string
119
+ id: number
120
+ node_id: string
121
+ avatar_url: string
122
+ gravatar_id: string
123
+ url: string
124
+ html_url: string
125
+ followers_url: string
126
+ following_url: string
127
+ gists_url: string
128
+ starred_url: string
129
+ subscriptions_url: string
130
+ organizations_url: string
131
+ repos_url: string
132
+ events_url: string
133
+ received_events_url: string
134
+ type: string
135
+ site_admin: boolean
136
+ }
137
+ jobs_url: string
138
+ logs_url: string
139
+ check_suite_url: string
140
+ artifacts_url: string
141
+ cancel_url: string
142
+ rerun_url: string
143
+ previous_attempt_url: string | null
144
+ workflow_url: string
145
+ head_commit: {
146
+ id: string
147
+ tree_id: string
148
+ message: string
149
+ timestamp: string
150
+ author: {
151
+ name: string
152
+ email: string
153
+ }
154
+ committer: {
155
+ name: string
156
+ email: string
157
+ }
158
+ }
159
+ repository: {
160
+ id: number
161
+ node_id: string
162
+ name: string
163
+ full_name: string
164
+ private: boolean
165
+ owner: {
166
+ login: string
167
+ id: number
168
+ node_id: string
169
+ avatar_url: string
170
+ gravatar_id: string
171
+ url: string
172
+ html_url: string
173
+ followers_url: string
174
+ following_url: string
175
+ gists_url: string
176
+ starred_url: string
177
+ subscriptions_url: string
178
+ organizations_url: string
179
+ repos_url: string
180
+ events_url: string
181
+ received_events_url: string
182
+ type: string
183
+ site_admin: boolean
184
+ }
185
+ html_url: string
186
+ description: string
187
+ fork: boolean
188
+ url: string
189
+ forks_url: string
190
+ keys_url: string
191
+ collaborators_url: string
192
+ teams_url: string
193
+ hooks_url: string
194
+ issue_events_url: string
195
+ events_url: string
196
+ assignees_url: string
197
+ branches_url: string
198
+ tags_url: string
199
+ blobs_url: string
200
+ git_tags_url: string
201
+ git_refs_url: string
202
+ trees_url: string
203
+ statuses_url: string
204
+ languages_url: string
205
+ stargazers_url: string
206
+ contributors_url: string
207
+ subscribers_url: string
208
+ subscription_url: string
209
+ commits_url: string
210
+ git_commits_url: string
211
+ comments_url: string
212
+ issue_comment_url: string
213
+ contents_url: string
214
+ compare_url: string
215
+ merges_url: string
216
+ archive_url: string
217
+ downloads_url: string
218
+ issues_url: string
219
+ pulls_url: string
220
+ milestones_url: string
221
+ notifications_url: string
222
+ labels_url: string
223
+ releases_url: string
224
+ deployments_url: string
225
+ }
226
+ head_repository: {
227
+ id: number
228
+ node_id: string
229
+ name: string
230
+ full_name: string
231
+ private: boolean
232
+ owner: {
233
+ login: string
234
+ id: number
235
+ node_id: string
236
+ avatar_url: string
237
+ gravatar_id: string
238
+ url: string
239
+ html_url: string
240
+ followers_url: string
241
+ following_url: string
242
+ gists_url: string
243
+ starred_url: string
244
+ subscriptions_url: string
245
+ organizations_url: string
246
+ repos_url: string
247
+ events_url: string
248
+ received_events_url: string
249
+ type: string
250
+ site_admin: boolean
251
+ }
252
+ html_url: string
253
+ description: string
254
+ fork: boolean
255
+ url: string
256
+ forks_url: string
257
+ keys_url: string
258
+ collaborators_url: string
259
+ teams_url: string
260
+ hooks_url: string
261
+ issue_events_url: string
262
+ events_url: string
263
+ assignees_url: string
264
+ branches_url: string
265
+ tags_url: string
266
+ blobs_url: string
267
+ git_tags_url: string
268
+ git_refs_url: string
269
+ trees_url: string
270
+ statuses_url: string
271
+ languages_url: string
272
+ stargazers_url: string
273
+ contributors_url: string
274
+ subscribers_url: string
275
+ subscription_url: string
276
+ commits_url: string
277
+ git_commits_url: string
278
+ comments_url: string
279
+ issue_comment_url: string
280
+ contents_url: string
281
+ compare_url: string
282
+ merges_url: string
283
+ archive_url: string
284
+ downloads_url: string
285
+ issues_url: string
286
+ pulls_url: string
287
+ milestones_url: string
288
+ notifications_url: string
289
+ labels_url: string
290
+ releases_url: string
291
+ deployments_url: string
292
+ }
293
+ }
@@ -0,0 +1,60 @@
1
+ // Model events, derived rather than listed.
2
+ //
3
+ // This file used to be generated: 97 models x 8 events, 817 lines, rebuilt by
4
+ // `buddy generate:types` and stale the moment a model was added without it.
5
+ // None of that is necessary. A mapped type with key remapping turns the models
6
+ // barrel into the event map directly, so `User` becoming a model is the same
7
+ // event as `'user:created'` existing - not two facts that have to be kept in
8
+ // agreement.
9
+ //
10
+ // The barrel it reads is generated, but for the RUNTIME: `injectGlobalAutoImports`
11
+ // imports it to put the models on `globalThis`. So this adds no generation step
12
+ // of its own, and cannot drift from what the runtime actually has.
13
+
14
+ import type { ModelRow } from '@stacksjs/orm'
15
+
16
+ /** Every model, by the name the ORM exposes it under. */
17
+ type Models = typeof import('../auto-imports/models')
18
+
19
+ /**
20
+ * Fired after the write, with the row.
21
+ *
22
+ * `define-model.ts` dispatches these with `toEventPayload(model)`, which
23
+ * returns `{...attributes}` plus the primary key.
24
+ */
25
+ type AfterEvent = 'created' | 'updated' | 'saved' | 'deleted'
26
+
27
+ /**
28
+ * Fired before the write, with the model object; returning `false` cancels.
29
+ */
30
+ type BeforeEvent = 'saving' | 'creating' | 'updating' | 'deleting'
31
+
32
+ /**
33
+ * `'user:created'`, `'productcategory:saved'` - the model's name lowercased,
34
+ * matching `definition.name.toLowerCase()` in `define-model.ts`.
35
+ */
36
+ type ModelAfterEvents = {
37
+ [K in keyof Models & string as `${Lowercase<K>}:${AfterEvent}`]: ModelRow<Models[K]>
38
+ }
39
+
40
+ type ModelBeforeEvents = {
41
+ [K in keyof Models & string as `${Lowercase<K>}:${BeforeEvent}`]: ModelWriteEventOf<Models[K]>
42
+ }
43
+
44
+ /**
45
+ * What a BEFORE event carries: the model object, not the row.
46
+ *
47
+ * `toEventPayload` reading `model?.attributes` is what establishes that the
48
+ * object has one. Typed as far as that goes and no further - the rest of the
49
+ * instance belongs to the query builder.
50
+ */
51
+ interface ModelWriteEventOf<TModel> {
52
+ attributes: ModelRow<TModel>
53
+ [key: string]: unknown
54
+ }
55
+
56
+ declare module '@stacksjs/events' {
57
+ interface AppEvents extends ModelAfterEvents, ModelBeforeEvents {}
58
+ }
59
+
60
+ export {}
@@ -0,0 +1,28 @@
1
+ // The models, and their tables, derived rather than listed.
2
+ //
3
+ // `ModelNames` and `TableNames` were hand-written unions in `@stacksjs/types`:
4
+ // 50 model names against the 97 the application had, and 62 table names with
5
+ // two duplicates. A list maintained alongside the thing it lists only ever
6
+ // drifts one way, and the drift is silent until somebody writes
7
+ // `belongsTo: ['Site']` for a model that plainly exists and is told it does not.
8
+ //
9
+ // The barrel this reads is generated, but for the RUNTIME:
10
+ // `injectGlobalAutoImports` imports it to put the models on `globalThis`. So
11
+ // this adds no generation step of its own, and cannot drift from what the
12
+ // runtime actually has.
13
+
14
+ /** Every model, by the name the ORM exposes it under. */
15
+ type Models = typeof import('../auto-imports/models')
16
+
17
+ /** A model's own `table`, when it declares one as a literal. */
18
+ type TableOf<TModel> = TModel extends { table: infer TTable extends string } ? TTable : never
19
+
20
+ /** Every table the models write to. */
21
+ type Tables = { [K in keyof Models]: TableOf<Models[K]> }[keyof Models]
22
+
23
+ declare module '@stacksjs/types' {
24
+ interface ModelRegistry extends Record<keyof Models & string, true> {}
25
+ interface TableRegistry extends Record<Tables, true> {}
26
+ }
27
+
28
+ export {}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Global type declarations for ORM utilities and request types.
3
+ *
4
+ * These types are available globally without imports in all Stacks application code.
5
+ * Model types are derived dynamically from defineModel() definitions via `as const`.
6
+ *
7
+ * @example
8
+ * // No imports needed — all of these are global:
9
+ * type PostRow = ModelRow<typeof Post>
10
+ * type NewPost = NewModelData<typeof Post>
11
+ *
12
+ * // Actions with model: Post get fully typed requests automatically:
13
+ * new Action({
14
+ * model: Post,
15
+ * async handle(request) {
16
+ * request.get('title') // autocompletes to Post fields, returns string
17
+ * request.get('invalid') // TS error!
18
+ * }
19
+ * })
20
+ */
21
+
22
+ import type { UserLoggedInEvent as _UserLoggedInEvent, UserLoggedOutEvent as _UserLoggedOutEvent, UserPasswordEvent as _UserPasswordEvent, UserRegisteredEvent as _UserRegisteredEvent } from '@stacksjs/events'
23
+ import type { NotificationChannel as _NotificationChannel } from '@stacksjs/notifications'
24
+ import type { ModelRow as _ModelRow, NewModelData as _NewModelData, UpdateModelData as _UpdateModelData } from '@stacksjs/orm'
25
+ import type { RequestInstance as _RequestInstance } from '@stacksjs/types'
26
+
27
+ export {}
28
+
29
+ declare global {
30
+ /**
31
+ * Full database row type derived from a defineModel() definition.
32
+ * Includes model attributes + system fields (id, uuid, timestamps) + FK columns.
33
+ *
34
+ * @example
35
+ * type PostRow = ModelRow<typeof Post>
36
+ */
37
+ type ModelRow<T> = _ModelRow<T>
38
+
39
+ /**
40
+ * Insertable data type derived from a defineModel() definition.
41
+ * All fields are optional for flexible creation.
42
+ *
43
+ * @example
44
+ * type NewPost = NewModelData<typeof Post>
45
+ */
46
+ type NewModelData<T> = _NewModelData<T>
47
+
48
+ /**
49
+ * Updateable data type derived from a defineModel() definition.
50
+ * All fields are optional for partial updates.
51
+ *
52
+ * @example
53
+ * type PostUpdate = UpdateModelData<typeof Post>
54
+ */
55
+ type UpdateModelData<T> = _UpdateModelData<T>
56
+
57
+ /**
58
+ * Model-aware request interface.
59
+ *
60
+ * When parameterized with a model type, all input methods narrow to that model's
61
+ * field names and types. Automatically inferred in Action handlers via `model: Post`.
62
+ *
63
+ * @example
64
+ * // Bare — accepts any key:
65
+ * function handle(_request: RequestInstance) { ... }
66
+ *
67
+ * @example
68
+ * // Model-aware — full narrowing:
69
+ * function handle(request: RequestInstance<typeof Post>) {
70
+ * request.get('title') // autocompletes, typed return
71
+ * }
72
+ */
73
+ type RequestInstance<TModel = never> = [TModel] extends [never]
74
+ ? _RequestInstance
75
+ : _RequestInstance<_ModelRow<TModel>>
76
+
77
+ /**
78
+ * Auth event payloads — surfaced as globals so listener actions in
79
+ * `app/Actions/` can type their `handle(input)` parameter without an
80
+ * explicit import (matching the rest of Stacks' "no imports needed"
81
+ * authoring style).
82
+ */
83
+ type UserRegisteredEvent = _UserRegisteredEvent
84
+ type UserLoggedInEvent = _UserLoggedInEvent
85
+ type UserLoggedOutEvent = _UserLoggedOutEvent
86
+ type UserPasswordEvent = _UserPasswordEvent
87
+
88
+ /**
89
+ * Channel name accepted by `notify()`. Lifted to a global so listener
90
+ * actions can declare `const channels: NotificationChannel[] = []`
91
+ * without importing from `@stacksjs/notifications`.
92
+ */
93
+ type NotificationChannel = _NotificationChannel
94
+ }
@@ -0,0 +1,133 @@
1
+ // The name-addressed registries, derived rather than generated.
2
+ //
3
+ // Actions, listeners, policies, middleware and jobs are all resolved by NAME at
4
+ // runtime - out of a route string, an `app/Events.ts` entry, an `app/Gates.ts`
5
+ // mapping, a `.middleware(...)` call, a `job(...)` dispatch. Every one of those
6
+ // names used to be `string` to the compiler, and the fix for that used to be
7
+ // `storage/framework/types/actions.d.ts`: 1500 lines of hand-generated unions
8
+ // that were correct only until somebody added a file without re-running
9
+ // `buddy generate:types`.
10
+ //
11
+ // Nothing is generated for the types now. Each registry is a lazy map the
12
+ // RESOLVER reads - `storage/framework/auto-imports/*.ts`, written by
13
+ // `generateAutoImportFiles()` for the runtime - and the type is `keyof` over
14
+ // the same object. A name that type-checks is a name that resolves, because
15
+ // there is only one list.
16
+ //
17
+ // Every registry falls back to "any string" when its barrel is missing, so a
18
+ // project that has not run `buddy generate` yet compiles exactly as before.
19
+
20
+ /** Every action, by the path a route string names it with. */
21
+ type Actions = typeof import('../auto-imports/actions')['actions']
22
+
23
+ /** Every listener module under `app/Listeners/`, and the defaults behind it. */
24
+ type Listeners = typeof import('../auto-imports/listeners')['listeners']
25
+
26
+ /** Every policy class under `app/Policies/`, and the defaults behind it. */
27
+ type Policies = typeof import('../auto-imports/policies')['policies']
28
+
29
+ /** Every middleware class under `app/Middleware/`, and the defaults behind it. */
30
+ type Middlewares = typeof import('../auto-imports/middleware')['middleware']
31
+
32
+ /** Every email template under `resources/emails/`, and the defaults behind it. */
33
+ type EmailTemplates = typeof import('../auto-imports/emails')['emails']
34
+
35
+ /** Every job, by the name `resolveJobFile` finds it under. */
36
+ type JobRegistry = typeof import('../auto-imports/jobs')
37
+ type Jobs = JobRegistry
38
+
39
+ /** The application's middleware alias map, and the framework's behind it. */
40
+ type AppAliases = typeof import('../../../app/Middleware')['default']
41
+ type DefaultAliases = typeof import('../defaults/app/Middleware')['default']
42
+
43
+ type ActionName = keyof Actions & string
44
+ type ListenerModuleName = keyof Listeners & string
45
+ type PolicyClassName = keyof Policies & string
46
+ type MiddlewareClassName = keyof Middlewares & string
47
+ type JobName = keyof Jobs & string
48
+ type EmailTemplateName = keyof EmailTemplates & string
49
+
50
+ /**
51
+ * `'Actions/Auth/LoginAction'` → `'Auth/LoginAction'`.
52
+ *
53
+ * A generic, so the conditional distributes over the union. A conditional whose
54
+ * checked type is a union ALIAS rather than a naked type parameter does not
55
+ * distribute, and would collapse the whole set to `never`.
56
+ */
57
+ type WithoutActionsPrefix<T> = T extends `Actions/${infer TRest}` ? TRest : never
58
+
59
+ /**
60
+ * What `app/Events.ts` may name against an event: a listener module, or an
61
+ * action by the name `resolveListener` joins back on (no `Actions/` prefix).
62
+ */
63
+ type EventListenerName = ListenerModuleName | WithoutActionsPrefix<ActionName>
64
+
65
+ /**
66
+ * A middleware reference, in every form the resolver reads.
67
+ *
68
+ * The aliases come from the alias maps themselves - they are ordinary modules,
69
+ * and `defineMiddleware` keeps their literal keys - so there is nothing to
70
+ * generate for them either. Class names are included because they resolve:
71
+ * an unaliased middleware is reached by name through `toPascalCase`.
72
+ */
73
+ type MiddlewareAliasName = (keyof DefaultAliases | keyof AppAliases) & string
74
+ type MiddlewareReferenceName = MiddlewareAliasName | MiddlewareClassName
75
+
76
+ /**
77
+ * Every named route, and the path it resolves to.
78
+ *
79
+ * The one name here that is not read off the filesystem: a route name is
80
+ * produced by running code, so `buddy generate:types` loads the route table and
81
+ * writes the answer as a runtime map. Read here the same way as the others.
82
+ */
83
+ type NamedRoutes = typeof import('../auto-imports/routes')['routeNames']
84
+
85
+ // Augment the public entry so declarations follow the router's own dependency instance.
86
+ declare module '@stacksjs/router' {
87
+ interface RouterTypeRegistry {
88
+ actions: ActionName | `${string}Controller@${string}`
89
+ middleware: MiddlewareReferenceName | `!${MiddlewareReferenceName}`
90
+ routes: NamedRoutes
91
+ }
92
+
93
+ interface MiddlewareClasses extends Record<MiddlewareClassName, true> {}
94
+ }
95
+
96
+ declare module '@stacksjs/events' {
97
+ interface EventListeners extends Record<EventListenerName, true> {}
98
+ }
99
+
100
+ declare module '@stacksjs/auth' {
101
+ interface PolicyClasses extends Record<PolicyClassName, true> {}
102
+ }
103
+
104
+ declare module '@stacksjs/email' {
105
+ interface EmailTemplates extends Record<EmailTemplateName, true> {}
106
+ }
107
+
108
+ declare module '@stacksjs/queue' {
109
+ /*
110
+ * The job MODULES, not `Record<JobName, true>`.
111
+ *
112
+ * The names are the same either way - `JobName` is `keyof` this - but the
113
+ * value carries the `Job<Payload>` instance, so dispatching by name can be
114
+ * checked against the payload its handler declares. Mapped to `true`, the
115
+ * registry knew a job existed and nothing about how to call it, and
116
+ * `job('SendWelcomeEmail', { emial: … })` was accepted.
117
+ *
118
+ * Free here: the jobs auto-import is a real re-export barrel rather than a
119
+ * name-to-path map, so `JobName` already resolves these modules.
120
+ */
121
+ interface Jobs extends JobRegistry {}
122
+ }
123
+
124
+ declare module '@stacksjs/actions' {
125
+ interface RunnableActions extends Record<ActionName, true> {}
126
+ }
127
+
128
+ declare module '@stacksjs/scheduler' {
129
+ interface SchedulableJobs extends Record<JobName, true> {}
130
+ interface SchedulableActions extends Record<ActionName, true> {}
131
+ }
132
+
133
+ export {}
@@ -0,0 +1,57 @@
1
+ // Hand-written, unlike its neighbours — `server-auto-imports.d.ts` is
2
+ // regenerated whenever the API starts and would lose these.
3
+ //
4
+ // What a `<script server>` block can reach (stacksjs/stacks#2232). tsc cannot
5
+ // see inside a `.stx` file, but `typecheck:views` hands this file to
6
+ // `stx typecheck` with `--lib`, so the templates are checked against it. It
7
+ // also gives an editor something to complete against.
8
+ //
9
+ // `requestContext` is typed as the object the servers install
10
+ // (`StacksRequestContext` in @stacksjs/config), not restated here. A copy of
11
+ // its methods written out in this file drifted: it had no `site()`, so a
12
+ // template calling `requestContext.site()` as core/sites/README.md tells it to
13
+ // failed `typecheck:views`. The import is relative, as the paths in
14
+ // `browser-auto-imports.d.ts` are: through `@stacksjs/config`, `stx typecheck`
15
+ // resolved the type to `any` and passed a call to a method that does not
16
+ // exist.
17
+
18
+ export {}
19
+
20
+ declare global {
21
+ /**
22
+ * The request this server script is rendering.
23
+ *
24
+ * Installed by `buddy dev` and `buddy serve` (`installRequestScope`), and
25
+ * read from the scope each server opens for a request, so a page, its
26
+ * layout and its components each read their own request while others are
27
+ * in flight. Outside a request, every accessor answers with its empty
28
+ * value rather than throwing. Only those two servers install it: in a
29
+ * process that started neither, `requestContext` is undefined.
30
+ */
31
+ const requestContext: import('../core/config/src/request-context').StacksRequestContext
32
+
33
+ /**
34
+ * Query parameters injected onto the render context by the serve path.
35
+ *
36
+ * Declared optional-shaped for a reason: this is injected by
37
+ * bun-plugin-stx's serve path only. A standalone render (the SSG path, or
38
+ * `processDirectives` called without a request) supplies nothing, and a bare
39
+ * `query` is a ReferenceError there rather than an empty result. Until that
40
+ * path injects an empty-but-shaped object, reads still need a guard:
41
+ *
42
+ * const params = typeof query !== 'undefined' ? query : {}
43
+ *
44
+ * In a process that installed `requestContext`, `requestContext.query()`
45
+ * answers `{}` there instead.
46
+ */
47
+ const query: Record<string, string>
48
+
49
+ /**
50
+ * Cookies injected onto the render context by the serve path.
51
+ *
52
+ * Same caveat as `query`: absent in a standalone render, where a process
53
+ * that installed `requestContext` answers `requestContext.cookie(name)`
54
+ * with null.
55
+ */
56
+ const cookies: Record<string, string>
57
+ }
@@ -0,0 +1 @@
1
+ import '@total-typescript/ts-reset'
@@ -0,0 +1,20 @@
1
+ /* eslint-disable */
2
+
3
+ declare interface Window {
4
+ // extend the window
5
+ }
6
+
7
+ // Markdown files can be treated as components
8
+ declare module '*.md' {
9
+ import type { DefineComponent } from '@stacksjs/stx'
10
+
11
+ const component: DefineComponent<{}, {}, any>
12
+ export default component
13
+ }
14
+
15
+ declare module '*.stx' {
16
+ import type { DefineComponent } from '@stacksjs/stx'
17
+
18
+ const component: DefineComponent<{}, {}, any>
19
+ export default component
20
+ }
@@ -0,0 +1,25 @@
1
+ export interface AuthGuard {
2
+ driver: 'session' | 'token'
3
+ provider: string
4
+ }
5
+
6
+ export interface AuthProvider {
7
+ driver: 'database'
8
+ table: string
9
+ }
10
+
11
+ export interface AuthConfig {
12
+ default: string
13
+ guards: {
14
+ [key: string]: AuthGuard
15
+ }
16
+ providers: {
17
+ [key: string]: AuthProvider
18
+ }
19
+ username: string
20
+ password: string
21
+ tokenExpiry: number
22
+ tokenRotation: number
23
+ defaultAbilities: string[]
24
+ defaultTokenName: string
25
+ }
@@ -0,0 +1,230 @@
1
+ import { Request } from '../core/router/src/request'
2
+
3
+ interface ValidationRule {
4
+ validate: (value: unknown) => { valid: boolean, errors?: Array<{ message: string }> }
5
+ }
6
+
7
+ interface ValidationField {
8
+ rule: ValidationRule
9
+ message: Record<string, string>
10
+ }
11
+
12
+ interface CustomAttributes {
13
+ [key: string]: ValidationField
14
+ }
15
+
16
+ interface RequestDataMigrations {
17
+ name: string
18
+ timestamp: string
19
+
20
+ }
21
+ export interface MigrationsRequestType extends Request {
22
+ validate(attributes?: CustomAttributes): void
23
+ get: <T = string>(element: string, defaultValue?: T) => T
24
+ all(): RequestDataMigrations
25
+ name: string
26
+ timestamp: string
27
+
28
+ }
29
+
30
+ interface RequestDataPasswordResets {
31
+ email: string
32
+ token: string
33
+ created_at: string
34
+
35
+ }
36
+ export interface PasswordResetsRequestType extends Request {
37
+ validate(attributes?: CustomAttributes): void
38
+ get: <T = string>(element: string, defaultValue?: T) => T
39
+ all(): RequestDataPasswordResets
40
+ email: string
41
+ token: string
42
+ created_at: string
43
+
44
+ }
45
+
46
+ interface RequestDataPasskeys {
47
+ id: number
48
+ cred_public_key: string
49
+ user_id: number
50
+ webauthn_user_id: string
51
+ counter: number
52
+ credential_type: string
53
+ device_type: string
54
+ backup_eligible: boolean
55
+ backup_status: boolean
56
+ transports: string
57
+ created_at: string
58
+ updated_at: string
59
+ last_used_at: string
60
+
61
+ }
62
+ export interface PasskeysRequestType extends Request {
63
+ validate(attributes?: CustomAttributes): void
64
+ get: <T = string>(element: string, defaultValue?: T) => T
65
+ all(): RequestDataPasskeys
66
+ id: number
67
+ cred_public_key: string
68
+ user_id: number
69
+ webauthn_user_id: string
70
+ counter: number
71
+ credential_type: string
72
+ device_type: string
73
+ backup_eligible: boolean
74
+ backup_status: boolean
75
+ transports: string
76
+ created_at: string
77
+ updated_at: string
78
+ last_used_at: string
79
+
80
+ }
81
+
82
+ interface RequestDataCommentables {
83
+ id: number
84
+ title: string
85
+ body: string
86
+ status: string
87
+ approved_at: number | null
88
+ rejected_at: number | null
89
+ commentables_id: number
90
+ commentables_type: string
91
+ user_id: number | null
92
+ created_at: string
93
+ updated_at: string | null
94
+
95
+ }
96
+ export interface CommentablesRequestType extends Request {
97
+ validate(attributes?: CustomAttributes): void
98
+ get: <T = string>(element: string, defaultValue?: T) => T
99
+ all(): RequestDataCommentables
100
+ id: number
101
+ title: string
102
+ body: string
103
+ status: string
104
+ approved_at: number | null
105
+ rejected_at: number | null
106
+ commentables_id: number
107
+ commentables_type: string
108
+ user_id: number | null
109
+ created_at: string
110
+ updated_at: string | null
111
+
112
+ }
113
+
114
+ interface RequestDataCommentableUpvotes {
115
+ id: number
116
+ user_id: number
117
+ upvoteable_id: number
118
+ upvoteable_type: string
119
+ created_at: string
120
+
121
+ }
122
+ export interface CommentableUpvotesRequestType extends Request {
123
+ validate(attributes?: CustomAttributes): void
124
+ get: <T = string>(element: string, defaultValue?: T) => T
125
+ all(): RequestDataCommentableUpvotes
126
+ id: number
127
+ user_id: number
128
+ upvoteable_id: number
129
+ upvoteable_type: string
130
+ created_at: string
131
+
132
+ }
133
+
134
+ interface RequestDataCategorizable {
135
+ id: number
136
+ name: string
137
+ slug: string
138
+ description: string
139
+ is_active: boolean
140
+ categorizable_type: string
141
+ created_at: string
142
+ updated_at: string
143
+
144
+ }
145
+ export interface CategorizableRequestType extends Request {
146
+ validate(attributes?: CustomAttributes): void
147
+ get: <T = string>(element: string, defaultValue?: T) => T
148
+ all(): RequestDataCategorizable
149
+ id: number
150
+ name: string
151
+ slug: string
152
+ description: string
153
+ is_active: boolean
154
+ categorizable_type: string
155
+ created_at: string
156
+ updated_at: string
157
+
158
+ }
159
+
160
+ interface RequestDataTaggable {
161
+ id: number
162
+ name: string
163
+ slug: string
164
+ description: string
165
+ is_active: boolean
166
+ taggable_type: string
167
+ created_at: string
168
+ updated_at: string
169
+
170
+ }
171
+ export interface TaggableRequestType extends Request {
172
+ validate(attributes?: CustomAttributes): void
173
+ get: <T = string>(element: string, defaultValue?: T) => T
174
+ all(): RequestDataTaggable
175
+ id: number
176
+ name: string
177
+ slug: string
178
+ description: string
179
+ is_active: boolean
180
+ taggable_type: string
181
+ created_at: string
182
+ updated_at: string
183
+
184
+ }
185
+
186
+ interface RequestDataTaggableModels {
187
+ id: number
188
+ tag_id: number
189
+ taggable_id: number
190
+ taggable_type: string
191
+ created_at: string
192
+ updated_at: string | null
193
+
194
+ }
195
+ export interface TaggableModelsRequestType extends Request {
196
+ validate(attributes?: CustomAttributes): void
197
+ get: <T = string>(element: string, defaultValue?: T) => T
198
+ all(): RequestDataTaggableModels
199
+ id: number
200
+ tag_id: number
201
+ taggable_id: number
202
+ taggable_type: string
203
+ created_at: string
204
+ updated_at: string | null
205
+
206
+ }
207
+
208
+ interface RequestDataCategorizableModels {
209
+ id: number
210
+ category_id: number
211
+ categorizable_id: number
212
+ categorizable_type: string
213
+ created_at: string
214
+ updated_at: string | null
215
+
216
+ }
217
+ export interface CategorizableModelsRequestType extends Request {
218
+ validate(attributes?: CustomAttributes): void
219
+ get: <T = string>(element: string, defaultValue?: T) => T
220
+ all(): RequestDataCategorizableModels
221
+ id: number
222
+ category_id: number
223
+ categorizable_id: number
224
+ categorizable_type: string
225
+ created_at: string
226
+ updated_at: string | null
227
+
228
+ }
229
+
230
+ export type TraitRequest = MigrationsRequestType | PasswordResetsRequestType | PasskeysRequestType | CommentablesRequestType | CommentableUpvotesRequestType | CategorizableRequestType | TaggableRequestType | TaggableModelsRequestType | CategorizableModelsRequestType