@stacksjs/router 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.
@@ -33,4 +33,5 @@ export declare class Middleware {
33
33
  readonly priority: number;
34
34
  readonly handle: (request: EnhancedRequest) => void | Promise<void>;
35
35
  constructor(config: MiddlewareConfig);
36
+ toRouterHandler(): (req: EnhancedRequest, next: () => Promise<Response>) => Promise<Response>;
36
37
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Validate and return a path parameter, throwing if it's unsafe to use
3
+ * in filesystem interpolation.
4
+ *
5
+ * The default contract is single-segment: no `/`, no `\`, no `..`, no
6
+ * absolute path, no null bytes, no control characters, length ≤ 255.
7
+ * Pass `allowSlashes: true` for a multi-segment path (still rejects
8
+ * the rest).
9
+ *
10
+ * @throws {PathParamError} when the value fails any check.
11
+ */
12
+ export declare function sanitizePathParam(value: unknown, options?: SanitizePathParamOptions): string;
13
+ /**
14
+ * Non-throwing variant. Returns the sanitized value or `null` if any
15
+ * check failed. Use when you want a fast yes/no in a conditional
16
+ * without a try/catch around the throw site.
17
+ */
18
+ export declare function safePathParam(value: unknown, options?: SanitizePathParamOptions): string | null;
19
+ export declare interface SanitizePathParamOptions {
20
+ context?: string
21
+ maxLength?: number
22
+ allowSlashes?: boolean
23
+ }
24
+ /**
25
+ * Path-parameter sanitization helpers.
26
+ *
27
+ * Route params arrive from the URL as untyped strings and are merged
28
+ * directly into `req.params`. Actions that interpolate those values
29
+ * into filesystem paths or shell commands without first scrubbing
30
+ * them are vulnerable to `..`-traversal, absolute-path takeovers, and
31
+ * null-byte truncation attacks.
32
+ *
33
+ * The router itself can't auto-sanitize every param (some are
34
+ * deliberately path-shaped — file servers, asset proxies, etc.). What
35
+ * we ship instead is a single canonical helper that callers reach for
36
+ * at the boundary where the param meets the filesystem.
37
+ *
38
+ * See stacksjs/stacks#1870 R-12.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { sanitizePathParam } from '@stacksjs/router'
43
+ *
44
+ * const filename = sanitizePathParam(req.params.filename, {
45
+ * context: 'avatar download',
46
+ * })
47
+ * return new Response(Bun.file(path.appPath(`avatars/${filename}`)))
48
+ * ```
49
+ */
50
+ /**
51
+ * Reasons {@link sanitizePathParam} rejects a value. Surfaced via the
52
+ * thrown error so callers can log or branch.
53
+ */
54
+ export type PathParamRejection = | 'empty'
55
+ | 'not-string'
56
+ | 'absolute-path'
57
+ | 'traversal'
58
+ | 'null-byte'
59
+ | 'control-char'
60
+ | 'too-long';
61
+ export declare class PathParamError extends Error {
62
+ readonly reason: PathParamRejection;
63
+ constructor(reason: PathParamRejection, value: unknown, context?: string);
64
+ }
@@ -0,0 +1,67 @@
1
+ import type { FileInfo } from '@stacksjs/bun-router';
2
+ /**
3
+ * Stacks-specific marker fields attached to the request by the
4
+ * router itself and the framework's default middleware.
5
+ *
6
+ * Markers are deliberately prefixed with `_` so they can't collide
7
+ * with userland keys on the request, and their lifetimes are bounded
8
+ * by the request's lifetime — they're never persisted.
9
+ */
10
+ export declare interface StacksRequestMarkers {
11
+ _corsConfig?: unknown
12
+ _forceJson?: boolean
13
+ _skipCsrf?: boolean
14
+ _compress?: boolean
15
+ _middlewareParams?: Record<string, string>
16
+ _requestId?: string
17
+ _startNs?: bigint
18
+ _authenticatedUser?: unknown
19
+ _currentAccessToken?: unknown
20
+ _bodyParsed?: boolean
21
+ }
22
+ /**
23
+ * Laravel-style request-input macros that Stacks attaches in
24
+ * `enhanceRequest` (router/src/stacks-router.ts). These shadow some
25
+ * of bun-router's `RequestMacroMethods` with Stacks-specific
26
+ * implementations (more permissive `T = any` generics so action
27
+ * callers don't have to specify the return type for every read).
28
+ *
29
+ * Listed here as part of the augmentation so call sites like
30
+ * `request.input(key)` type-check without `as any`.
31
+ */
32
+ export declare interface StacksRequestMacros {
33
+ input?: <T = unknown>(key: string, defaultValue?: T) => T
34
+ get?: <T = unknown>(key: string, defaultValue?: T) => T
35
+ all?: () => Record<string, unknown>
36
+ only?: <T extends Record<string, unknown>>(keys: string[]) => T
37
+ except?: <T extends Record<string, unknown>>(keys: string[]) => T
38
+ has?: (key: string | string[]) => boolean
39
+ hasAny?: (keys: string[]) => boolean
40
+ missing?: (key: string) => boolean
41
+ filled?: (key: string) => boolean
42
+ integer?: (key: string, defaultValue?: number) => number
43
+ float?: (key: string, defaultValue?: number) => number
44
+ boolean?: (key: string, defaultValue?: boolean) => boolean
45
+ string?: (key: string, defaultValue?: string) => string
46
+ array?: <T = unknown>(key: string, defaultValue?: T[]) => T[]
47
+ file?: (key: string) => FileInfo | null
48
+ files?: (key: string) => FileInfo[]
49
+ hasFile?: (key: string) => boolean
50
+ allFiles?: () => Record<string, FileInfo | FileInfo[]>
51
+ getFiles?: () => Record<string, FileInfo | FileInfo[]>
52
+ user?: () => Promise<unknown>
53
+ userToken?: () => Promise<unknown>
54
+ tokenCan?: (ability: string) => Promise<boolean>
55
+ tokenCant?: (ability: string) => Promise<boolean>
56
+ can?: (ability: string, ...args: unknown[]) => Promise<boolean>
57
+ cannot?: (ability: string, ...args: unknown[]) => Promise<boolean>
58
+ authorize?: (ability: string, ...args: unknown[]) => Promise<void>
59
+ }
60
+ /**
61
+ * Union of Stacks markers + macros — useful as a single type alias for
62
+ * places that previously cast to `any`.
63
+ */
64
+ export type StacksRequestExtensions = StacksRequestMarkers & StacksRequestMacros;
65
+ declare module '@stacksjs/bun-router' {
66
+ interface EnhancedRequestextends StacksRequestExtensions {}
67
+ }
@@ -1,4 +1,5 @@
1
1
  import type { EnhancedRequest } from '@stacksjs/bun-router';
2
+ import type { RequestInstance } from '@stacksjs/types';
2
3
  /**
3
4
  * Read the active trace id, or `undefined` outside any traced scope.
4
5
  *
@@ -36,6 +37,16 @@ export declare function cacheRequestQuery<T>(key: string, fetcher: () => T | Pro
36
37
  * Called by middleware/router when handling a request
37
38
  */
38
39
  export declare function setCurrentRequest(req: EnhancedRequest): void;
40
+ /**
41
+ * Clear the current request context.
42
+ *
43
+ * `setCurrentRequest` uses `AsyncLocalStorage.enterWith`, which mutates the
44
+ * caller's async scope and never restores it. Call this in test teardown
45
+ * (`afterEach`) whenever a test body calls `setCurrentRequest`, so the leaked
46
+ * frame doesn't poison subsequently-collected test files (bun's runner
47
+ * mis-registers tests when collected on a foreign async frame).
48
+ */
49
+ export declare function clearCurrentRequest(): void;
39
50
  /**
40
51
  * Run a function with a request context
41
52
  * All code executed within the callback will have access to the request
@@ -47,13 +58,25 @@ export declare function runWithRequest<T>(req: EnhancedRequest, fn: () => T): T;
47
58
  export declare function getCurrentRequest(): EnhancedRequest | undefined;
48
59
  /**
49
60
  * Request proxy that provides access to the current request
50
- * Similar to Laravel's request() helper
61
+ * (Laravel's `request()` helper, but typed).
51
62
  *
52
- * Methods:
53
- * - bearerToken() - Get the bearer token from Authorization header
54
- * - user() - Get the authenticated user (async)
55
- * - userToken() - Get the current access token (async)
56
- * - tokenCan(ability) - Check if token has an ability (async)
57
- * - tokenCant(ability) - Check if token doesn't have an ability (async)
58
- */
59
- export declare const request: Proxy;
63
+ * The proxy is statically typed as {@link RequestInstance} —
64
+ * the canonical Stacks-side action-request surface
65
+ * (stacksjs/stacks#1851 Phase 1). All the macros action handlers
66
+ * reach for (`all`, `get`, `input`, `cookies`, `param`, `validate`,
67
+ * `user`, `bearerToken`, …) resolve to their declared types instead
68
+ * of `any`, eliminating most `(request as any)` casts in action code.
69
+ *
70
+ * Runtime is unchanged — the proxy still delegates to whichever
71
+ * `EnhancedRequest` is in the AsyncLocalStorage slot. The type swap
72
+ * is API-compatible: every method action code uses on `request`
73
+ * existed on either type already, but only `RequestInstance` carries
74
+ * the model-aware / path-aware narrowing.
75
+ *
76
+ * Methods worth knowing about:
77
+ * - `bearerToken()` — Authorization header
78
+ * - `user()` — authenticated user (async)
79
+ * - `userToken()` — current access token (async)
80
+ * - `tokenCan(ability)` / `tokenCant(ability)` — async ability checks
81
+ */
82
+ export declare const request: RequestInstance;
@@ -1,4 +1,4 @@
1
- import type { RouteRegistry } from '../../../../../app/Routes';
1
+ import type { RouteRegistry } from './route-types';
2
2
  /**
3
3
  * Load all routes from the registry
4
4
  */
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Route registry types — owned by `@stacksjs/router` because the router
3
+ * consumes them. `app/Routes.ts` (the project-level route map) imports
4
+ * these via the public package name rather than a relative reach into
5
+ * the framework defaults tree (stacksjs/stacks#1863, T-10).
6
+ */
7
+ export declare interface RouteDefinition {
8
+ path: string
9
+ prefix?: string
10
+ middleware?: string | string[]
11
+ }
12
+ export type RouteRegistry = Record<string, string | RouteDefinition>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Apply the default security headers to a Headers instance in-place.
3
+ *
4
+ * Headers applied unconditionally (cheap, no compat risk):
5
+ * - `X-Content-Type-Options: nosniff` — blocks MIME-sniff XSS
6
+ * - `X-Frame-Options: SAMEORIGIN` — clickjacking protection (CSP
7
+ * `frame-ancestors` is the modern equivalent but XFO still ships)
8
+ * - `Referrer-Policy: strict-origin-when-cross-origin` — modern default
9
+ *
10
+ * Production-only:
11
+ * - `Strict-Transport-Security: max-age=31536000; includeSubDomains` —
12
+ * tells browsers to commit to HTTPS for a year. Omits `preload` since
13
+ * that's an irreversible commitment to the browser preload list.
14
+ *
15
+ * Skips overwriting any header that's already set — explicit userland
16
+ * config wins. Skips entirely when `STACKS_SECURITY_HEADERS_DISABLE=true`.
17
+ */
18
+ export declare function applySecurityHeaders(headers: Headers): void;
19
+ /** Test helper — reset the cached env-derived flags. */
20
+ export declare function __resetSecurityHeadersCache(): void;
@@ -0,0 +1,57 @@
1
+ import { createSessionStore } from '@stacksjs/bun-router';
2
+ import type { RedisClient, SessionConfig, SessionData, SessionStore } from '@stacksjs/bun-router';
3
+ // Re-export bun-router's session types so app code only has to
4
+ // import from `@stacksjs/router` (one less package boundary to
5
+ // learn). Drivers stay accessible by name for callers that want
6
+ // to assemble a custom store manually.
7
+ export type {
8
+ RedisClient,
9
+ SessionConfig,
10
+ SessionData,
11
+ SessionStore,
12
+ };
13
+ /**
14
+ * Build a session store from the Stacks config. Mirrors what
15
+ * `mail` / `Jobs` do for their driver registries — call once at
16
+ * boot, pass the result into the session middleware.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // config/session.ts (typical)
21
+ * export const session = {
22
+ * driver: 'redis',
23
+ * ttl: 60 * 60 * 24, // 24h
24
+ * cookie: { name: 'sid', httpOnly: true, sameSite: 'lax' },
25
+ * redis: { client: useRedis() },
26
+ * } satisfies StacksSessionConfig
27
+ *
28
+ * // Then at boot:
29
+ * const store = createStacksSessionStore(session)
30
+ * app.use(sessionMiddleware({ store }))
31
+ * ```
32
+ */
33
+ export declare function createStacksSessionStore(config: StacksSessionConfig): SessionStore<SessionData>;
34
+ /**
35
+ * Stacks-level session configuration. Extends bun-router's
36
+ * {@link SessionConfig} with:
37
+ *
38
+ * - `encrypt`: opt-in/out of {@link EncryptedSessionStore}
39
+ * wrapping (default: `'auto'` — on in production, off in
40
+ * dev/test where readability matters more than encryption-
41
+ * at-rest)
42
+ * - `appKey`: override key for the encryption (defaults to
43
+ * `process.env.APP_KEY`)
44
+ *
45
+ * All other fields pass through to bun-router unchanged.
46
+ */
47
+ export declare interface StacksSessionConfig extends SessionConfig {
48
+ encrypt?: boolean | 'auto'
49
+ appKey?: string
50
+ }
51
+ export {
52
+ DatabaseSessionStore,
53
+ FileSessionStore,
54
+ MemorySessionStore,
55
+ RedisSessionStore,
56
+ createSessionStore,
57
+ } from '@stacksjs/bun-router';
@@ -0,0 +1,50 @@
1
+ import type { EnhancedRequest } from '@stacksjs/bun-router';
2
+ /**
3
+ * Sign an existing URL (full or path-only). Returns a new URL string
4
+ * with `expires` (optional) and `signature` query params appended.
5
+ *
6
+ * Path-only inputs (`/api/email/verify?user=42`) inherit `APP_URL` as
7
+ * the origin — same convention as {@link buildUrl}.
8
+ */
9
+ export declare function signUrl(input: string, options?: SignedUrlOptions): string;
10
+ /**
11
+ * Convenience wrapper that resolves a named route to a URL via {@link buildUrl}
12
+ * and then signs it. Mirrors Laravel's `URL::signedRoute()`.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * route.get('/api/email/verify', VerifyEmailAction).name('email.verify')
17
+ * const link = signedUrl('email.verify', { user: 42 }, { ttl: 60 * 60 * 24 })
18
+ * // → https://app.example.com/api/email/verify?user=42&expires=1716470400&signature=…
19
+ * ```
20
+ */
21
+ export declare function signedUrl(routeName: string, params?: Record<string, string | number>, options?: SignedUrlOptions): string;
22
+ /**
23
+ * Verify the `signature` (and optional `expires`) on an incoming URL.
24
+ * Returns a discriminated result so callers can pick their own status
25
+ * code per failure mode.
26
+ */
27
+ export declare function verifySignedUrl(input: string | URL): SignedUrlVerifyResult;
28
+ /**
29
+ * Middleware shape for `route.middleware('signed')`. Verifies the
30
+ * incoming URL's signature and throws a `Response` (the router's
31
+ * short-circuit contract) when it fails. Drop in as a route-level
32
+ * middleware on any URL minted by {@link signedUrl}.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * route.get('/email/verify', 'Actions/VerifyEmail').middleware('signed')
37
+ * ```
38
+ */
39
+ export declare function verifySignedUrlMiddleware(req: EnhancedRequest): Promise<void>;
40
+ export declare interface SignedUrlOptions {
41
+ expiresAt?: number
42
+ ttl?: number
43
+ }
44
+ /**
45
+ * Result of {@link verifySignedUrl}. The `reason` only fires when `valid`
46
+ * is `false` — gives callers (and middleware) enough to decide whether
47
+ * to log, return 401, or return 410 (expired vs. tampered).
48
+ */
49
+ export type SignedUrlVerifyResult = | { valid: true }
50
+ | { valid: false, reason: 'missing-signature' | 'expired' | 'invalid-signature' }
@@ -1,6 +1,8 @@
1
1
  import type { Server } from 'bun';
2
+ import './request-augmentation';
2
3
  import { Router } from '@stacksjs/bun-router';
3
4
  import type { ActionHandler, EnhancedRequest, Route, ServerOptions } from '@stacksjs/bun-router';
5
+ import type { ActionValidations, ValidationResult } from '@stacksjs/actions';
4
6
  /**
5
7
  * Generate a full URL for a named route, like Laravel's route() helper.
6
8
  *
@@ -54,6 +56,39 @@ export declare function clearMiddlewareCache(): void;
54
56
  * old version forever.
55
57
  */
56
58
  export declare function installMiddlewareHotReload(): () => void;
59
+ /**
60
+ * Resolve every middleware alias referenced by a registered route and
61
+ * report the ones that don't load. `csrf` is always checked too — it's
62
+ * auto-injected on unsafe methods even when no route lists it.
63
+ *
64
+ * Resolution is inherently lazy (`.middleware(name)` is a sync chainable
65
+ * that just records a string; the alias map and middleware modules load
66
+ * via async dynamic import), so a throw at literal registration time is
67
+ * impossible. Calling this after all routes are registered — the end of
68
+ * `importRoutes()` and the compiled-binary boot in core/server — IS
69
+ * effectively registration-time validation. See stacksjs/stacks#1957.
70
+ */
71
+ export declare function findUnresolvableRouteMiddleware(): Promise<Array<{ alias: string, routes: string[] }>>;
72
+ /**
73
+ * Throw when any registered route references a middleware alias that
74
+ * cannot be resolved. Fail-closed boot validation: a typo'd `auth` alias
75
+ * must abort startup loudly, not serve the route unprotected (the
76
+ * request-time guard in createMiddlewareHandler 500s as a backstop).
77
+ */
78
+ export declare function assertRouteMiddlewareResolvable(): Promise<void>;
79
+ /**
80
+ * Run an action's declarative `validations:` against the request.
81
+ *
82
+ * @internal Exported for regression coverage of path-param coercion
83
+ * (stacksjs/stacks#1865). Production callers should rely on the
84
+ * router's action-resolution path, which invokes this for you.
85
+ */
86
+ export declare function validateActionInput(req: EnhancedRequest, validations: ActionValidations): Promise<ValidationResult>;
87
+ export declare function stream(source: ReadableStream | AsyncIterable<string | Uint8Array>, options?: StreamOptions): Response;
88
+ // Decorate the incoming request with the helpers the framework's middleware
89
+ // and actions assume are always available. Names follow Laravel's convention
90
+ // because that's the API surface Stacks userland expects.
91
+ export declare function enhanceRequest(req: EnhancedRequest): EnhancedRequest;
57
92
  /**
58
93
  * Create a Stacks-enhanced router
59
94
  */
@@ -74,6 +109,7 @@ declare interface StacksRouterConfig {
74
109
  declare interface GroupOptions {
75
110
  prefix?: string
76
111
  middleware?: string | string[]
112
+ apiResponse?: boolean
77
113
  }
78
114
  declare interface ResourceRouteOptions {
79
115
  only?: ResourceAction[]
@@ -87,6 +123,41 @@ declare interface ChainableRoute {
87
123
  middleware: (name: string) => ChainableRoute
88
124
  name: (routeName: string) => ChainableRoute
89
125
  skipCsrf: () => ChainableRoute
126
+ requireCsrf: () => ChainableRoute
127
+ rateLimit: (max: number, window: 'second' | 'minute' | 'hour' | 'day' | number) => ChainableRoute
128
+ }
129
+ /**
130
+ * Helper for streaming responses — wraps a `ReadableStream` or async
131
+ * generator with the right headers for the chosen content type.
132
+ *
133
+ * Common shapes:
134
+ *
135
+ * ```ts
136
+ * // Server-Sent Events
137
+ * return stream(async function* () {
138
+ * for await (const evt of source) yield `data: ${JSON.stringify(evt)}\n\n`
139
+ * }, { type: 'sse' })
140
+ *
141
+ * // Chunked JSON (NDJSON) — one JSON object per line
142
+ * return stream(async function* () {
143
+ * for await (const row of rows) yield `${JSON.stringify(row)}\n`
144
+ * }, { type: 'ndjson' })
145
+ *
146
+ * // Raw bytes — caller supplies a ReadableStream of Uint8Array chunks
147
+ * return stream(myReadable, { contentType: 'application/octet-stream' })
148
+ * ```
149
+ *
150
+ * The wrapper sets `Cache-Control: no-cache` and `Connection: keep-alive`
151
+ * for SSE — the two headers a sane proxy / browser pair won't ignore — and
152
+ * leaves backpressure / cancellation to the underlying stream.
153
+ *
154
+ * See stacksjs/stacks#1870 R-4.
155
+ */
156
+ export declare interface StreamOptions {
157
+ type?: 'sse' | 'ndjson'
158
+ contentType?: string
159
+ headers?: HeadersInit
160
+ status?: number
90
161
  }
91
162
  export declare interface StacksRouterInstance {
92
163
  bunRouter: Router
@@ -105,9 +176,31 @@ export declare interface StacksRouterInstance {
105
176
  register: (routePath: string, options?: { prefix?: string, middleware?: string | string[] }) => Promise<StacksRouterInstance>
106
177
  serve: (options?: ServerOptions) => Promise<Server<unknown>>
107
178
  handleRequest: (req: Request) => Promise<Response>
179
+ getAllowedMethods: (pathname: string, domain?: string) => string[]
108
180
  importRoutes: () => Promise<void>
109
181
  loadDiscoveredRoutes: () => Promise<void>
110
182
  }
111
183
  declare type RouteHandlerFn = (_req: EnhancedRequest) => Response | Promise<Response>;
112
184
  declare type StacksHandler = string | RouteHandlerFn;
113
185
  declare type ResourceAction = 'index' | 'store' | 'show' | 'update' | 'destroy';
186
+ /**
187
+ * FIFO-bounded Map. Wraps `Map` with a hard size cap; on overflow,
188
+ * the oldest entry (Map insertion order) is evicted. Used for the
189
+ * router's small framework-internal caches whose size is normally
190
+ * bounded by action count, but which had no upper limit before —
191
+ * tests that instantiate many short-lived routers would leak entries
192
+ * across `createStacksRouter()` calls (stacksjs/stacks#1863 T-8).
193
+ *
194
+ * Insertion-order LRU is appropriate here because the access pattern
195
+ * is "set once at action-load time, then many reads" — refreshing on
196
+ * get would buy nothing since reads dominate.
197
+ */
198
+ declare class BoundedMap<K, V> {
199
+ constructor(max: number);
200
+ get(key: K): V | undefined;
201
+ has(key: K): boolean;
202
+ set(key: K, value: V): this;
203
+ delete(key: K): boolean;
204
+ clear(): void;
205
+ get size(): number;
206
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/router",
3
3
  "type": "module",
4
- "version": "0.70.44",
4
+ "version": "0.70.53",
5
5
  "description": "The Stacks framework router.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -49,18 +49,18 @@
49
49
  "prepublishOnly": "bun run build"
50
50
  },
51
51
  "dependencies": {
52
- "@stacksjs/bun-router": "^0.0.13"
52
+ "@stacksjs/bun-router": "0.0.17"
53
53
  },
54
54
  "devDependencies": {
55
- "@stacksjs/actions": "^0.70.44",
56
- "@stacksjs/config": "^0.70.44",
55
+ "@stacksjs/actions": "0.70.53",
56
+ "@stacksjs/config": "0.70.53",
57
57
  "better-dx": "^0.2.12",
58
- "@stacksjs/error-handling": "^0.70.44",
59
- "@stacksjs/logging": "^0.70.44",
60
- "@stacksjs/orm": "^0.70.44",
61
- "@stacksjs/path": "^0.70.44",
62
- "@stacksjs/storage": "^0.70.44",
63
- "@stacksjs/types": "^0.70.44",
64
- "@stacksjs/validation": "^0.70.44"
58
+ "@stacksjs/error-handling": "0.70.53",
59
+ "@stacksjs/logging": "0.70.53",
60
+ "@stacksjs/orm": "0.70.53",
61
+ "@stacksjs/path": "0.70.53",
62
+ "@stacksjs/storage": "0.70.53",
63
+ "@stacksjs/types": "0.70.53",
64
+ "@stacksjs/validation": "0.70.53"
65
65
  }
66
66
  }