@stacksjs/types 0.70.44 → 0.70.53

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/dist/request.d.ts CHANGED
@@ -1,10 +1,32 @@
1
1
  import { User } from '@stacksjs/orm';
2
2
  import type { AuthToken } from '@stacksjs/types';
3
+ import type { Infer } from '@stacksjs/ts-validation';
3
4
  import type { ModelRow } from '@stacksjs/orm';
4
5
  import type { UploadedFile } from '@stacksjs/storage';
5
6
  declare interface RequestData {
6
7
  [key: string]: any
7
8
  }
9
+ /**
10
+ * Cookie-access helper exposed via `request.cookies` on
11
+ * {@link RequestInstance}. The methods mirror bun-router's
12
+ * `CookieAccessor` — duplicated locally so this package doesn't
13
+ * have to depend on bun-router for a single type. Keep the surface
14
+ * in sync if bun-router extends its accessor.
15
+ */
16
+ export declare interface RequestCookies {
17
+ get: (name: string) => string | undefined
18
+ set: (name: string, value: string, options?: {
19
+ path?: string
20
+ domain?: string
21
+ secure?: boolean
22
+ httpOnly?: boolean
23
+ sameSite?: 'strict' | 'lax' | 'none'
24
+ maxAge?: number
25
+ expires?: Date
26
+ }) => void
27
+ delete: (name: string, options?: { path?: string, domain?: string }) => void
28
+ getAll: () => Record<string, string>
29
+ }
8
30
  /**
9
31
  * Collection interface for array data (Laravel-style)
10
32
  */
@@ -45,15 +67,16 @@ export declare interface SafeData<T extends Record<string, any> = Record<string,
45
67
  has: (key: keyof T) => boolean
46
68
  get: <K extends keyof T>(key: K) => T[K]
47
69
  }
