@luckystack/core 0.6.7 → 0.7.1
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/CHANGELOG.md +148 -107
- package/CLAUDE.md +245 -245
- package/LICENSE +21 -21
- package/README.md +93 -93
- package/dist/{chunk-WFABOO6J.js → chunk-AFG7KKMS.js} +3 -3
- package/dist/chunk-AFG7KKMS.js.map +1 -0
- package/dist/{chunk-3AWP2YEA.js → chunk-J3UOWYT4.js} +2 -28
- package/dist/chunk-J3UOWYT4.js.map +1 -0
- package/dist/{chunk-LU3IPFTR.js → chunk-LTCDQU5Z.js} +1 -1
- package/dist/chunk-LTCDQU5Z.js.map +1 -0
- package/dist/chunk-R7IRJFVC.js +67 -0
- package/dist/chunk-R7IRJFVC.js.map +1 -0
- package/dist/client.d.ts +3 -2
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/config-DZCzBNwP.d.ts +122 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +31 -0
- package/dist/eslint/index.js.map +1 -1
- package/dist/index.d.ts +21 -149
- package/dist/index.js +66 -70
- package/dist/index.js.map +1 -1
- package/dist/pageRouteValidation-sJJCX-YV.d.ts +270 -0
- package/dist/{projectConfig-XL5VB76A.js → projectConfig-DBLSQ3NY.js} +2 -2
- package/dist/{sentrySetup-4NTWLN2A.js → sentrySetup-MF6JSVWA.js} +3 -3
- package/dist/sentrySetup-MF6JSVWA.js.map +1 -0
- package/dist/{pageRouteValidation-gqktrJci.d.ts → sessionTypes-Dzen_rPK.d.ts} +23 -265
- package/docs/app-bootstrap.md +418 -418
- package/docs/config-registry.md +348 -348
- package/docs/csrf-config.md +88 -88
- package/docs/error-tracker-registry.md +194 -174
- package/docs/hooks.md +298 -298
- package/docs/rate-limit-strategy.md +170 -170
- package/docs/redis-adapter.md +111 -111
- package/docs/session-types.md +180 -180
- package/docs/socket-bootstrap.md +366 -366
- package/package.json +6 -2
- package/dist/chunk-3AWP2YEA.js.map +0 -1
- package/dist/chunk-LU3IPFTR.js.map +0 -1
- package/dist/chunk-WFABOO6J.js.map +0 -1
- /package/dist/{projectConfig-XL5VB76A.js.map → config.js.map} +0 -0
- /package/dist/{sentrySetup-4NTWLN2A.js.map → projectConfig-DBLSQ3NY.js.map} +0 -0
package/docs/session-types.md
CHANGED
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
# Session Types
|
|
2
|
-
|
|
3
|
-
> Deep specs for the foundational session/auth types plus `validateRequest`, `isFalsy`, and `validateInputByType`. Source: `packages/core/src/sessionTypes.ts`, `validateRequest.ts`, `runtimeTypeValidation.ts`. Bijgewerkt: 2026-05-20.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
`BaseSessionLayout` is the structural contract every project session type extends. It lives in `@luckystack/core` (not `@luckystack/login`) so `validateRequest` can consume it without creating a circular dep: login depends on core for prisma/redis, so the session shape has to live in the lower layer. `@luckystack/login` re-exports the type for ergonomics — existing `import type { BaseSessionLayout } from '@luckystack/login'` paths keep working.
|
|
8
|
-
|
|
9
|
-
`AuthProps` defines the auth gate that every API/sync handler exports (`login: true/false` + an optional `additional[]` predicate list). `validateRequest({ auth, user })` is the runtime check that turns those declarations into a `ValidationResult`.
|
|
10
|
-
|
|
11
|
-
`validateInputByType` is the runtime input validator used by the api/sync handlers in dev. It lazy-loads `@luckystack/devkit` (the heavy TS compiler resolver) only when running in non-production. In production it short-circuits to success because the prod esbuild bundle marks devkit as external and the validator should not need TS to validate request bodies at request time.
|
|
12
|
-
|
|
13
|
-
## Types
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
export interface SessionLocation {
|
|
17
|
-
pathName: string;
|
|
18
|
-
searchParams?: Record<string, string>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface BaseSessionLayout {
|
|
22
|
-
id: string;
|
|
23
|
-
token: string;
|
|
24
|
-
email?: string | null;
|
|
25
|
-
name?: string | null;
|
|
26
|
-
avatar?: string | null;
|
|
27
|
-
avatarFallback?: string | null;
|
|
28
|
-
admin?: boolean | null;
|
|
29
|
-
language?: string | null;
|
|
30
|
-
location?: SessionLocation;
|
|
31
|
-
roomCodes?: string[];
|
|
32
|
-
csrfToken?: string;
|
|
33
|
-
lastLogin?: Date | string | null;
|
|
34
|
-
previousLogin?: Date | string | null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface AuthProps {
|
|
38
|
-
login: boolean;
|
|
39
|
-
additional?: {
|
|
40
|
-
key: keyof BaseSessionLayout;
|
|
41
|
-
value?: unknown;
|
|
42
|
-
type?: 'string' | 'number' | 'boolean';
|
|
43
|
-
nullish?: boolean;
|
|
44
|
-
mustBeFalsy?: boolean;
|
|
45
|
-
}[];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ValidationResult {
|
|
49
|
-
status: 'success' | 'error';
|
|
50
|
-
errorCode?: string;
|
|
51
|
-
errorParams?: { key: string; value: string | number | boolean }[];
|
|
52
|
-
httpStatus?: number;
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Extending `BaseSessionLayout`
|
|
57
|
-
|
|
58
|
-
Projects extend the type in their own `config.ts`:
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import type { BaseSessionLayout } from '@luckystack/core';
|
|
62
|
-
|
|
63
|
-
export interface SessionLayout extends BaseSessionLayout {
|
|
64
|
-
organizationId?: string;
|
|
65
|
-
role?: 'member' | 'admin' | 'owner';
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
The framework's `validateRequest` only inspects keys it knows from `BaseSessionLayout`; project-specific keys are checked via `additional[]`.
|
|
70
|
-
|
|
71
|
-
## API Reference
|
|
72
|
-
|
|
73
|
-
### `isFalsy(value: unknown): boolean`
|
|
74
|
-
|
|
75
|
-
**Signature:**
|
|
76
|
-
```typescript
|
|
77
|
-
export const isFalsy = (value: unknown): boolean
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**Behavior:** Returns `true` for `false`, `0`, `0n`, `''`, `null`, `undefined`, or `NaN`. Everything else is truthy.
|
|
81
|
-
|
|
82
|
-
**Note:** This is a stricter "falsy" than JavaScript's coercion-based check because it ALSO treats `NaN` as falsy. Used inside `validateRequest` for the `mustBeFalsy` predicate.
|
|
83
|
-
|
|
84
|
-
### `validateRequest({ auth, user }): ValidationResult`
|
|
85
|
-
|
|
86
|
-
**Signature:**
|
|
87
|
-
```typescript
|
|
88
|
-
export const validateRequest = ({
|
|
89
|
-
auth,
|
|
90
|
-
user,
|
|
91
|
-
}: {
|
|
92
|
-
auth: AuthProps;
|
|
93
|
-
user: BaseSessionLayout;
|
|
94
|
-
}): ValidationResult
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Behavior (in order):**
|
|
98
|
-
1. If `auth.additional` is undefined → returns `{ status: 'success' }` immediately. (The `login` flag is enforced by the surrounding handler, not by this function.)
|
|
99
|
-
2. For each `condition` in `auth.additional`:
|
|
100
|
-
- If `condition.key` is not in `user` → returns `{ status: 'error', errorCode: 'auth.invalidCondition', errorParams: [{ key: 'key', value: condition.key }], httpStatus: 500 }`. This is a developer setup error, not a runtime auth failure.
|
|
101
|
-
- Reads `val = user[condition.key]`. Computes `isNullish = val === null || val === undefined`.
|
|
102
|
-
- `nullish: true` requires `isNullish`; `nullish: false` requires NOT `isNullish`. Mismatch → forbid.
|
|
103
|
-
- `type: 'string' | 'number' | 'boolean'` requires `typeof val === <type>` (skipped when `val` is nullish). Mismatch → forbid.
|
|
104
|
-
- `'value' in condition` requires strict equality `val === condition.value`. The membership check distinguishes "no exact-value constraint" from `value: undefined` (the latter explicitly demands `val === undefined`).
|
|
105
|
-
- `mustBeFalsy: true` requires `isFalsy(val)`; `mustBeFalsy: false` requires `!isFalsy(val)`. Mismatch → forbid.
|
|
106
|
-
3. After all conditions pass → returns `{ status: 'success' }`.
|
|
107
|
-
|
|
108
|
-
**Forbid shape:**
|
|
109
|
-
```typescript
|
|
110
|
-
{ status: 'error', errorCode: 'auth.forbidden', errorParams: [{ key: 'key', value: <key> }], httpStatus: 403 }
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Constraint AND-semantics:** Multiple fields on a single `additional` entry are AND'd (every specified field must pass). Entries across the array are also AND'd.
|
|
114
|
-
|
|
115
|
-
**Example — admin-only endpoint:**
|
|
116
|
-
```typescript
|
|
117
|
-
export const auth: AuthProps = {
|
|
118
|
-
login: true,
|
|
119
|
-
additional: [{ key: 'admin', value: true, type: 'boolean' }],
|
|
120
|
-
};
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**Example — verified email required:**
|
|
124
|
-
```typescript
|
|
125
|
-
export const auth: AuthProps = {
|
|
126
|
-
login: true,
|
|
127
|
-
additional: [{ key: 'email', nullish: false, type: 'string' }],
|
|
128
|
-
};
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### `validateInputByType({ typeText, value, rootKey, filePath? }): Promise<ValidationResult>`
|
|
132
|
-
|
|
133
|
-
**Signature:**
|
|
134
|
-
```typescript
|
|
135
|
-
type ValidationResult =
|
|
136
|
-
| { status: 'success' }
|
|
137
|
-
| { status: 'error'; message: string };
|
|
138
|
-
|
|
139
|
-
export const validateInputByType = async ({
|
|
140
|
-
typeText,
|
|
141
|
-
value,
|
|
142
|
-
rootKey,
|
|
143
|
-
filePath,
|
|
144
|
-
}: {
|
|
145
|
-
typeText?: string;
|
|
146
|
-
value: unknown;
|
|
147
|
-
rootKey: string;
|
|
148
|
-
filePath?: string;
|
|
149
|
-
}): Promise<ValidationResult>
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**Behavior:**
|
|
153
|
-
1. When `typeText` is empty, whitespace, or literal `'any'` → returns `{ status: 'success' }`.
|
|
154
|
-
2. When `process.env.NODE_ENV === 'production'` → returns `{ status: 'success' }` (avoids loading the TS compiler in prod).
|
|
155
|
-
3. Otherwise lazy-imports `@luckystack/devkit` via an indirect module ID (`const devkitModuleId: string = '@luckystack/devkit'; await import(devkitModuleId)`) so `tsc` doesn't try to resolve devkit at build time (devkit depends on core — a literal import would be a build-time circular dep).
|
|
156
|
-
4. Calls `devkit.resolveRuntimeTypeText({ typeText, filePath })`. On `status: 'error'` returns `{ status: 'error', message: '<rootKey>: <message>' }`.
|
|
157
|
-
5. Otherwise calls the internal `validateType(resolvedType.typeText, value, rootKey)` recursive validator.
|
|
158
|
-
|
|
159
|
-
**Supported type forms (validateType):**
|
|
160
|
-
- Primitives: `string`, `number`, `boolean`, `true`, `false`, `null`, `undefined`, `Date` (accepts ISO string or `Date` instance).
|
|
161
|
-
- String/number literal types (single-quoted or double-quoted).
|
|
162
|
-
- Unions (`A | B`) — succeeds on first matching branch.
|
|
163
|
-
- Intersections (`A & B`) — requires all branches to succeed.
|
|
164
|
-
- Arrays (`T[]`) — checks every element.
|
|
165
|
-
- `Record<K, V>` — accepts any non-array object.
|
|
166
|
-
- Object literals `{ key?: T; key2: T2; [index: keyof IndexKey]: T }` — checks each named field, applies index signatures for unknown keys.
|
|
167
|
-
- `any` / `unknown` — always succeeds.
|
|
168
|
-
- Unresolved type aliases or generics return `{ status: 'error', message: '<path>: unresolved type <name>' }`.
|
|
169
|
-
|
|
170
|
-
**Edge cases:**
|
|
171
|
-
- The `__RUNTIME_UNRESOLVED__::<message>` sentinel from devkit surfaces as `{ status: 'error', message: '<path>: <message>' }` so a missing project type doesn't crash the request.
|
|
172
|
-
- Object validation rejects unknown keys when there is no matching index signature.
|
|
173
|
-
- Index-signature keys can be `string`, `number`, string-literal unions, or literal types; `number` keys require the key to match `/^-?\d+(\.\d+)?$/`.
|
|
174
|
-
|
|
175
|
-
## Related
|
|
176
|
-
|
|
177
|
-
- Function INDEX: `packages/core/CLAUDE.md`
|
|
178
|
-
- Architecture: `docs/ARCHITECTURE_SESSION.md`, `docs/ARCHITECTURE_AUTH.md`, `docs/ARCHITECTURE_API.md`
|
|
179
|
-
- README: `packages/core/README.md`
|
|
180
|
-
- Source: `packages/core/src/sessionTypes.ts`, `validateRequest.ts`, `runtimeTypeValidation.ts`
|
|
1
|
+
# Session Types
|
|
2
|
+
|
|
3
|
+
> Deep specs for the foundational session/auth types plus `validateRequest`, `isFalsy`, and `validateInputByType`. Source: `packages/core/src/sessionTypes.ts`, `validateRequest.ts`, `runtimeTypeValidation.ts`. Bijgewerkt: 2026-05-20.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`BaseSessionLayout` is the structural contract every project session type extends. It lives in `@luckystack/core` (not `@luckystack/login`) so `validateRequest` can consume it without creating a circular dep: login depends on core for prisma/redis, so the session shape has to live in the lower layer. `@luckystack/login` re-exports the type for ergonomics — existing `import type { BaseSessionLayout } from '@luckystack/login'` paths keep working.
|
|
8
|
+
|
|
9
|
+
`AuthProps` defines the auth gate that every API/sync handler exports (`login: true/false` + an optional `additional[]` predicate list). `validateRequest({ auth, user })` is the runtime check that turns those declarations into a `ValidationResult`.
|
|
10
|
+
|
|
11
|
+
`validateInputByType` is the runtime input validator used by the api/sync handlers in dev. It lazy-loads `@luckystack/devkit` (the heavy TS compiler resolver) only when running in non-production. In production it short-circuits to success because the prod esbuild bundle marks devkit as external and the validator should not need TS to validate request bodies at request time.
|
|
12
|
+
|
|
13
|
+
## Types
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
export interface SessionLocation {
|
|
17
|
+
pathName: string;
|
|
18
|
+
searchParams?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface BaseSessionLayout {
|
|
22
|
+
id: string;
|
|
23
|
+
token: string;
|
|
24
|
+
email?: string | null;
|
|
25
|
+
name?: string | null;
|
|
26
|
+
avatar?: string | null;
|
|
27
|
+
avatarFallback?: string | null;
|
|
28
|
+
admin?: boolean | null;
|
|
29
|
+
language?: string | null;
|
|
30
|
+
location?: SessionLocation;
|
|
31
|
+
roomCodes?: string[];
|
|
32
|
+
csrfToken?: string;
|
|
33
|
+
lastLogin?: Date | string | null;
|
|
34
|
+
previousLogin?: Date | string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AuthProps {
|
|
38
|
+
login: boolean;
|
|
39
|
+
additional?: {
|
|
40
|
+
key: keyof BaseSessionLayout;
|
|
41
|
+
value?: unknown;
|
|
42
|
+
type?: 'string' | 'number' | 'boolean';
|
|
43
|
+
nullish?: boolean;
|
|
44
|
+
mustBeFalsy?: boolean;
|
|
45
|
+
}[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ValidationResult {
|
|
49
|
+
status: 'success' | 'error';
|
|
50
|
+
errorCode?: string;
|
|
51
|
+
errorParams?: { key: string; value: string | number | boolean }[];
|
|
52
|
+
httpStatus?: number;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Extending `BaseSessionLayout`
|
|
57
|
+
|
|
58
|
+
Projects extend the type in their own `config.ts`:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import type { BaseSessionLayout } from '@luckystack/core';
|
|
62
|
+
|
|
63
|
+
export interface SessionLayout extends BaseSessionLayout {
|
|
64
|
+
organizationId?: string;
|
|
65
|
+
role?: 'member' | 'admin' | 'owner';
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The framework's `validateRequest` only inspects keys it knows from `BaseSessionLayout`; project-specific keys are checked via `additional[]`.
|
|
70
|
+
|
|
71
|
+
## API Reference
|
|
72
|
+
|
|
73
|
+
### `isFalsy(value: unknown): boolean`
|
|
74
|
+
|
|
75
|
+
**Signature:**
|
|
76
|
+
```typescript
|
|
77
|
+
export const isFalsy = (value: unknown): boolean
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Behavior:** Returns `true` for `false`, `0`, `0n`, `''`, `null`, `undefined`, or `NaN`. Everything else is truthy.
|
|
81
|
+
|
|
82
|
+
**Note:** This is a stricter "falsy" than JavaScript's coercion-based check because it ALSO treats `NaN` as falsy. Used inside `validateRequest` for the `mustBeFalsy` predicate.
|
|
83
|
+
|
|
84
|
+
### `validateRequest({ auth, user }): ValidationResult`
|
|
85
|
+
|
|
86
|
+
**Signature:**
|
|
87
|
+
```typescript
|
|
88
|
+
export const validateRequest = ({
|
|
89
|
+
auth,
|
|
90
|
+
user,
|
|
91
|
+
}: {
|
|
92
|
+
auth: AuthProps;
|
|
93
|
+
user: BaseSessionLayout;
|
|
94
|
+
}): ValidationResult
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Behavior (in order):**
|
|
98
|
+
1. If `auth.additional` is undefined → returns `{ status: 'success' }` immediately. (The `login` flag is enforced by the surrounding handler, not by this function.)
|
|
99
|
+
2. For each `condition` in `auth.additional`:
|
|
100
|
+
- If `condition.key` is not in `user` → returns `{ status: 'error', errorCode: 'auth.invalidCondition', errorParams: [{ key: 'key', value: condition.key }], httpStatus: 500 }`. This is a developer setup error, not a runtime auth failure.
|
|
101
|
+
- Reads `val = user[condition.key]`. Computes `isNullish = val === null || val === undefined`.
|
|
102
|
+
- `nullish: true` requires `isNullish`; `nullish: false` requires NOT `isNullish`. Mismatch → forbid.
|
|
103
|
+
- `type: 'string' | 'number' | 'boolean'` requires `typeof val === <type>` (skipped when `val` is nullish). Mismatch → forbid.
|
|
104
|
+
- `'value' in condition` requires strict equality `val === condition.value`. The membership check distinguishes "no exact-value constraint" from `value: undefined` (the latter explicitly demands `val === undefined`).
|
|
105
|
+
- `mustBeFalsy: true` requires `isFalsy(val)`; `mustBeFalsy: false` requires `!isFalsy(val)`. Mismatch → forbid.
|
|
106
|
+
3. After all conditions pass → returns `{ status: 'success' }`.
|
|
107
|
+
|
|
108
|
+
**Forbid shape:**
|
|
109
|
+
```typescript
|
|
110
|
+
{ status: 'error', errorCode: 'auth.forbidden', errorParams: [{ key: 'key', value: <key> }], httpStatus: 403 }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Constraint AND-semantics:** Multiple fields on a single `additional` entry are AND'd (every specified field must pass). Entries across the array are also AND'd.
|
|
114
|
+
|
|
115
|
+
**Example — admin-only endpoint:**
|
|
116
|
+
```typescript
|
|
117
|
+
export const auth: AuthProps = {
|
|
118
|
+
login: true,
|
|
119
|
+
additional: [{ key: 'admin', value: true, type: 'boolean' }],
|
|
120
|
+
};
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Example — verified email required:**
|
|
124
|
+
```typescript
|
|
125
|
+
export const auth: AuthProps = {
|
|
126
|
+
login: true,
|
|
127
|
+
additional: [{ key: 'email', nullish: false, type: 'string' }],
|
|
128
|
+
};
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `validateInputByType({ typeText, value, rootKey, filePath? }): Promise<ValidationResult>`
|
|
132
|
+
|
|
133
|
+
**Signature:**
|
|
134
|
+
```typescript
|
|
135
|
+
type ValidationResult =
|
|
136
|
+
| { status: 'success' }
|
|
137
|
+
| { status: 'error'; message: string };
|
|
138
|
+
|
|
139
|
+
export const validateInputByType = async ({
|
|
140
|
+
typeText,
|
|
141
|
+
value,
|
|
142
|
+
rootKey,
|
|
143
|
+
filePath,
|
|
144
|
+
}: {
|
|
145
|
+
typeText?: string;
|
|
146
|
+
value: unknown;
|
|
147
|
+
rootKey: string;
|
|
148
|
+
filePath?: string;
|
|
149
|
+
}): Promise<ValidationResult>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Behavior:**
|
|
153
|
+
1. When `typeText` is empty, whitespace, or literal `'any'` → returns `{ status: 'success' }`.
|
|
154
|
+
2. When `process.env.NODE_ENV === 'production'` → returns `{ status: 'success' }` (avoids loading the TS compiler in prod).
|
|
155
|
+
3. Otherwise lazy-imports `@luckystack/devkit` via an indirect module ID (`const devkitModuleId: string = '@luckystack/devkit'; await import(devkitModuleId)`) so `tsc` doesn't try to resolve devkit at build time (devkit depends on core — a literal import would be a build-time circular dep).
|
|
156
|
+
4. Calls `devkit.resolveRuntimeTypeText({ typeText, filePath })`. On `status: 'error'` returns `{ status: 'error', message: '<rootKey>: <message>' }`.
|
|
157
|
+
5. Otherwise calls the internal `validateType(resolvedType.typeText, value, rootKey)` recursive validator.
|
|
158
|
+
|
|
159
|
+
**Supported type forms (validateType):**
|
|
160
|
+
- Primitives: `string`, `number`, `boolean`, `true`, `false`, `null`, `undefined`, `Date` (accepts ISO string or `Date` instance).
|
|
161
|
+
- String/number literal types (single-quoted or double-quoted).
|
|
162
|
+
- Unions (`A | B`) — succeeds on first matching branch.
|
|
163
|
+
- Intersections (`A & B`) — requires all branches to succeed.
|
|
164
|
+
- Arrays (`T[]`) — checks every element.
|
|
165
|
+
- `Record<K, V>` — accepts any non-array object.
|
|
166
|
+
- Object literals `{ key?: T; key2: T2; [index: keyof IndexKey]: T }` — checks each named field, applies index signatures for unknown keys.
|
|
167
|
+
- `any` / `unknown` — always succeeds.
|
|
168
|
+
- Unresolved type aliases or generics return `{ status: 'error', message: '<path>: unresolved type <name>' }`.
|
|
169
|
+
|
|
170
|
+
**Edge cases:**
|
|
171
|
+
- The `__RUNTIME_UNRESOLVED__::<message>` sentinel from devkit surfaces as `{ status: 'error', message: '<path>: <message>' }` so a missing project type doesn't crash the request.
|
|
172
|
+
- Object validation rejects unknown keys when there is no matching index signature.
|
|
173
|
+
- Index-signature keys can be `string`, `number`, string-literal unions, or literal types; `number` keys require the key to match `/^-?\d+(\.\d+)?$/`.
|
|
174
|
+
|
|
175
|
+
## Related
|
|
176
|
+
|
|
177
|
+
- Function INDEX: `packages/core/CLAUDE.md`
|
|
178
|
+
- Architecture: `docs/ARCHITECTURE_SESSION.md`, `docs/ARCHITECTURE_AUTH.md`, `docs/ARCHITECTURE_API.md`
|
|
179
|
+
- README: `packages/core/README.md`
|
|
180
|
+
- Source: `packages/core/src/sessionTypes.ts`, `validateRequest.ts`, `runtimeTypeValidation.ts`
|