@stacksjs/types 0.70.45 → 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/LICENSE.md +21 -0
- package/dist/auth.d.ts +7 -0
- package/dist/cache.d.ts +10 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cms.d.ts +12 -0
- package/dist/commerce.d.ts +14 -0
- package/dist/cors.d.ts +47 -0
- package/dist/dashboard.d.ts +75 -0
- package/dist/database.d.ts +10 -0
- package/dist/email.d.ts +6 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1 -1
- package/dist/logging.d.ts +3 -0
- package/dist/marketing.d.ts +12 -0
- package/dist/model-dashboard-augmentation.d.ts +8 -0
- package/dist/model.d.ts +4 -1
- package/dist/monitoring.d.ts +12 -0
- package/dist/queue.d.ts +2 -0
- package/dist/realtime.d.ts +8 -8
- package/dist/request.d.ts +107 -4
- package/dist/search-engine.d.ts +8 -1
- package/dist/services.d.ts +9 -0
- package/dist/stacks.d.ts +6 -1
- package/package.json +5 -5
- package/src/auth.ts +39 -0
- package/src/cache.ts +49 -2
- package/src/cli.ts +1 -0
- package/src/cms.ts +14 -0
- package/src/commerce.ts +18 -0
- package/src/cors.ts +86 -0
- package/src/dashboard.ts +178 -0
- package/src/database.ts +14 -0
- package/src/email.ts +94 -0
- package/src/index.ts +9 -0
- package/src/logging.ts +32 -0
- package/src/marketing.ts +14 -0
- package/src/model-dashboard-augmentation.ts +51 -0
- package/src/model.ts +33 -1
- package/src/monitoring.ts +14 -0
- package/src/queue.ts +8 -0
- package/src/request.ts +166 -5
- package/src/search-engine.ts +32 -1
- package/src/services.ts +26 -0
- package/src/stacks.ts +46 -3
package/src/index.ts
CHANGED
|
@@ -15,10 +15,17 @@ export * from './cdn'
|
|
|
15
15
|
export * from './chat'
|
|
16
16
|
export * from './cli'
|
|
17
17
|
export * from './cloud'
|
|
18
|
+
export * from './cms'
|
|
19
|
+
export * from './commerce'
|
|
18
20
|
export * from './components'
|
|
19
21
|
export * from './configure'
|
|
22
|
+
export * from './cors'
|
|
20
23
|
export * from './cron-jobs'
|
|
21
24
|
export * from './dashboard'
|
|
25
|
+
// Module-augments bun-query-builder's BrowserModelDefinition with the
|
|
26
|
+
// stacks `dashboard` slot. Importing this file (transitively via the
|
|
27
|
+
// barrel) is what makes `defineModel({ dashboard: {...} })` typecheck.
|
|
28
|
+
export * from './model-dashboard-augmentation'
|
|
22
29
|
export * from './database'
|
|
23
30
|
export * from './dependencies'
|
|
24
31
|
export * from './deploy'
|
|
@@ -37,8 +44,10 @@ export * from './i18n'
|
|
|
37
44
|
export * from './library'
|
|
38
45
|
export * from './logging'
|
|
39
46
|
export * from './manifest'
|
|
47
|
+
export * from './marketing'
|
|
40
48
|
export * from './model'
|
|
41
49
|
export * from './model-names'
|
|
50
|
+
export * from './monitoring'
|
|
42
51
|
export * from './notifications'
|
|
43
52
|
export * from './pages'
|
|
44
53
|
export * from './payments'
|
package/src/logging.ts
CHANGED
|
@@ -25,6 +25,38 @@ export interface LoggingOptions {
|
|
|
25
25
|
* @default 'storage/logs/deployments.log'
|
|
26
26
|
*/
|
|
27
27
|
deploymentsPath: string
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* **Minimum Log Level**
|
|
31
|
+
*
|
|
32
|
+
* Messages below this level are suppressed. The `LOG_LEVEL` env var
|
|
33
|
+
* overrides this when set (stacksjs/stacks#1935).
|
|
34
|
+
*
|
|
35
|
+
* @default 'info'
|
|
36
|
+
*/
|
|
37
|
+
level?: 'debug' | 'info' | 'success' | 'warning' | 'error'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* **Output Format**
|
|
41
|
+
*
|
|
42
|
+
* `'json'` for structured output (production), `'text'` for the
|
|
43
|
+
* human-readable dev view. The `LOG_FORMAT` env var overrides this;
|
|
44
|
+
* default is `'json'` in production, `'text'` otherwise.
|
|
45
|
+
*
|
|
46
|
+
* @default 'text'
|
|
47
|
+
*/
|
|
48
|
+
format?: 'json' | 'text'
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* **Write To File**
|
|
52
|
+
*
|
|
53
|
+
* Whether logs are persisted to `logsPath`'s directory as daily
|
|
54
|
+
* files. Set `false` for console-only output (e.g. when the platform
|
|
55
|
+
* captures stdout).
|
|
56
|
+
*
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
writeToFile?: boolean
|
|
28
60
|
}
|
|
29
61
|
|
|
30
62
|
export type LoggingConfig = Partial<LoggingOptions>
|
package/src/marketing.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Marketing Options**
|
|
3
|
+
*
|
|
4
|
+
* Top-level feature gate for the marketing bundle (`/api/email/subscribe`,
|
|
5
|
+
* `/api/contact`, Campaign / EmailList / SocialPost). Stays inert at boot
|
|
6
|
+
* when `enabled` is `false`.
|
|
7
|
+
*/
|
|
8
|
+
export interface MarketingOptions {
|
|
9
|
+
enabled?: boolean
|
|
10
|
+
/** Optional deploy-target gate, e.g. `['production']`. */
|
|
11
|
+
env?: string[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type MarketingConfig = Partial<MarketingOptions>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module augmentation that adds the Stacks-specific `dashboard` slot to
|
|
3
|
+
* bun-query-builder's `BrowserModelDefinition`.
|
|
4
|
+
*
|
|
5
|
+
* Why this lives here (Stacks, not bqb):
|
|
6
|
+
*
|
|
7
|
+
* The `dashboard` config is a Stacks framework concept — it influences
|
|
8
|
+
* how the model appears in `buddy dev --dashboard`'s sidebar. It has no
|
|
9
|
+
* meaning outside the dashboard surface, so adding it to bqb's core
|
|
10
|
+
* types would force every bqb consumer to carry weight they don't need.
|
|
11
|
+
*
|
|
12
|
+
* Declaration merging keeps the typing clean at every call site:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { defineModel } from '@stacksjs/orm'
|
|
16
|
+
*
|
|
17
|
+
* defineModel({
|
|
18
|
+
* name: 'AuditLog',
|
|
19
|
+
* dashboard: { section: 'management', roles: ['admin'] }, // ← typed
|
|
20
|
+
* attributes: { … },
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Without this augmentation, the `dashboard` property would still pass
|
|
25
|
+
* (because bqb's `defineModel<TDef extends BrowserModelDefinition>` uses
|
|
26
|
+
* `extends`, which permits excess properties), but with no autocomplete
|
|
27
|
+
* and no shape validation. The augmentation gives both.
|
|
28
|
+
*
|
|
29
|
+
* Loading note: this file only declares types — no runtime effects. It
|
|
30
|
+
* must be reachable via `@stacksjs/types`' barrel so any package that
|
|
31
|
+
* already imports from `@stacksjs/types` picks up the augmentation in
|
|
32
|
+
* the same compilation. Re-exported from `index.ts`.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import type { DashboardModelOptions } from './dashboard'
|
|
36
|
+
|
|
37
|
+
declare module 'bun-query-builder' {
|
|
38
|
+
interface BrowserModelDefinition {
|
|
39
|
+
/**
|
|
40
|
+
* Stacks dashboard sidebar configuration for this model.
|
|
41
|
+
* See {@link DashboardModelOptions} for the full shape.
|
|
42
|
+
*
|
|
43
|
+
* Omit to use defaults (model is shown under its auto-categorised
|
|
44
|
+
* section using `iconMap` + the model name).
|
|
45
|
+
*/
|
|
46
|
+
readonly dashboard?: DashboardModelOptions
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Re-export for `import type { DashboardModelOptions } from '@stacksjs/types'`.
|
|
51
|
+
export type { DashboardModelOptions }
|
package/src/model.ts
CHANGED
|
@@ -103,10 +103,15 @@ type Action = ActionPath | ActionName | undefined
|
|
|
103
103
|
|
|
104
104
|
export type ApiRoutes = 'index' | 'show' | 'store' | 'update' | 'destroy'
|
|
105
105
|
|
|
106
|
-
export type SocialProviders = 'google' | 'github' | 'twitter' | 'facebook'
|
|
106
|
+
export type SocialProviders = 'google' | 'github' | 'apple' | 'twitter' | 'facebook'
|
|
107
107
|
|
|
108
108
|
export interface SeedOptions {
|
|
109
109
|
count: number
|
|
110
|
+
/**
|
|
111
|
+
* Fixed rows merged over factory output for the first N entries (N = fixtures.length).
|
|
112
|
+
* Keys use model attribute names (camelCase); stored as snake_case columns.
|
|
113
|
+
*/
|
|
114
|
+
fixtures?: Array<Record<string, unknown>>
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
type LogAttribute = string
|
|
@@ -185,7 +190,17 @@ export interface ModelOptions extends Base {
|
|
|
185
190
|
commentables?: boolean // defaults to false
|
|
186
191
|
useAuth?: boolean | UserAuthOptions // defaults to false
|
|
187
192
|
authenticatable?: boolean | UserAuthOptions // useAuth alias
|
|
193
|
+
/**
|
|
194
|
+
* @deprecated stacksjs/stacks#1929 — the `useSeeder` trait only
|
|
195
|
+
* existed to drive the auto-walker, which is removed from
|
|
196
|
+
* `./buddy seed` (stacksjs/stacks#1919). Seeding is now owned by
|
|
197
|
+
* class seeders: a `database/seeders/<Model>Seeder.ts` file calling
|
|
198
|
+
* `factory.generate(Model, { count })`. Run `./buddy seed:scaffold`
|
|
199
|
+
* to codemod existing traits into seeder files (and strip the
|
|
200
|
+
* trait). This field is scheduled for removal in the next major.
|
|
201
|
+
*/
|
|
188
202
|
useSeeder?: boolean | SeedOptions // defaults to a count of 10
|
|
203
|
+
/** @deprecated alias of {@link useSeeder} — see stacksjs/stacks#1929. */
|
|
189
204
|
seedable?: boolean | SeedOptions // useSeeder alias
|
|
190
205
|
useSearch?: boolean | SearchOptions // defaults to false
|
|
191
206
|
useSocials?: SocialOptions // defaults to false
|
|
@@ -253,6 +268,23 @@ export interface Attribute {
|
|
|
253
268
|
export interface CompositeIndex {
|
|
254
269
|
name: string
|
|
255
270
|
columns: string[]
|
|
271
|
+
/**
|
|
272
|
+
* Emit `UNIQUE` on the index — turns a multi-column index into a
|
|
273
|
+
* multi-column unique constraint. Combine with `where:` for a
|
|
274
|
+
* partial unique index (stacksjs/stacks#1943).
|
|
275
|
+
*
|
|
276
|
+
* @default false
|
|
277
|
+
*/
|
|
278
|
+
unique?: boolean
|
|
279
|
+
/**
|
|
280
|
+
* Partial-index `WHERE` clause as a raw SQL expression — e.g.
|
|
281
|
+
* `'user_id IS NOT NULL'`. Lets the constraint apply to a subset of
|
|
282
|
+
* rows; the canonical case is "prevent a logged-in user from flagging
|
|
283
|
+
* the same review twice, but allow anonymous flags (user_id NULL) to
|
|
284
|
+
* repeat" (stacksjs/stacks#1943). Emitted verbatim, so don't
|
|
285
|
+
* interpolate untrusted input.
|
|
286
|
+
*/
|
|
287
|
+
where?: string
|
|
256
288
|
}
|
|
257
289
|
|
|
258
290
|
export interface AttributesElements {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Monitoring Options**
|
|
3
|
+
*
|
|
4
|
+
* Top-level feature gate for the monitoring bundle (Error model +
|
|
5
|
+
* error-tracking views and actions). Stays inert at boot when `enabled`
|
|
6
|
+
* is `false`.
|
|
7
|
+
*/
|
|
8
|
+
export interface MonitoringOptions {
|
|
9
|
+
enabled?: boolean
|
|
10
|
+
/** Optional deploy-target gate, e.g. `['production']`. */
|
|
11
|
+
env?: string[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type MonitoringConfig = Partial<MonitoringOptions>
|
package/src/queue.ts
CHANGED
|
@@ -250,6 +250,14 @@ export interface Dispatchable {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
export interface QueueOptions {
|
|
253
|
+
/**
|
|
254
|
+
* Top-level feature gate. When `false`, the queue runtime is skipped at
|
|
255
|
+
* boot (no Job/FailedJob model load, no queue worker startup). Missing or
|
|
256
|
+
* `true` means the queue feature is on.
|
|
257
|
+
*/
|
|
258
|
+
enabled?: boolean
|
|
259
|
+
/** Optional deploy-target gate, e.g. `['production']`. */
|
|
260
|
+
env?: string[]
|
|
253
261
|
/** Default queue driver */
|
|
254
262
|
default: QueueDriver
|
|
255
263
|
/** Queue connections */
|
package/src/request.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type { ModelRow } from '@stacksjs/orm'
|
|
|
2
2
|
import { User } from '@stacksjs/orm'
|
|
3
3
|
import type { UploadedFile } from '@stacksjs/storage'
|
|
4
4
|
import type { AuthToken, RouteParam } from '@stacksjs/types'
|
|
5
|
+
// `Infer<T extends Validator<U>>` resolves to the validator's output
|
|
6
|
+
// type — `Infer<typeof schema.string()> → string`. Type-only import
|
|
7
|
+
// keeps this package free of a runtime ts-validation dependency.
|
|
8
|
+
import type { Infer } from '@stacksjs/ts-validation'
|
|
5
9
|
|
|
6
10
|
type UserJsonResponse = ModelRow<typeof User>
|
|
7
11
|
|
|
@@ -9,10 +13,96 @@ interface RequestData {
|
|
|
9
13
|
[key: string]: any
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Loose route-param shape kept around for back-compat with code that
|
|
18
|
+
* predates {@link RequestInstance}'s `TParams` generic. New code should
|
|
19
|
+
* rely on the path-extracted `TParams` instead — see {@link ExtractParams}.
|
|
20
|
+
*
|
|
21
|
+
* @deprecated Use {@link ExtractParams}-driven typing on the action /
|
|
22
|
+
* route signature instead. URL route params are always strings at
|
|
23
|
+
* runtime; the `string | number` here misled callers into thinking
|
|
24
|
+
* the framework coerced numbers automatically (it doesn't —
|
|
25
|
+
* `Number(request.params.id)` is the correct pattern).
|
|
26
|
+
*/
|
|
12
27
|
type RouteParams = { [key: string]: string | number } | null
|
|
13
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Legacy hard-coded list of param names treated as `number` by
|
|
31
|
+
* {@link RequestInstance.getParam}. The name-match heuristic is
|
|
32
|
+
* brittle (`'judgeId'`, `'user_id'`, etc. all silently fall through
|
|
33
|
+
* to `string`) and will be retired in a future release.
|
|
34
|
+
*
|
|
35
|
+
* @deprecated The name-match returns `number` for these specific keys
|
|
36
|
+
* only — pass the value through {@link Number} or
|
|
37
|
+
* {@link RequestInstance.getParamAsInt} for explicit, predictable
|
|
38
|
+
* coercion. See stacksjs/stacks#1851 Phase 3.
|
|
39
|
+
*/
|
|
14
40
|
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'
|
|
15
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Cookie-access helper exposed via `request.cookies` on
|
|
44
|
+
* {@link RequestInstance}. The methods mirror bun-router's
|
|
45
|
+
* `CookieAccessor` — duplicated locally so this package doesn't
|
|
46
|
+
* have to depend on bun-router for a single type. Keep the surface
|
|
47
|
+
* in sync if bun-router extends its accessor.
|
|
48
|
+
*/
|
|
49
|
+
export interface RequestCookies {
|
|
50
|
+
get: (name: string) => string | undefined
|
|
51
|
+
set: (name: string, value: string, options?: {
|
|
52
|
+
path?: string
|
|
53
|
+
domain?: string
|
|
54
|
+
secure?: boolean
|
|
55
|
+
httpOnly?: boolean
|
|
56
|
+
sameSite?: 'strict' | 'lax' | 'none'
|
|
57
|
+
maxAge?: number
|
|
58
|
+
expires?: Date
|
|
59
|
+
}) => void
|
|
60
|
+
delete: (name: string, options?: { path?: string, domain?: string }) => void
|
|
61
|
+
getAll: () => Record<string, string>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Template-literal helper that extracts named route params from a
|
|
66
|
+
* path string (stacksjs/stacks#1851 Phase 2a). Supports both Stacks's
|
|
67
|
+
* brace-style (`/users/{id}`) and Express-style (`/users/:id`) so a
|
|
68
|
+
* project using either gets typed `params` out of the box.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ExtractParams<'/api/judges/{id}/follow'> // { id: string }
|
|
72
|
+
* ExtractParams<'/api/orders/:orderId/items/:itemId'> // { orderId: string, itemId: string }
|
|
73
|
+
* ExtractParams<'/api/health'> // Record<string, never>
|
|
74
|
+
*/
|
|
75
|
+
// Step 1: pull the next param name out of either `{name}` or `:name`,
|
|
76
|
+
// recurse on the rest, and union the keys.
|
|
77
|
+
type ExtractParamKeys<S extends string> =
|
|
78
|
+
// brace form: `…/{name}/…`
|
|
79
|
+
S extends `${string}{${infer Key}}${infer Rest}`
|
|
80
|
+
? Key | ExtractParamKeys<Rest>
|
|
81
|
+
// colon form: `…/:name/…` (colon must be at a segment boundary
|
|
82
|
+
// so we don't match `:` inside e.g. a port number; the `/` before
|
|
83
|
+
// it enforces that)
|
|
84
|
+
: S extends `${string}/:${infer Key}/${infer Rest}`
|
|
85
|
+
? KeyHead<Key> | ExtractParamKeys<`/${Rest}`>
|
|
86
|
+
// tail colon-form: `…/:name` (no trailing slash)
|
|
87
|
+
: S extends `${string}/:${infer Key}`
|
|
88
|
+
? KeyHead<Key>
|
|
89
|
+
: never
|
|
90
|
+
|
|
91
|
+
// `KeyHead<'name>'>` → `'name>'` because TS template literal infers
|
|
92
|
+
// the longest possible match. We need to handle params that are at
|
|
93
|
+
// the end of the path AND followed by a query string. This util
|
|
94
|
+
// truncates a captured key at the first non-name character.
|
|
95
|
+
type KeyHead<S extends string> =
|
|
96
|
+
S extends `${infer H}/${string}` ? H :
|
|
97
|
+
S extends `${infer H}?${string}` ? H :
|
|
98
|
+
S extends `${infer H}.${string}` ? H :
|
|
99
|
+
S
|
|
100
|
+
|
|
101
|
+
export type ExtractParams<S extends string> =
|
|
102
|
+
[ExtractParamKeys<S>] extends [never]
|
|
103
|
+
? Record<string, never>
|
|
104
|
+
: { [K in ExtractParamKeys<S>]: string }
|
|
105
|
+
|
|
16
106
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE'
|
|
17
107
|
|
|
18
108
|
/**
|
|
@@ -103,9 +193,35 @@ export interface SafeData<T extends Record<string, any> = Record<string, any>> {
|
|
|
103
193
|
* })
|
|
104
194
|
* ```
|
|
105
195
|
*/
|
|
106
|
-
export type ActionRequest<
|
|
196
|
+
export type ActionRequest<
|
|
197
|
+
TFields extends Record<string, any> = Record<string, any>,
|
|
198
|
+
TParams extends Record<string, string> = Record<string, string>,
|
|
199
|
+
> = RequestInstance<TFields, TParams>
|
|
107
200
|
|
|
108
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Read the body shape declared by an action's `validations:` field as
|
|
203
|
+
* a TypeScript object type (stacksjs/stacks#1851 Phase 2b). Threaded
|
|
204
|
+
* into {@link RequestInstance}'s `TFields` so `request.all()` returns
|
|
205
|
+
* the body shape with the field types {@link Infer}'d from each
|
|
206
|
+
* `schema.X()` rule.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* const validations = {
|
|
210
|
+
* email: { rule: schema.string().email() },
|
|
211
|
+
* password: { rule: schema.string().min(8) },
|
|
212
|
+
* remember: { rule: schema.boolean() },
|
|
213
|
+
* } as const
|
|
214
|
+
* type Body = InferValidations<typeof validations>
|
|
215
|
+
* // → { email: string, password: string, remember: boolean }
|
|
216
|
+
*/
|
|
217
|
+
export type InferValidations<V extends Record<string, { rule: any }>> = {
|
|
218
|
+
[K in keyof V]: Infer<V[K]['rule']>
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface RequestInstance<
|
|
222
|
+
TFields extends Record<string, any> = Record<string, any>,
|
|
223
|
+
TParams extends Record<string, string> = Record<string, string>,
|
|
224
|
+
> {
|
|
109
225
|
// ==========================================================================
|
|
110
226
|
// Native Request properties
|
|
111
227
|
// ==========================================================================
|
|
@@ -116,10 +232,28 @@ export interface RequestInstance<TFields extends Record<string, any> = Record<st
|
|
|
116
232
|
|
|
117
233
|
// Raw data access (always untyped — use get()/input() for typed access)
|
|
118
234
|
query: RequestData
|
|
119
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Route parameters from the URL. When the action is bound to a path
|
|
237
|
+
* literal (via {@link ActionOptions.path} or a typed
|
|
238
|
+
* `route.get<TPath>(...)` overload), `TParams` narrows to the
|
|
239
|
+
* extracted keys so `request.params.id` is `string` with no `as any`.
|
|
240
|
+
*
|
|
241
|
+
* For un-narrowed callers (bare `RequestInstance`), this falls
|
|
242
|
+
* back to `Record<string, string>` — the runtime shape is always
|
|
243
|
+
* string-keyed even for "numeric" params, since URL segments are
|
|
244
|
+
* never coerced.
|
|
245
|
+
*/
|
|
246
|
+
params: TParams
|
|
120
247
|
jsonBody?: any
|
|
121
248
|
formBody?: any
|
|
122
249
|
files: Record<string, File | File[]>
|
|
250
|
+
/**
|
|
251
|
+
* Cookies parsed from the request. The accessor exposes
|
|
252
|
+
* `get`/`set`/`delete`/`getAll` helpers that mirror bun-router's
|
|
253
|
+
* cookie handling. Optional because not every request middleware
|
|
254
|
+
* stack runs the cookie parser.
|
|
255
|
+
*/
|
|
256
|
+
cookies?: RequestCookies
|
|
123
257
|
|
|
124
258
|
// ==========================================================================
|
|
125
259
|
// Model-aware Input Methods
|
|
@@ -325,12 +459,39 @@ export interface RequestInstance<TFields extends Record<string, any> = Record<st
|
|
|
325
459
|
bearerToken: () => string | null | AuthToken
|
|
326
460
|
|
|
327
461
|
// ==========================================================================
|
|
328
|
-
// Route & Param Methods
|
|
462
|
+
// Route & Param Methods
|
|
463
|
+
//
|
|
464
|
+
// `params` (the field above) is the typed primary surface — narrow
|
|
465
|
+
// it via the action's `path:` literal for full inference. These
|
|
466
|
+
// method-form helpers stay around for back-compat and for cases
|
|
467
|
+
// where the param key isn't statically known.
|
|
329
468
|
// ==========================================================================
|
|
330
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Look up a single route param, optionally with a default. Narrows
|
|
472
|
+
* to {@link TParams} when the key is part of the path-extracted
|
|
473
|
+
* keyset; falls back to `string | undefined` otherwise (covers
|
|
474
|
+
* dynamic key access without `as any`).
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* const id = request.param('id') // typed string (from path)
|
|
478
|
+
* const note = request.param('note', '') // string with default
|
|
479
|
+
*/
|
|
480
|
+
param: <K extends keyof TParams | string, D = string>(
|
|
481
|
+
key: K,
|
|
482
|
+
defaultValue?: D,
|
|
483
|
+
) => K extends keyof TParams ? TParams[K] : (string | D)
|
|
484
|
+
/**
|
|
485
|
+
* @deprecated Use {@link param} for typed param lookups, or
|
|
486
|
+
* `Number(request.params.id)` for explicit numeric coercion.
|
|
487
|
+
* The name-match heuristic returning `number` for {@link NumericField}
|
|
488
|
+
* keys (`id`, `count`, `amount`, …) is brittle: it silently falls
|
|
489
|
+
* through to `string` for `judgeId`, `user_id`, etc.
|
|
490
|
+
* See stacksjs/stacks#1851 Phase 3.
|
|
491
|
+
*/
|
|
331
492
|
getParam: <K extends string>(key: K) => K extends NumericField ? number : string
|
|
332
493
|
route: (key: string) => number | string | null
|
|
333
|
-
getParams: () =>
|
|
494
|
+
getParams: () => TParams
|
|
334
495
|
getParamAsInt: (key: string) => number | null
|
|
335
496
|
|
|
336
497
|
// ==========================================================================
|
package/src/search-engine.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface SearchEngineOptions {
|
|
|
40
40
|
* @default string 'meilisearch'
|
|
41
41
|
* @see https://stacksjs.com/docs/search-engine
|
|
42
42
|
*/
|
|
43
|
-
driver: 'meilisearch' | 'algolia' | 'opensearch'
|
|
43
|
+
driver: 'meilisearch' | 'algolia' | 'opensearch' | 'typesense'
|
|
44
44
|
|
|
45
45
|
opensearch?: {
|
|
46
46
|
host: string
|
|
@@ -63,6 +63,13 @@ export interface SearchEngineOptions {
|
|
|
63
63
|
searchOnlyApiKey?: string
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
typesense?: {
|
|
67
|
+
host?: string
|
|
68
|
+
port?: number
|
|
69
|
+
protocol?: string
|
|
70
|
+
apiKey?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
filters?: {
|
|
67
74
|
[key: string]: string
|
|
68
75
|
}
|
|
@@ -204,6 +211,30 @@ export interface SearchOptions {
|
|
|
204
211
|
sortable: string[]
|
|
205
212
|
filterable: string[]
|
|
206
213
|
options?: SearchEngineOptions
|
|
214
|
+
/**
|
|
215
|
+
* Cross-table denormalisation for searchable fields that live on a
|
|
216
|
+
* related model (stacksjs/stacks#1918). Maps an indexed-document
|
|
217
|
+
* field name to a dot-path resolved against the model instance's
|
|
218
|
+
* `_relations`. Without this, `toSearchableObject` only reads from
|
|
219
|
+
* `_attributes` and silently emits `undefined` for any field that
|
|
220
|
+
* exists on a `belongsTo` / `hasOne` / `hasMany` relation.
|
|
221
|
+
*
|
|
222
|
+
* Example: a `Judge` belongsTo a `CourtHouse`. To make the court
|
|
223
|
+
* house's `name` searchable on the judge index:
|
|
224
|
+
*
|
|
225
|
+
* useSearch: {
|
|
226
|
+
* searchable: ['name', 'court_name'],
|
|
227
|
+
* displayable: ['id', 'name', 'court_name'],
|
|
228
|
+
* denormalize: { court_name: 'court_house.name' },
|
|
229
|
+
* }
|
|
230
|
+
*
|
|
231
|
+
* The caller is responsible for eager-loading the named relations
|
|
232
|
+
* (e.g. via `Judge.query().with('court_house').get()`) — the live
|
|
233
|
+
* observer hook and the CLI bulk-index path do this automatically
|
|
234
|
+
* for every distinct head segment in the `denormalize` map.
|
|
235
|
+
* `toSearchableObject` stays synchronous; no per-row database lookup.
|
|
236
|
+
*/
|
|
237
|
+
denormalize?: Record<string, string>
|
|
207
238
|
}
|
|
208
239
|
|
|
209
240
|
export type {
|
package/src/services.ts
CHANGED
|
@@ -25,6 +25,24 @@ export interface ServicesOptions {
|
|
|
25
25
|
scopes?: string[]
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Sign in with Apple. Apple has no static client secret — the driver
|
|
30
|
+
* signs a short-lived ES256 JWT from teamId + keyId + privateKey
|
|
31
|
+
* (the .p8 file's contents) instead.
|
|
32
|
+
*/
|
|
33
|
+
apple?: {
|
|
34
|
+
/** The Services ID identifier, e.g. `org.example.web` */
|
|
35
|
+
clientId: string
|
|
36
|
+
/** Apple Developer Team ID (10 chars) */
|
|
37
|
+
teamId: string
|
|
38
|
+
/** Key ID of the "Sign in with Apple" key */
|
|
39
|
+
keyId: string
|
|
40
|
+
/** Contents of the downloaded .p8 private key */
|
|
41
|
+
privateKey: string
|
|
42
|
+
redirectUrl: string
|
|
43
|
+
scopes?: string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
28
46
|
facebook?: {
|
|
29
47
|
clientId: string
|
|
30
48
|
clientSecret: string
|
|
@@ -168,6 +186,14 @@ export interface ServicesOptions {
|
|
|
168
186
|
stripe?: {
|
|
169
187
|
secretKey?: string
|
|
170
188
|
publicKey?: string
|
|
189
|
+
/**
|
|
190
|
+
* Signing secret for verifying inbound Stripe webhook requests
|
|
191
|
+
* (`whsec_...`, from the Stripe dashboard's webhook endpoint
|
|
192
|
+
* config). Required by any app receiving Stripe webhooks —
|
|
193
|
+
* without it, `stripe.webhooks.constructEvent` has nothing to
|
|
194
|
+
* verify the `stripe-signature` header against.
|
|
195
|
+
*/
|
|
196
|
+
webhookSecret?: string
|
|
171
197
|
/**
|
|
172
198
|
* Pinned Stripe API version. Defaults to whatever the bundled SDK
|
|
173
199
|
* was compiled against; override here when rolling forward without
|
package/src/stacks.ts
CHANGED
|
@@ -6,6 +6,9 @@ import type {
|
|
|
6
6
|
BinaryConfig,
|
|
7
7
|
CacheConfig,
|
|
8
8
|
CloudConfig,
|
|
9
|
+
CmsConfig,
|
|
10
|
+
CommerceConfig,
|
|
11
|
+
CorsConfig,
|
|
9
12
|
DashboardConfig,
|
|
10
13
|
DatabaseConfig,
|
|
11
14
|
DnsConfig,
|
|
@@ -17,6 +20,8 @@ import type {
|
|
|
17
20
|
HashingConfig,
|
|
18
21
|
LibraryConfig,
|
|
19
22
|
LoggingConfig,
|
|
23
|
+
MarketingConfig,
|
|
24
|
+
MonitoringConfig,
|
|
20
25
|
NotificationConfig,
|
|
21
26
|
PaymentConfig,
|
|
22
27
|
Ports,
|
|
@@ -67,6 +72,14 @@ export interface StacksOptions {
|
|
|
67
72
|
*/
|
|
68
73
|
auth: AuthConfig
|
|
69
74
|
|
|
75
|
+
/**
|
|
76
|
+
* **CORS Options**
|
|
77
|
+
*
|
|
78
|
+
* Cross-origin resource sharing policy applied to API responses.
|
|
79
|
+
* Configured via `config/cors.ts`. See {@link CorsConfig}.
|
|
80
|
+
*/
|
|
81
|
+
cors?: CorsConfig
|
|
82
|
+
|
|
70
83
|
/**
|
|
71
84
|
* **Realtime Options**
|
|
72
85
|
*
|
|
@@ -103,12 +116,26 @@ export interface StacksOptions {
|
|
|
103
116
|
*/
|
|
104
117
|
cloud: CloudConfig
|
|
105
118
|
|
|
119
|
+
/**
|
|
120
|
+
* **CMS Options**
|
|
121
|
+
*
|
|
122
|
+
* Top-level feature gate for the CMS bundle (Post / Page / Author /
|
|
123
|
+
* Comment / Tag / Category models + content-edit dashboards).
|
|
124
|
+
*/
|
|
125
|
+
cms: CmsConfig
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* **Commerce Options**
|
|
129
|
+
*
|
|
130
|
+
* Top-level feature gate for the commerce bundle plus storefront defaults.
|
|
131
|
+
*/
|
|
132
|
+
commerce: CommerceConfig
|
|
133
|
+
|
|
106
134
|
/**
|
|
107
135
|
* **Dashboard Options**
|
|
108
136
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* project doesn't use (e.g. `commerce.enabled: false`).
|
|
137
|
+
* Top-level feature gate plus per-section visibility toggles for the
|
|
138
|
+
* `buddy dev --dashboard` sidebar.
|
|
112
139
|
*/
|
|
113
140
|
dashboard: DashboardConfig
|
|
114
141
|
|
|
@@ -193,6 +220,22 @@ export interface StacksOptions {
|
|
|
193
220
|
*/
|
|
194
221
|
logging: LoggingConfig
|
|
195
222
|
|
|
223
|
+
/**
|
|
224
|
+
* **Marketing Options**
|
|
225
|
+
*
|
|
226
|
+
* Top-level feature gate for the marketing bundle (`/api/email/subscribe`,
|
|
227
|
+
* `/api/contact`, Campaign / EmailList / SocialPost).
|
|
228
|
+
*/
|
|
229
|
+
marketing: MarketingConfig
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* **Monitoring Options**
|
|
233
|
+
*
|
|
234
|
+
* Top-level feature gate for the monitoring bundle (Error model +
|
|
235
|
+
* error-tracking views and actions).
|
|
236
|
+
*/
|
|
237
|
+
monitoring: MonitoringConfig
|
|
238
|
+
|
|
196
239
|
/**
|
|
197
240
|
* **Notification Options**
|
|
198
241
|
*
|