48
- export declare interface RequestInstance<TFields extends Record<string, any> = Record<string, any>> {
70
+ export declare interface RequestInstance<TFields extends Record<string, any> = Record<string, any>, TParams extends Record<string, string> = Record<string, string>,> {
49
71
  url: string
50
72
  method: string
51
73
  headers: Headers
52
74
  query: RequestData
53
- params: RouteParams
75
+ params: TParams
54
76
  jsonBody?: any
55
77
  formBody?: any
56
78
  files: Record<string, File | File[]>
79
+ cookies?: RequestCookies
57
80
  get: <K extends keyof TFields & string>(key: K, defaultValue?: TFields[K]) => TFields[K]
58
81
  input: <K extends keyof TFields & string>(key: K, defaultValue?: TFields[K]) => TFields[K]
59
82
  all: () => TFields
@@ -85,9 +108,13 @@ export declare interface RequestInstance<TFields extends Record<string, any> = R
85
108
  allFiles: () => Record<string, UploadedFile | UploadedFile[]>
86
109
  header: (key: string) => string | null
87
110
  bearerToken: () => string | null | AuthToken
111
+ param: <K extends keyof TParams | string, D = string>(
112
+ key: K,
113
+ defaultValue?: D,
114
+ ) => K extends keyof TParams ? TParams[K] : (string | D)
88
115
  getParam: <K extends string>(key: K) => K extends NumericField ? number : string
89
116
  route: (key: string) => number | string | null
90
- getParams: () => RouteParams
117
+ getParams: () => TParams
91
118
  getParamAsInt: (key: string) => number | null
92
119
  old: <T = unknown>(key: keyof TFields & string, defaultValue?: T) => T
93
120
  flashInput: (keys?: (keyof TFields & string)[]) => void
@@ -102,8 +129,65 @@ export declare interface RequestInstance<TFields extends Record<string, any> = R
102
129
  user: () => Promise<UserJsonResponse | undefined>
103
130
  }
104
131
  declare type UserJsonResponse = ModelRow<typeof User>;
132
+ /**
133
+ * Loose route-param shape kept around for back-compat with code that
134
+ * predates {@link RequestInstance}'s `TParams` generic. New code should
135
+ * rely on the path-extracted `TParams` instead — see {@link ExtractParams}.
136
+ *
137
+ * @deprecated Use {@link ExtractParams}-driven typing on the action /
138
+ * route signature instead. URL route params are always strings at
139
+ * runtime; the `string | number` here misled callers into thinking
140
+ * the framework coerced numbers automatically (it doesn't —
141
+ * `Number(request.params.id)` is the correct pattern).
142
+ */
105
143
  declare type RouteParams = { [key: string]: string | number } | null;
144
+ /**
145
+ * Legacy hard-coded list of param names treated as `number` by
146
+ * {@link RequestInstance.getParam}. The name-match heuristic is
147
+ * brittle (`'judgeId'`, `'user_id'`, etc. all silently fall through
148
+ * to `string`) and will be retired in a future release.
149
+ *
150
+ * @deprecated The name-match returns `number` for these specific keys
151
+ * only — pass the value through {@link Number} or
152
+ * {@link RequestInstance.getParamAsInt} for explicit, predictable
153
+ * coercion. See stacksjs/stacks#1851 Phase 3.
154
+ */
106
155
  declare type NumericField = 'id' | 'age' | 'count' | 'quantity' | 'amount' | 'price' | 'total' | 'score' | 'rating' | 'duration' | 'size' | 'weight' | 'height' | 'width' | 'length' | 'distance' | 'speed' | 'temperature' | 'volume' | 'capacity' | 'density' | 'pressure' | 'force' | 'energy' | 'power' | 'frequency' | 'voltage' | 'current' | 'resistance' | 'time' | 'date' | 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond';
156
+ /**
157
+ * Template-literal helper that extracts named route params from a
158
+ * path string (stacksjs/stacks#1851 Phase 2a). Supports both Stacks's
159
+ * brace-style (`/users/{id}`) and Express-style (`/users/:id`) so a
160
+ * project using either gets typed `params` out of the box.
161
+ *
162
+ * @example
163
+ * ExtractParams<'/api/judges/{id}/follow'> // { id: string }
164
+ * ExtractParams<'/api/orders/:orderId/items/:itemId'> // { orderId: string, itemId: string }
165
+ * ExtractParams<'/api/health'> // Record<string, never>
166
+ */
167
+ // Step 1: pull the next param name out of either `{name}` or `:name`,
168
+ // recurse on the rest, and union the keys.
169
+ declare type ExtractParamKeys<S extends string> = S extends `${string}{${infer Key}}${infer Rest}`
170
+ ? Key | ExtractParamKeys<Rest>
171
+ // colon form: `…/:name/…` (colon must be at a segment boundary
172
+ // so we don't match `:` inside e.g. a port number; the `/` before
173
+ // it enforces that)
174
+ : S extends `${string}/:${infer Key}/${infer Rest}`
175
+ ? KeyHead<Key> | ExtractParamKeys<`/${Rest}`>
176
+ // tail colon-form: `…/:name` (no trailing slash)
177
+ : S extends `${string}/:${infer Key}`
178
+ ? KeyHead<Key>
179
+ : never;
180
+ // `KeyHead<'name>'>` → `'name>'` because TS template literal infers
181
+ // the longest possible match. We need to handle params that are at
182
+ // the end of the path AND followed by a query string. This util
183
+ // truncates a captured key at the first non-name character.
184
+ declare type KeyHead<S extends string> = S extends `${infer H}/${string}` ? H :
185
+ S extends `${infer H}?${string}` ? H :
186
+ S extends `${infer H}.${string}` ? H :
187
+ S;
188
+ export type ExtractParams<S extends string> = [ExtractParamKeys<S>] extends [never]
189
+ ? Record<string, never>
190
+ : { [K in ExtractParamKeys<S>]: string }
107
191
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
108
192
  /**
109
193
  * RequestInstance - Generic, model-aware request interface with Laravel-style methods.
@@ -151,4 +235,23 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' |
151
235
  * })
152
236
  * ```
153
237
  */
154
- export type ActionRequest<TFields extends Record<string, any> = Record<string, any>> = RequestInstance<TFields>;
238
+ export type ActionRequest<TFields extends Record<string, any> = Record<string, any>, TParams extends Record<string, string> = Record<string, string>,> = RequestInstance<TFields, TParams>;
239
+ /**
240
+ * Read the body shape declared by an action's `validations:` field as
241
+ * a TypeScript object type (stacksjs/stacks#1851 Phase 2b). Threaded
242
+ * into {@link RequestInstance}'s `TFields` so `request.all()` returns
243
+ * the body shape with the field types {@link Infer}'d from each
244
+ * `schema.X()` rule.
245
+ *
246
+ * @example
247
+ * const validations = {
248
+ * email: { rule: schema.string().email() },
249
+ * password: { rule: schema.string().min(8) },
250
+ * remember: { rule: schema.boolean() },
251
+ * } as const
252
+ * type Body = InferValidations<typeof validations>
253
+ * // → { email: string, password: string, remember: boolean }
254
+ */
255
+ export type InferValidations<V extends Record<string, { rule: any }>> = {
256
+ [K in keyof V]: Infer<V[K]['rule']>
257
+ }
@@ -24,7 +24,7 @@ export type {
24
24
  // type Sorts = any
25
25
  // type Sort = any
26
26
  export declare interface SearchEngineOptions {
27
- driver: 'meilisearch' | 'algolia' | 'opensearch'
27
+ driver: 'meilisearch' | 'algolia' | 'opensearch' | 'typesense'
28
28
  opensearch?: {
29
29
  host: string
30
30
  protocol: number
@@ -43,6 +43,12 @@ export declare interface SearchEngineOptions {
43
43
  apiKey: string
44
44
  searchOnlyApiKey?: string
45
45
  }
46
+ typesense?: {
47
+ host?: string
48
+ port?: number
49
+ protocol?: string
50
+ apiKey?: string
51
+ }
46
52
  filters?: {
47
53
  [key: string]: string
48
54
  }
@@ -118,5 +124,6 @@ export declare interface SearchOptions {
118
124
  sortable: string[]
119
125
  filterable: string[]
120
126
  options?: SearchEngineOptions
127
+ denormalize?: Record<string, string>
121
128
  }
122
129
  export type SearchEngineConfig = Partial<SearchEngineOptions>;
@@ -21,6 +21,14 @@ export declare interface ServicesOptions {
21
21
  redirectUrl: string
22
22
  scopes?: string[]
23
23
  }
24
+ apple?: {
25
+ clientId: string
26
+ teamId: string
27
+ keyId: string
28
+ privateKey: string
29
+ redirectUrl: string
30
+ scopes?: string[]
31
+ }
24
32
  facebook?: {
25
33
  clientId: string
26
34
  clientSecret: string
@@ -134,6 +142,7 @@ export declare interface ServicesOptions {
134
142
  stripe?: {
135
143
  secretKey?: string
136
144
  publicKey?: string
145
+ webhookSecret?: string
137
146
  apiVersion?: string
138
147
  }
139
148
  }
package/dist/stacks.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AiConfig, AnalyticsConfig, AppConfig, AuthConfig, BinaryConfig, CacheConfig, CloudConfig, DashboardConfig, DatabaseConfig, DnsConfig, DocsConfig, EmailConfig, ErrorConfig, FilesystemsConfig, GitConfig, HashingConfig, LibraryConfig, LoggingConfig, NotificationConfig, PaymentConfig, Ports, QueueConfig, RealtimeConfig, SaasConfig, SearchEngineConfig, SecurityConfig, ServicesConfig, Team, UiConfig } from '.';
1
+ import type { AiConfig, AnalyticsConfig, AppConfig, AuthConfig, BinaryConfig, CacheConfig, CloudConfig, CmsConfig, CommerceConfig, CorsConfig, DashboardConfig, DatabaseConfig, DnsConfig, DocsConfig, EmailConfig, ErrorConfig, FilesystemsConfig, GitConfig, HashingConfig, LibraryConfig, LoggingConfig, MarketingConfig, MonitoringConfig, NotificationConfig, PaymentConfig, Ports, QueueConfig, RealtimeConfig, SaasConfig, SearchEngineConfig, SecurityConfig, ServicesConfig, Team, UiConfig } from '.';
2
2
  /**
3
3
  * **Stacks Options**
4
4
  *
@@ -11,10 +11,13 @@ export declare interface StacksOptions {
11
11
  analytics: AnalyticsConfig
12
12
  app: AppConfig
13
13
  auth: AuthConfig
14
+ cors?: CorsConfig
14
15
  realtime: RealtimeConfig
15
16
  cache: CacheConfig
16
17
  cli: BinaryConfig
17
18
  cloud: CloudConfig
19
+ cms: CmsConfig
20
+ commerce: CommerceConfig
18
21
  dashboard: DashboardConfig
19
22
  database: DatabaseConfig
20
23
  dns: DnsConfig
@@ -25,6 +28,8 @@ export declare interface StacksOptions {
25
28
  hashing: HashingConfig
26
29
  library: LibraryConfig
27
30
  logging: LoggingConfig
31
+ marketing: MarketingConfig
32
+ monitoring: MonitoringConfig
28
33
  notification: NotificationConfig
29
34
  payment: PaymentConfig
30
35
  ports: Ports
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/types",
3
3
  "type": "module",
4
- "version": "0.70.44",
4
+ "version": "0.70.53",
5
5
  "description": "The Stacks framework types.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -45,12 +45,12 @@
45
45
  "prepublishOnly": "bun run build"
46
46
  },
47
47
  "dependencies": {
48
- "@stacksjs/bunpress": "^0.1.4",
49
- "@stacksjs/ts-validation": "^0.4.10",
50
- "bun-query-builder": "^0.1.21"
48
+ "@stacksjs/bunpress": "^0.1.11",
49
+ "@stacksjs/ts-validation": "^0.5.0",
50
+ "bun-query-builder": "^0.1.38"
51
51
  },
52
52
  "devDependencies": {
53
- "@stacksjs/validation": "^0.70.44",
53
+ "@stacksjs/validation": "0.70.53",
54
54
  "@types/bun": "^1.3.11",
55
55
  "typescript": "^5.9.3"
56
56
  },
package/src/auth.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  export interface AuthOptions {
2
+ /**
3
+ * Top-level feature gate. When `false`, the auth feature is inert at boot
4
+ * (no token/password-reset/email-verification flows wired up). Missing or
5
+ * `true` means auth is on.
6
+ */
7
+ enabled?: boolean
8
+ /** Optional deploy-target gate, e.g. `['production']`. */
9
+ env?: string[]
2
10
  /**
3
11
  * The default authentication guard to use
4
12
  */
@@ -90,6 +98,37 @@ export interface AuthOptions {
90
98
  * @default 60
91
99
  */
92
100
  throttle: number
101
+
102
+ /**
103
+ * Reset-link URL template. Supports `{token}` and `{email}`
104
+ * placeholders. Absolute templates (`https://…`) are used as-is;
105
+ * path templates are prefixed with the app URL. Lets apps whose
106
+ * reset page lives on a custom route reuse `passwordResets().sendEmail()`
107
+ * instead of hand-rolling the send.
108
+ * @default '/password/reset/{token}?email={email}'
109
+ */
110
+ url?: string
111
+ }
112
+
113
+ /**
114
+ * Email verification configuration
115
+ */
116
+ emailVerification?: {
117
+ /**
118
+ * Token expiration time in minutes
119
+ * @default 60
120
+ */
121
+ expire?: number
122
+
123
+ /**
124
+ * Verification-link URL template. Supports `{id}` and `{token}`
125
+ * placeholders. Absolute templates (`https://…`) are used as-is;
126
+ * path templates are prefixed with the app URL. Lets apps whose
127
+ * verify page lives on a custom route reuse `sendVerificationEmail()`
128
+ * instead of hand-rolling the send.
129
+ * @default '/verify-email/{id}/{token}'
130
+ */
131
+ url?: string
93
132
  }
94
133
  }
95
134
 
package/src/cache.ts CHANGED
@@ -6,11 +6,15 @@ export interface CacheOptions {
6
6
  * **Cache Driver**
7
7
  *
8
8
  * The cache driver that will be used by your application to store
9
- * cached data. Supports 'memory' and 'redis' drivers.
9
+ * cached data. Supports 'memory', 'redis', and 'singlestore' drivers.
10
+ *
11
+ * The 'singlestore' driver persists cache entries in a SingleStore
12
+ * rowstore table (with epoch-based TTL) — useful when you want a single
13
+ * SingleStore cluster to back both your primary data and your cache.
10
14
  *
11
15
  * @default "memory"
12
16
  */
13
- driver: 'memory' | 'redis'
17
+ driver: 'memory' | 'redis' | 'singlestore'
14
18
 
15
19
  /**
16
20
  * **Cache Prefix**
@@ -117,6 +121,49 @@ export interface CacheOptions {
117
121
  */
118
122
  deleteOnExpire?: boolean
119
123
  }
124
+
125
+ singlestore?: {
126
+ /**
127
+ * SingleStore host (MySQL wire protocol)
128
+ * @default "127.0.0.1"
129
+ */
130
+ host?: string
131
+
132
+ /**
133
+ * SingleStore port
134
+ * @default 3306
135
+ */
136
+ port?: number
137
+
138
+ /**
139
+ * SingleStore username
140
+ * @default "root"
141
+ */
142
+ username?: string
143
+
144
+ /**
145
+ * SingleStore password
146
+ */
147
+ password?: string
148
+
149
+ /**
150
+ * Database that holds the cache table
151
+ * @default "stacks"
152
+ */
153
+ database?: string
154
+
155
+ /**
156
+ * Table used to store cache entries
157
+ * @default "stacks_cache"
158
+ */
159
+ table?: string
160
+
161
+ /**
162
+ * Enable TLS (required by managed SingleStore / Helios)
163
+ * @default false
164
+ */
165
+ ssl?: boolean
166
+ }
120
167
  }
121
168
  }
122
169
 
package/src/cli.ts CHANGED
@@ -238,6 +238,7 @@ export type CreateBooleanOption =
238
238
  | 'functions'
239
239
  | 'api'
240
240
  | 'database'
241
+ | 'minimal'
241
242
  export type CreateOptions = {
242
243
  [key in CreateBooleanOption]: boolean
243
244
  } & {
@@ -250,6 +251,7 @@ export type DevOption =
250
251
  | 'frontend'
251
252
  | 'api'
252
253
  | 'desktop'
254
+ | 'native'
253
255
  | 'all'
254
256
  | 'email'
255
257
  | 'system-tray'
package/src/cms.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * **CMS Options**
3
+ *
4
+ * Top-level feature gate plus any future CMS-wide settings. The CMS bundle
5
+ * (Post / Page / Author / Comment / Tag / Category models + edit dashboards)
6
+ * stays inert at boot when `enabled` is `false`.
7
+ */
8
+ export interface CmsOptions {
9
+ enabled?: boolean
10
+ /** Optional deploy-target gate, e.g. `['production']`. */
11
+ env?: string[]
12
+ }
13
+
14
+ export type CmsConfig = Partial<CmsOptions>
@@ -0,0 +1,18 @@
1
+ /**
2
+ * **Commerce Options**
3
+ *
4
+ * Top-level feature gate plus storefront defaults. The commerce bundle
5
+ * (Order / Cart / Product / Customer / Coupon / GiftCard / Shipping models +
6
+ * storefront API) stays inert at boot when `enabled` is `false`.
7
+ */
8
+ export interface CommerceOptions {
9
+ enabled?: boolean
10
+ /** Optional deploy-target gate, e.g. `['production']`. */
11
+ env?: string[]
12
+ /** Default storefront currency (ISO 4217), e.g. `'USD'`. */
13
+ currency?: string
14
+ /** Default tax rate applied when no product/region rule overrides. */
15
+ defaultTaxRate?: number
16
+ }
17
+
18
+ export type CommerceConfig = Partial<CommerceOptions>
package/src/cors.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * CORS configuration types (stacksjs/stacks#1859 H-2).
3
+ *
4
+ * Lives in `@stacksjs/types` so userland `config/cors.ts` files can
5
+ * `import type { CorsConfig } from '@stacksjs/types'` and get full
6
+ * IntelliSense, and so the Cors middleware can read
7
+ * `config.cors` with proper typing instead of an `as any` cast.
8
+ */
9
+
10
+ /**
11
+ * Raw CORS configuration — every field optional. Defaults fill in
12
+ * any field that's missing when the middleware resolves config.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // config/cors.ts
17
+ * import type { CorsConfig } from '@stacksjs/types'
18
+ *
19
+ * export default {
20
+ * origin: ['https://app.example.com', 'https://admin.example.com'],
21
+ * credentials: true,
22
+ * methods: ['GET', 'POST', 'PUT', 'DELETE'],
23
+ * allowedHeaders: ['Content-Type', 'Authorization', 'X-CSRF-Token'],
24
+ * maxAge: 600,
25
+ * } satisfies CorsConfig
26
+ * ```
27
+ */
28
+ export interface CorsConfig {
29
+ /**
30
+ * Allowed origins.
31
+ *
32
+ * - `'*'` permits any origin (incompatible with `credentials: true`).
33
+ * - A string array enumerates the explicit allow-list; the request's
34
+ * `Origin` header is reflected verbatim when it matches.
35
+ * - A predicate function lets you implement dynamic allow-lists
36
+ * (e.g. wildcards, regex, environment-dependent).
37
+ */
38
+ origin?: '*' | string[] | ((origin: string) => boolean)
39
+
40
+ /**
41
+ * HTTP methods the server accepts cross-origin. Echoed back in
42
+ * preflight `Access-Control-Allow-Methods` responses.
43
+ */
44
+ methods?: string[]
45
+
46
+ /**
47
+ * Request headers the browser is allowed to send cross-origin.
48
+ * Echoed back in preflight `Access-Control-Allow-Headers` responses.
49
+ */
50
+ allowedHeaders?: string[]
51
+
52
+ /**
53
+ * Response headers the browser is allowed to expose to JS via
54
+ * `fetch(...).then(r => r.headers.get(...))`. Anything not listed
55
+ * here is hidden from the SPA even though it's on the wire.
56
+ */
57
+ exposedHeaders?: string[]
58
+
59
+ /**
60
+ * Whether cookies / Authorization headers may be included
61
+ * cross-origin. **Setting this to `true` is incompatible with
62
+ * `origin: '*'`** — the spec requires an explicit origin.
63
+ */
64
+ credentials?: boolean
65
+
66
+ /**
67
+ * How long the browser may cache the preflight response, in
68
+ * seconds. Default 86_400 (24h). Lower values cost more preflights
69
+ * but pick up policy changes faster.
70
+ */
71
+ maxAge?: number
72
+ }
73
+
74
+ /**
75
+ * Resolved CORS configuration with all defaults applied. This is the
76
+ * shape the middleware passes through `applyCorsHeaders` /
77
+ * `buildPreflightResponse`.
78
+ */
79
+ export interface ResolvedCorsConfig {
80
+ origin: '*' | string[] | ((origin: string) => boolean)
81
+ methods: string[]
82
+ allowedHeaders: string[]
83
+ exposedHeaders: string[]
84
+ credentials: boolean
85
+ maxAge: number
86
+ }